Every MCP server I audit, I end up running the same twenty curl commands. Fingerprint the endpoint. Pull the three well-known OAuth documents and diff them. Enumerate the tool list. Poke the error responses. Test the CORS headers. Over a dozen servers the pattern got old, so I packaged the methodology into one CLI.
mcp-recon is an open-source scanner that runs ten generic checks against a Model Context Protocol server and flags behavior that matches classes of publicly disclosed bugs. It does not declare a server “safe” or “unsafe.” It reports observations, and you interpret them in context.
- Language: Python 3.10+
- Install:
pipx install mcp-recon - GitHub: github.com/jashidsany/mcp-recon
- PyPI: pypi.org/project/mcp-recon
Why I Built This
Three pieces of my own prior research kept recurring as the “starter move” on every MCP audit:
-
OAuth scope enforcement bypass on a production MCP server (Zomato MCP writeup). The initial tell was that two
.well-known/documents on the same server advertised differentscopes_supportedlists. Inconsistent discovery docs are a smell. -
DNS rebinding in
mcp-server-fetch(GHSA filed, CVE pending). The server made two outbound HTTP calls per tool invocation, each with an independent DNS resolution, and no IP pinning between them. Any MCP tool that takes a URL and does N > 1 outbound requests has this shape. -
Permission-prompt misrepresentation in Claude Code’s MCP trust model (five findings at jashidsany.com/security-research/ai-security). What the user saw in the consent dialog did not match what the server was about to execute. If a malicious MCP server can embed zero-width or bidi-override characters in tool names, the same class extends.
Each of those audits started with the same reconnaissance: fingerprint the server, enumerate tools, pull the well-known docs, read the error responses. After the fourth time, writing scripts for each one felt silly. A single tool that runs all the checks in order and flags the patterns I know to look for is a strict time-saver for me, and if other researchers find it useful, even better.
I also deliberately named it mcp-recon, not mcp-scan. Invariant Labs / Snyk ship a product called mcp-scan that is a more mature, commercial MCP security tool. mcp-recon is explicitly a lightweight reconnaissance scanner, not a competitor to their auditing platform. Picking a different name avoids inviting the wrong comparison.
What It Checks
Ten checks, all generic. None of them hard-code fingerprints of past bugs. Each one describes what it observed rather than declaring a verdict.
| Check | What it looks for | Related bug class |
|---|---|---|
fingerprint |
MCP protocol version, tools/list, resources/list, prompts/list | baseline recon |
transport-hygiene |
HTTP (not HTTPS), unexpected success on GET/OPTIONS, Server: headers |
transport confidentiality (CWE-319), routing misconfig |
cors-policy |
Wildcard-with-credentials, echoed-Origin-with-credentials, null allowance |
browser-based cross-origin abuse (CWE-942) |
auth-header-hygiene |
Infra hints, filesystem paths, stack traces inside WWW-Authenticate |
information disclosure in auth errors (CWE-209) |
discovery-consistency |
Inconsistent scopes_supported / grant_types_supported across .well-known documents |
scope enforcement bypass |
error-verbosity |
Stack traces, filesystem paths, secret-shaped tokens in error responses | information disclosure (CWE-209, CWE-200) |
tool-description-anomalies |
Zero-width, control, or bidi-override characters in tool names and descriptions | permission-prompt misrepresentation |
multi-request-pattern |
Tools whose inputs accept URLs; flags N > 1 outbound-request risk | DNS rebinding TOCTOU |
undocumented-capabilities |
MCP methods returning results outside advertised capabilities | debug endpoint exposure |
scope-binding |
Can a token with one scope call tools that advertised different scopes? Requires --token. |
authorization bypass (CWE-863, CWE-285) |
Every flagged observation ships with a plain-English summary, the concrete evidence that triggered it, a suggested manual follow-up command, and references to the public CVEs or writeups that exemplify the class. The tool surfaces signals. The operator makes the judgment call.
Usage
Basic scan
$ mcp-recon scan https://example.com/api/mcp
target: https://example.com/api/mcp
started: 2026-04-22T19:30:15+00:00
schema: 1.0 tool: 0.1.0
duration: 117 ms
+ fingerprint (ran, 40 ms)
protocol_version: 2024-11-05
server_info: {'name': 'ExampleMCP', 'version': '1.0'}
capabilities: ['tools', 'resources']
tools: <list[2]>
+ discovery-consistency (ran, 17 ms)
paths_found: ['.well-known/oauth-authorization-server', '.well-known/openid-configuration']
! discovery documents disagree on scopes_supported [medium]
Two OAuth/OIDC discovery documents advertise different sets of
supported scopes. This is a smell: it has preceded scope enforcement
bypass bugs in other MCP and OAuth deployments.
follow-up:
curl https://example.com/.well-known/oauth-authorization-server
curl https://example.com/.well-known/openid-configuration
# compare scopes_supported manually
+ multi-request-pattern (ran, 0 ms)
tools_inspected: 2
! tools with URL-shaped input parameters [low]
One or more advertised tools accept URL-like parameters. If the
server performs more than one outbound request per invocation,
each request resolves DNS independently by default. A DNS-rebinding
attacker can split the two lookups across different IPs.
... (8 more checks) ...
summary
observations flagged: 2
checks run: 9
checks skipped: 1
checks errored: 0
exit code: 1
Two observations flagged, exit code 1. Worth reviewing before moving on. Neither is a vulnerability claim; both are patterns worth investigating manually.
Three output formats
mcp-recon scan <url> # human-readable (default)
mcp-recon scan <url> --output json # machine-readable, versioned schema
mcp-recon scan <url> --output markdown # paste into bug-bounty reports
The JSON format carries a schema_version field so downstream tools can pin to a version.
Proxy + token
# route through Burp or mitmproxy
mcp-recon scan <url> --proxy http://127.0.0.1:8080
# scope-binding probe on an OAuth-gated server
MCP_RECON_TOKEN=<access-token> mcp-recon scan <url>
The token is redacted from artifact files by default. Pass --include-secrets only when you need raw evidence and understand the risk.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Clean. All checks ran, no observations flagged. |
| 1 | One or more observations flagged for review. |
| 2 | Scan error (target unreachable, protocol error). |
| 3 | Invalid arguments. |
Exit codes make CI integration straightforward. Drop mcp-recon scan into a release pipeline for your own MCP server and fail the build on exit 1.
Artifacts
Every scan writes a directory of raw JSON artifacts to ./mcp-recon-artifacts/<target>_<timestamp>/:
report.json the structured result (schema-versioned)
exchanges.json every HTTP request/response pair, Authorization/Cookie
headers redacted by default
Directory is created chmod 700, files chmod 600. Secrets are redacted unless you pass --include-secrets.
The artifacts directory is the nmap -oA pattern: you can re-analyze without re-scanning, diff scans over time, and keep evidence for bug-bounty submissions without needing to replay the scan.
Honest Limits
Worth stating up front so nobody mistakes the tool for something it isn’t:
- A clean scan does not mean a server is secure. The tool checks a finite set of known-issue patterns. It does not attempt comprehensive vulnerability coverage.
- Observations are not vulnerabilities. They are signals that match classes of previously disclosed bugs. Always validate manually before reporting.
- It is read-only at the MCP protocol layer. The scanner never calls state-changing tools (no
update_cart, nosend_message, no file writes). Worst case, it makes atools/callwith empty arguments during the scope-binding probe, which servers generally handle as a validation error rather than a mutation. - Rate limiting is on by default (100ms between requests). Use
--aggressiveonly against infrastructure you own. - Do not run it against servers you don’t have permission to test. Unauthorized scanning may violate computer misuse laws in your jurisdiction. The tool is a scanner, and scanners belong to the operator who runs them responsibly.
The README opens with the same caution. I’d rather be clear up front than have someone run a clean scan, assume their server is audited, and get popped.
Security Considerations
The tool makes outbound HTTP requests to user-provided endpoints. The intended trust boundaries:
- The user is trusted. They choose what to scan.
- The target MCP server is not trusted. Its responses are attacker-controlled from the tool’s perspective.
- The artifacts directory is trusted (created
chmod 700).
The main hardening choices:
- No subprocess or shell execution. Nothing the server returns can become a command on the operator’s machine.
- Authorization / Cookie / API-key headers redacted in saved artifacts by default.
- No pickle, no arbitrary deserialization. All structured data is JSON.
- Dependencies pinned to major versions (
httpx,typer,rich). - CI runs with
permissions: contents: read. A compromised third-party action cannot push commits or mint releases. - PyPI publishing uses Trusted Publishing (OIDC). No long-lived API tokens stored anywhere.
Full threat model in SECURITY.md on the repo.
Install
pipx install mcp-recon
mcp-recon --version
Plain pip install also works inside a venv. Requires Python 3.10 or later. Runs on Linux, macOS, Windows.
What Comes Next
The MVP is ten generic checks. A few things I want to add as the MCP ecosystem matures:
- MCP stdio transport. Right now the tool is HTTP-only. Scanning a locally launched stdio MCP server would be useful for developer workflows.
- Known-bad signature database. A small curated list of published advisories against specific MCP server projects + versions. I deliberately left this out of the MVP to avoid maintenance burden, but the ecosystem is picking up enough CVEs that it’ll start paying off.
- Differential scanning. Compare two scans and flag only new findings. Handy for CI jobs that re-scan staging environments on every deploy.
If you use it and find a rough edge or a bug class I should cover, open an issue. Feedback from other researchers is what shapes where this goes.
Try It
github.com/jashidsany/mcp-recon · pypi.org/project/mcp-recon
pipx install mcp-recon
mcp-recon scan https://your-mcp-server.example/api/mcp
Related research this tool distilled from:
- Zomato MCP OAuth scope not enforced
- Claude Code MCP trust-model findings
mcp-server-fetchDNS rebinding (GHSA filed, CVE pending)
Jashid Sany - github.com/jashidsany