Skip to content

Commit 0253fb2

Browse files
authored
ui: Add request timeout for MCP tool calls (ggml-org#23138)
* feat: Add request timeout for MCP tool calls in llama-ui * feat: MCP Settings tab with max timeout setting
1 parent 3a92bc9 commit 0253fb2

9 files changed

Lines changed: 43 additions & 8 deletions

File tree

tools/ui/src/lib/components/app/settings/SettingsChat/SettingsChatFields.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@
7979
<div class="relative w-full">
8080
<Input
8181
id={field.key}
82+
type={field.isPositiveInteger ? 'number' : 'text'}
83+
{...field.isPositiveInteger ? { min: '1', step: '1' } : {}}
8284
value={currentValue}
8385
oninput={(e) => {
8486
// Update local config immediately for real-time badge feedback

tools/ui/src/lib/constants/routes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export const SETTINGS_SECTION_SLUGS = {
88
PENALTIES: 'penalties',
99
AGENTIC: 'agentic',
1010
DEVELOPER: 'developer',
11+
MCP: 'mcp',
1112
TOOLS: 'tools',
1213
IMPORT_EXPORT: 'import-export'
1314
} as const;

tools/ui/src/lib/constants/settings-keys.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export const SETTINGS_KEYS = {
5353
DRY_PENALTY_LAST_N: 'dry_penalty_last_n',
5454
// MCP
5555
MCP_SERVERS: 'mcpServers',
56+
MCP_REQUEST_TIMEOUT_SECONDS: 'mcpRequestTimeoutSeconds',
5657
AGENTIC_MAX_TURNS: 'agenticMaxTurns',
5758
ALWAYS_SHOW_AGENTIC_TURNS: 'alwaysShowAgenticTurns',
5859
AGENTIC_MAX_TOOL_PREVIEW_LINES: 'agenticMaxToolPreviewLines',

tools/ui/src/lib/constants/settings-registry.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ import type {
2323
SettingsSectionEntry,
2424
SettingsSection
2525
} from '$lib/types';
26-
import { CLI_FLAGS } from '$lib/constants';
26+
import { CLI_FLAGS, DEFAULT_MCP_CONFIG } from '$lib/constants';
27+
import McpLogo from '$lib/components/app/mcp/McpLogo.svelte';
2728
import { SETTINGS_KEYS } from './settings-keys';
2829
import { ROUTES, SETTINGS_SECTION_SLUGS } from './routes';
2930
import { TITLE_GENERATION } from './title-generation';
@@ -35,6 +36,7 @@ export const SETTINGS_SECTION_TITLES = {
3536
PENALTIES: 'Penalties',
3637
AGENTIC: 'Agentic',
3738
TOOLS: 'Tools',
39+
MCP: 'MCP',
3840
IMPORT_EXPORT: 'Import/Export',
3941
DEVELOPER: 'Developer'
4042
} as const;
@@ -657,6 +659,22 @@ const SETTINGS_REGISTRY: Record<string, SettingsSectionEntry> = {
657659
section: SETTINGS_SECTION_SLUGS.DEVELOPER
658660
}
659661
]
662+
},
663+
[SETTINGS_SECTION_SLUGS.MCP]: {
664+
title: SETTINGS_SECTION_TITLES.MCP,
665+
slug: SETTINGS_SECTION_SLUGS.MCP,
666+
icon: McpLogo,
667+
settings: [
668+
{
669+
key: SETTINGS_KEYS.MCP_REQUEST_TIMEOUT_SECONDS,
670+
label: 'Request timeout (seconds)',
671+
help: 'Default timeout for individual MCP tool calls. Can be overridden per server.',
672+
defaultValue: DEFAULT_MCP_CONFIG.requestTimeoutSeconds,
673+
type: SettingsFieldType.INPUT,
674+
section: SETTINGS_SECTION_SLUGS.MCP,
675+
isPositiveInteger: true
676+
}
677+
]
660678
}
661679
} as const;
662680

@@ -727,6 +745,7 @@ export const SETTINGS_CHAT_SECTIONS: SettingsSection[] = [
727745
label: s.label,
728746
type: s.type,
729747
isExperimental: s.isExperimental,
748+
isPositiveInteger: s.isPositiveInteger,
730749
help: s.help,
731750
options: s.options
732751
}))

tools/ui/src/lib/services/mcp.service.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -665,7 +665,9 @@ export class MCPService {
665665
tools: [],
666666
serverName,
667667
transportType,
668-
connectionTimeMs: 0
668+
connectionTimeMs: 0,
669+
requestTimeoutMs:
670+
serverConfig.requestTimeoutMs ?? DEFAULT_MCP_CONFIG.requestTimeoutSeconds * 1000
669671
});
670672

671673
const connectionTimeMs = Math.round(performance.now() - startTime);
@@ -694,7 +696,9 @@ export class MCPService {
694696
clientCapabilities: effectiveCapabilities,
695697
protocolVersion: DEFAULT_MCP_CONFIG.protocolVersion,
696698
instructions,
697-
connectionTimeMs
699+
connectionTimeMs,
700+
requestTimeoutMs:
701+
serverConfig.requestTimeoutMs ?? DEFAULT_MCP_CONFIG.requestTimeoutSeconds * 1000
698702
};
699703
}
700704

@@ -813,7 +817,7 @@ export class MCPService {
813817
const result = await connection.client.callTool(
814818
{ name: params.name, arguments: params.arguments },
815819
undefined,
816-
{ signal }
820+
{ signal, timeout: connection.requestTimeoutMs }
817821
);
818822

819823
return {

tools/ui/src/lib/stores/mcp.svelte.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,9 @@ class MCPStore {
168168
enabled: Boolean((entry as { enabled?: unknown })?.enabled),
169169
url,
170170
name: (entry as { name?: string })?.name,
171-
requestTimeoutSeconds: DEFAULT_MCP_CONFIG.requestTimeoutSeconds,
171+
requestTimeoutSeconds:
172+
(entry as { requestTimeoutSeconds?: number })?.requestTimeoutSeconds ??
173+
DEFAULT_MCP_CONFIG.requestTimeoutSeconds,
172174
headers: headers || undefined,
173175
useProxy: Boolean((entry as { useProxy?: unknown })?.useProxy)
174176
} satisfies MCPServerSettingsEntry;
@@ -554,7 +556,8 @@ class MCPStore {
554556
url: serverData.url.trim(),
555557
name: serverData.name,
556558
headers: serverData.headers?.trim() || undefined,
557-
requestTimeoutSeconds: DEFAULT_MCP_CONFIG.requestTimeoutSeconds,
559+
requestTimeoutSeconds:
560+
Number(config().mcpRequestTimeoutSeconds) || DEFAULT_MCP_CONFIG.requestTimeoutSeconds,
558561
useProxy: serverData.useProxy
559562
};
560563
settingsStore.updateConfig(SETTINGS_KEYS.MCP_SERVERS, JSON.stringify([...servers, newServer]));

tools/ui/src/lib/types/mcp.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ export interface MCPConnection {
135135
protocolVersion?: string;
136136
instructions?: string;
137137
connectionTimeMs: number;
138+
/** Configured timeout for individual requests (tool calls, etc.) in milliseconds */
139+
requestTimeoutMs: number;
138140
}
139141

140142
/**

tools/ui/src/lib/types/settings.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export interface SettingsFieldConfig {
4242
label: string;
4343
type: SettingsFieldType;
4444
isExperimental?: boolean;
45+
isPositiveInteger?: boolean;
4546
help?: string;
4647
options?: Array<{ value: string; label: string; icon?: typeof Icon }>;
4748
}

tools/ui/src/lib/utils/mcp.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export function detectMcpTransportFromUrl(url: string): MCPTransportType {
4949

5050
/**
5151
* Parses MCP server settings from a JSON string or array.
52-
* requestTimeoutSeconds is not user-configurable in the UI, so we always use the default value.
52+
* Preserves per-server requestTimeoutSeconds if stored, otherwise falls back to the global default.
5353
* @param rawServers - The raw servers to parse
5454
* @returns An empty array if the input is invalid.
5555
*/
@@ -88,7 +88,9 @@ export function parseMcpServerSettings(rawServers: unknown): MCPServerSettingsEn
8888
enabled: Boolean((entry as { enabled?: unknown })?.enabled),
8989
url,
9090
name: (entry as { name?: string })?.name,
91-
requestTimeoutSeconds: DEFAULT_MCP_CONFIG.requestTimeoutSeconds,
91+
requestTimeoutSeconds:
92+
(entry as { requestTimeoutSeconds?: number })?.requestTimeoutSeconds ??
93+
DEFAULT_MCP_CONFIG.requestTimeoutSeconds,
9294
headers: headers || undefined,
9395
useProxy: Boolean((entry as { useProxy?: unknown })?.useProxy)
9496
} satisfies MCPServerSettingsEntry;

0 commit comments

Comments
 (0)