From cfd312b6a10115b87cc768f97ace794b0570d04a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 20:05:28 +0000 Subject: [PATCH 1/3] Initial plan From 120a779bffdd10026f9094348ea756a1b942e4d7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 20:09:04 +0000 Subject: [PATCH 2/3] Add MCP tool detection helper methods and update tool call processing Co-authored-by: rajbos <6085745+rajbos@users.noreply.github.com> --- src/extension.ts | 97 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 82 insertions(+), 15 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index fec91b6..e73d526 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1653,12 +1653,23 @@ class CopilotTokenTracker implements vscode.Disposable { // Detect tool calls from Copilot CLI if (event.type === 'tool.call' || event.type === 'tool.result') { - analysis.toolCalls.total++; const toolName = event.data?.toolName || event.toolName || 'unknown'; - analysis.toolCalls.byTool[toolName] = (analysis.toolCalls.byTool[toolName] || 0) + 1; + + // Check if this is an MCP tool by name pattern + if (this.isMcpTool(toolName)) { + // Count as MCP tool + analysis.mcpTools.total++; + const serverName = this.extractMcpServerName(toolName); + analysis.mcpTools.byServer[serverName] = (analysis.mcpTools.byServer[serverName] || 0) + 1; + analysis.mcpTools.byTool[toolName] = (analysis.mcpTools.byTool[toolName] || 0) + 1; + } else { + // Count as regular tool call + analysis.toolCalls.total++; + analysis.toolCalls.byTool[toolName] = (analysis.toolCalls.byTool[toolName] || 0) + 1; + } } - // Detect MCP tools + // Detect MCP tools from explicit MCP events if (event.type === 'mcp.tool.call' || (event.data?.mcpServer)) { analysis.mcpTools.total++; const serverName = event.data?.mcpServer || 'unknown'; @@ -1726,12 +1737,23 @@ class CopilotTokenTracker implements vscode.Disposable { // Detect tool invocations if (responseItem.kind === 'toolInvocationSerialized' || responseItem.kind === 'prepareToolInvocation') { - analysis.toolCalls.total++; const toolName = responseItem.toolId || responseItem.toolName || responseItem.invocationMessage?.toolName || 'unknown'; - analysis.toolCalls.byTool[toolName] = (analysis.toolCalls.byTool[toolName] || 0) + 1; + + // Check if this is an MCP tool by name pattern + if (this.isMcpTool(toolName)) { + // Count as MCP tool + analysis.mcpTools.total++; + const serverName = this.extractMcpServerName(toolName); + analysis.mcpTools.byServer[serverName] = (analysis.mcpTools.byServer[serverName] || 0) + 1; + analysis.mcpTools.byTool[toolName] = (analysis.mcpTools.byTool[toolName] || 0) + 1; + } else { + // Count as regular tool call + analysis.toolCalls.total++; + analysis.toolCalls.byTool[toolName] = (analysis.toolCalls.byTool[toolName] || 0) + 1; + } } // Detect MCP servers starting @@ -1826,6 +1848,27 @@ class CopilotTokenTracker implements vscode.Disposable { } } + /** + * Check if a tool name indicates it's an MCP (Model Context Protocol) tool. + * MCP tools are identified by names starting with "mcp." or "mcp_" + */ + private isMcpTool(toolName: string): boolean { + return toolName.startsWith('mcp.') || toolName.startsWith('mcp_'); + } + + /** + * Extract server name from an MCP tool name. + * MCP tool names follow the format: mcp.server.tool or mcp_server_tool + */ + private extractMcpServerName(toolName: string): string { + // Remove the mcp. or mcp_ prefix + const withoutPrefix = toolName.replace(/^mcp[._]/, ''); + // Split on . or _ and take the first part + const parts = withoutPrefix.split(/[._]/); + // Return the first non-empty part, or 'unknown' if none exist + return parts[0] || 'unknown'; + } + /** * Analyze a request object for all context references. * This is the unified method that processes text, contentReferences, and variableData. @@ -2862,11 +2905,27 @@ class CopilotTokenTracker implements vscode.Disposable { if ((event.type === 'tool.call' || event.type === 'tool.result') && turns.length > 0) { const lastTurn = turns[turns.length - 1]; const toolName = event.data?.toolName || event.toolName || 'unknown'; - lastTurn.toolCalls.push({ - toolName, - arguments: event.type === 'tool.call' ? JSON.stringify(event.data?.arguments || {}) : undefined, - result: event.type === 'tool.result' ? event.data?.output : undefined - }); + + // Check if this is an MCP tool by name pattern + if (this.isMcpTool(toolName)) { + const serverName = this.extractMcpServerName(toolName); + lastTurn.mcpTools.push({ server: serverName, tool: toolName }); + } else { + // Add to regular tool calls + lastTurn.toolCalls.push({ + toolName, + arguments: event.type === 'tool.call' ? JSON.stringify(event.data?.arguments || {}) : undefined, + result: event.type === 'tool.result' ? event.data?.output : undefined + }); + } + } + + // Handle explicit MCP tool calls from CLI + if ((event.type === 'mcp.tool.call' || event.data?.mcpServer) && turns.length > 0) { + const lastTurn = turns[turns.length - 1]; + const serverName = event.data?.mcpServer || 'unknown'; + const toolName = event.data?.toolName || event.toolName || 'unknown'; + lastTurn.mcpTools.push({ server: serverName, tool: toolName }); } } catch { // Skip malformed lines @@ -3015,11 +3074,19 @@ class CopilotTokenTracker implements vscode.Disposable { // Extract tool invocations if (item.kind === 'toolInvocationSerialized' || item.kind === 'prepareToolInvocation') { const toolName = item.toolId || item.toolName || item.invocationMessage?.toolName || item.toolSpecificData?.kind || 'unknown'; - toolCalls.push({ - toolName, - arguments: item.input ? JSON.stringify(item.input) : undefined, - result: item.result ? (typeof item.result === 'string' ? item.result : JSON.stringify(item.result)) : undefined - }); + + // Check if this is an MCP tool by name pattern + if (this.isMcpTool(toolName)) { + const serverName = this.extractMcpServerName(toolName); + mcpTools.push({ server: serverName, tool: toolName }); + } else { + // Add to regular tool calls + toolCalls.push({ + toolName, + arguments: item.input ? JSON.stringify(item.input) : undefined, + result: item.result ? (typeof item.result === 'string' ? item.result : JSON.stringify(item.result)) : undefined + }); + } } // Extract MCP tools From 12c0f38527bb1d5cbde0329b7e29eab49629e189 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 20:09:54 +0000 Subject: [PATCH 3/3] Improve documentation for extractMcpServerName method Co-authored-by: rajbos <6085745+rajbos@users.noreply.github.com> --- src/extension.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/extension.ts b/src/extension.ts index e73d526..b4c2b54 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1859,11 +1859,13 @@ class CopilotTokenTracker implements vscode.Disposable { /** * Extract server name from an MCP tool name. * MCP tool names follow the format: mcp.server.tool or mcp_server_tool + * For example: "mcp.io.github.git" → "io", "mcp_io_github_git" → "io" + * Note: Splits on both '.' and '_' to handle various naming conventions */ private extractMcpServerName(toolName: string): string { // Remove the mcp. or mcp_ prefix const withoutPrefix = toolName.replace(/^mcp[._]/, ''); - // Split on . or _ and take the first part + // Split on . or _ and take the first part (server identifier) const parts = withoutPrefix.split(/[._]/); // Return the first non-empty part, or 'unknown' if none exist return parts[0] || 'unknown';