Configuration & Environment
The server — or the tokens it holds — can do much more than the tools it exposes actually need, so a single bug becomes a full compromise.
This is the least-privilege violation pattern. A tool that only needs to read issues has been given a GitHub admin token. A tool that only needs `SELECT` has a `root` database user. The tool implementation may be fine; the blast radius when it is not is unnecessarily large.
Because MCP servers are glued together from whatever credentials the user happened to have lying around, they routinely get more access than their tool list requires. Nobody runs a permissions audit when wiring a new server. The result is that "call MCP server X to do Y" often gives X access to do A through Z as well.
# Single OAuth scope: repo (full read/write on all repos) |
# Tool only needs to open issues. |
GH = github.Client(token=os.environ["GH_TOKEN"], scopes=["repo"]) |
@server.tool() |
def open_issue(repo: str, title: str, body: str) -> int: |
return GH.create_issue(repo, title, body).id |
# Request only the scopes the tool set actually needs. |
GH = github.Client( |
token=os.environ["GH_TOKEN"], |
scopes=["public_repo", "read:user"], # write only to public repos |
) |
We diff the capabilities of advertised tools against the scopes/permissions the server requests at startup. Excess scopes are surfaced with a specific remediation ("drop `delete_repo`; none of your tools need it").
See the full threat catalog for every documented detection.
MCPSafe runs this check — and every other rule in the catalog — on any MCP server you paste in.
Scan now