feat: Integrate with `llama-server` proxy + improve MCP Server Edit Form
This commit is contained in:
parent
93378a5a5f
commit
be09ff9f4a
|
|
@ -1,10 +1,11 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Plus, X } from '@lucide/svelte';
|
import { Plus, Trash2 } from '@lucide/svelte';
|
||||||
import { Input } from '$lib/components/ui/input';
|
import { Input } from '$lib/components/ui/input';
|
||||||
import { autoResizeTextarea } from '$lib/utils';
|
import { autoResizeTextarea } from '$lib/utils';
|
||||||
import type { KeyValuePair } from '$lib/types';
|
import type { KeyValuePair } from '$lib/types';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
class?: string;
|
||||||
pairs: KeyValuePair[];
|
pairs: KeyValuePair[];
|
||||||
onPairsChange: (pairs: KeyValuePair[]) => void;
|
onPairsChange: (pairs: KeyValuePair[]) => void;
|
||||||
keyPlaceholder?: string;
|
keyPlaceholder?: string;
|
||||||
|
|
@ -16,6 +17,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
|
class: className = '',
|
||||||
pairs,
|
pairs,
|
||||||
onPairsChange,
|
onPairsChange,
|
||||||
keyPlaceholder = 'Key',
|
keyPlaceholder = 'Key',
|
||||||
|
|
@ -47,7 +49,7 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div>
|
<div class={className}>
|
||||||
<div class="mb-2 flex items-center justify-between">
|
<div class="mb-2 flex items-center justify-between">
|
||||||
{#if sectionLabel}
|
{#if sectionLabel}
|
||||||
<span class="text-xs font-medium">
|
<span class="text-xs font-medium">
|
||||||
|
|
@ -70,7 +72,7 @@
|
||||||
{#if pairs.length > 0}
|
{#if pairs.length > 0}
|
||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
{#each pairs as pair, index (index)}
|
{#each pairs as pair, index (index)}
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-start gap-2">
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={keyPlaceholder}
|
placeholder={keyPlaceholder}
|
||||||
|
|
@ -93,11 +95,11 @@
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="shrink-0 cursor-pointer rounded-md p-1 text-muted-foreground hover:bg-destructive/10 hover:text-destructive"
|
class="mt-1.5 shrink-0 cursor-pointer rounded-md p-1 text-muted-foreground hover:bg-destructive/10 hover:text-destructive"
|
||||||
onclick={() => removePair(index)}
|
onclick={() => removePair(index)}
|
||||||
aria-label="Remove item"
|
aria-label="Remove item"
|
||||||
>
|
>
|
||||||
<X class="h-3.5 w-3.5" />
|
<Trash2 class="h-3.5 w-3.5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@
|
||||||
async function startEditing() {
|
async function startEditing() {
|
||||||
isEditing = true;
|
isEditing = true;
|
||||||
await tick();
|
await tick();
|
||||||
editFormRef?.setInitialValues(server.url, server.headers || '');
|
editFormRef?.setInitialValues(server.url, server.headers || '', server.useProxy || false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancelEditing() {
|
function cancelEditing() {
|
||||||
|
|
@ -78,15 +78,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveEditing(url: string, headers: string) {
|
function saveEditing(url: string, headers: string, useProxy: boolean) {
|
||||||
onUpdate({
|
onUpdate({
|
||||||
url: url,
|
url: url,
|
||||||
headers: headers || undefined
|
headers: headers || undefined,
|
||||||
|
useProxy: useProxy
|
||||||
});
|
});
|
||||||
isEditing = false;
|
isEditing = false;
|
||||||
|
|
||||||
if (server.enabled && url) {
|
if (server.enabled && url) {
|
||||||
setTimeout(() => mcpStore.runHealthCheck({ ...server, url }), 100);
|
setTimeout(() => mcpStore.runHealthCheck({ ...server, url, useProxy }), 100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -101,6 +102,7 @@
|
||||||
bind:this={editFormRef}
|
bind:this={editFormRef}
|
||||||
serverId={server.id}
|
serverId={server.id}
|
||||||
serverUrl={server.url}
|
serverUrl={server.url}
|
||||||
|
serverUseProxy={server.useProxy}
|
||||||
onSave={saveEditing}
|
onSave={saveEditing}
|
||||||
onCancel={cancelEditing}
|
onCancel={cancelEditing}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,20 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { X } from '@lucide/svelte';
|
|
||||||
import { Button } from '$lib/components/ui/button';
|
import { Button } from '$lib/components/ui/button';
|
||||||
import { McpServerForm } from '$lib/components/app/mcp';
|
import { McpServerForm } from '$lib/components/app/mcp';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
serverId: string;
|
serverId: string;
|
||||||
serverUrl: string;
|
serverUrl: string;
|
||||||
onSave: (url: string, headers: string) => void;
|
serverUseProxy?: boolean;
|
||||||
|
onSave: (url: string, headers: string, useProxy: boolean) => void;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { serverId, serverUrl, onSave, onCancel }: Props = $props();
|
let { serverId, serverUrl, serverUseProxy = false, onSave, onCancel }: Props = $props();
|
||||||
|
|
||||||
let editUrl = $state(serverUrl);
|
let editUrl = $state(serverUrl);
|
||||||
let editHeaders = $state('');
|
let editHeaders = $state('');
|
||||||
|
let editUseProxy = $state(serverUseProxy);
|
||||||
|
|
||||||
let urlError = $derived.by(() => {
|
let urlError = $derived.by(() => {
|
||||||
if (!editUrl.trim()) return 'URL is required';
|
if (!editUrl.trim()) return 'URL is required';
|
||||||
|
|
@ -29,34 +30,34 @@
|
||||||
|
|
||||||
function handleSave() {
|
function handleSave() {
|
||||||
if (!canSave) return;
|
if (!canSave) return;
|
||||||
onSave(editUrl.trim(), editHeaders.trim());
|
onSave(editUrl.trim(), editHeaders.trim(), editUseProxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setInitialValues(url: string, headers: string) {
|
export function setInitialValues(url: string, headers: string, useProxy: boolean) {
|
||||||
editUrl = url;
|
editUrl = url;
|
||||||
editHeaders = headers;
|
editHeaders = headers;
|
||||||
|
editUseProxy = useProxy;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<div class="flex items-center justify-between">
|
<p class="font-medium">Configure Server</p>
|
||||||
<p class="font-medium">Configure Server</p>
|
|
||||||
<Button variant="ghost" size="icon" class="h-7 w-7" onclick={onCancel} aria-label="Cancel">
|
|
||||||
<X class="h-3.5 w-3.5" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<McpServerForm
|
<McpServerForm
|
||||||
url={editUrl}
|
url={editUrl}
|
||||||
headers={editHeaders}
|
headers={editHeaders}
|
||||||
|
useProxy={editUseProxy}
|
||||||
onUrlChange={(v) => (editUrl = v)}
|
onUrlChange={(v) => (editUrl = v)}
|
||||||
onHeadersChange={(v) => (editHeaders = v)}
|
onHeadersChange={(v) => (editHeaders = v)}
|
||||||
|
onUseProxyChange={(v) => (editUseProxy = v)}
|
||||||
urlError={editUrl ? urlError : null}
|
urlError={editUrl ? urlError : null}
|
||||||
id={serverId}
|
id={serverId}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="flex items-center justify-end">
|
<div class="flex items-center justify-end gap-2">
|
||||||
<Button variant="default" size="sm" onclick={handleSave} disabled={!canSave} aria-label="Save">
|
<Button variant="secondary" size="sm" onclick={onCancel}>Cancel</Button>
|
||||||
|
|
||||||
|
<Button size="sm" onclick={handleSave} disabled={!canSave}>
|
||||||
{serverUrl.trim() ? 'Update' : 'Add'}
|
{serverUrl.trim() ? 'Update' : 'Add'}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Input } from '$lib/components/ui/input';
|
import { Input } from '$lib/components/ui/input';
|
||||||
|
import { Switch } from '$lib/components/ui/switch';
|
||||||
import { KeyValuePairs } from '$lib/components/app';
|
import { KeyValuePairs } from '$lib/components/app';
|
||||||
import type { KeyValuePair } from '$lib/types';
|
import type { KeyValuePair } from '$lib/types';
|
||||||
import { parseHeadersToArray, serializeHeaders } from '$lib/utils';
|
import { parseHeadersToArray, serializeHeaders } from '$lib/utils';
|
||||||
|
|
@ -7,8 +8,10 @@
|
||||||
interface Props {
|
interface Props {
|
||||||
url: string;
|
url: string;
|
||||||
headers: string;
|
headers: string;
|
||||||
|
useProxy?: boolean;
|
||||||
onUrlChange: (url: string) => void;
|
onUrlChange: (url: string) => void;
|
||||||
onHeadersChange: (headers: string) => void;
|
onHeadersChange: (headers: string) => void;
|
||||||
|
onUseProxyChange?: (useProxy: boolean) => void;
|
||||||
urlError?: string | null;
|
urlError?: string | null;
|
||||||
id?: string;
|
id?: string;
|
||||||
}
|
}
|
||||||
|
|
@ -16,12 +19,18 @@
|
||||||
let {
|
let {
|
||||||
url,
|
url,
|
||||||
headers,
|
headers,
|
||||||
|
useProxy = false,
|
||||||
onUrlChange,
|
onUrlChange,
|
||||||
onHeadersChange,
|
onHeadersChange,
|
||||||
|
onUseProxyChange,
|
||||||
urlError = null,
|
urlError = null,
|
||||||
id = 'server'
|
id = 'server'
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
|
let isWebSocket = $derived(
|
||||||
|
url.toLowerCase().startsWith('ws://') || url.toLowerCase().startsWith('wss://')
|
||||||
|
);
|
||||||
|
|
||||||
let headerPairs = $derived<KeyValuePair[]>(parseHeadersToArray(headers));
|
let headerPairs = $derived<KeyValuePair[]>(parseHeadersToArray(headers));
|
||||||
|
|
||||||
function updateHeaderPairs(newPairs: KeyValuePair[]) {
|
function updateHeaderPairs(newPairs: KeyValuePair[]) {
|
||||||
|
|
@ -48,9 +57,21 @@
|
||||||
{#if urlError}
|
{#if urlError}
|
||||||
<p class="mt-1.5 text-xs text-destructive">{urlError}</p>
|
<p class="mt-1.5 text-xs text-destructive">{urlError}</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{#if !isWebSocket && onUseProxyChange}
|
||||||
|
<label class="mt-3 flex cursor-pointer items-center gap-2">
|
||||||
|
<Switch
|
||||||
|
id="use-proxy-{id}"
|
||||||
|
checked={useProxy}
|
||||||
|
onCheckedChange={(checked) => onUseProxyChange?.(checked)}
|
||||||
|
/>
|
||||||
|
<span class="text-xs text-muted-foreground">Use llama-server proxy</span>
|
||||||
|
</label>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<KeyValuePairs
|
<KeyValuePairs
|
||||||
|
class="mt-2"
|
||||||
pairs={headerPairs}
|
pairs={headerPairs}
|
||||||
onPairsChange={updateHeaderPairs}
|
onPairsChange={updateHeaderPairs}
|
||||||
keyPlaceholder="Header name"
|
keyPlaceholder="Header name"
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,25 @@
|
||||||
import { conversationsStore } from '$lib/stores/conversations.svelte';
|
import { conversationsStore } from '$lib/stores/conversations.svelte';
|
||||||
import { McpServerCard, McpServerForm } from '$lib/components/app/mcp';
|
import { McpServerCard, McpServerForm } from '$lib/components/app/mcp';
|
||||||
import { Skeleton } from '$lib/components/ui/skeleton';
|
import { Skeleton } from '$lib/components/ui/skeleton';
|
||||||
|
|
||||||
let servers = $derived(mcpStore.getServersSorted());
|
let servers = $derived(mcpStore.getServersSorted());
|
||||||
let allServersHealthChecked = $derived(
|
|
||||||
servers.length > 0 &&
|
let initialLoadComplete = $state(false);
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
if (initialLoadComplete) return;
|
||||||
|
|
||||||
|
const allChecked =
|
||||||
|
servers.length > 0 &&
|
||||||
servers.every((server) => {
|
servers.every((server) => {
|
||||||
const state = mcpStore.getHealthCheckState(server.id);
|
const state = mcpStore.getHealthCheckState(server.id);
|
||||||
return state.status === 'success' || state.status === 'error';
|
return state.status === 'success' || state.status === 'error';
|
||||||
})
|
});
|
||||||
);
|
|
||||||
|
if (allChecked) {
|
||||||
|
initialLoadComplete = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let isAddingServer = $state(false);
|
let isAddingServer = $state(false);
|
||||||
let newServerUrl = $state('');
|
let newServerUrl = $state('');
|
||||||
|
|
@ -118,7 +129,7 @@
|
||||||
{#if servers.length > 0}
|
{#if servers.length > 0}
|
||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
{#each servers as server (server.id)}
|
{#each servers as server (server.id)}
|
||||||
{#if !allServersHealthChecked}
|
{#if !initialLoadComplete}
|
||||||
<Card.Root class="grid gap-3 p-4">
|
<Card.Root class="grid gap-3 p-4">
|
||||||
<div class="flex items-center justify-between gap-4">
|
<div class="flex items-center justify-between gap-4">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ import type {
|
||||||
import { MCPConnectionPhase, MCPLogLevel, MCPTransportType } from '$lib/enums';
|
import { MCPConnectionPhase, MCPLogLevel, MCPTransportType } from '$lib/enums';
|
||||||
import { DEFAULT_MCP_CONFIG } from '$lib/constants/mcp';
|
import { DEFAULT_MCP_CONFIG } from '$lib/constants/mcp';
|
||||||
import { throwIfAborted, isAbortError } from '$lib/utils';
|
import { throwIfAborted, isAbortError } from '$lib/utils';
|
||||||
|
import { base } from '$app/paths';
|
||||||
|
|
||||||
interface ToolResultContentItem {
|
interface ToolResultContentItem {
|
||||||
type: string;
|
type: string;
|
||||||
|
|
@ -76,9 +77,24 @@ export class MCPService {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a proxied URL that routes through llama-server's CORS proxy.
|
||||||
|
* @param targetUrl - The original MCP server URL
|
||||||
|
* @returns URL pointing to the CORS proxy with target encoded
|
||||||
|
*/
|
||||||
|
private static buildProxiedUrl(targetUrl: string): URL {
|
||||||
|
const proxyPath = `${base}/cors-proxy`;
|
||||||
|
const proxyUrl = new URL(proxyPath, window.location.origin);
|
||||||
|
|
||||||
|
proxyUrl.searchParams.set('url', targetUrl);
|
||||||
|
|
||||||
|
return proxyUrl;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create transport based on server configuration.
|
* Create transport based on server configuration.
|
||||||
* Supports WebSocket, StreamableHTTP (modern), and SSE (legacy) transports.
|
* Supports WebSocket, StreamableHTTP (modern), and SSE (legacy) transports.
|
||||||
|
* When useProxy is enabled, routes HTTP requests through llama-server's CORS proxy.
|
||||||
* Returns both transport and the type used.
|
* Returns both transport and the type used.
|
||||||
*/
|
*/
|
||||||
static createTransport(config: MCPServerConfig): {
|
static createTransport(config: MCPServerConfig): {
|
||||||
|
|
@ -89,7 +105,7 @@ export class MCPService {
|
||||||
throw new Error('MCP server configuration is missing url');
|
throw new Error('MCP server configuration is missing url');
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = new URL(config.url);
|
const useProxy = config.useProxy ?? false;
|
||||||
const requestInit: RequestInit = {};
|
const requestInit: RequestInit = {};
|
||||||
|
|
||||||
if (config.headers) {
|
if (config.headers) {
|
||||||
|
|
@ -101,7 +117,17 @@ export class MCPService {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.transport === 'websocket') {
|
if (config.transport === 'websocket') {
|
||||||
console.log(`[MCPService] Creating WebSocket transport for ${url.href}`);
|
if (useProxy) {
|
||||||
|
throw new Error(
|
||||||
|
'WebSocket transport is not supported when using CORS proxy. Use HTTP transport instead.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = new URL(config.url);
|
||||||
|
|
||||||
|
if (import.meta.env.DEV) {
|
||||||
|
console.log(`[MCPService] Creating WebSocket transport for ${url.href}`);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
transport: new WebSocketClientTransport(url),
|
transport: new WebSocketClientTransport(url),
|
||||||
|
|
@ -109,8 +135,16 @@ export class MCPService {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const url = useProxy ? this.buildProxiedUrl(config.url) : new URL(config.url);
|
||||||
|
|
||||||
|
if (useProxy && import.meta.env.DEV) {
|
||||||
|
console.log(`[MCPService] Using CORS proxy for ${config.url} -> ${url.href}`);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log(`[MCPService] Creating StreamableHTTP transport for ${url.href}`);
|
if (import.meta.env.DEV) {
|
||||||
|
console.log(`[MCPService] Creating StreamableHTTP transport for ${url.href}`);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
transport: new StreamableHTTPClientTransport(url, {
|
transport: new StreamableHTTPClientTransport(url, {
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,8 @@ function parseServerSettings(rawServers: unknown): MCPServerSettingsEntry[] {
|
||||||
url,
|
url,
|
||||||
name: (entry as { name?: string })?.name,
|
name: (entry as { name?: string })?.name,
|
||||||
requestTimeoutSeconds: DEFAULT_MCP_CONFIG.requestTimeoutSeconds,
|
requestTimeoutSeconds: DEFAULT_MCP_CONFIG.requestTimeoutSeconds,
|
||||||
headers: headers || undefined
|
headers: headers || undefined,
|
||||||
|
useProxy: Boolean((entry as { useProxy?: unknown })?.useProxy)
|
||||||
} satisfies MCPServerSettingsEntry;
|
} satisfies MCPServerSettingsEntry;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -104,7 +105,8 @@ function buildServerConfig(
|
||||||
transport: detectMcpTransportFromUrl(entry.url),
|
transport: detectMcpTransportFromUrl(entry.url),
|
||||||
handshakeTimeoutMs: connectionTimeoutMs,
|
handshakeTimeoutMs: connectionTimeoutMs,
|
||||||
requestTimeoutMs: Math.round(entry.requestTimeoutSeconds * 1000),
|
requestTimeoutMs: Math.round(entry.requestTimeoutSeconds * 1000),
|
||||||
headers
|
headers,
|
||||||
|
useProxy: entry.useProxy
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -304,7 +306,8 @@ class MCPStore {
|
||||||
url: serverData.url.trim(),
|
url: serverData.url.trim(),
|
||||||
name: serverData.name,
|
name: serverData.name,
|
||||||
headers: serverData.headers?.trim() || undefined,
|
headers: serverData.headers?.trim() || undefined,
|
||||||
requestTimeoutSeconds: DEFAULT_MCP_CONFIG.requestTimeoutSeconds
|
requestTimeoutSeconds: DEFAULT_MCP_CONFIG.requestTimeoutSeconds,
|
||||||
|
useProxy: serverData.useProxy
|
||||||
};
|
};
|
||||||
settingsStore.updateConfig('mcpServers', JSON.stringify([...servers, newServer]));
|
settingsStore.updateConfig('mcpServers', JSON.stringify([...servers, newServer]));
|
||||||
}
|
}
|
||||||
|
|
@ -845,7 +848,8 @@ class MCPStore {
|
||||||
transport: detectMcpTransportFromUrl(trimmedUrl),
|
transport: detectMcpTransportFromUrl(trimmedUrl),
|
||||||
handshakeTimeoutMs: DEFAULT_MCP_CONFIG.connectionTimeoutMs,
|
handshakeTimeoutMs: DEFAULT_MCP_CONFIG.connectionTimeoutMs,
|
||||||
requestTimeoutMs: timeoutMs,
|
requestTimeoutMs: timeoutMs,
|
||||||
headers
|
headers,
|
||||||
|
useProxy: server.useProxy
|
||||||
},
|
},
|
||||||
DEFAULT_MCP_CONFIG.clientInfo,
|
DEFAULT_MCP_CONFIG.clientInfo,
|
||||||
DEFAULT_MCP_CONFIG.capabilities,
|
DEFAULT_MCP_CONFIG.capabilities,
|
||||||
|
|
|
||||||
|
|
@ -171,6 +171,7 @@ export interface HealthCheckParams {
|
||||||
url: string;
|
url: string;
|
||||||
requestTimeoutSeconds: number;
|
requestTimeoutSeconds: number;
|
||||||
headers?: string;
|
headers?: string;
|
||||||
|
useProxy?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MCPServerConfig = {
|
export type MCPServerConfig = {
|
||||||
|
|
@ -182,6 +183,7 @@ export type MCPServerConfig = {
|
||||||
handshakeTimeoutMs?: number;
|
handshakeTimeoutMs?: number;
|
||||||
requestTimeoutMs?: number;
|
requestTimeoutMs?: number;
|
||||||
capabilities?: ClientCapabilities;
|
capabilities?: ClientCapabilities;
|
||||||
|
useProxy?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MCPClientConfig = {
|
export type MCPClientConfig = {
|
||||||
|
|
@ -210,6 +212,7 @@ export type MCPServerSettingsEntry = {
|
||||||
headers?: string;
|
headers?: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
iconUrl?: string;
|
iconUrl?: string;
|
||||||
|
useProxy?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface MCPHostManagerConfig {
|
export interface MCPHostManagerConfig {
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,8 @@ export function parseMcpServerSettings(rawServers: unknown): MCPServerSettingsEn
|
||||||
enabled: Boolean((entry as { enabled?: unknown })?.enabled),
|
enabled: Boolean((entry as { enabled?: unknown })?.enabled),
|
||||||
url,
|
url,
|
||||||
requestTimeoutSeconds: DEFAULT_MCP_CONFIG.requestTimeoutSeconds,
|
requestTimeoutSeconds: DEFAULT_MCP_CONFIG.requestTimeoutSeconds,
|
||||||
headers: headers || undefined
|
headers: headers || undefined,
|
||||||
|
useProxy: Boolean((entry as { useProxy?: unknown })?.useProxy)
|
||||||
} satisfies MCPServerSettingsEntry;
|
} satisfies MCPServerSettingsEntry;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,8 @@ export default defineConfig({
|
||||||
proxy: {
|
proxy: {
|
||||||
'/v1': 'http://localhost:8080',
|
'/v1': 'http://localhost:8080',
|
||||||
'/props': 'http://localhost:8080',
|
'/props': 'http://localhost:8080',
|
||||||
'/models': 'http://localhost:8080'
|
'/models': 'http://localhost:8080',
|
||||||
|
'/cors-proxy': 'http://localhost:8080'
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
'Cross-Origin-Embedder-Policy': 'require-corp',
|
'Cross-Origin-Embedder-Policy': 'require-corp',
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue