Skip to content

feat(tool): forward LLM tool-call id to MCP servers via _meta#158

Open
alenkacz wants to merge 1 commit into
mainfrom
av/mcp-forward-tool-call-id
Open

feat(tool): forward LLM tool-call id to MCP servers via _meta#158
alenkacz wants to merge 1 commit into
mainfrom
av/mcp-forward-tool-call-id

Conversation

@alenkacz

Copy link
Copy Markdown
Contributor

What

Threads the originating LLM tool-call id (llm.ToolRequestPart.ID) from the agent loop down to the MCP server on every tools/call:

  • tool.WithCallID / tool.CallIDFromContext — new context helpers (tool/callid.go).
  • Registry.Execute stashes req.ID on the execution context immediately before invoking a tool (tool/registry.go). Harmless for tools that ignore it.
  • The MCP client reads it and forwards it in the tools/call request _meta under the domain-namespaced key redpanda.com/llm-tool-call-id, exported as mcp.MetaKeyLLMToolCallID (tool/mcp/tools.go).

Why

Today, when an agent runs LLM and MCP calls through a gateway (e.g. Redpanda's aigw), the gateway's MCP tool-execution span carries the tool name but not the which-tool-call id, so it can't be tied back to the specific model turn that requested it — only grouped by conversation. Forwarding the id lets the gateway stamp gen_ai.tool.call.id on its span, enabling a precise tool-use ↔ invocation join (and lets the transcripts consumer dedup the gateway span against the in-agent execute_tool span by conversation + tool.call.id).

_meta is the MCP protocol's sanctioned extensibility slot: servers that don't understand the key ignore it, and the tool's own arguments are never touched. When the context carries no id (e.g. a direct ExecuteTool call outside the registry), the _meta key is simply omitted — no behavior change.

Tests

  • TestWithCallIDRoundTrip, TestRegistryExecuteExposesToolCallID — context plumbing.
  • TestExecuteToolForwardsLLMToolCallID — asserts the id reaches the server in _meta when present, and is absent when the context carries none.

Consumer side (separate, in cloudv2)

The matching aigw change (read params._meta → stamp gen_ai.tool.call.id on the MCP span) is a follow-up in the cloudv2 repo; this PR is the producer half and is a no-op until that lands.

🤖 Generated with Claude Code

The Registry now carries the originating LLM tool-call id
(llm.ToolRequestPart.ID) on the execution context (tool.WithCallID /
tool.CallIDFromContext), and the MCP client forwards it to the server in
the tools/call request _meta under the domain-namespaced key
"redpanda.com/llm-tool-call-id" (exported as mcp.MetaKeyLLMToolCallID).

This lets a gateway or observability layer read the id off the wire and
stamp gen_ai.tool.call.id on its tool-execution span, tying the tool call
back to the specific model turn that requested it. _meta is the MCP
protocol's sanctioned extensibility slot, so servers that don't understand
the key simply ignore it and the tool's own arguments are untouched.

No behavior change when the context carries no id: the _meta key is omitted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@alenkacz alenkacz marked this pull request as ready for review June 22, 2026 06:17
@alenkacz alenkacz requested review from birdayz and weeco June 22, 2026 06:18
@blacksmith-sh

blacksmith-sh Bot commented Jun 22, 2026

Copy link
Copy Markdown

Found 1 test failure on Blacksmith runners:

Failure

Test View Logs
github.com/redpanda-data/ai-sdk-go/tool/TestRegistry_WebfetchToolWithLLM_Integration View Logs

Fix in Cursor

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.

2 participants