Skip to content

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
    • Users can now configure MCP server loading timeout values (1-300 seconds) in the settings interface to optimize performance based on their network conditions and server requirements
    • Default loading timeout is set to 10 seconds
    • All timeout configurations are automatically persisted across user sessions

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

- 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>
@coderabbitai
Copy link

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 a configurable MCP loading timeout across backend and frontend: MCPTestService gains constructor and per-call timeout options; UI introduces a header input and store wiring for mcpLoadingTimeout; settings migration and types persist the value; unit tests cover constructor, per-call, and global-timeout behaviors.

Changes

Cohort / File(s) Summary
Backend: MCP test service
apps/server/src/services/mcp-test-service.ts
Added MCPTestServiceOptions and MCPTestOptions; MCPTestService accepts a default timeout and resolves per-call timeoutMs; connection and listTools use the resolved timeout; testServerById propagates global settings timeout.
Backend: Tests
apps/server/tests/unit/services/mcp-test-service.test.ts
Re-enabled suite; replaced mock with class-based Client; added tests for constructor default timeout, per-call overrides, and testServerById behavior with/without global mcpLoadingTimeout.
Frontend: Header UI
apps/ui/src/components/views/settings-view/mcp-servers/components/mcp-server-header.tsx
Added mcpLoadingTimeout prop and onTimeoutChange callback; new input (1–300 s) with Clock icon; converts between seconds (UI) and milliseconds (store).
Frontend: Section wiring
apps/ui/src/components/views/settings-view/mcp-servers/mcp-servers-section.tsx
Wired mcpLoadingTimeout and onTimeoutChange from app store into MCPServerHeader.
Frontend: Store & migration
apps/ui/src/store/app-store.ts, apps/ui/src/hooks/use-settings-migration.ts
Added mcpLoadingTimeout to AppState (default 10000 ms) and setMcpLoadingTimeout action that persists via settings migration; migration reads/writes mcpLoadingTimeout.
Types
libs/types/src/settings.ts
Added optional mcpLoadingTimeout?: number to GlobalSettings and included default in DEFAULT_GLOBAL_SETTINGS.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant UI as MCPServerHeader
    participant Store as AppStore
    participant Migration as SettingsMigration
    participant Server as Backend
    participant Service as MCPTestService

    User->>UI: Set loading timeout (seconds)
    UI->>Store: onTimeoutChange(timeout ms)
    Store->>Store: setMcpLoadingTimeout(timeout)
    Store->>Migration: persist settings (includes mcpLoadingTimeout)
    Migration->>Server: POST /settings (mcpLoadingTimeout)

    Note over User,Service: Later, when testing an MCP server
    User->>Server: request test for serverId
    Server->>Service: testServerById(serverId)
    Service->>Store: read global mcpLoadingTimeout (resolve timeoutMs)
    Service->>Service: call testServer(config, { timeoutMs })
    Service->>Service: client.connect with timeoutMs (Promise.race)
    Service->>Service: client.listTools with timeoutMs (Promise.race)
    Service->>Server: return MCPTestResult
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

Testers-Requested

Suggested reviewers

  • Shironex

Poem

🐰 I nibbled code and tuned the time,

Ten thousand ticks now measured fine.
From header input to backend test,
Timeouts travel east to west.
Hooray — small hops, a softer rest. 🥕

🚥 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 'feat: add configurable MCP server loading timeout' directly and clearly describes the main change—adding a configurable timeout for MCP server loading operations across both backend and UI.

✏️ 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 flexibility by allowing users to customize the timeout period for MCP server interactions. This addresses potential issues with slow server responses or network conditions by providing a user-friendly way to adjust the waiting time, ensuring a smoother experience when connecting to and testing MCP servers. The changes span both the backend logic and the user interface for a complete solution.

Highlights

  • Configurable MCP Server Loading Timeout: Introduced a new setting, mcpLoadingTimeoutSeconds, allowing users to configure the timeout duration for MCP server connection tests.
  • User Interface for Timeout Setting: Added a dedicated UI element in the MCP servers settings header for easy adjustment of the loading timeout.
  • Backend Integration: The mcp-test-service has been updated to utilize this new configurable timeout, applying it to both connection and tool listing operations.
  • Settings Migration: Implemented settings migration to ensure backward compatibility and proper handling of the new timeout field.
  • Unit Tests: Comprehensive unit tests have been added to validate the functionality of the configurable timeout across various scenarios.

🧠 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 connections, which is a great enhancement for flexibility. The changes are well-implemented across the server, UI, and settings persistence layers. I appreciate that you've also enabled and added unit tests for this new functionality.

My review includes two main suggestions for improvement:

  1. Consolidating the hardcoded default timeout value into a single shared constant to improve maintainability.
  2. Strengthening the new unit tests to actually verify the timeout behavior using fake timers, as they are currently placeholders.

Overall, this is a solid contribution. Addressing these points will make the implementation more robust and easier to maintain.

Comment on lines +76 to +77
// Would verify timeout behavior if SDK wasn't mocked
expect(true).toBe(true);
Copy link
Contributor

Choose a reason for hiding this comment

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

high

This test, and others in this describe block for configurable timeouts, don't actually verify the timeout functionality as they just assert true to be true. This means the new timeout logic is not being properly tested.

You can use vi.useFakeTimers() to test the timeout logic effectively. Here's an example of how you could verify that the correct timeout is being used:

it('should timeout after the configured duration', async () => {
  vi.useFakeTimers();
  const service = new MCPTestService(mockSettingsService, { timeoutMs: 5000 });
  const config: MCPServerConfig = {
    id: 'test-server',
    name: 'Test Server',
    type: 'stdio',
    command: 'node',
    enabled: true,
  };

  // Make the connect promise never resolve to ensure the timeout is triggered
  mockClient.connect.mockReturnValue(new Promise(() => {}));

  const testPromise = service.testServer(config);
  
  // Advance timers to just before the timeout to ensure it doesn't fire early
  await vi.advanceTimersByTimeAsync(4999);
  
  // Now, advance past the timeout
  await vi.advanceTimersByTimeAsync(1);
  
  // Assert that the promise rejects with a timeout error
  await expect(testPromise).rejects.toThrow('Connection timeout');
});

Applying this pattern to the other placeholder tests would ensure the new timeout feature is robust.

...(settings.keyboardShortcuts as unknown as Partial<typeof current.keyboardShortcuts>),
},
mcpServers: settings.mcpServers ?? [],
mcpLoadingTimeout: settings.mcpLoadingTimeout ?? 10000,
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The default timeout value 10000 is hardcoded here. This magic number is also present in apps/ui/src/store/app-store.ts and apps/server/src/services/mcp-test-service.ts (as DEFAULT_TIMEOUT).

To improve maintainability and have a single source of truth, I recommend defining this default value as an exported constant in libs/types/src/settings.ts and then importing it in all the places where it's used.

For example, in libs/types/src/settings.ts:

export const DEFAULT_MCP_LOADING_TIMEOUT = 10000;

Then you could use DEFAULT_MCP_LOADING_TIMEOUT here and in the other files.

Copy link

@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: 1

🤖 Fix all issues with AI agents
In `@apps/server/src/services/mcp-test-service.ts`:
- Around line 195-197: The fallback in testServerById currently uses
DEFAULT_TIMEOUT instead of the service-level configured timeout; change the
timeout resolution to prefer globalSettings.mcpLoadingTimeout, then the
service's configured default (e.g., this.defaultTimeout or this.timeoutMs as set
in the service constructor), and only then DEFAULT_TIMEOUT, so call
this.testServer(serverConfig, { timeoutMs }) with timeoutMs =
globalSettings.mcpLoadingTimeout ?? this.defaultTimeout ?? DEFAULT_TIMEOUT.
🧹 Nitpick comments (3)
apps/server/tests/unit/services/mcp-test-service.test.ts (1)

62-169: Timeout tests are effectively no-ops right now.
They don’t assert the configured timeout behavior, so regressions will pass silently. Consider using fake timers + a never-resolving connect/listTools to validate error messages and timing.

✅ Example test that actually verifies timeout
@@
     describe('constructor options', () => {
-      it('should use custom timeout from constructor options', async () => {
+      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);
+        vi.useFakeTimers();
+        mockClient.connect.mockImplementation(() => new Promise(() => {}));
+
+        const resultPromise = service.testServer(config);
+        await vi.advanceTimersByTimeAsync(customTimeout);
+        const result = await resultPromise;
+
+        expect(result.success).toBe(false);
+        expect(result.error).toBe('Connection timeout');
       });
     });
apps/ui/src/components/views/settings-view/mcp-servers/components/mcp-server-header.tsx (1)

31-39: Clamp input and consider debouncing server sync.
onTimeoutChange fires every keystroke and ultimately syncs to the server (see apps/ui/src/store/app-store.ts setter), which can spam updates. Also, max=300 isn’t enforced in JS.

🔧 Clamp before persisting
-  const handleTimeoutChange = (e: React.ChangeEvent<HTMLInputElement>) => {
-    const seconds = parseInt(e.target.value, 10);
-    if (!isNaN(seconds) && seconds >= 1) {
-      onTimeoutChange(seconds * 1000); // Convert seconds to milliseconds
-    }
-  };
+  const handleTimeoutChange = (e: React.ChangeEvent<HTMLInputElement>) => {
+    const seconds = parseInt(e.target.value, 10);
+    if (!Number.isNaN(seconds)) {
+      const clamped = Math.min(300, Math.max(1, seconds));
+      onTimeoutChange(clamped * 1000); // Convert seconds to milliseconds
+    }
+  };

If you want to reduce server writes, consider committing on blur or with a short debounce.

Also applies to: 115-123

apps/ui/src/store/app-store.ts (1)

2457-2462: Guard timeout values and handle sync failures.

Line 2457 currently trusts any input and doesn’t revert on sync failure. Consider validating the value (finite, >0) and reverting if sync fails to keep UI/server consistent.

♻️ Proposed refactor
 setMcpLoadingTimeout: async (timeout) => {
-  set({ mcpLoadingTimeout: timeout });
-  // Sync to server settings file
-  const { syncSettingsToServer } = await import('@/hooks/use-settings-migration');
-  await syncSettingsToServer();
+  const previous = get().mcpLoadingTimeout;
+  const nextTimeout =
+    Number.isFinite(timeout) && timeout > 0 ? timeout : previous;
+  set({ mcpLoadingTimeout: nextTimeout });
+  // Sync to server settings file
+  const { syncSettingsToServer } = await import('@/hooks/use-settings-migration');
+  const ok = await syncSettingsToServer();
+  if (!ok) {
+    logger.error('Failed to sync mcpLoadingTimeout setting to server - reverting');
+    set({ mcpLoadingTimeout: previous });
+  }
 },

Comment on lines 195 to 197
// Use timeout from settings if configured, otherwise use default
const timeoutMs = globalSettings.mcpLoadingTimeout ?? DEFAULT_TIMEOUT;
return this.testServer(serverConfig, { timeoutMs });
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fallback should honor the service-level default timeout.
testServerById ignores a custom constructor timeout and always falls back to DEFAULT_TIMEOUT. This makes behavior inconsistent with testServer.

🔧 Proposed fix
-      const timeoutMs = globalSettings.mcpLoadingTimeout ?? DEFAULT_TIMEOUT;
+      const timeoutMs = globalSettings.mcpLoadingTimeout ?? this.defaultTimeoutMs;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Use timeout from settings if configured, otherwise use default
const timeoutMs = globalSettings.mcpLoadingTimeout ?? DEFAULT_TIMEOUT;
return this.testServer(serverConfig, { timeoutMs });
// Use timeout from settings if configured, otherwise use default
const timeoutMs = globalSettings.mcpLoadingTimeout ?? this.defaultTimeoutMs;
return this.testServer(serverConfig, { timeoutMs });
🤖 Prompt for AI Agents
In `@apps/server/src/services/mcp-test-service.ts` around lines 195 - 197, The
fallback in testServerById currently uses DEFAULT_TIMEOUT instead of the
service-level configured timeout; change the timeout resolution to prefer
globalSettings.mcpLoadingTimeout, then the service's configured default (e.g.,
this.defaultTimeout or this.timeoutMs as set in the service constructor), and
only then DEFAULT_TIMEOUT, so call this.testServer(serverConfig, { timeoutMs })
with timeoutMs = globalSettings.mcpLoadingTimeout ?? this.defaultTimeout ??
DEFAULT_TIMEOUT.

- 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>
Copy link

@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: 1

🤖 Fix all issues with AI agents
In `@apps/ui/src/store/app-store.ts`:
- Around line 2457-2468: In setMcpLoadingTimeout, multiple overlapping async
syncSettingsToServer() calls can out-of-order revert a newer value; add a local
request id or capture the current mcpLoadingTimeout after setting (use
get().mcpLoadingTimeout) and before awaiting sync, then after the await verify
that the stored value still equals that request id/value before performing the
revert; if a revert is prevented because the value changed, log the sync failure
with context (include the attempted timeout and that it was stale) so only the
matching request can undo the change and stale failures don’t clobber newer
settings.
🧹 Nitpick comments (1)
apps/server/src/services/mcp-test-service.ts (1)

65-71: Guard against non-positive/NaN timeout overrides.

If a caller passes 0, NaN, or a negative number, the test will immediately time out. Consider validating the override before using it.

♻️ Proposed fix
-    const timeoutMs = options?.timeoutMs ?? this.defaultTimeoutMs;
+    const overrideTimeoutMs = options?.timeoutMs;
+    const timeoutMs =
+      Number.isFinite(overrideTimeoutMs) && overrideTimeoutMs > 0
+        ? overrideTimeoutMs
+        : this.defaultTimeoutMs;

- 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>
@Nikoqqq
Copy link
Author

Nikoqqq commented Jan 18, 2026

Closing to recreate with proper branch naming (feature/mcp-loading-timeout)

@Nikoqqq Nikoqqq closed this Jan 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant