Use with caution. Address findings before production.
Scanned 5/13/2026, 5:06:02 AMยทCached resultยทDeep Scanยท91 rulesยทHow we decide โ
AIVSS Score
Medium
Severity Breakdown
0
critical
2
high
12
medium
0
low
MCP Server Information
Findings
This package received a C grade with a safety score of 78/100 and carries 2 high-severity prompt injection vulnerabilities alongside 12 medium-severity server configuration issues. The prompt injection risks could allow attackers to manipulate model behavior through crafted inputs, while the configuration weaknesses may expose the server to unauthorized access or misuse. You should address these vulnerabilities before deployment, particularly the high-severity prompt injection flaws.
AIPer-finding remediation generated by bedrock-claude-haiku-4-5 โ 14 of 14 findings. Click any finding to read.
No known CVEs found for this package or its dependencies.
Scan Details
Done
Sign in to save scan history and re-scan automatically on new commits.
Building your own MCP server?
Same rules, same LLM judges, same grade. Private scans stay isolated to your account and never appear in the public registry. Required for code your team hasnโt shipped yet.
14 of 14 findings
14 findings
Tool 'add' shadows generic reserved tool name; lacks server-specific prefix.
RemediationAI
The problem is that the tool named 'add' collides with a reserved MCP tool name, creating ambiguity and potential namespace conflicts. Rename the tool by changing the `@app.tool()` decorator or function name to include a server-specific prefix, such as `@app.tool(name='myserver_add')` or renaming the function to `add_numbers()` with a corresponding tool name. This eliminates the collision and ensures the tool is uniquely identifiable within the MCP ecosystem. Verify the fix by running `mcp list-tools` or inspecting the MCP manifest to confirm the new prefixed name appears without conflicts.
Tool 'add' uses module-level FastMCP app instance created at import time without consulting caller identity or per-request credentials.
RemediationAI
The problem is that the module-level FastMCP app instance is created at import time without validating the caller's identity or checking per-request credentials, allowing any code that imports the module to invoke tools with full privileges. Refactor to create the app instance inside a request handler or factory function that accepts and validates caller credentials, then pass those credentials to tool execution; alternatively, use FastMCP's built-in authentication hooks via `@app.before_tool_call()` to enforce per-request credential validation. This ensures each tool invocation is authenticated and authorized before execution. Verify by adding a test that attempts to call a tool without valid credentials and confirms it is rejected.
Service binds to 0.0.0.0 โ all network interfaces. For MCP servers that only need to talk to a single parent process, bind to 127.0.0.1 (or a Unix domain socket) instead.
Evidence
| 33 | if __name__ == "__main__": |
| 34 | import uvicorn |
| 35 | |
| 36 | uvicorn.run(app, host="0.0.0.0", port=8000) |
RemediationAI
The problem is that binding to 0.0.0.0 exposes the MCP server to all network interfaces, allowing remote attackers to connect if the port is accessible. Change the uvicorn.run() call in sample-servers/tool-example/app.py from `host="0.0.0.0"` to `host="127.0.0.1"` to bind only to localhost. This restricts access to the local machine only, eliminating remote network exposure. Verify the fix by running the server and confirming that `netstat -tlnp` or `ss -tlnp` shows the service listening only on 127.0.0.1:8000.
Service binds to 0.0.0.0 โ all network interfaces. For MCP servers that only need to talk to a single parent process, bind to 127.0.0.1 (or a Unix domain socket) instead.
Evidence
| 27 | EXPOSE 8000 |
| 28 | |
| 29 | # Start FastAPI via Uvicorn on HTTP |
| 30 | CMD ["fastmcp", "run", "src/main.py:app", "--transport", "streamable-http", "--port", "8000", "--host", "0.0.0.0"] |
RemediationAI
The problem is that the Dockerfile and fastmcp command expose the MCP server to all network interfaces via 0.0.0.0, creating an unnecessary attack surface. Change the `--host 0.0.0.0` flag in the CMD to `--host 127.0.0.1` in sample-servers/mcp-example/Dockerfile. This restricts the server to localhost-only access, eliminating remote network exposure. Verify by building the image, running the container, and confirming via `docker exec` that the service binds only to 127.0.0.1.
Service binds to 0.0.0.0 โ all network interfaces. For MCP servers that only need to talk to a single parent process, bind to 127.0.0.1 (or a Unix domain socket) instead.
Evidence
| 48 | EXPOSE 8000 |
| 49 | |
| 50 | ENTRYPOINT ["dumb-init", "--"] |
| 51 | CMD ["fastmcp", "run", "src/main.py:app", "--transport", "streamable-http", "--port", "8000", "--host", "0.0.0.0"] |
RemediationAI
The problem is that the Dockerfile and fastmcp command expose the MCP server to all network interfaces via 0.0.0.0, creating an unnecessary attack surface. Change the `--host 0.0.0.0` flag in the CMD to `--host 127.0.0.1` in sample-servers/mcp-proxy/Dockerfile. This restricts the server to localhost-only access, eliminating remote network exposure. Verify by building the image, running the container, and confirming via `docker exec` that the service binds only to 127.0.0.1.
MCP tool input schema exposes an unconstrained string/any field with a risky name (command/query/sql/code/script/url/path/expr/ eval). Any caller can pass arbitrary values, which typically widens the tool's blast radius well beyond its intent. Narrow the schema with `.enum()`, `.regex()`, `.max()`, `Literal[...]`, Pydantic `Field(max_length=..., pattern=...)`, or a JSON Schema `enum` / `pattern` / `maxLength`.
Evidence
| 656 | "description": "HTTP method for the tool execution", |
| 657 | "enum": ["GET", "POST", "PUT", "DELETE", "PATCH"] |
| 658 | }, |
| 659 | "path": { |
| 660 | "type": "string", |
| 661 | "description": "Path for the tool execution endpoint" |
| 662 | } |
| 663 | } |
| 664 | }, |
| 665 | "inputSchema": { |
RemediationAI
The problem is that the 'path' field in the MCP tool schema is an unconstrained string, allowing callers to inject arbitrary paths that could lead to path traversal or unintended endpoint access. Add a `pattern` constraint or `maxLength` to the path field in the openapi/mcp-gateway.openapi.json schema, for example: `"path": {"type": "string", "pattern": "^/[a-zA-Z0-9/_-]+$", "maxLength": 256}`. This restricts the path to a safe whitelist of characters and length, preventing injection attacks. Verify by testing the tool with a malicious path like `../../../etc/passwd` and confirming it is rejected by schema validation.
MCP manifest declares tools but no authentication field is present (none of: auth, authorization, bearer, oauth, mtls, apiKey, api_key, basic, token, authToken). Absence is a weak signal โ confirm whether the server relies on network-layer or host-level auth, or declare the real mechanism explicitly so reviewers can audit it.
Evidence
| 1 | from fastmcp import FastMCP |
| 2 | import os, sys, shlex |
| 3 | import validators |
| 4 | |
| 5 | MAX_ARGS = 64 |
| 6 | MAX_ARG_LEN = 512 |
| 7 | |
| 8 | def bad(msg: str): |
| 9 | print(f"[shim] {msg}", file=sys.stderr) |
| 10 | sys.exit(1) |
| 11 | |
| 12 | def safe(arg: str) -> bool: |
| 13 | return ( |
| 14 | isinstance(arg, str) |
| 15 | and 0 < len(arg) <= MAX_ARG_LEN |
| 16 | and "\x00" not in arg |
| 17 | and "\n" not in arg |
| 18 | and "\r" not in arg |
| 19 | ) |
| 20 | |
| 21 | proxy_url = os.environ.get("MCP_PROXY_URL") |
| 22 | if proxy_url: |
| 23 | if not validators.url(proxy_url): |
| 24 | bad("Invali |
RemediationAI
The problem is that the MCP manifest in sample-servers/mcp-proxy/src/main.py declares tools but lacks any authentication or authorization field, making it unclear how the server protects against unauthorized access. Add an explicit `auth` or `authorization` field to the FastMCP app metadata, such as `app.meta(auth="bearer")` or a docstring comment documenting the authentication mechanism (e.g., network-layer auth, host-level auth, or token-based auth). This makes the security model explicit and auditable. Verify by reviewing the generated MCP manifest and confirming the authentication mechanism is clearly documented.
MCP Example Server declares or may emit notifications/tools/list_changed but tools/list response lacks per-tool content-bound integrity fields (version, etag, digest, sha256, hash), enabling undetected tool swaps at runtime.
RemediationAI
The problem is that the MCP server may emit tool list changes via notifications but the tools/list response lacks integrity fields (version, etag, digest, sha256) to detect unauthorized tool swaps at runtime. Add a version or etag field to each tool in the tools/list response, for example by modifying the tool definition to include `"version": "1.0"` or `"etag": "sha256:abc123..."` in the JSON schema. This allows clients to detect when a tool has been modified or replaced. Verify by capturing the tools/list response, modifying a tool definition, and confirming the client can detect the change via the etag or version mismatch.
File registers a state-changing HTTP route (POST / PUT / PATCH / DELETE) but no CSRF protection middleware is applied anywhere in the file. If the server uses cookie-based session auth, a cross-site request from any origin can hit this route while the user's cookies ride along. Apply CSRF middleware: - Express: `csurf` / `csrf-csrf` / `lusca.csrf()` - FastAPI: `fastapi-csrf-protect` - Flask: `flask_wtf.csrf.CSRFProtect` Or, if the route is a JSON API authenticated by bearer tokens (no co
Evidence
| 1 | from fastapi import FastAPI, HTTPException |
| 2 | from pydantic import BaseModel |
| 3 | |
| 4 | app = FastAPI() |
| 5 | |
| 6 | |
| 7 | class WeatherRequest(BaseModel): |
| 8 | location: str | None = None |
| 9 | unit: str | None = None |
| 10 | |
| 11 | |
| 12 | @app.post("/score") |
| 13 | async def get_weather(payload: WeatherRequest): |
| 14 | """Mocked weather endpoint matching the MCP tool contract.""" |
| 15 | location = payload.location |
| 16 | unit = (payload.unit or "fahrenheit").lower() |
| 17 | if not location: |
| 18 | raise HTTPException(status_code=400, detail="Missing 'location' fie |
RemediationAI
The problem is that the POST /score endpoint in sample-servers/tool-example/app.py accepts state-changing requests without CSRF protection, allowing cross-site requests to invoke the endpoint if the client uses cookie-based session auth. Add CSRF middleware by installing `fastapi-csrf-protect` and decorating the route with `@csrf_protect.validate_csrf` or applying middleware globally via `app.add_middleware(CSRFProtectMiddleware)`. This ensures only same-origin requests can invoke state-changing endpoints. Verify by writing a test that sends a POST request from a different origin without a valid CSRF token and confirms it is rejected with a 403 status.
GitHub Actions `uses:` reference is not pinned to a 40-character commit SHA. Tags (`@v4`) and branches (`@main`) are mutable โ a compromised maintainer or a tag rewrite can substitute malicious code into your CI pipeline silently. Pin to a SHA: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab`. For readability, include the version as a trailing comment: `# v4.1.1`. Tools like `pinact` / `ratchet` automate this. Allowed unpinned forms (excluded by the rule): - Local actions `.
Evidence
| 27 | password: ${{ secrets.GITHUB_TOKEN }} |
| 28 | |
| 29 | - name: Setup .NET SDK |
| 30 | uses: actions/setup-dotnet@v4 |
| 31 | with: |
| 32 | dotnet-version: '8.x' |
RemediationAI
The problem is that the GitHub Actions workflow in .github/workflows/image.yml uses `actions/setup-dotnet@v4`, which is a mutable tag that could be rewritten by a compromised maintainer to inject malicious code. Replace `uses: actions/setup-dotnet@v4` with a pinned commit SHA, for example `uses: actions/setup-dotnet@3f331fb0418290d103602cdc18bc29b8558147ec # v4.0.0`. This ensures the exact version of the action is used and cannot be silently replaced. Verify by running `git log --oneline actions/setup-dotnet` to find the correct SHA and confirming the workflow file is updated.
GitHub Actions `uses:` reference is not pinned to a 40-character commit SHA. Tags (`@v4`) and branches (`@main`) are mutable โ a compromised maintainer or a tag rewrite can substitute malicious code into your CI pipeline silently. Pin to a SHA: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab`. For readability, include the version as a trailing comment: `# v4.1.1`. Tools like `pinact` / `ratchet` automate this. Allowed unpinned forms (excluded by the rule): - Local actions `.
Evidence
| 17 | steps: |
| 18 | - name: Checkout source |
| 19 | uses: actions/checkout@v3 |
| 20 | |
| 21 | - name: Log in to the Container registry |
| 22 | uses: docker/login-action@v3 |
RemediationAI
The problem is that the GitHub Actions workflow in .github/workflows/image.yml uses `actions/checkout@v3` and `docker/login-action@v3`, which are mutable tags that could be rewritten to inject malicious code. Replace `uses: actions/checkout@v3` with `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.6.0` and `uses: docker/login-action@v3` with the corresponding pinned SHA. This ensures the exact versions are used and cannot be silently replaced. Verify by running `git log --oneline` on each action repository to find the correct SHAs and confirming the workflow file is updated.
GitHub Actions `uses:` reference is not pinned to a 40-character commit SHA. Tags (`@v4`) and branches (`@main`) are mutable โ a compromised maintainer or a tag rewrite can substitute malicious code into your CI pipeline silently. Pin to a SHA: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab`. For readability, include the version as a trailing comment: `# v4.1.1`. Tools like `pinact` / `ratchet` automate this. Allowed unpinned forms (excluded by the rule): - Local actions `.
Evidence
| 17 | steps: |
| 18 | - name: Checkout source |
| 19 | uses: actions/checkout@v3 |
| 20 | |
| 21 | - name: Setup .NET SDK |
| 22 | uses: actions/setup-dotnet@v4 |
RemediationAI
The problem is that the GitHub Actions workflow in .github/workflows/main.yml uses `actions/checkout@v3` and `actions/setup-dotnet@v4`, which are mutable tags that could be rewritten to inject malicious code. Replace `uses: actions/checkout@v3` with `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.6.0` and `uses: actions/setup-dotnet@v4` with the corresponding pinned SHA. This ensures the exact versions are used and cannot be silently replaced. Verify by running `git log --oneline` on each action repository to find the correct SHAs and confirming the workflow file is updated.
GitHub Actions `uses:` reference is not pinned to a 40-character commit SHA. Tags (`@v4`) and branches (`@main`) are mutable โ a compromised maintainer or a tag rewrite can substitute malicious code into your CI pipeline silently. Pin to a SHA: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab`. For readability, include the version as a trailing comment: `# v4.1.1`. Tools like `pinact` / `ratchet` automate this. Allowed unpinned forms (excluded by the rule): - Local actions `.
Evidence
| 20 | uses: actions/checkout@v3 |
| 21 | |
| 22 | - name: Log in to the Container registry |
| 23 | uses: docker/login-action@v3 |
| 24 | with: |
| 25 | registry: ghcr.io |
| 26 | username: ${{ github.actor }} |
RemediationAI
The problem is that the GitHub Actions workflow in .github/workflows/image.yml uses `actions/checkout@v3` and `docker/login-action@v3`, which are mutable tags that could be rewritten to inject malicious code. Replace `uses: actions/checkout@v3` with `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.6.0` and `uses: docker/login-action@v3` with the corresponding pinned SHA. This ensures the exact versions are used and cannot be silently replaced. Verify by running `git log --oneline` on each action repository to find the correct SHAs and confirming the workflow file is updated.
GitHub Actions `uses:` reference is not pinned to a 40-character commit SHA. Tags (`@v4`) and branches (`@main`) are mutable โ a compromised maintainer or a tag rewrite can substitute malicious code into your CI pipeline silently. Pin to a SHA: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab`. For readability, include the version as a trailing comment: `# v4.1.1`. Tools like `pinact` / `ratchet` automate this. Allowed unpinned forms (excluded by the rule): - Local actions `.
Evidence
| 20 | uses: actions/checkout@v3 |
| 21 | |
| 22 | - name: Setup .NET SDK |
| 23 | uses: actions/setup-dotnet@v4 |
| 24 | with: |
| 25 | dotnet-version: '8.x' |
RemediationAI
The problem is that the GitHub Actions workflow in .github/workflows/main.yml uses `actions/checkout@v3` and `actions/setup-dotnet@v4`, which are mutable tags that could be rewritten to inject malicious code. Replace `uses: actions/checkout@v3` with `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.6.0` and `uses: actions/setup-dotnet@v4` with the corresponding pinned SHA. This ensures the exact versions are used and cannot be silently replaced. Verify by running `git log --oneline` on each action repository to find the correct SHAs and confirming the workflow file is updated.