<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
PENALTIES: 'penalties',
AGENTIC: 'agentic',
DEVELOPER: 'developer',
+ MCP: 'mcp',
TOOLS: 'tools',
IMPORT_EXPORT: 'import-export'
} as const;
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',
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';
PENALTIES: 'Penalties',
AGENTIC: 'Agentic',
TOOLS: 'Tools',
+ MCP: 'MCP',
IMPORT_EXPORT: 'Import/Export',
DEVELOPER: 'Developer'
} as const;
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;
label: s.label,
type: s.type,
isExperimental: s.isExperimental,
+ isPositiveInteger: s.isPositiveInteger,
help: s.help,
options: s.options
}))
tools: [],
serverName,
transportType,
- connectionTimeMs: 0
+ connectionTimeMs: 0,
+ requestTimeoutMs:
+ serverConfig.requestTimeoutMs ?? DEFAULT_MCP_CONFIG.requestTimeoutSeconds * 1000
});
const connectionTimeMs = Math.round(performance.now() - startTime);
clientCapabilities: effectiveCapabilities,
protocolVersion: DEFAULT_MCP_CONFIG.protocolVersion,
instructions,
- connectionTimeMs
+ connectionTimeMs,
+ requestTimeoutMs:
+ serverConfig.requestTimeoutMs ?? DEFAULT_MCP_CONFIG.requestTimeoutSeconds * 1000
};
}
const result = await connection.client.callTool(
{ name: params.name, arguments: params.arguments },
undefined,
- { signal }
+ { signal, timeout: connection.requestTimeoutMs }
);
return {
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;
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]));
protocolVersion?: string;
instructions?: string;
connectionTimeMs: number;
+ /** Configured timeout for individual requests (tool calls, etc.) in milliseconds */
+ requestTimeoutMs: number;
}
/**
label: string;
type: SettingsFieldType;
isExperimental?: boolean;
+ isPositiveInteger?: boolean;
help?: string;
options?: Array<{ value: string; label: string; icon?: typeof Icon }>;
}
/**
* 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.
*/
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;