Skip to content

Commit a106aa6

Browse files
committed
Address PR feedback: preserve trace flags/state, plumb inbound $invokeTool
- TraceContext now carries optional traceFlags + traceState (W3C); the real OTel impl populates them so the built traceparent reflects the upstream sampling decision instead of hardcoding '01'. - toolsService: build traceparent with the actual flag, forward tracestate. - extHostLanguageModelTools.$invokeTool: deliver traceparent/tracestate to extension-implemented tools for symmetry with outbound $invokeTool. - Reword toolsService comment so it matches the unconditional behavior.
1 parent 1810e63 commit a106aa6

4 files changed

Lines changed: 30 additions & 10 deletions

File tree

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

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -175,14 +175,22 @@ 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`;
178+
// Propagate W3C trace context to tool invocations so downstream spans can be
179+
// correlated with this `execute_tool` span. MCP tools forward this onto
180+
// `_meta.traceparent`/`_meta.tracestate` of the JSON-RPC `tools/call` payload
181+
// (MCP SEP-414, see #302301). Only set if not already supplied by the caller.
182+
const optionsWithTrace = options as vscode.LanguageModelToolInvocationOptions<Object> & { traceparent?: string; tracestate?: string };
183+
const ctx = span.getSpanContext();
184+
if (ctx) {
185+
if (!optionsWithTrace.traceparent) {
186+
// Preserve the upstream W3C trace flags when available. Fall back to `01`
187+
// (sampled) so downstream MCP servers continue to participate in the trace
188+
// when the abstraction does not surface flags (e.g. tests, in-memory impl).
189+
const flags = (ctx.traceFlags ?? 0x01).toString(16).padStart(2, '0');
190+
optionsWithTrace.traceparent = `00-${ctx.traceId}-${ctx.spanId}-${flags}`;
191+
}
192+
if (!optionsWithTrace.tracestate && ctx.traceState) {
193+
optionsWithTrace.tracestate = ctx.traceState;
186194
}
187195
}
188196

extensions/copilot/src/platform/otel/common/otelService.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ export const IOTelService = createServiceIdentifier<IOTelService>('IOTelService'
1515
export interface TraceContext {
1616
readonly traceId: string;
1717
readonly spanId: string;
18+
/**
19+
* W3C trace flags from the source span context (e.g. `0x01` for sampled). Optional
20+
* because not all impls preserve it; consumers that build a W3C `traceparent` should
21+
* fall back to a sampled value when unset.
22+
*/
23+
readonly traceFlags?: number;
24+
/** W3C tracestate serialized as a comma-separated key=value list, when present. */
25+
readonly traceState?: string;
1826
}
1927

2028
/**

extensions/copilot/src/platform/otel/node/otelServiceImpl.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ export class NodeOTelService implements IOTelService {
344344
if (!ctx.traceId || !ctx.spanId) {
345345
return undefined;
346346
}
347-
return { traceId: ctx.traceId, spanId: ctx.spanId };
347+
return { traceId: ctx.traceId, spanId: ctx.spanId, traceFlags: ctx.traceFlags, traceState: ctx.traceState?.serialize() };
348348
}
349349

350350
// ── Trace Context Store ── (for cross-boundary propagation)
@@ -620,7 +620,9 @@ class RealSpanHandle implements ISpanHandle {
620620

621621
getSpanContext(): TraceContext | undefined {
622622
const ctx = this._span.spanContext();
623-
return ctx.traceId && ctx.spanId ? { traceId: ctx.traceId, spanId: ctx.spanId } : undefined;
623+
return ctx.traceId && ctx.spanId
624+
? { traceId: ctx.traceId, spanId: ctx.spanId, traceFlags: ctx.traceFlags, traceState: ctx.traceState?.serialize() }
625+
: undefined;
624626
}
625627

626628
end(): void {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@ export class ExtHostLanguageModelTools implements ExtHostLanguageModelToolsShape
193193
options.chatInteractionId = dto.chatInteractionId;
194194
options.chatSessionResource = URI.revive(dto.context?.sessionResource);
195195
options.subAgentInvocationId = dto.subAgentInvocationId;
196+
options.traceparent = dto.traceparent;
197+
options.tracestate = dto.tracestate;
196198
}
197199

198200
if (isProposedApiEnabled(item.extension, 'chatParticipantAdditions') && dto.modelId) {

0 commit comments

Comments
 (0)