Skip to content

feat: add configurable MCP server loading timeout#584

Closed
Nikoqqq wants to merge 5 commits intoAutoMaker-Org:v0.13.0rcfrom
Nikoqqq:feature/mcp-loading-timeout
Closed

feat: add configurable MCP server loading timeout#584
Nikoqqq wants to merge 5 commits intoAutoMaker-Org:v0.13.0rcfrom
Nikoqqq:feature/mcp-loading-timeout

Conversation

@Nikoqqq
Copy link

@Nikoqqq Nikoqqq commented Jan 18, 2026

Summary

Some MCP servers take longer than the default 10 seconds to initialize and respond, especially those that need to download models, connect to external services, or perform complex startup routines. This PR adds a configurable timeout setting so users can adjust the wait time based on their specific MCP server requirements.

Changes

  • Add mcpLoadingTimeoutSeconds setting to control MCP server connection test timeout
  • Add timeout configuration UI in MCP servers settings header (1-300 seconds range)
  • Update mcp-test-service to use configurable timeout with proper fallback chain
  • Add settings migration for new timeout field
  • Add unit tests for timeout functionality

Test plan

  • Unit tests added for timeout functionality
  • All existing tests pass (1254 server tests, 492 package tests)
  • Build passes
  • Format check passes

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Configurable MCP server loading timeout exposed in the UI (1–300 seconds, default 10s) with per-test override.
    • Timeout persisted to user settings and surfaced in application state with validation and safe server sync/rollback behavior.
  • Tests

    • Re-enabled and expanded test suite to cover constructor options, per-call timeout overrides, global timeout fallback, and settings-driven timeout behavior.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 18, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds configurable MCP loading timeouts end-to-end: backend supports service-level and per-call timeout overrides, tests updated and re-enabled, UI exposes a seconds input persisted to the app store, and types/settings include a new mcpLoadingTimeout field (default 10000 ms).

Changes

Cohort / File(s) Summary
Backend: MCP test service
apps/server/src/services/mcp-test-service.ts
Add MCPTestServiceOptions/MCPTestOptions, store defaultTimeoutMs in constructor, validate inputs, accept per-call timeoutMs, replace hard-coded DEFAULT_TIMEOUT with computed timeout for connect/list-tools flows, forward globalSettings.mcpLoadingTimeout from testServerById.
Backend tests & SDK mock
apps/server/tests/unit/services/mcp-test-service.test.ts, @modelcontextprotocol/sdk/client/index.js
Re-enable test suite; convert SDK client mock to class-based MockClient; add tests for constructor default timeout, per-call overrides, and testServerById settings-driven timeout behavior.
Frontend: header UI
apps/ui/src/components/views/settings-view/mcp-servers/components/mcp-server-header.tsx
Add mcpLoadingTimeout and onTimeoutChange props; render seconds input (1–300s) with ms↔s conversion and clamping; import Clock, Input, Label.
Frontend: section wiring
apps/ui/src/components/views/settings-view/mcp-servers/mcp-servers-section.tsx
Wire mcpLoadingTimeout from store into MCPServerHeader and pass onTimeoutChange to update store.
Frontend: store & migration
apps/ui/src/store/app-store.ts, apps/ui/src/hooks/use-settings-migration.ts
Add mcpLoadingTimeout to AppState (default 10000 ms) and setMcpLoadingTimeout action with validation, server sync, and rollback-on-failure; hydrate/persist setting in migrations.
Types / Defaults
libs/types/src/settings.ts
Add optional GlobalSettings.mcpLoadingTimeout?: number and set DEFAULT_GLOBAL_SETTINGS.mcpLoadingTimeout = 10000.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI as "MCPServerHeader (UI)"
    participant Store as "App Store"
    participant API as "Backend API"
    participant MCPSvc as "MCPTestService"

    User->>UI: set timeout (1–300s)
    UI->>Store: setMcpLoadingTimeout(ms)
    Store->>Store: validate & update state
    Store->>API: syncSettingsToServer()
    API-->>Store: ack / error

    User->>MCPSvc: testServer(config[, options])
    MCPSvc->>MCPSvc: determine timeoutMs (per-call || default || global)
    MCPSvc->>API: connect/list-tools (with timeoutMs)
    API-->>MCPSvc: response or timeout
    MCPSvc-->>User: test result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

Ready-To-Merge

Suggested reviewers

  • Shironex

Poem

🐰 Hopping through milliseconds bright,

I set the timeout snug and tight.
From header input to server test,
Timeouts travel — let MCPs do their best! 🥕⏱️

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the primary change: adding a configurable timeout feature for MCP server loading, which is the main objective across all modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @Nikoqqq, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the application's robustness and user experience by introducing a configurable timeout for MCP server loading. This feature is crucial for environments where MCP servers might take longer to initialize, providing users with the flexibility to adjust the waiting period. The changes span across the server-side logic for testing connections, the user interface for setting the timeout, and the underlying settings management to ensure persistence and proper handling of this new configuration.

Highlights

  • Configurable MCP Server Loading Timeout: Introduced a new setting, mcpLoadingTimeoutSeconds, allowing users to configure the timeout duration for MCP server connections and tool listing, addressing issues with servers requiring longer initialization times.
  • User Interface for Timeout Setting: Added a dedicated UI input field in the MCP servers settings header, enabling users to adjust the loading timeout between 1 and 300 seconds.
  • Dynamic Timeout Application: The mcp-test-service has been updated to dynamically use the configured timeout, with a robust fallback mechanism to a default value if no custom timeout is specified or if the provided value is invalid.
  • Settings Persistence and Migration: The new mcpLoadingTimeout setting is integrated into the application's global settings, ensuring it persists across sessions and is handled correctly during settings migration.
  • Enhanced Test Coverage: New unit tests have been added to validate the configurable timeout functionality, covering various scenarios including default, custom, and per-test timeout overrides, and ensuring proper fallback behavior.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a configurable timeout for MCP server loading, which is a valuable feature for handling servers with long initialization times. The implementation is well-executed across the backend service, UI components, and state management. The settings migration and fallback logic for timeouts are also correctly handled. My only suggestion is to improve the newly added unit tests to make them more robust by asserting the actual timeout values being used.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@apps/server/src/services/mcp-test-service.ts`:
- Around line 53-58: The constructor currently assigns this.defaultTimeoutMs =
options?.timeoutMs ?? DEFAULT_TIMEOUT which allows NaN or non-positive values;
update the constructor in MCPTestService to validate options?.timeoutMs from
MCPTestServiceOptions (check Number.isFinite(timeoutMs) && timeoutMs > 0) and if
it fails, fall back to DEFAULT_TIMEOUT so defaultTimeoutMs is always a positive
finite number.

In `@apps/server/tests/unit/services/mcp-test-service.test.ts`:
- Around line 62-170: The remaining three tests still use no-op assertions —
replace those expect(true).toBe(true) placeholders with the same spy pattern
used in the first constructor test: create a spy on the SDK/client "timeout"
method (the same spy variable used in the first "should use default timeout"
test), call MCPTestService.testServer (or testServerById where applicable) and
assert the spy was called with the expected timeout values (customTimeout 30000,
per-test 60000, and constructor 20000), then restore/clear the spy; locate
usages around MCPTestService.testServer and MCPTestService.testServerById and
mockSettingsService.getGlobalSettings to apply the assertions.
🧹 Nitpick comments (1)
apps/ui/src/components/views/settings-view/mcp-servers/components/mcp-server-header.tsx (1)

1-35: Import React event type explicitly to avoid namespace issues.

React.ChangeEvent can fail to type-check when the React namespace isn’t globally available (common with modern TS/React setups). A type-only import avoids that dependency.

♻️ Proposed change
-import { Plug, RefreshCw, Download, Code, FileJson, Plus, Clock } from 'lucide-react';
+import { Plug, RefreshCw, Download, Code, FileJson, Plus, Clock } from 'lucide-react';
+import type { ChangeEvent } from 'react';
@@
-  const handleTimeoutChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+  const handleTimeoutChange = (e: ChangeEvent<HTMLInputElement>) => {

Comment on lines 62 to 170
describe('configurable timeout', () => {
describe('constructor options', () => {
it('should use default timeout when no options provided', async () => {
const service = new MCPTestService(mockSettingsService);
const config: MCPServerConfig = {
id: 'test-server',
name: 'Test Server',
type: 'stdio',
command: 'node',
enabled: true,
};

// Default timeout is 10000ms
await service.testServer(config);
// Would verify timeout behavior if SDK wasn't mocked
expect(true).toBe(true);
});

it('should use custom timeout from constructor options', async () => {
const customTimeout = 30000; // 30 seconds
const service = new MCPTestService(mockSettingsService, { timeoutMs: customTimeout });
const config: MCPServerConfig = {
id: 'test-server',
name: 'Test Server',
type: 'stdio',
command: 'node',
enabled: true,
};

await service.testServer(config);
// Would verify timeout is 30000ms if SDK wasn't mocked
expect(true).toBe(true);
});
});

describe('testServer options', () => {
it('should use per-test timeout when provided', async () => {
const service = new MCPTestService(mockSettingsService, { timeoutMs: 10000 });
const config: MCPServerConfig = {
id: 'test-server',
name: 'Test Server',
type: 'stdio',
command: 'node',
enabled: true,
};

// Override with per-test timeout
await service.testServer(config, { timeoutMs: 60000 });
// Would verify timeout is 60000ms if SDK wasn't mocked
expect(true).toBe(true);
});

it('should fall back to default timeout when no per-test timeout provided', async () => {
const service = new MCPTestService(mockSettingsService, { timeoutMs: 20000 });
const config: MCPServerConfig = {
id: 'test-server',
name: 'Test Server',
type: 'stdio',
command: 'node',
enabled: true,
};

await service.testServer(config);
// Would verify timeout is 20000ms (from constructor) if SDK wasn't mocked
expect(true).toBe(true);
});
});

describe('testServerById with mcpLoadingTimeout from settings', () => {
it('should use mcpLoadingTimeout from global settings', async () => {
const serverConfig: MCPServerConfig = {
id: 'server-1',
name: 'Server One',
type: 'stdio',
command: 'node',
enabled: true,
};

mockSettingsService.getGlobalSettings.mockResolvedValue({
mcpServers: [serverConfig],
mcpLoadingTimeout: 45000, // Custom timeout from settings
});

await mcpTestService.testServerById('server-1');

expect(mockSettingsService.getGlobalSettings).toHaveBeenCalled();
// Would verify timeout is 45000ms if SDK wasn't mocked
});

it('should use default timeout when mcpLoadingTimeout not set in settings', async () => {
const serverConfig: MCPServerConfig = {
id: 'server-1',
name: 'Server One',
type: 'stdio',
command: 'node',
enabled: true,
};

mockSettingsService.getGlobalSettings.mockResolvedValue({
mcpServers: [serverConfig],
// mcpLoadingTimeout not set
});

await mcpTestService.testServerById('server-1');

expect(mockSettingsService.getGlobalSettings).toHaveBeenCalled();
// Would verify timeout falls back to DEFAULT_TIMEOUT (10000ms) if SDK wasn't mocked
});
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "mcp-test-service*" | head -20

Repository: AutoMaker-Org/automaker

Length of output: 172


🏁 Script executed:

git ls-files | grep -E "(mcp-test|MCPTestService)" | head -20

Repository: AutoMaker-Org/automaker

Length of output: 168


🏁 Script executed:

cat -n ./apps/server/src/services/mcp-test-service.ts

Repository: AutoMaker-Org/automaker

Length of output: 10956


🏁 Script executed:

wc -l ./apps/server/tests/unit/services/mcp-test-service.test.ts

Repository: AutoMaker-Org/automaker

Length of output: 129


🏁 Script executed:

sed -n '62,170p' ./apps/server/tests/unit/services/mcp-test-service.test.ts

Repository: AutoMaker-Org/automaker

Length of output: 3893


🏁 Script executed:

head -30 ./apps/server/tests/unit/services/mcp-test-service.test.ts

Repository: AutoMaker-Org/automaker

Length of output: 959


🏁 Script executed:

grep -n "MCPTestService" ./apps/server/tests/unit/services/mcp-test-service.test.ts | head -5

Repository: AutoMaker-Org/automaker

Length of output: 371


🏁 Script executed:

sed -n '1,50p' ./apps/server/tests/unit/services/mcp-test-service.test.ts | grep -E "^(import|from)"

Repository: AutoMaker-Org/automaker

Length of output: 522


🏁 Script executed:

find . -type d -name "@automaker" 2>/dev/null | head -5

Repository: AutoMaker-Org/automaker

Length of output: 49


🏁 Script executed:

find . -type d -name "services" | grep -E "(shared|packages|automaker)" | head -10

Repository: AutoMaker-Org/automaker

Length of output: 49


🏁 Script executed:

git ls-files | grep -E "package\.json|packages" | head -20

Repository: AutoMaker-Org/automaker

Length of output: 433


🏁 Script executed:

rg "@automaker" --type json -A 2 | head -40

Repository: AutoMaker-Org/automaker

Length of output: 2189


Three of four timeout tests still use no-op assertions.
The first test in the constructor options group has been updated with a spy on the timeout method, but the remaining timeout tests still have expect(true).toBe(true) placeholders. Apply the same spy pattern to the other three timeout tests to properly verify that the correct timeout values are passed.

🤖 Prompt for AI Agents
In `@apps/server/tests/unit/services/mcp-test-service.test.ts` around lines 62 -
170, The remaining three tests still use no-op assertions — replace those
expect(true).toBe(true) placeholders with the same spy pattern used in the first
constructor test: create a spy on the SDK/client "timeout" method (the same spy
variable used in the first "should use default timeout" test), call
MCPTestService.testServer (or testServerById where applicable) and assert the
spy was called with the expected timeout values (customTimeout 30000, per-test
60000, and constructor 20000), then restore/clear the spy; locate usages around
MCPTestService.testServer and MCPTestService.testServerById and
mockSettingsService.getGlobalSettings to apply the assertions.

@Nikoqqq
Copy link
Author

Nikoqqq commented Jan 18, 2026

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 18, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Shironex Shironex added the Enhancement Improvements to existing functionality or UI. label Jan 18, 2026
Nikoqqq and others added 5 commits January 19, 2026 08:18
- Add mcpLoadingTimeoutSeconds setting to control connection test timeout
- Add timeout configuration UI in MCP servers settings header
- Update mcp-test-service to use configurable timeout
- Add settings migration for new timeout field
- Add unit tests for timeout functionality

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use service default timeout as fallback in testServerById instead of constant
- Clamp timeout input between 1-300 seconds in UI component
- Add validation and sync failure handling in store setter

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Prevent race condition in setMcpLoadingTimeout by checking value before revert
- Validate timeout override in testServer to guard against NaN/negative values

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@Nikoqqq Nikoqqq force-pushed the feature/mcp-loading-timeout branch from a046806 to 7e9c47e Compare January 19, 2026 05:19
@Nikoqqq
Copy link
Author

Nikoqqq commented Jan 19, 2026

@Shironex

@webdevcody webdevcody deleted the branch AutoMaker-Org:v0.13.0rc January 20, 2026 23:48
@webdevcody webdevcody closed this Jan 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Enhancement Improvements to existing functionality or UI.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants