[BUG] MCP tool results silently replaced with full file content when input parameter is named `file_path`

Resolved 💬 3 comments Opened Apr 6, 2026 by reverinudog Closed Apr 9, 2026

Summary

Claude Code silently discards MCP tool results and replaces them with the raw file content when the MCP tool's input schema contains a parameter named file_path. This makes it impossible for MCP servers to return transformed/compressed file representations when using that parameter name.

Environment

  • Claude Code version: 2.1.92
  • OS: Windows 11 (10.0.22631)
  • MCP SDK: @modelcontextprotocol/sdk 1.x (stdio transport)

Reproduction

MCP server setup

A custom MCP tool that reads a file via an external CLI (rtk) and returns a structurally compressed version (function signatures only, bodies omitted):

server.registerTool("rtk_read", {
  title: "RTK Read",
  description: "Structural file overview with 56-80% token reduction",
  inputSchema: {
    file_path: z.string().describe("Absolute path to the file"),  // ← THIS NAME TRIGGERS THE BUG
  },
  annotations: {
    readOnlyHint: true,
    openWorldHint: false,
  },
}, async ({ file_path }) => {
  const output = await runRtk(["read", "--level", "aggressive", file_path]);
  return { content: [{ type: "text", text: output }] };
});

Steps to reproduce

  1. Register the MCP tool above in .mcp.json
  2. Ask Claude to call rtk_read on a 340-line JavaScript file
  3. Observe that Claude receives 340 lines (full file) instead of the MCP server's 54-line compressed output

Verification at each layer

The MCP server's output was verified at every layer to confirm the server is NOT the source of the problem:

| Layer | Output (src/game/dice.js, 340 lines) |
|-------|------|
| RTK CLI direct (rtk read --level aggressive) | 55 lines / 3,263 chars ✅ |
| Node.js execFile calling RTK | 56 lines / 3,121 chars ✅ |
| MCP JSON-RPC direct test (stdin/stdout) | 56 lines / 3,121 chars ✅ |
| Claude Code → MCP tool call | 340 lines / full file ❌ |

Root cause isolation

Three variables were tested independently:

| Parameter name | readOnlyHint | Result |
|---|---|---|
| file_path | true | ❌ 340 lines (full file — overridden) |
| target | absent | ✅ 54 lines (compressed — correct) |
| target | true | ✅ 54 lines (compressed — correct) |

Conclusion: The file_path parameter name is the sole trigger. readOnlyHint has no effect.

Fix / Workaround

Renaming the input parameter from file_path to any other name (e.g. target, source, filepath) prevents the override. This is a viable workaround but is surprising and undocumented behavior.

Expected behavior

Claude Code should pass MCP tool results through to the model unmodified, regardless of parameter naming. If Claude Code needs to augment MCP results (e.g., for caching or context), it should do so additively (appending metadata) rather than replacing the tool's output.

Actual behavior

When an MCP tool has an input parameter named file_path, Claude Code:

  1. Calls the MCP tool (server receives the call and returns compressed output)
  2. Silently discards the MCP server's response
  3. Reads the file at the file_path value using its own built-in file reader
  4. Returns the full file content to the model as if it were the MCP tool's output

This happens transparently — there is no error, warning, or log indicating the replacement.

Impact

  • Token waste: MCP tools designed for token compression (structural overviews, summarization) are rendered useless. In our case, 84% compression savings are completely lost.
  • Silent failure: Tool authors have no indication their output is being replaced. Debugging requires testing at every layer to isolate where the substitution occurs.
  • Fragile contract: MCP tool behavior depends on parameter naming conventions that are undocumented. Renaming file_path to target changes behavior with no schema or spec basis.
  • Broader risk: Other parameter names matching built-in tools (e.g., command, pattern, query) may trigger similar undocumented overrides.

Questions for the team

  1. Is this intentional behavior (e.g., for file content caching/deduplication)?
  2. If intentional, can it be made opt-out via an annotation or tool configuration?
  3. Are there other parameter names that trigger similar overrides?
  4. Related: #32105 (updatedToolOutput for PostToolUse hooks) — could this mechanism serve as a workaround for MCP tools?

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗