Skip to content

Commit 79624fe

Browse files
authored
feat: add MCP init timeut and minor fixes (#1304)
1 parent c6f96d7 commit 79624fe

File tree

4 files changed

+39
-2
lines changed

4 files changed

+39
-2
lines changed

server/aws-lsp-codewhisperer/src/language-server/agenticChat/errors.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ type AgenticChatErrorCode =
88
| 'InputTooLong' // too much context given to backend service.
99
| 'PromptCharacterLimit' // customer prompt exceeds
1010
| 'ResponseProcessingTimeout' // response didn't finish streaming in the allowed time
11+
| 'MCPServerInitTimeout' // mcp server failed to start within allowed time
1112

1213
export const customerFacingErrorCodes: AgenticChatErrorCode[] = [
1314
'QModelResponse',

server/aws-lsp-codewhisperer/src/language-server/agenticChat/tools/mcp/mcpManager.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js'
88
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'
99
import type { MCPServerConfig, McpToolDefinition, ListToolsResponse } from './mcpTypes'
1010
import { loadMcpServerConfigs } from './mcpUtils'
11+
import { AgenticChatError } from '../../errors'
1112

1213
/**
1314
* Manages MCP servers and their tools
@@ -71,8 +72,10 @@ export class McpManager {
7172
* Errors are logged but do not stop discovery of other servers.
7273
*/
7374
private async initOneServer(serverName: string, cfg: MCPServerConfig): Promise<void> {
75+
const DEFAULT_SERVER_INIT_TIMEOUT_MS = 60_000
7476
try {
7577
this.features.logging.debug(`MCP: initializing server [${serverName}]`)
78+
7679
const mergedEnv = {
7780
...(process.env as Record<string, string>),
7881
...(cfg.env ?? {}),
@@ -86,9 +89,32 @@ export class McpManager {
8689
name: `mcp-client-${serverName}`,
8790
version: '1.0.0',
8891
})
89-
await client.connect(transport)
90-
this.clients.set(serverName, client)
9192

93+
const connectPromise = client.connect(transport)
94+
const timeoutMs = cfg.initializationTimeout ?? DEFAULT_SERVER_INIT_TIMEOUT_MS
95+
const timeoutPromise = new Promise<never>((_, reject) =>
96+
setTimeout(
97+
() =>
98+
reject(
99+
new AgenticChatError(
100+
`MCP: server '${serverName}' initialization timed out after ${timeoutMs} ms`,
101+
'MCPServerInitTimeout'
102+
)
103+
),
104+
timeoutMs
105+
)
106+
)
107+
108+
try {
109+
await Promise.race([connectPromise, timeoutPromise])
110+
} catch (err: unknown) {
111+
if (err instanceof AgenticChatError && err.code === 'MCPServerInitTimeout') {
112+
this.features.logging.error(err.message)
113+
}
114+
throw err
115+
}
116+
117+
this.clients.set(serverName, client)
92118
this.mcpTools = this.mcpTools.filter(t => t.serverName !== serverName)
93119
const resp = (await client.listTools()) as ListToolsResponse
94120
for (const t of resp.tools) {
@@ -161,6 +187,7 @@ export class McpManager {
161187

162188
const client = this.clients.get(server)
163189
if (!client) throw new Error(`MCP: server '${server}' not connected`)
190+
164191
return client.callTool({ name: tool, arguments: args })
165192
}
166193

@@ -177,6 +204,7 @@ export class McpManager {
177204
command: cfg.command,
178205
args: cfg.args,
179206
env: cfg.env,
207+
initializationTimeout: cfg.initializationTimeout,
180208
disabled: cfg.disabled,
181209
autoApprove: cfg.autoApprove,
182210
toolOverrides: cfg.toolOverrides,

server/aws-lsp-codewhisperer/src/language-server/agenticChat/tools/mcp/mcpTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export interface MCPServerConfig {
1414
command: string
1515
args?: string[]
1616
env?: Record<string, string>
17+
initializationTimeout?: number
1718
disabled?: boolean
1819
autoApprove?: boolean
1920
toolOverrides?: Record<string, { autoApprove?: boolean; disabled?: boolean }>

server/aws-lsp-codewhisperer/src/language-server/agenticChat/tools/mcp/mcpUtils.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ export async function loadMcpServerConfigs(
7777
logging.warn(`MCP server '${name}' in ${fsPath} missing required 'command', skipping.`)
7878
continue
7979
}
80+
if ((entry as any).timeout !== undefined && typeof (entry as any).timeout !== 'number') {
81+
logging.warn(`Invalid timeout value on '${name}', ignoring.`)
82+
}
8083
const cfg: MCPServerConfig = {
8184
command: (entry as any).command,
8285
args: Array.isArray((entry as any).args) ? (entry as any).args.map(String) : [],
@@ -91,6 +94,10 @@ export async function loadMcpServerConfigs(
9194
}
9295
return {}
9396
})(),
97+
initializationTimeout:
98+
typeof (entry as any).initializationTimeout === 'number'
99+
? (entry as any).initializationTimeout
100+
: undefined,
94101
__configPath__: fsPath,
95102
}
96103

0 commit comments

Comments
 (0)