🐞 fix: Update MCP server initialization to skip non-startup and oauth servers (#9049)

This commit is contained in:
Theo N. Truong 2025-08-13 14:19:55 -06:00 committed by GitHub
parent e6cebdf2b6
commit b6413b06bc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 33 additions and 42 deletions

View file

@ -62,21 +62,23 @@ export class MCPServersRegistry {
// Fetches all metadata for a single server in parallel // Fetches all metadata for a single server in parallel
private async gatherServerInfo(serverName: string) { private async gatherServerInfo(serverName: string) {
try { try {
await this.fetchOAuthRequirement(serverName);
const config = this.parsedConfigs[serverName];
if (config.startup !== false && !config.requiresOAuth) {
await Promise.allSettled([ await Promise.allSettled([
this.fetchOAuthRequirement(serverName).catch((error) =>
logger.error(`${this.prefix(serverName)} Failed to fetch OAuth requirement:`, error),
),
this.fetchServerInstructions(serverName).catch((error) => this.fetchServerInstructions(serverName).catch((error) =>
logger.error(`${this.prefix(serverName)} Failed to fetch server instructions:`, error), logger.warn(`${this.prefix(serverName)} Failed to fetch server instructions:`, error),
), ),
this.fetchServerCapabilities(serverName).catch((error) => this.fetchServerCapabilities(serverName).catch((error) =>
logger.error(`${this.prefix(serverName)} Failed to fetch server capabilities:`, error), logger.warn(`${this.prefix(serverName)} Failed to fetch server capabilities:`, error),
), ),
]); ]);
}
this.logUpdatedConfig(serverName); this.logUpdatedConfig(serverName);
} catch (error) { } catch (error) {
logger.error(`${this.prefix(serverName)} Failed to initialize server:`, error); logger.warn(`${this.prefix(serverName)} Failed to initialize server:`, error);
} }
} }
@ -117,7 +119,7 @@ export class MCPServersRegistry {
const toolFunctions = await this.getToolFunctions(serverName, conn); const toolFunctions = await this.getToolFunctions(serverName, conn);
Object.assign(allToolFunctions, toolFunctions); Object.assign(allToolFunctions, toolFunctions);
} catch (error) { } catch (error) {
logger.error(`${this.prefix(serverName)} Error fetching tool functions:`, error); logger.warn(`${this.prefix(serverName)} Error fetching tool functions:`, error);
} }
} }
this.toolFunctions = allToolFunctions; this.toolFunctions = allToolFunctions;
@ -147,14 +149,16 @@ export class MCPServersRegistry {
} }
// Determines if server requires OAuth if not already specified in the config // Determines if server requires OAuth if not already specified in the config
private async fetchOAuthRequirement(serverName: string) { private async fetchOAuthRequirement(serverName: string): Promise<boolean> {
const config = this.parsedConfigs[serverName]; const config = this.parsedConfigs[serverName];
if (config.requiresOAuth != null) return; if (config.requiresOAuth != null) return config.requiresOAuth;
if (config.url == null) return (config.requiresOAuth = false); if (config.url == null) return (config.requiresOAuth = false);
if (config.startup === false) return (config.requiresOAuth = false);
const result = await detectOAuthRequirement(config.url); const result = await detectOAuthRequirement(config.url);
config.requiresOAuth = result.requiresOAuth; config.requiresOAuth = result.requiresOAuth;
config.oauthMetadata = result.metadata; config.oauthMetadata = result.metadata;
return config.requiresOAuth;
} }
// Retrieves server instructions from MCP server if enabled in the config // Retrieves server instructions from MCP server if enabled in the config
@ -186,11 +190,13 @@ export class MCPServersRegistry {
private logUpdatedConfig(serverName: string) { private logUpdatedConfig(serverName: string) {
const prefix = this.prefix(serverName); const prefix = this.prefix(serverName);
const config = this.parsedConfigs[serverName]; const config = this.parsedConfigs[serverName];
logger.info(`${prefix} URL: ${config.url ?? 'N/A'}`); logger.info(`${prefix} -------------------------------------------------┐`);
logger.info(`${prefix} URL: ${config.url}`);
logger.info(`${prefix} OAuth Required: ${config.requiresOAuth}`); logger.info(`${prefix} OAuth Required: ${config.requiresOAuth}`);
logger.info(`${prefix} Capabilities: ${config.capabilities}`); logger.info(`${prefix} Capabilities: ${config.capabilities}`);
logger.info(`${prefix} Tools: ${config.tools}`); logger.info(`${prefix} Tools: ${config.tools}`);
logger.info(`${prefix} Server Instructions: ${config.serverInstructions ?? 'None'}`); logger.info(`${prefix} Server Instructions: ${config.serverInstructions}`);
logger.info(`${prefix} -------------------------------------------------┘`);
} }
// Returns formatted log prefix for server messages // Returns formatted log prefix for server messages

View file

@ -179,10 +179,10 @@ describe('MCPServersRegistry - Initialize Function', () => {
new Set(['oauth_server', 'oauth_predefined', 'oauth_startup_enabled']), new Set(['oauth_server', 'oauth_predefined', 'oauth_startup_enabled']),
); );
// Test serverInstructions // Test serverInstructions - OAuth servers keep their original boolean value, non-OAuth fetch actual strings
expect(registry.serverInstructions).toEqual({ expect(registry.serverInstructions).toEqual({
oauth_server: 'GitHub MCP server instructions',
stdio_server: 'Follow these instructions for stdio server', stdio_server: 'Follow these instructions for stdio server',
oauth_server: true,
non_oauth_server: 'Public API instructions', non_oauth_server: 'Public API instructions',
}); });
@ -193,16 +193,8 @@ describe('MCPServersRegistry - Initialize Function', () => {
non_oauth_server: rawConfigs.non_oauth_server, non_oauth_server: rawConfigs.non_oauth_server,
}); });
// Test toolFunctions (only 2 servers have tools: oauth_server has 1, stdio_server has 2) // Test toolFunctions (only non-OAuth servers get their tools fetched during initialization)
const expectedToolFunctions = { const expectedToolFunctions = {
get_repository_mcp_oauth_server: {
type: 'function',
function: {
name: 'get_repository_mcp_oauth_server',
description: 'Description for get_repository',
parameters: { type: 'object', properties: { input: { type: 'string' } } },
},
},
file_read_mcp_stdio_server: { file_read_mcp_stdio_server: {
type: 'function', type: 'function',
function: { function: {
@ -235,9 +227,9 @@ describe('MCPServersRegistry - Initialize Function', () => {
expect(registry.oauthServers).toBeInstanceOf(Set); expect(registry.oauthServers).toBeInstanceOf(Set);
expect(registry.toolFunctions).toBeDefined(); expect(registry.toolFunctions).toBeDefined();
// Error should be logged // Error should be logged as a warning at the higher level
expect(mockLogger.error).toHaveBeenCalledWith( expect(mockLogger.warn).toHaveBeenCalledWith(
expect.stringContaining('[MCP][oauth_server] Failed to fetch OAuth requirement:'), expect.stringContaining('[MCP][oauth_server] Failed to initialize server:'),
expect.any(Error), expect.any(Error),
); );
}); });
@ -250,7 +242,7 @@ describe('MCPServersRegistry - Initialize Function', () => {
expect(mockConnectionsRepo.disconnectAll).toHaveBeenCalledTimes(1); expect(mockConnectionsRepo.disconnectAll).toHaveBeenCalledTimes(1);
}); });
it('should log configuration updates for each server', async () => { it('should log configuration updates for each startup-enabled server', async () => {
const registry = new MCPServersRegistry(rawConfigs); const registry = new MCPServersRegistry(rawConfigs);
await registry.initialize(); await registry.initialize();

View file

@ -7,13 +7,11 @@ oauth_server:
url: "https://api.github.com/mcp" url: "https://api.github.com/mcp"
headers: headers:
Authorization: "Bearer {{GITHUB_TOKEN}}" Authorization: "Bearer {{GITHUB_TOKEN}}"
serverInstructions: "GitHub MCP server instructions" serverInstructions: true
requiresOAuth: true requiresOAuth: true
oauthMetadata: oauthMetadata:
authorization_url: "https://github.com/login/oauth/authorize" authorization_url: "https://github.com/login/oauth/authorize"
token_url: "https://github.com/login/oauth/access_token" token_url: "https://github.com/login/oauth/access_token"
capabilities: '{"tools":{"listChanged":true},"resources":{},"prompts":{}}'
tools: "get_repository"
oauth_predefined: oauth_predefined:
_processed: true _processed: true
@ -23,8 +21,6 @@ oauth_predefined:
oauthMetadata: oauthMetadata:
authorization_url: "https://example.com/oauth/authorize" authorization_url: "https://example.com/oauth/authorize"
token_url: "https://example.com/oauth/token" token_url: "https://example.com/oauth/token"
capabilities: '{"tools":{},"resources":{},"prompts":{}}'
tools: ""
stdio_server: stdio_server:
_processed: true _processed: true
@ -50,11 +46,10 @@ websocket_server:
disabled_server: disabled_server:
_processed: true _processed: true
requiresOAuth: false
type: "streamable-http" type: "streamable-http"
url: "https://api.disabled.com/mcp" url: "https://api.disabled.com/mcp"
startup: false startup: false
requiresOAuth: false
oauthMetadata: null
non_oauth_server: non_oauth_server:
_processed: true _processed: true
@ -70,5 +65,3 @@ oauth_startup_enabled:
type: "sse" type: "sse"
url: "https://api.oauth-startup.com/sse" url: "https://api.oauth-startup.com/sse"
requiresOAuth: true requiresOAuth: true
capabilities: '{"tools":{},"resources":{},"prompts":{}}'
tools: ""