github / github/copilot-sdk

Expose LLM tool-call id to MCP tool invocations

Đang mở
#1,332 1 bình luận 0 reaction 0 người được giao Xem trên GitHub
enhancement
Ngôn ngữ chính
Java
Star
10.5k
Fork
1.5k
Merge trung bình
1 ngày 11 giờ
Pull request đã merge (30 ngày)
128

Mô tả

# 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.

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.