Skip to content

Commit 1810e63

Browse files
committed
Forward W3C traceparent to MCP servers via _meta (SEP-414)
Plumb the active execute_tool span's W3C traceparent through LanguageModelToolInvocationOptions and IMcpToolCallContext, then inject it into the JSON-RPC tools/call payload's _meta so MCP server-side spans can be parented to the client trace. Refs #302301
1 parent 0910f0d commit 1810e63

8 files changed

Lines changed: 81 additions & 1 deletion

File tree

extensions/copilot/src/extension/tools/vscode-node/toolsService.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,17 @@ export class ToolsService extends BaseToolsService {
175175

176176
const startTime = Date.now();
177177

178+
// Propagate W3C trace context to MCP tools so server-side spans can be correlated
179+
// with this `execute_tool` span (MCP SEP-414, see #302301). Only set if not already
180+
// supplied by the caller and OTel produced a real span context.
181+
const optionsWithTrace = options as vscode.LanguageModelToolInvocationOptions<Object> & { traceparent?: string };
182+
if (!optionsWithTrace.traceparent) {
183+
const ctx = span.getSpanContext();
184+
if (ctx) {
185+
optionsWithTrace.traceparent = `00-${ctx.traceId}-${ctx.spanId}-01`;
186+
}
187+
}
188+
178189
return vscode.lm.invokeTool(getContributedToolName(name), options, token).then(
179190
result => {
180191
span.setStatus(SpanStatusCode.OK);

src/vs/workbench/api/common/extHostLanguageModelTools.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ export class ExtHostLanguageModelTools implements ExtHostLanguageModelToolsShape
130130
subAgentInvocationId: isProposedApiEnabled(extension, 'chatParticipantPrivate') ? options.subAgentInvocationId : undefined,
131131
chatStreamToolCallId: isProposedApiEnabled(extension, 'chatParticipantAdditions') ? options.chatStreamToolCallId : undefined,
132132
preToolUseResult: isProposedApiEnabled(extension, 'chatParticipantPrivate') ? options.preToolUseResult : undefined,
133+
traceparent: isProposedApiEnabled(extension, 'chatParticipantPrivate') ? options.traceparent : undefined,
134+
tracestate: isProposedApiEnabled(extension, 'chatParticipantPrivate') ? options.tracestate : undefined,
133135
}, token);
134136

135137
const dto: Dto<IToolResult> = result instanceof SerializableObjectWithBuffers ? result.value : result;

src/vs/workbench/contrib/chat/common/tools/languageModelToolsService.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,14 @@ export interface IToolInvocation {
197197
selectedCustomButton?: string;
198198
/** Pre-tool-use hook result passed from the extension, if the hook was already executed externally. */
199199
preToolUseResult?: IExternalPreToolUseHookResult;
200+
/**
201+
* Optional W3C trace context `traceparent` value identifying the parent distributed
202+
* tracing span for this tool invocation. Forwarded to MCP tool implementations as
203+
* `_meta.traceparent` (MCP SEP-414).
204+
*/
205+
traceparent?: string;
206+
/** Optional W3C trace context `tracestate` value paired with {@link traceparent}. */
207+
tracestate?: string;
200208
}
201209

202210
export interface IToolInvocationContext {

src/vs/workbench/contrib/mcp/common/mcpLanguageModelToolContribution.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,12 @@ class McpToolImplementation implements IToolImpl {
266266
content: []
267267
};
268268

269-
const callResult = await this._tool.callWithProgress(invocation.parameters as Record<string, unknown>, progress, { chatRequestId: invocation.chatRequestId, chatSessionResource: invocation.context?.sessionResource }, token);
269+
const callResult = await this._tool.callWithProgress(invocation.parameters as Record<string, unknown>, progress, {
270+
chatRequestId: invocation.chatRequestId,
271+
chatSessionResource: invocation.context?.sessionResource,
272+
traceparent: invocation.traceparent,
273+
tracestate: invocation.tracestate,
274+
}, token);
270275
const details: Mutable<IToolResultInputOutputDetails> = {
271276
input: JSON.stringify(invocation.parameters, undefined, 2),
272277
output: [],

src/vs/workbench/contrib/mcp/common/mcpServer.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,6 +1199,14 @@ export class McpTool implements IMcpTool {
11991199
if (context?.chatRequestId) {
12001200
meta['vscode.requestId'] = context.chatRequestId;
12011201
}
1202+
// Propagate W3C trace context to the MCP server (MCP SEP-414) so server-side
1203+
// spans can be correlated with the client trace.
1204+
if (context?.traceparent) {
1205+
meta['traceparent'] = context.traceparent;
1206+
if (context.tracestate) {
1207+
meta['tracestate'] = context.tracestate;
1208+
}
1209+
}
12021210

12031211
const taskHint = this._definition.execution?.taskSupport;
12041212
const serverSupportsTasksForTools = h.capabilities.tasks?.requests?.tools?.call !== undefined;

src/vs/workbench/contrib/mcp/common/mcpTypes.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,13 @@ export interface IMcpPromptMessage extends MCP.PromptMessage { }
453453
export interface IMcpToolCallContext {
454454
chatSessionResource: URI | undefined;
455455
chatRequestId?: string;
456+
/**
457+
* Optional W3C trace context `traceparent` value to forward to the MCP server
458+
* via `_meta.traceparent` on the JSON-RPC `tools/call` request (MCP SEP-414).
459+
*/
460+
traceparent?: string;
461+
/** Optional W3C trace context `tracestate` value paired with {@link traceparent}. */
462+
tracestate?: string;
456463
}
457464

458465
/**

src/vs/workbench/contrib/mcp/test/common/mcpServerRequestHandler.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,34 @@ suite('Workbench - MCP - ServerRequestHandler', () => {
381381
assert.strictEqual(e.name, 'Canceled');
382382
}
383383
});
384+
385+
test('callTool forwards _meta.traceparent to the JSON-RPC payload (MCP SEP-414)', async () => {
386+
const traceparent = '00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01';
387+
const tracestate = 'rojo=00f067aa0ba902b7';
388+
389+
const callPromise = handler.callTool({
390+
name: 'echo',
391+
arguments: { hello: 'world' },
392+
_meta: { traceparent, tracestate, progressToken: 'tok-1' },
393+
});
394+
395+
const sentMessages = transport.getSentMessages();
396+
const callRequest = sentMessages[2] as MCP.JSONRPCRequest & MCP.CallToolRequest;
397+
assert.strictEqual(callRequest.method, 'tools/call');
398+
assert.deepStrictEqual(callRequest.params._meta, {
399+
traceparent,
400+
tracestate,
401+
progressToken: 'tok-1',
402+
});
403+
404+
transport.simulateReceiveMessage({
405+
jsonrpc: MCP.JSONRPC_VERSION,
406+
id: callRequest.id,
407+
result: { content: [] },
408+
});
409+
410+
await callPromise;
411+
});
384412
});
385413

386414
suite.skip('Workbench - MCP - McpTask', () => { // TODO@connor4312 https://github.com/microsoft/vscode/issues/280126

src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,17 @@ declare module 'vscode' {
303303
* Unique ID for the subagent invocation, used to group tool calls from the same subagent run together.
304304
*/
305305
subAgentInvocationId?: string;
306+
/**
307+
* W3C trace context `traceparent` header value identifying the active distributed
308+
* tracing span. When provided to a tool implementation backed by an MCP server, this
309+
* value is forwarded as `_meta.traceparent` on the JSON-RPC `tools/call` request so
310+
* downstream servers can correlate their spans (MCP SEP-414).
311+
*/
312+
traceparent?: string;
313+
/**
314+
* Optional W3C trace context `tracestate` header value paired with `traceparent`.
315+
*/
316+
tracestate?: string;
306317
/**
307318
* Pre-tool-use hook result, if the hook was already executed by the caller.
308319
* When provided, the tools service will skip executing its own preToolUse hook

0 commit comments

Comments
 (0)