feat: MCP connection details WIP
This commit is contained in:
parent
0009c0c300
commit
5407b2efab
|
|
@ -2,7 +2,8 @@
|
|||
import { onMount } from 'svelte';
|
||||
import * as Card from '$lib/components/ui/card';
|
||||
import type { MCPServerSettingsEntry, HealthCheckState } from '$lib/types';
|
||||
import { MCPConnectionPhase } from '$lib/enums';
|
||||
import { HealthCheckStatus } from '$lib/enums';
|
||||
import { mcpStore } from '$lib/stores/mcp.svelte';
|
||||
import { mcpClient } from '$lib/clients/mcp.client';
|
||||
import McpServerCardHeader from './McpServerCardHeader.svelte';
|
||||
import McpServerCardActions from './McpServerCardActions.svelte';
|
||||
|
|
@ -22,11 +23,39 @@
|
|||
let { server, displayName, faviconUrl, onToggle, onUpdate, onDelete }: Props = $props();
|
||||
|
||||
let healthState = $derived<HealthCheckState>(mcpStore.getHealthCheckState(server.id));
|
||||
let isHealthChecking = $derived(healthState.status === 'loading');
|
||||
let isConnected = $derived(healthState.status === 'success');
|
||||
let isError = $derived(healthState.status === 'error');
|
||||
let errorMessage = $derived(healthState.status === 'error' ? healthState.message : undefined);
|
||||
let tools = $derived(healthState.status === 'success' ? healthState.tools : []);
|
||||
let isHealthChecking = $derived(healthState.status === HealthCheckStatus.Connecting);
|
||||
let isConnected = $derived(healthState.status === HealthCheckStatus.Success);
|
||||
let isError = $derived(healthState.status === HealthCheckStatus.Error);
|
||||
let errorMessage = $derived(
|
||||
healthState.status === HealthCheckStatus.Error ? healthState.message : undefined
|
||||
);
|
||||
let tools = $derived(healthState.status === HealthCheckStatus.Success ? healthState.tools : []);
|
||||
|
||||
let connectionLogs = $derived(
|
||||
healthState.status === HealthCheckStatus.Connecting ||
|
||||
healthState.status === HealthCheckStatus.Success ||
|
||||
healthState.status === HealthCheckStatus.Error
|
||||
? healthState.logs
|
||||
: []
|
||||
);
|
||||
let serverInfo = $derived(
|
||||
healthState.status === HealthCheckStatus.Success ? healthState.serverInfo : undefined
|
||||
);
|
||||
let capabilities = $derived(
|
||||
healthState.status === HealthCheckStatus.Success ? healthState.capabilities : undefined
|
||||
);
|
||||
let transportType = $derived(
|
||||
healthState.status === HealthCheckStatus.Success ? healthState.transportType : undefined
|
||||
);
|
||||
let protocolVersion = $derived(
|
||||
healthState.status === HealthCheckStatus.Success ? healthState.protocolVersion : undefined
|
||||
);
|
||||
let connectionTimeMs = $derived(
|
||||
healthState.status === HealthCheckStatus.Success ? healthState.connectionTimeMs : undefined
|
||||
);
|
||||
let instructions = $derived(
|
||||
healthState.status === HealthCheckStatus.Success ? healthState.instructions : undefined
|
||||
);
|
||||
|
||||
let isEditing = $state(!server.url.trim());
|
||||
let showDeleteDialog = $state(false);
|
||||
|
|
|
|||
|
|
@ -49,11 +49,33 @@ interface ToolCallResult {
|
|||
}
|
||||
|
||||
export class MCPService {
|
||||
/**
|
||||
* Create a connection log entry
|
||||
*/
|
||||
private static createLog(
|
||||
phase: MCPConnectionPhase,
|
||||
message: string,
|
||||
level: MCPLogLevel = MCPLogLevel.Info,
|
||||
details?: unknown
|
||||
): MCPConnectionLog {
|
||||
return {
|
||||
timestamp: new Date(),
|
||||
phase,
|
||||
message,
|
||||
level,
|
||||
details
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create transport based on server configuration.
|
||||
* Supports WebSocket, StreamableHTTP (modern), and SSE (legacy) transports.
|
||||
* Returns both transport and the type used.
|
||||
*/
|
||||
static createTransport(config: MCPServerConfig): Transport {
|
||||
static createTransport(config: MCPServerConfig): {
|
||||
transport: Transport;
|
||||
type: MCPTransportType;
|
||||
} {
|
||||
if (!config.url) {
|
||||
throw new Error('MCP server configuration is missing url');
|
||||
}
|
||||
|
|
@ -64,26 +86,38 @@ export class MCPService {
|
|||
if (config.headers) {
|
||||
requestInit.headers = config.headers;
|
||||
}
|
||||
|
||||
if (config.credentials) {
|
||||
requestInit.credentials = config.credentials;
|
||||
}
|
||||
|
||||
if (config.transport === 'websocket') {
|
||||
console.log(`[MCPService] Creating WebSocket transport for ${url.href}`);
|
||||
return new WebSocketClientTransport(url);
|
||||
|
||||
return {
|
||||
transport: new WebSocketClientTransport(url),
|
||||
type: MCPTransportType.Websocket
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
console.log(`[MCPService] Creating StreamableHTTP transport for ${url.href}`);
|
||||
return new StreamableHTTPClientTransport(url, {
|
||||
requestInit,
|
||||
sessionId: config.sessionId
|
||||
});
|
||||
|
||||
return {
|
||||
transport: new StreamableHTTPClientTransport(url, {
|
||||
requestInit,
|
||||
sessionId: config.sessionId
|
||||
}),
|
||||
type: MCPTransportType.StreamableHttp
|
||||
};
|
||||
} catch (httpError) {
|
||||
console.warn(`[MCPService] StreamableHTTP failed, trying SSE transport...`, httpError);
|
||||
|
||||
try {
|
||||
return new SSEClientTransport(url, { requestInit });
|
||||
return {
|
||||
transport: new SSEClientTransport(url, { requestInit }),
|
||||
type: MCPTransportType.SSE
|
||||
};
|
||||
} catch (sseError) {
|
||||
const httpMsg = httpError instanceof Error ? httpError.message : String(httpError);
|
||||
const sseMsg = sseError instanceof Error ? sseError.message : String(sseError);
|
||||
|
|
@ -93,20 +127,58 @@ export class MCPService {
|
|||
}
|
||||
|
||||
/**
|
||||
* Connect to a single MCP server.
|
||||
* Returns connection object with client, transport, and discovered tools.
|
||||
* Extract server info from SDK Implementation type
|
||||
*/
|
||||
private static extractServerInfo(impl: Implementation | undefined): MCPServerInfo | undefined {
|
||||
if (!impl) return undefined;
|
||||
return {
|
||||
name: impl.name,
|
||||
version: impl.version,
|
||||
title: impl.title,
|
||||
description: impl.description,
|
||||
websiteUrl: impl.websiteUrl,
|
||||
icons: impl.icons?.map((icon) => ({
|
||||
src: icon.src,
|
||||
mimeType: icon.mimeType,
|
||||
sizes: icon.sizes
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to a single MCP server with detailed phase tracking.
|
||||
* Returns connection object with client, transport, discovered tools, and connection details.
|
||||
* @param onPhase - Optional callback for connection phase changes
|
||||
*/
|
||||
static async connect(
|
||||
serverName: string,
|
||||
serverConfig: MCPServerConfig,
|
||||
clientInfo?: Implementation,
|
||||
capabilities?: ClientCapabilities
|
||||
capabilities?: ClientCapabilities,
|
||||
onPhase?: MCPPhaseCallback
|
||||
): Promise<MCPConnection> {
|
||||
const startTime = performance.now();
|
||||
const effectiveClientInfo = clientInfo ?? DEFAULT_MCP_CONFIG.clientInfo;
|
||||
const effectiveCapabilities = capabilities ?? DEFAULT_MCP_CONFIG.capabilities;
|
||||
|
||||
// Phase: Creating transport
|
||||
onPhase?.(
|
||||
MCPConnectionPhase.TransportCreating,
|
||||
this.createLog(
|
||||
MCPConnectionPhase.TransportCreating,
|
||||
`Creating transport for ${serverConfig.url}`
|
||||
)
|
||||
);
|
||||
|
||||
console.log(`[MCPService][${serverName}] Creating transport...`);
|
||||
const transport = this.createTransport(serverConfig);
|
||||
const { transport, type: transportType } = this.createTransport(serverConfig);
|
||||
|
||||
// Phase: Transport ready
|
||||
onPhase?.(
|
||||
MCPConnectionPhase.TransportReady,
|
||||
this.createLog(MCPConnectionPhase.TransportReady, `Transport ready (${transportType})`),
|
||||
{ transportType }
|
||||
);
|
||||
|
||||
const client = new Client(
|
||||
{
|
||||
|
|
@ -116,19 +188,83 @@ export class MCPService {
|
|||
{ capabilities: effectiveCapabilities }
|
||||
);
|
||||
|
||||
// Phase: Initializing
|
||||
onPhase?.(
|
||||
MCPConnectionPhase.Initializing,
|
||||
this.createLog(MCPConnectionPhase.Initializing, 'Sending initialize request...')
|
||||
);
|
||||
|
||||
console.log(`[MCPService][${serverName}] Connecting to server...`);
|
||||
await client.connect(transport);
|
||||
|
||||
console.log(`[MCPService][${serverName}] Connected, listing tools...`);
|
||||
const tools = await this.listTools({ client, transport, tools: [], serverName });
|
||||
const serverVersion = client.getServerVersion();
|
||||
const serverCapabilities = client.getServerCapabilities();
|
||||
const instructions = client.getInstructions();
|
||||
const serverInfo = this.extractServerInfo(serverVersion);
|
||||
|
||||
console.log(`[MCPService][${serverName}] Initialization complete with ${tools.length} tools`);
|
||||
// Phase: Capabilities exchanged
|
||||
onPhase?.(
|
||||
MCPConnectionPhase.CapabilitiesExchanged,
|
||||
this.createLog(
|
||||
MCPConnectionPhase.CapabilitiesExchanged,
|
||||
'Capabilities exchanged successfully',
|
||||
MCPLogLevel.Info,
|
||||
{
|
||||
serverCapabilities,
|
||||
serverInfo
|
||||
}
|
||||
),
|
||||
{
|
||||
serverInfo,
|
||||
serverCapabilities,
|
||||
clientCapabilities: effectiveCapabilities,
|
||||
instructions
|
||||
}
|
||||
);
|
||||
|
||||
// Phase: Listing tools
|
||||
onPhase?.(
|
||||
MCPConnectionPhase.ListingTools,
|
||||
this.createLog(MCPConnectionPhase.ListingTools, 'Listing available tools...')
|
||||
);
|
||||
|
||||
console.log(`[MCPService][${serverName}] Connected, listing tools...`);
|
||||
const tools = await this.listTools({
|
||||
client,
|
||||
transport,
|
||||
tools: [],
|
||||
serverName,
|
||||
transportType,
|
||||
connectionTimeMs: 0
|
||||
});
|
||||
|
||||
const connectionTimeMs = Math.round(performance.now() - startTime);
|
||||
|
||||
// Phase: Connected
|
||||
onPhase?.(
|
||||
MCPConnectionPhase.Connected,
|
||||
this.createLog(
|
||||
MCPConnectionPhase.Connected,
|
||||
`Connection established with ${tools.length} tools (${connectionTimeMs}ms)`
|
||||
)
|
||||
);
|
||||
|
||||
console.log(
|
||||
`[MCPService][${serverName}] Initialization complete with ${tools.length} tools in ${connectionTimeMs}ms`
|
||||
);
|
||||
|
||||
return {
|
||||
client,
|
||||
transport,
|
||||
tools,
|
||||
serverName
|
||||
serverName,
|
||||
transportType,
|
||||
serverInfo,
|
||||
serverCapabilities,
|
||||
clientCapabilities: effectiveCapabilities,
|
||||
protocolVersion: DEFAULT_MCP_CONFIG.protocolVersion,
|
||||
instructions,
|
||||
connectionTimeMs
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue