Implement version-based cache invalidation for MCP tool definitions

- Added serverVersion property to McpServerDefinitionProvider that updates on override reloads
- VS Code now refreshes cached tool definitions when server version changes
- Fixed tool override caching issue where old descriptions persisted
- Removed unnecessary delay in notification system
- Added debug endpoint /debug/tools for inspecting server-side tool definitions

Progress: Startup override loading now works correctly, investigating override reload hang in VS Code
This commit is contained in:
B Harper
2025-10-26 16:40:44 +11:00
parent 60ea857b54
commit 94d004338a
4 changed files with 63 additions and 8 deletions
+4
View File
@@ -46,3 +46,7 @@
2025-10-26T04:35:24.503Z - RESPONSE ENDPOINT CALLED - RequestID: 8-1761453324492
2025-10-26T04:35:34.152Z - RESPONSE ENDPOINT CALLED - RequestID: 9-1761453327469
2025-10-26T04:35:59.241Z - RESPONSE ENDPOINT CALLED - RequestID: 4-1761453262457
2025-10-26T04:37:02.349Z - RESPONSE ENDPOINT CALLED - RequestID: 5-1761453363480
2025-10-26T04:37:38.418Z - RESPONSE ENDPOINT CALLED - RequestID: 10-1761453337063
2025-10-26T04:58:25.162Z - RESPONSE ENDPOINT CALLED - RequestID: 3-1761454660214
2025-10-26T04:59:00.155Z - RESPONSE ENDPOINT CALLED - RequestID: 4-1761454713902
+10 -2
View File
@@ -17,6 +17,7 @@ let serverManager: ServerManager;
class HumanAgentMcpProvider implements vscode.McpServerDefinitionProvider {
private _onDidChangeMcpServerDefinitions = new vscode.EventEmitter<void>();
readonly onDidChangeMcpServerDefinitions = this._onDidChangeMcpServerDefinitions.event;
private serverVersion: string = Date.now().toString();
constructor(private sessionId: string) {}
@@ -24,13 +25,20 @@ class HumanAgentMcpProvider implements vscode.McpServerDefinitionProvider {
// Use separate endpoint for MCP tools to avoid SSE conflicts with webview
const serverUrl = `http://127.0.0.1:3737/mcp-tools?sessionId=${this.sessionId}`;
const serverUri = vscode.Uri.parse(serverUrl);
const server = new vscode.McpHttpServerDefinition('HumanAgent MCP', serverUri);
console.log('HumanAgent MCP: Using separate MCP tools endpoint to avoid SSE conflicts');
const server = new vscode.McpHttpServerDefinition('HumanAgent MCP', serverUri, {}, this.serverVersion);
console.log(`HumanAgent MCP: Using separate MCP tools endpoint to avoid SSE conflicts (version: ${this.serverVersion})`);
return [server];
}
// Update version to force VS Code to refresh cached tool definitions
updateServerVersion(): void {
this.serverVersion = Date.now().toString();
console.log(`HumanAgent MCP: Updated server version to ${this.serverVersion} to force tool cache refresh`);
}
// Method to fire the change event when override files are reloaded
notifyServerDefinitionsChanged(): void {
this.updateServerVersion(); // Force VS Code to refresh cached tool definitions
console.log('HumanAgent MCP: Firing onDidChangeMcpServerDefinitions event');
this._onDidChangeMcpServerDefinitions.fire();
}
+45 -1
View File
@@ -553,7 +553,7 @@ export class McpServer extends EventEmitter {
// Web interface for multi-session chat
await this.handleWebInterface(req, res);
return;
} else if (req.url?.startsWith('/sessions') || req.url === '/response' || req.url?.startsWith('/tools') || req.url === '/reload' || req.url?.startsWith('/messages/')) {
} else if (req.url?.startsWith('/sessions') || req.url === '/response' || req.url?.startsWith('/tools') || req.url?.startsWith('/debug') || req.url === '/reload' || req.url?.startsWith('/messages/')) {
// Session management, response, tools, reload, messages, and chat endpoints
await this.handleSessionEndpoint(req, res);
return;
@@ -1012,6 +1012,39 @@ export class McpServer extends EventEmitter {
const finalTools = Array.from(toolMap.values());
res.end(JSON.stringify({ tools: finalTools, merged: true }));
}
} else if (req.method === 'GET' && url.pathname.startsWith('/debug/tools')) {
// Debug endpoint to inspect tools for a specific session
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json');
const sessionId = url.searchParams.get('sessionId');
if (!sessionId) {
res.end(JSON.stringify({
error: 'sessionId parameter required',
usage: '/debug/tools?sessionId=<session-id>',
availableSessions: Array.from(this.activeSessions)
}));
return;
}
const sessionTools = this.sessionTools.get(sessionId);
const defaultTools = Array.from(this.tools.values());
const tools = this.getAvailableTools(sessionId);
res.end(JSON.stringify({
sessionId,
hasSessionTools: sessionTools !== undefined,
sessionToolCount: sessionTools ? sessionTools.size : 0,
sessionToolNames: sessionTools ? Array.from(sessionTools.keys()) : [],
defaultToolCount: defaultTools.length,
finalToolCount: tools.length,
humanAgentChatTool: tools.find(t => t.name === 'HumanAgent_Chat'),
debugInfo: {
sessionExists: this.activeSessions.has(sessionId),
sessionToolsRegistered: this.sessionTools.has(sessionId)
}
}));
} else if (req.method === 'POST' && url.pathname === '/reload') {
// Reload workspace overrides
let body = '';
@@ -1809,6 +1842,7 @@ export class McpServer extends EventEmitter {
this.debugLogger.log('TOOLS', `tools/list request - Extension sessionId: ${this.sessionId}, Message params: ${JSON.stringify(message.params)}`);
this.debugLogger.log('TOOLS', `Final sessionIdToUse: ${sessionIdToUse || 'default'}`);
this.debugLogger.log('TOOLS', `Available session tools: ${Array.from(this.sessionTools.keys()).join(', ')}`);
const tools = this.getAvailableTools(sessionIdToUse);
@@ -1818,9 +1852,19 @@ export class McpServer extends EventEmitter {
const sessionTools = this.sessionTools.get(sessionIdToUse);
if (sessionTools) {
this.debugLogger.log('TOOLS', `Session tools found: ${Array.from(sessionTools.keys()).join(', ')}`);
// Log the actual HumanAgent_Chat tool description
const chatTool = sessionTools.get('HumanAgent_Chat');
if (chatTool) {
this.debugLogger.log('TOOLS', `HumanAgent_Chat description: ${chatTool.description.substring(0, 100)}...`);
}
}
} else {
this.debugLogger.log('TOOLS', `Using default tools (no session ID available)`);
// Also log default tool description for comparison
const defaultChatTool = this.tools.get('HumanAgent_Chat');
if (defaultChatTool) {
this.debugLogger.log('TOOLS', `Default HumanAgent_Chat description: ${defaultChatTool.description.substring(0, 100)}...`);
}
}
return {
+4 -5
View File
@@ -322,6 +322,7 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
}
// Fire VS Code native MCP event to refresh tools
// The version update will force VS Code to refresh cached tool definitions
if (this.mcpProvider) {
this.mcpProvider.notifyServerDefinitionsChanged();
console.log('Fired onDidChangeMcpServerDefinitions event to refresh VS Code tools');
@@ -988,12 +989,10 @@ export class ChatWebviewProvider implements vscode.WebviewViewProvider {
// Update status text based on configuration
const statusElement = document.getElementById('server-status-text');
if (statusElement && message.data.configType) {
if (message.data.configType === 'workspace') {
statusElement.textContent = 'HumanAgent MCP Server (Workspace)';
} else if (message.data.configType === 'global') {
statusElement.textContent = 'HumanAgent MCP Server (Global)';
if (message.data.configType === 'native') {
statusElement.textContent = 'HumanAgent MCP Server (Connected)';
} else {
statusElement.textContent = 'HumanAgent MCP Server (Not Configured)';
statusElement.textContent = 'HumanAgent MCP Server (Unknown)';
}
}
console.log('Server status:', message.data);