Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

97 changes: 82 additions & 15 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1574,12 +1574,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';
Expand Down Expand Up @@ -1691,12 +1702,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
Expand Down Expand Up @@ -1791,6 +1813,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 text for context references like #file, #selection, @workspace
*/
Expand Down Expand Up @@ -2577,11 +2620,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
Expand Down Expand Up @@ -2736,11 +2795,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
Expand Down
Loading