Skip to content

Python: Add models for the MCP server SDKs (mcp, fastmcp) - #22703

Open
Alex-Hofer wants to merge 2 commits into
github:mainfrom
Alex-Hofer:python-mcp-models
Open

Alex-Hofer wants to merge 2 commits into
github:mainfrom
Alex-Hofer:python-mcp-models

Conversation

@Alex-Hofer

@Alex-Hofer Alex-Hofer commented Sep 30, 2026 •

Copy link
Copy Markdown

Implements the proposal in #22702. The observation there about py/xxe, py/xml-bomb and
py/nosql-injection taking only RemoteFlowSource has since been fixed by #22822.

MCP servers expose tools to LLM agents. Every parameter of a tool, resource or prompt handler is
chosen by the client, that is by a model that prompt injection can steer, or by anyone who can
reach the server over HTTP. This PR models that input for the two Python SDKs as Models-as-Data
next to the existing Stdlib.model.yml sources: the handler parameters as sources of the opt-in
ai threat model (#22810), the transport headers and the bearer token as sources of the remote
threat model.

Covered:

  • mcp 1.x FastMCP and 2.x MCPServer: tool(), resource(), prompt(), add_tool(fn).
  • The low-level Server: the 1.x decorators call_tool(), read_resource(), get_prompt(), and
    the 2.x constructor handlers on_call_tool, on_read_resource, on_get_prompt, where
    params.arguments / params.uri is the source.
  • fastmcp 2.x to 4.x: tool and prompt bare, called or as a plain call, resource(),
    add_tool, add_prompt, Tool.from_function, fastmcp.tools.tool, the transport headers via
    get_http_headers() and get_http_request().headers.
  • Both SDKs: the bearer token of the Authorization header, in verify_token of TokenVerifier
    (and AuthProvider) subclasses and from get_access_token().token.
  • typeModel rows for the re-exported import paths of the classes.

Tests: library-tests/frameworks/mcp and library-tests/frameworks/fastmcp use MaDTest
(mad-source__ai, mad-source__remote); every row has a handler with an inline expectation, and classes with the
same method name but no SDK base class have none.

Evidence: with these rows (as a model pack) CodeQL 2.27.1 finds 13 of 28 real, publicly disclosed
MCP server vulnerabilities in mcp-vulnbench
instead of 1; on the half of the cases that was held out while writing the rows, 9 of 16 instead
of 0, at 0.27 to 1.09 alarms per KLOC. Known limits of this form (handlers behind project
decorators, Pydantic-typed parameters, headers read through the Context object) are documented
there and in the issue. The measurement used the rows as remote, which is on by default; with
ai the same sources apply once that threat model is enabled.

Handler parameters are produced by a language model, so they are sources of the opt-in ai threat model added in github#22810. HTTP headers and bearer tokens come from the HTTP client and stay sources of the remote threat model.
@Alex-Hofer

Copy link
Copy Markdown
Author

Following the suggestion in #22749, the handler parameters are now sources of the opt-in ai threat model (#22810); the HTTP headers and bearer tokens stay remote, since they come from the HTTP client. Rebased on main. With #22822 merged, the note in the description about py/xxe, py/xml-bomb and py/nosql-injection is settled: these queries now see sources from data extensions.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants