]> git.djapps.eu Git - pkg/ggml/sources/llama.cpp/commitdiff
ui: Add request timeout for MCP tool calls (#23138)
authorAleksander Grygier <redacted>
Sat, 16 May 2026 13:20:27 +0000 (15:20 +0200)
committerGitHub <redacted>
Sat, 16 May 2026 13:20:27 +0000 (15:20 +0200)
* feat: Add request timeout for MCP tool calls in llama-ui

* feat: MCP Settings tab with max timeout setting

tools/ui/src/lib/components/app/settings/SettingsChat/SettingsChatFields.svelte
tools/ui/src/lib/constants/routes.ts
tools/ui/src/lib/constants/settings-keys.ts
tools/ui/src/lib/constants/settings-registry.ts
tools/ui/src/lib/services/mcp.service.ts
tools/ui/src/lib/stores/mcp.svelte.ts
tools/ui/src/lib/types/mcp.d.ts
tools/ui/src/lib/types/settings.d.ts
tools/ui/src/lib/utils/mcp.ts

index 3ecf00adce8c47fd24f102d09e6fa4b6326a3cef..069855eebef363daf715ce5890f55825ceead13b 100644 (file)
@@ -79,6 +79,8 @@
                        <div class="relative w-full">
                                <Input
                                        id={field.key}
+                                       type={field.isPositiveInteger ? 'number' : 'text'}
+                                       {...field.isPositiveInteger ? { min: '1', step: '1' } : {}}
                                        value={currentValue}
                                        oninput={(e) => {
                                                // Update local config immediately for real-time badge feedback
index 14416478f084e3dffb7e3c5608e4d209ffc67f10..3b3fceea448ea5e5555305bc97d91a1e4560b2ed 100644 (file)
@@ -8,6 +8,7 @@ export const SETTINGS_SECTION_SLUGS = {
        PENALTIES: 'penalties',
        AGENTIC: 'agentic',
        DEVELOPER: 'developer',
+       MCP: 'mcp',
        TOOLS: 'tools',
        IMPORT_EXPORT: 'import-export'
 } as const;
index b673bff278d3b816d25cf0f8ba0467a94bd86a05..92a57f88acf2a52d5a05bd95e2c97a3d59352795 100644 (file)
@@ -53,6 +53,7 @@ export const SETTINGS_KEYS = {
        DRY_PENALTY_LAST_N: 'dry_penalty_last_n',
        // MCP
        MCP_SERVERS: 'mcpServers',
+       MCP_REQUEST_TIMEOUT_SECONDS: 'mcpRequestTimeoutSeconds',
        AGENTIC_MAX_TURNS: 'agenticMaxTurns',
        ALWAYS_SHOW_AGENTIC_TURNS: 'alwaysShowAgenticTurns',
        AGENTIC_MAX_TOOL_PREVIEW_LINES: 'agenticMaxToolPreviewLines',
index c4fc3fb301eb6416e63c2ef3dee3ab1637dd4c89..bdbb17d962caed4089c516d3380508d6bfa435b2 100644 (file)
@@ -23,7 +23,8 @@ import type {
        SettingsSectionEntry,
        SettingsSection
 } from '$lib/types';
-import { CLI_FLAGS } from '$lib/constants';
+import { CLI_FLAGS, DEFAULT_MCP_CONFIG } from '$lib/constants';
+import McpLogo from '$lib/components/app/mcp/McpLogo.svelte';
 import { SETTINGS_KEYS } from './settings-keys';
 import { ROUTES, SETTINGS_SECTION_SLUGS } from './routes';
 import { TITLE_GENERATION } from './title-generation';
@@ -35,6 +36,7 @@ export const SETTINGS_SECTION_TITLES = {
        PENALTIES: 'Penalties',
        AGENTIC: 'Agentic',
        TOOLS: 'Tools',
+       MCP: 'MCP',
        IMPORT_EXPORT: 'Import/Export',
        DEVELOPER: 'Developer'
 } as const;
@@ -657,6 +659,22 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
                                section: SETTINGS_SECTION_SLUGS.DEVELOPER
                        }
                ]
+       },
+       [SETTINGS_SECTION_SLUGS.MCP]: {
+               title: SETTINGS_SECTION_TITLES.MCP,
+               slug: SETTINGS_SECTION_SLUGS.MCP,
+               icon: McpLogo,
+               settings: [
+                       {
+                               key: SETTINGS_KEYS.MCP_REQUEST_TIMEOUT_SECONDS,
+                               label: 'Request timeout (seconds)',
+                               help: 'Default timeout for individual MCP tool calls. Can be overridden per server.',
+                               defaultValue: DEFAULT_MCP_CONFIG.requestTimeoutSeconds,
+                               type: SettingsFieldType.INPUT,
+                               section: SETTINGS_SECTION_SLUGS.MCP,
+                               isPositiveInteger: true
+                       }
+               ]
        }
 } as const;
 
@@ -727,6 +745,7 @@ export const SETTINGS_CHAT_SECTIONS: SettingsSection[] = [
                        label: s.label,
                        type: s.type,
                        isExperimental: s.isExperimental,
+                       isPositiveInteger: s.isPositiveInteger,
                        help: s.help,
                        options: s.options
                }))
index 458013b5acb12360c565f9814b5892c93d9e37bb..44cbd4a8aaf23e1b7040a0df6350547613916363 100644 (file)
@@ -665,7 +665,9 @@ export class MCPService {
                        tools: [],
                        serverName,
                        transportType,
-                       connectionTimeMs: 0
+                       connectionTimeMs: 0,
+                       requestTimeoutMs:
+                               serverConfig.requestTimeoutMs ?? DEFAULT_MCP_CONFIG.requestTimeoutSeconds * 1000
                });
 
                const connectionTimeMs = Math.round(performance.now() - startTime);
@@ -694,7 +696,9 @@ export class MCPService {
                        clientCapabilities: effectiveCapabilities,
                        protocolVersion: DEFAULT_MCP_CONFIG.protocolVersion,
                        instructions,
-                       connectionTimeMs
+                       connectionTimeMs,
+                       requestTimeoutMs:
+                               serverConfig.requestTimeoutMs ?? DEFAULT_MCP_CONFIG.requestTimeoutSeconds * 1000
                };
        }
 
@@ -813,7 +817,7 @@ export class MCPService {
                        const result = await connection.client.callTool(
                                { name: params.name, arguments: params.arguments },
                                undefined,
-                               { signal }
+                               { signal, timeout: connection.requestTimeoutMs }
                        );
 
                        return {
index 2a1eb3ff51d80cc2950001a82a3b29ec9f55fcf6..8fb306da881c0f2b2e7c19286333e2021bee1e26 100644 (file)
@@ -168,7 +168,9 @@ class MCPStore {
                                enabled: Boolean((entry as { enabled?: unknown })?.enabled),
                                url,
                                name: (entry as { name?: string })?.name,
-                               requestTimeoutSeconds: DEFAULT_MCP_CONFIG.requestTimeoutSeconds,
+                               requestTimeoutSeconds:
+                                       (entry as { requestTimeoutSeconds?: number })?.requestTimeoutSeconds ??
+                                       DEFAULT_MCP_CONFIG.requestTimeoutSeconds,
                                headers: headers || undefined,
                                useProxy: Boolean((entry as { useProxy?: unknown })?.useProxy)
                        } satisfies MCPServerSettingsEntry;
@@ -554,7 +556,8 @@ class MCPStore {
                        url: serverData.url.trim(),
                        name: serverData.name,
                        headers: serverData.headers?.trim() || undefined,
-                       requestTimeoutSeconds: DEFAULT_MCP_CONFIG.requestTimeoutSeconds,
+                       requestTimeoutSeconds:
+                               Number(config().mcpRequestTimeoutSeconds) || DEFAULT_MCP_CONFIG.requestTimeoutSeconds,
                        useProxy: serverData.useProxy
                };
                settingsStore.updateConfig(SETTINGS_KEYS.MCP_SERVERS, JSON.stringify([...servers, newServer]));
index 3837bcdf1b84660ee0f8037b7693d40c9169a9b9..7aa050cdfa728c488e762e91d0def2a5b67674fb 100644 (file)
@@ -135,6 +135,8 @@ export interface MCPConnection {
        protocolVersion?: string;
        instructions?: string;
        connectionTimeMs: number;
+       /** Configured timeout for individual requests (tool calls, etc.) in milliseconds */
+       requestTimeoutMs: number;
 }
 
 /**
index 1ab7a7e5d541ad857dc08e4489ec2b72d7fea091..65096db344949d52c9b5c32ad2fc09a535a33cce 100644 (file)
@@ -42,6 +42,7 @@ export interface SettingsFieldConfig {
        label: string;
        type: SettingsFieldType;
        isExperimental?: boolean;
+       isPositiveInteger?: boolean;
        help?: string;
        options?: Array<{ value: string; label: string; icon?: typeof Icon }>;
 }
index ee27798455db0c27ce4dcb7b3ab8624533c77025..05fe90048f0b2f265d805e6115846527be73b465 100644 (file)
@@ -49,7 +49,7 @@ export function detectMcpTransportFromUrl(url: string): MCPTransportType {
 
 /**
  * Parses MCP server settings from a JSON string or array.
- * requestTimeoutSeconds is not user-configurable in the UI, so we always use the default value.
+ * Preserves per-server requestTimeoutSeconds if stored, otherwise falls back to the global default.
  * @param rawServers - The raw servers to parse
  * @returns An empty array if the input is invalid.
  */
@@ -88,7 +88,9 @@ export function parseMcpServerSettings(rawServers: unknown): MCPServerSettingsEn
                        enabled: Boolean((entry as { enabled?: unknown })?.enabled),
                        url,
                        name: (entry as { name?: string })?.name,
-                       requestTimeoutSeconds: DEFAULT_MCP_CONFIG.requestTimeoutSeconds,
+                       requestTimeoutSeconds:
+                               (entry as { requestTimeoutSeconds?: number })?.requestTimeoutSeconds ??
+                               DEFAULT_MCP_CONFIG.requestTimeoutSeconds,
                        headers: headers || undefined,
                        useProxy: Boolean((entry as { useProxy?: unknown })?.useProxy)
                } satisfies MCPServerSettingsEntry;