← All notes

Field notes

Why Internal Details Never Belong in MCP Tool Docstrings

In Model Context Protocol tools, docstrings are parsed directly into the model tool schema—meaning internal implementation details leak straight into LLM context and user-facing clients.

2 min read
  • ai
  • mcp
  • fastmcp
  • security

The incident: When a docstring leaks into model context

While recently developing Model Context Protocol (MCP) servers and tools, an internal implementation note was accidentally included inside a tool function's docstring. The comment referenced internal database naming, upstream microservice topology, and private routing conventions.

Shortly after deployment, the LLM began citing those internal system details in user conversations. Because the model reads the tool schema description verbatim to decide when and how to invoke functions, private implementation details were immediately exposed to end users.

How MCP converts docstrings into schemas

Frameworks like FastMCP automatically reflect Python function signatures, type annotations, and docstrings into standard JSON Schema definitions exposed to MCP clients:

  # ❌ INCORRECT: Leaking backend internals in the tool docstring
  @mcp.tool()
  def search_temperature_records(city: str) -> str:
      """
      Queries cluster-db-primary via read-replica on port 5433.
      Note: Temporary workaround until the v2 migration completes.
      Uses redis key cache:temp:v1.
      """
      return run_query(city)

To an LLM, that docstring is not a developer comment—it is operational context. The model uses it to understand the tool's purpose, but it can also echo those private hostnames, database names, and internal workarounds directly into responses. Furthermore, MCP clients (such as Claude Desktop or custom agent interfaces) often display tool descriptions verbatim in their inspector panels.

The clean approach: Separate interface contract from implementation

Docstrings on MCP tools must serve strictly as the public interface contract for the agent:

  # ✅ CORRECT: Concise, user-safe description focused strictly on function and parameters
  @mcp.tool()
  def search_temperature_records(city: str) -> str:
      """
      Search recorded surface temperature and heat index metrics for a given city.

      Args:
          city: The name of the city to retrieve temperature data for.
      """
      # Internal notes, connection details, and migration context
      # belong strictly in inline source comments, which are never exposed.
      return run_query(city)

Guidelines for safe MCP tool authoring

  1. Describe only what the tool does: Explain the function capabilities, expected parameters, and returned structure in clear, functional language.
  2. Never mention internal infrastructure: Do not reference private URLs, internal cluster names, microservice architecture, API keys, or database schemas.
  3. Use inline comments for developer notes: Code-level comments (#) stay in the codebase and are never compiled into the MCP tool definition.
  4. Treat docstrings as user-facing prompts: Review tool docstrings with the same scrutiny given to system prompts and public documentation.

Related work

For more context on agent backends and tool contracts, see the FortyGuard case study.