github / github/copilot-sdk

Expose LLM tool-call id to MCP tool invocations

Abierto
#1,332 1 comentario 0 reacciones 0 asignados Ver en GitHub
enhancement
Lenguaje dominante
Java
Estrellas
10.5k
Forks
1.5k
Merge medio
1 d 11 h
PR fusionados (30 d)
128

Descripción

# Expose LLM tool-call id to MCP tool invocations

## Background

When the SDK invokes an MCP tool, our host process needs to know the LLM-side tool call id that triggered the call, and (ideally) propagate it to the MCP server. We use this id for:

- Correlating MCP server-side audit logs with LLM turns
- Distributed tracing across the LLM → CLI → MCP server boundary
- Reconciling outputs from multiple parallel tool calls in the same turn

Today, the id is already tracked inside the CLI — it surfaces in:

- `ToolInvocation.toolCallId` (local tools registered via `tools[]`)
- `tool.execution_start.data.toolCallId` (session event stream, read-only)
- `kind:"mcp"` PermissionRequest's `toolCallId` (only fires on permission path)

But none of these reaches the MCP server, and none lets the host inject the id into the outbound `tools/call` request.

## Asks

Either of the following would unblock us. **Option A** is preferred because it requires no host-side code.

### Option A — CLI auto-injects `_meta` on outbound MCP `tools/call`

When the CLI sends a `tools/call` JSON-RPC request to an MCP server, include the id in the standard MCP `_meta` request field:

```json
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "search",
"arguments": { ... },
"_meta": {
"copilot/toolCallId": ""
}
}
}
```

`_meta` is part of the MCP spec for out-of-band request metadata. It sits alongside `arguments`, isn't part of `inputSchema`, isn't visible to the LLM, and strict `additionalProperties: false` schemas don't reject it. Servers that don't read `_meta` are unaffected — zero breaking change.

Optionally include `sessionId`, `parentToolCallId`, and W3C `traceparent` to match the richness already present in `tool.execution_start.data`.

### Option B — Add `toolCallId` to `PreToolUseHookInput`

Currently:

```ts
export interface PreToolUseHookInput extends BaseHookInput {
toolName: string;
toolArgs: unknown;
}
```

Proposed (additive, back-compatible):

```ts
export interface PreToolUseHookInput extends BaseHookInput {
toolName: string;
toolArgs: unknown;
toolCallId?: string; // LLM-side tool call id
}
```

With the id available in the hook, hosts can use `PreToolUseHookOutput.modifiedArgs` to inject the id into MCP `tools/call` arguments themselves. This is strictly less clean than Option A (pollutes `arguments`, requires schema cooperation), but it gives hosts a self-service escape hatch.

## Today's workaround (and why it's bad)

The only way to pass `toolCallId` to an MCP server today is to reimplement the MCP server as a host-side proxy registered via `tools[]`, where `ToolInvocation.toolCallId` is available. The host then runs its own MCP client and forwards calls, injecting the id.

Costs of that workaround:

- Lose `kind:"mcp"` PermissionRequest metadata (`serverName`, `toolTitle`, `readOnly`)
- Reimplement MCP lifecycle: process spawn, reconnect, timeouts
- Reimplement OAuth flow (`mcp.oauth_required` event no longer fires)
- Lose `tool.execution_start.mcpServerName` / `mcpToolName` telemetry
- Tools show up as `kind:"custom-tool"` in UIs that branch on tool kind

For functionality the CLI already has internally, this is a lot of duplicated machinery on the host side.

## Summary

Option A is the simplest, smallest, and most aligned with the MCP spec — one place in the CLI's outbound `tools/call` construction needs the id added to `_meta`. Option B is a fallback if Option A is not feasible.

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.