feat(mcp): expose run command as an MCP tool - #1039
Conversation
There was a problem hiding this comment.
Pull request overview
This PR exposes ScanAPI’s existing run execution flow through an MCP server tool, while refactoring scan execution to return a reusable, structured “execution context” that can be shared between the CLI reporter and MCP responses.
Changes:
- Extracted execution context creation into
scanapi.context.build_context()and reused it fromReporter._build_context(). - Refactored
scan()intorun_scan()+ CLI wrapper to decouplesys.exit()from execution for long-running MCP usage. - Added an MCP server (
scanapi/mcp/server.py) with aruntool and introducedmcp>=1.0.0as a dependency.
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| uv.lock | Locks new dependency set including mcp and transitive requirements. |
| pyproject.toml | Adds mcp>=1.0.0 to core dependencies. |
| scanapi/context.py | Introduces shared context builder for scan execution results/summary. |
| scanapi/scan.py | Adds run_scan() returning context; keeps CLI scan() for legacy behavior. |
| scanapi/reporter.py | Switches reporter context building to reuse scanapi.context.build_context(). |
| scanapi/mcp/server.py | Implements MCP server and run tool wrapping existing scan flow. |
| scanapi/mcp/init.py | Declares MCP package module. |
| tests/unit/test_reporter.py | Updates reporter unit tests for the new context shape. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "session": { | ||
| "errors": session.errors, | ||
| "failures": session.failures, | ||
| "successes": session.successes, | ||
| "exit_code": session.exit_code, | ||
| }, |
| "session": { | ||
| "errors": 0, | ||
| "failures": 0, | ||
| "successes": 0, | ||
| "exit_code": ExitCode.OK, | ||
| }, |
| # Save preferences to the global settings | ||
| settings.save_preferences( | ||
| spec_path=spec_path, | ||
| output_path=output_path, | ||
| no_report=no_report, | ||
| config_path=config_path, | ||
| template=template, | ||
| open_browser=browser, | ||
| ) |
| context = run_scan() | ||
|
|
||
| # Generate report if needed | ||
| if not no_report: | ||
| from scanapi.scan import _write | ||
|
|
||
| _write(context["results"]) | ||
|
|
||
| return {"summary": context["summary"], "results": context["results"]} |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 6 changed files in this pull request and generated 1 comment.
Suppressed comments (2)
scanapi/scan.py:73
- The docstring has a grammatical error ("tries to scans"). This is user-facing documentation and should be corrected.
def scan():
"""Caller function that tries to scans the file and write the report."""
results = run_scan()
scanapi/mcp/server.py:6
- This new MCP server module is excluded from coverage (
# pragma: no cover) and there are currently no unit tests covering the MCPruntool behavior (argument mapping, error handling, and JSON-serializable output). Given the repo has extensive unit tests elsewhere, adding at least a small unit test suite for this integration would help prevent regressions.
from mcp.server.fastmcp import FastMCP
from scanapi.scan import run_scan # pragma: no cover
from scanapi.settings import settings # pragma: no cover
from scanapi.cli import configure_logging # pragma: no cover
| results = run_scan() | ||
|
|
||
| # Generate report if needed | ||
| if not no_report: | ||
| from scanapi.scan import _write | ||
|
|
||
| _write(results) | ||
|
|
||
| from scanapi.session import session | ||
|
|
||
| total_tests = session.successes + session.failures + session.errors | ||
|
|
||
| return { | ||
| "summary": { | ||
| "requests": len(results), | ||
| "tests": total_tests, | ||
| "passed": session.successes, | ||
| "failed": session.failures, | ||
| "success": session.succeed, | ||
| }, | ||
| "results": results, | ||
| } |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 6 changed files in this pull request and generated 1 comment.
Suppressed comments (4)
scanapi/mcp/server.py:66
- The MCP tool currently returns raw
results, which can containrequests.Responseobjects (and other non-JSON-serializable values). FastMCP transports typically JSON-encode tool results, so this can fail at runtime.
Return a JSON-safe representation (e.g., the serialized_results computed above) instead of the raw objects.
"requests": len(results),
"tests": total_tests,
"passed": session.successes,
"failed": session.failures,
scanapi/scan.py:85
- The
write_output()docstring mentions--no_reportand--browserwithout backticks, but the actual CLI flags are--no-reportand--browser(seescanapi/cli.py). Updating this avoids misleading documentation.
"""When the user passed the `--no-report` flag: prints the test results to
the console output.
When the user did not pass the `--no_report flag`: writes the results on a
report file and opens it using a browser, if the --browser flag is present.
scanapi/scan.py:72
- Grammar: the scan() docstring reads "tries to scans". This should be corrected for clarity.
def scan():
"""Caller function that tries to scans the file and write the report."""
scanapi/mcp/server.py:15
- There is no unit test coverage for the new MCP
runtool. The repo has extensive unit tests for the CLI scan flow, so adding a small unit test that callsscanapi.mcp.server.run()directly (mockingrun_scan/write_outputand validating the returned summary/result shape) would help prevent regressions.
@mcp.tool()
| configure_logging(log_level) | ||
|
|
||
| # Save preferences to the global settings | ||
| settings.save_preferences( | ||
| spec_path=spec_path, | ||
| output_path=output_path, | ||
| no_report=no_report, | ||
| config_path=config_path, | ||
| template=template, | ||
| open_browser=browser, | ||
| ) | ||
|
|
||
| results = run_scan() | ||
|
|
||
| # Generate report if needed | ||
| if not no_report: | ||
| write_output(results) | ||
|
|
There was a problem hiding this comment.
Thank you for supporting ScanAPI, and congratulations on your first contribution! A project committer will shortly review your contribution.
In the mean time, if you haven't had a chance please skim over the First Pull Request Guide which all pull requests must adhere to.
We hope to see you around!
|
📌 Esta mensagem está tanto em português quanto em inglês (mais abaixo) — assim todo mundo consegue acompanhar! 🇧🇷 Português 👋 Olá! Este PR está obsoleto porque ficou aberto por 30 dias sem atividade. Remova o rótulo de stale ou comente, caso contrário ele será fechado em 15 dias. 🇬🇧 English Hey there! 👋 This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 15 days. |
Fixes #939
Proposed Changes
scanapi.context.build_contextso the execution output payload can be properly shared between the CLI reporter and MCP server.scan()inscanapi.scanintorun_scan(). This decouples thesys.exit()logic from execution, enabling the long-running MCP server to execute test suites natively.mcp>=1.0.0as a dependency.scanapi/mcp/server.pywith aruntool matching the current CLI behavior!Tests
tests/unit/test_reporter.py). All 391 unit tests are passing!