google-gemini / google-gemini/gemini-cli
bug: MCP tools/list refresh path drops the configured server timeout (silently capped at 60s by SDK default)
- Dominant language
- TypeScript
- Stars
- 107k
- Forks
- 14.6k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 45
Description
## What happened?
`MCPClient.discover()` builds the options for `discoverTools()` with a spread that **replaces** rather than merges the configured timeout when any options object is provided. The `tools/list_changed` refresh path passes `{ signal }`, so `timeout` ends up `undefined` and falls back to the MCP SDK's default **60 seconds** — silently discarding `serverConfig.timeout` (default 10 minutes) and making the surrounding 10-minute abort controller dead in practice, since the SDK's own timer fires first.
## Affected code
`packages/core/src/tools/mcp-client.ts:334-346`:
```ts
return discoverTools(
this.serverName,
this.serverConfig,
this.client!,
cliConfig,
messageBus,
{
...(options ?? {
timeout: this.serverConfig.timeout ?? MCP_DEFAULT_TIMEOUT_MSEC,
}),
progressReporter: this,
},
);
```
The refresh caller (`packages/core/src/tools/mcp-client.ts:717-725`):
```ts
const timeoutMs = this.serverConfig.timeout ?? MCP_DEFAULT_TIMEOUT_MSEC;
const abortController = new AbortController();
const timeoutId = setTimeout(() => abortController.abort(), timeoutMs);
// ...
let newTools = await this.discoverTools(this.cliConfig,
registries.toolRegistry.getMessageBus(),
{ signal: abortController.signal }, // options present -> timeout branch skipped
);
```
Verified in the SDK: `@modelcontextprotocol/sdk` `shared/protocol.js` uses `options?.timeout ?? DEFAULT_REQUEST_TIMEOUT_MSEC` (60000).
## How can this be reproduced?
1. Configure an MCP server with `timeout: 600000` whose `tools/list` takes >60 s on regeneration.
2. Initial discovery at startup works (explicit timeout passed).
3. Trigger a tool-list-changed notification → refresh fails/times out at ~60 s despite the configured 10-minute budget.
## What did you expect to happen?
Refresh requests honor `serverConfig.timeout` like initial discovery does.
## Suggested direction
Merge instead of replace:
```ts
{
timeout: this.serverConfig.timeout ?? MCP_DEFAULT_TIMEOUT_MSEC,
...options,
progressReporter: this,
}
```
---
*Found by source audit on current `main` (commit `5411f113c`); platform-independent. Related but distinct: #28355 covers discovery blocking on mismatched responses; this is the dropped-timeout spread on the refresh path.*
Contributor guide
Research direction
Start in packages/core/src/tools/mcp-client.ts, reading MCPClient.discoverTools at lines 334-346 and the tools/list_changed refresh path at lines 717-725. Trace how the options object reaches discoverTools and verify that refresh requests retain serverConfig.timeout rather than the SDK default; done when a configured timeout is honored on both initial discovery and refresh.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 88/100