Skip to content

fix(ai-mcp): send Mcp-Param headers on spec 2026 tool calls - #1871

Merged
AlemTuzlak merged 1 commit into
mainfrom
1794-ai-mcp-modern-tool-calls-omit-x-mcp-header-parameter-headers-in-090
Oct 22, 2026
Merged

AlemTuzlak merged 1 commit into
mainfrom
1794-ai-mcp-modern-tool-calls-omit-x-mcp-header-parameter-headers-in-090

Conversation

@tombeckenham

@tombeckenham tombeckenham commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Spec 2026-07-28 tool calls from @tanstack/ai-mcp did not send the Mcp-Param-* headers that a tool's x-mcp-header schema fields ask for. Strict servers, such as GitHub's remote MCP server, reject those calls with HTTP 400 and error -32020. This PR sends spec 2026 tool calls through the SDK Client.callTool with the listed input schema, so the SDK adds the headers.

🎯 Changes

  • callMcpTool sends spec 2026 calls, and the input_required retry, through client.callTool. It passes toolDefinition: { name, inputSchema }.
  • tools(), tools([defs]), and MCPClient.callTool() pass the listed tool's inputSchema to the call.
  • Deleted the raw transport.send path (rawRequest, the transport tap, and the _meta envelope copy). The SDK now does timeout, abort, and progress for these calls.
  • Patch changeset for @tanstack/ai-mcp. No docs change: no page describes this behavior.

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested code changes locally with pnpm run test:pr, or these tests do not apply to this pull request.
  • I fully understand the code in this pull request, including any code generated with AI assistance.
  • Docs: I updated docs/ for this change, or this change is not user-facing.
  • Changeset: I added a changeset (pnpm changeset), or this PR does not change a published package.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

Root cause

Issue. On a spec 2026-07-28 connection, a tool with an x-mcp-header argument fails on strict servers. The Mcp-Param-* header is missing, so the server returns 400 / -32020. Tools without those arguments work, so a basic smoke test does not show the bug.
Cause. callMcpTool in packages/ai-mcp/src/tools.ts sent spec 2026 tools/call through rawRequest, which calls transport.send(message) directly. Only Client.callTool builds the Mcp-Param-* headers from the tool schema. The raw path skipped it. The SDK does not export its header builder.
Fix. Spec 2026 calls go through Client.callTool with toolDefinition: { name, inputSchema }. The SDK reads x-mcp-header from that schema and sends the headers. The definition has no outputSchema, so the SDK skips its output check. That check would reject an input_required result.

Possible alternatives

  • Fill the SDK tool cache with listTools() on spec 2026, and call callTool with no toolDefinition. This is the spec 2025 path. The SDK output check then runs and rejects an input_required result from a tool with an outputSchema.
  • Keep rawRequest and pass headers to transport.send(message, { headers }). The SDK does not export buildMcpParamHeaders, so this copies the SDK header and encoding rules. More code, and it can drift from the SDK.
  • Ask the SDK to export the header builder. Upstream change. This does not fix current releases.

Testing

Commands run

  • Agent-written repro (a strict fetch fixture, createMCPClient → tools() → execute({ region: 'eu' }), assert the Mcp-Param-Region header):
    • Clean main (3e84606af): fails.
      × repro #1794: spec 2026 tools/call sends Mcp-Param-Region
      AssertionError: expected [ null ] to deeply equal [ 'eu' ]
      
    • This branch: passes.
      Test Files  1 passed (1)
           Tests  1 passed (1)
      
  • tsc --noEmit on packages/ai-mcp: no new errors in the changed files.
  • Not run locally: pnpm test:pr and the full ai-mcp unit suite. CI runs them.

Manual test

  1. On main, connect createMCPClient to a spec 2026 server whose tool declares region: { type: 'string', 'x-mcp-header': 'Region' }. Call the tool. The request has no Mcp-Param-Region header.
  2. On this branch, repeat step 1. The request has Mcp-Param-Region: eu, and a strict server accepts it.

How this PR makes testing easy

  • tests/client.test.ts → spec 2026 Mcp-Param headers: a strict fixture server rejects a missing or wrong header. It covers tools() and MCPClient.callTool().
  • tests/tools.test.ts → callMcpTool — spec 2026 calls: checks the toolDefinition and the signal passed to the SDK, and the inputResponses / requestState retry.

Linked issues

Closes #1794

Risk / rollback

Low to medium. Spec 2026 tool calls now use the SDK request path, not a raw send. Timeout, abort, and progress now come from the SDK. In a browser, the SDK does not send Mcp-Param-* headers (the SDK decides this). Rollback: revert this PR.

Summary by CodeRabbit

  • New Features
    • Tool calls now include Mcp-Param-* headers for arguments designated by the server, improving compatibility with the 2026-07-28 MCP specification.
    • When a tool requests additional input, the integration can retry once with the supplied response; repeated requests are reported as errors.
  • Bug Fixes
    • Modern-protocol tool calls now use the SDK’s request handling, including its timeout, cancellation, and progress support.

Spec 2026 tools/call went through a raw transport send, so the SDK never
added the Mcp-Param-* headers from the x-mcp-header schema fields. Route
those calls (and the input_required retry) through Client.callTool with
the listed input schema, and delete the raw request path.

Closes #1794
@changeset-bot

changeset-bot Bot commented Oct 22, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 52732c8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@tanstack/ai-mcp Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Source mode: Auto
  • Repository: TanStack/ai
  • Branch: main
  • File: .coderabbit.yaml
  • Commit: 3e84606
  • Layers: Repository YAML
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 598faea5-de87-4d62-a1b6-3e8485922bc8
📥 Commits

Reviewing files that changed from the base of the PR and between 3e84606 and 52732c8.

📒 Files selected for processing: 5
  • .changeset/ai-mcp-param-headers.md
  • packages/ai-mcp/src/client.ts
  • packages/ai-mcp/src/tools.ts
  • packages/ai-mcp/tests/client.test.ts
  • packages/ai-mcp/tests/tools.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 5 remain after this review.

📝 Walkthrough

Modern MCP tool calls now use the SDK’s callTool method with the tool’s input schema. The adapter continues to handle legacy task-required calls and input-required responses. Tests check schema-derived HTTP headers through both tools() and callTool().

Changes: AI MCP tool calls

Layer / File(s) Summary
Pass input schemas into tool execution
packages/ai-mcp/src/client.ts, packages/ai-mcp/src/tools.ts, .changeset/ai-mcp-param-headers.md
Explicit binding and auto-discovery pass tool input schemas into execution. callTool() passes the discovered schema to callMcpTool. The changeset describes the Mcp-Param-* header behavior.
Use SDK calls for modern tools
packages/ai-mcp/src/tools.ts
Modern calls use the SDK’s client.callTool with the tool definition and schema. Legacy task-required calls still use tools/call. The custom raw transport request implementation and its transport-tapping state are removed.
Validate SDK calls and parameter headers
packages/ai-mcp/tests/tools.test.ts, packages/ai-mcp/tests/client.test.ts
Tool tests check SDK call parameters, abort signals, and input-required retries. HTTP fixture tests check matching Mcp-Param-Region headers through tools() and callTool().

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Medium

Sequence Diagram(s)

sequenceDiagram
  participant ToolExecutor
  participant callMcpTool
  participant MCPClient
  participant StreamableHTTPTransport
  ToolExecutor->>callMcpTool: invoke tool with arguments and input schema
  callMcpTool->>MCPClient: callTool with tool definition
  MCPClient->>StreamableHTTPTransport: send tools/call with schema-derived headers
Loading

Suggested reviewers: alemtuzlak

Merge Risk: ⚪ Minimal · up to 52732

No actionable issue remains from the reviewed change. It is ready for normal checks before merging.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 10 functions across 4 files. (1 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: sending Mcp-Param headers for spec 2026 tool calls.
Description check Passed The description explains the root cause, implementation, testing, release impact, risk, rollback, and linked issue. It includes the required sections and a changeset. The local test checklist remains …
Linked Issues check Passed Issue [#1794] requires schema-driven Mcp-Param-* headers for modern tool calls. callMcpTool now uses Client.callTool and passes toolDefinition.inputSchema. tools() and MCPClient.callTool()…
Out of Scope Changes check Passed The reviewed changes stay within issue [#1794]. They replace the raw modern request path, preserve input-required retry behavior, pass schemas through tool construction, and add regression coverage. S…
Full details: Description check

Explanation

The description explains the root cause, implementation, testing, release impact, risk, rollback, and linked issue. It includes the required sections and a changeset. The local test checklist remains unchecked, but the testing section clearly states which tests were not run and that CI runs them.

Full details: Linked Issues check

Explanation

Issue [#1794] requires schema-driven Mcp-Param-* headers for modern tool calls. callMcpTool now uses Client.callTool and passes toolDefinition.inputSchema. tools() and MCPClient.callTool() pass the discovered schema, and explicit tool definitions pass their schema. The new strict StreamableHTTPClientTransport tests verify Mcp-Param-Region: eu for both paths. The input-required retry uses the same SDK call path.

Full details: Out of Scope Changes check

Explanation

The reviewed changes stay within issue [#1794]. They replace the raw modern request path, preserve input-required retry behavior, pass schemas through tool construction, and add regression coverage. SDK timeout, abort, and progress handling follow from using Client.callTool. The changeset supports the published package fix. No unrelated source, test, or documentation changes are present.

Full details: Docstring Coverage

Explanation

Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 10 functions across 4 files. (1 skipped: 1 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nx-cloud

nx-cloud Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 52732c8

Command Status Duration Result
nx affected --targets=test:sherif,test:knip,tes... ✅ Succeeded 2m 4s View ↗
nx run-many --targets=test:coverage --projects=... ✅ Succeeded <1s View ↗
nx run-many --targets=build --exclude=examples/... ✅ Succeeded 4s View ↗

☁️ Nx Cloud last updated this comment at 2026-10-22 07:53:42 UTC

@github-actions

Copy link
Copy Markdown
Contributor

Coverage

✅ Coverage held across 1 compared package(s).

Each package is measured twice in this job — on this PR and on its merge-base with main — and compared. A metric fails only when it is at or above 60% on the merge-base and under 60% on this PR. Other drops are listed but do not fail. Packages your PR doesn't affect are not measured and not listed.

package statements branches functions lines
ai-mcp 94.82% (-0.24) 90.32% (-0.25) 97.77% (-0.06) 96.77% (-0.12) ok

@pkg-pr-new

pkg-pr-new Bot commented Oct 22, 2026

Copy link
Copy Markdown

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/@tanstack/ai@1871

@tanstack/ai-acp

npm i https://pkg.pr.new/@tanstack/ai-acp@1871

@tanstack/ai-angular

npm i https://pkg.pr.new/@tanstack/ai-angular@1871

@tanstack/ai-anthropic

npm i https://pkg.pr.new/@tanstack/ai-anthropic@1871

@tanstack/ai-bedrock

npm i https://pkg.pr.new/@tanstack/ai-bedrock@1871

@tanstack/ai-byteplus

npm i https://pkg.pr.new/@tanstack/ai-byteplus@1871

@tanstack/ai-claude-code

npm i https://pkg.pr.new/@tanstack/ai-claude-code@1871

@tanstack/ai-client

npm i https://pkg.pr.new/@tanstack/ai-client@1871

@tanstack/ai-cloudflare

npm i https://pkg.pr.new/@tanstack/ai-cloudflare@1871

@tanstack/ai-code-mode

npm i https://pkg.pr.new/@tanstack/ai-code-mode@1871

@tanstack/ai-code-mode-snippets

npm i https://pkg.pr.new/@tanstack/ai-code-mode-snippets@1871

@tanstack/ai-codex

npm i https://pkg.pr.new/@tanstack/ai-codex@1871

@tanstack/ai-cohere

npm i https://pkg.pr.new/@tanstack/ai-cohere@1871

@tanstack/ai-compaction

npm i https://pkg.pr.new/@tanstack/ai-compaction@1871

@tanstack/ai-computer-local

npm i https://pkg.pr.new/@tanstack/ai-computer-local@1871

@tanstack/ai-computer-playwright

npm i https://pkg.pr.new/@tanstack/ai-computer-playwright@1871

@tanstack/ai-computer-sandbox

npm i https://pkg.pr.new/@tanstack/ai-computer-sandbox@1871

@tanstack/ai-computer-use

npm i https://pkg.pr.new/@tanstack/ai-computer-use@1871

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/@tanstack/ai-devtools-core@1871

@tanstack/ai-durable-stream

npm i https://pkg.pr.new/@tanstack/ai-durable-stream@1871

@tanstack/ai-elevenlabs

npm i https://pkg.pr.new/@tanstack/ai-elevenlabs@1871

@tanstack/ai-event-client

npm i https://pkg.pr.new/@tanstack/ai-event-client@1871

@tanstack/ai-fal

npm i https://pkg.pr.new/@tanstack/ai-fal@1871

@tanstack/ai-gemini

npm i https://pkg.pr.new/@tanstack/ai-gemini@1871

@tanstack/ai-grok

npm i https://pkg.pr.new/@tanstack/ai-grok@1871

@tanstack/ai-grok-build

npm i https://pkg.pr.new/@tanstack/ai-grok-build@1871

@tanstack/ai-groq

npm i https://pkg.pr.new/@tanstack/ai-groq@1871

@tanstack/ai-isolate-cloudflare

npm i https://pkg.pr.new/@tanstack/ai-isolate-cloudflare@1871

@tanstack/ai-isolate-daytona

npm i https://pkg.pr.new/@tanstack/ai-isolate-daytona@1871

@tanstack/ai-isolate-e2b

npm i https://pkg.pr.new/@tanstack/ai-isolate-e2b@1871

@tanstack/ai-isolate-node

npm i https://pkg.pr.new/@tanstack/ai-isolate-node@1871

@tanstack/ai-isolate-quickjs

npm i https://pkg.pr.new/@tanstack/ai-isolate-quickjs@1871

@tanstack/ai-isolate-quickjs-bun

npm i https://pkg.pr.new/@tanstack/ai-isolate-quickjs-bun@1871

@tanstack/ai-llmgateway

npm i https://pkg.pr.new/@tanstack/ai-llmgateway@1871

@tanstack/ai-lovable

npm i https://pkg.pr.new/@tanstack/ai-lovable@1871

@tanstack/ai-mcp

npm i https://pkg.pr.new/@tanstack/ai-mcp@1871

@tanstack/ai-memory

npm i https://pkg.pr.new/@tanstack/ai-memory@1871

@tanstack/ai-mistral

npm i https://pkg.pr.new/@tanstack/ai-mistral@1871

@tanstack/ai-octane

npm i https://pkg.pr.new/@tanstack/ai-octane@1871

@tanstack/ai-ollama

npm i https://pkg.pr.new/@tanstack/ai-ollama@1871

@tanstack/ai-ollaya

npm i https://pkg.pr.new/@tanstack/ai-ollaya@1871

@tanstack/ai-openai

npm i https://pkg.pr.new/@tanstack/ai-openai@1871

@tanstack/ai-opencode

npm i https://pkg.pr.new/@tanstack/ai-opencode@1871

@tanstack/ai-openrouter

npm i https://pkg.pr.new/@tanstack/ai-openrouter@1871

@tanstack/ai-perplexity

npm i https://pkg.pr.new/@tanstack/ai-perplexity@1871

@tanstack/ai-persistence

npm i https://pkg.pr.new/@tanstack/ai-persistence@1871

@tanstack/ai-preact

npm i https://pkg.pr.new/@tanstack/ai-preact@1871

@tanstack/ai-react

npm i https://pkg.pr.new/@tanstack/ai-react@1871

@tanstack/ai-reactor

npm i https://pkg.pr.new/@tanstack/ai-reactor@1871

@tanstack/ai-remix

npm i https://pkg.pr.new/@tanstack/ai-remix@1871

@tanstack/ai-sandbox

npm i https://pkg.pr.new/@tanstack/ai-sandbox@1871

@tanstack/ai-sandbox-blaxel

npm i https://pkg.pr.new/@tanstack/ai-sandbox-blaxel@1871

@tanstack/ai-sandbox-boxd

npm i https://pkg.pr.new/@tanstack/ai-sandbox-boxd@1871

@tanstack/ai-sandbox-cloudflare

npm i https://pkg.pr.new/@tanstack/ai-sandbox-cloudflare@1871

@tanstack/ai-sandbox-daytona

npm i https://pkg.pr.new/@tanstack/ai-sandbox-daytona@1871

@tanstack/ai-sandbox-docker

npm i https://pkg.pr.new/@tanstack/ai-sandbox-docker@1871

@tanstack/ai-sandbox-e2b

npm i https://pkg.pr.new/@tanstack/ai-sandbox-e2b@1871

@tanstack/ai-sandbox-local-process

npm i https://pkg.pr.new/@tanstack/ai-sandbox-local-process@1871

@tanstack/ai-sandbox-railway

npm i https://pkg.pr.new/@tanstack/ai-sandbox-railway@1871

@tanstack/ai-sandbox-sprites

npm i https://pkg.pr.new/@tanstack/ai-sandbox-sprites@1871

@tanstack/ai-sandbox-upstash-box

npm i https://pkg.pr.new/@tanstack/ai-sandbox-upstash-box@1871

@tanstack/ai-sandbox-vercel

npm i https://pkg.pr.new/@tanstack/ai-sandbox-vercel@1871

@tanstack/ai-skills

npm i https://pkg.pr.new/@tanstack/ai-skills@1871

@tanstack/ai-solid

npm i https://pkg.pr.new/@tanstack/ai-solid@1871

@tanstack/ai-svelte

npm i https://pkg.pr.new/@tanstack/ai-svelte@1871

@tanstack/ai-typesafe

npm i https://pkg.pr.new/@tanstack/ai-typesafe@1871

@tanstack/ai-utils

npm i https://pkg.pr.new/@tanstack/ai-utils@1871

@tanstack/ai-vercel-gateway

npm i https://pkg.pr.new/@tanstack/ai-vercel-gateway@1871

@tanstack/ai-vertex

npm i https://pkg.pr.new/@tanstack/ai-vertex@1871

@tanstack/ai-vue

npm i https://pkg.pr.new/@tanstack/ai-vue@1871

@tanstack/ai-worldlabs

npm i https://pkg.pr.new/@tanstack/ai-worldlabs@1871

@tanstack/openai-base

npm i https://pkg.pr.new/@tanstack/openai-base@1871

@tanstack/preact-ai-devtools

npm i https://pkg.pr.new/@tanstack/preact-ai-devtools@1871

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/@tanstack/react-ai-devtools@1871

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/@tanstack/solid-ai-devtools@1871

@tanstack/svelte-ai-devtools

npm i https://pkg.pr.new/@tanstack/svelte-ai-devtools@1871

commit: 52732c8

@AlemTuzlak
AlemTuzlak merged commit d980ce1 into main Oct 22, 2026
15 checks passed
@AlemTuzlak
AlemTuzlak deleted the 1794-ai-mcp-modern-tool-calls-omit-x-mcp-header-parameter-headers-in-090 branch October 22, 2026 08:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ai-mcp: modern tool calls omit x-mcp-header parameter headers in 0.9.0

2 participants