Simplify MCP connection handling and remove health checks
- Remove auth type selection from UI - servers will auto-detect OAuth when needed - Remove auto-reconnect logic and health checks to reduce complexity - Always initialize OAuth provider for all connections - Simplify transport selection - always try streamable-http first, then SSE - Remove unused config options (transport preference, auto-reconnect) - Add TODO comment about CORS issues for failed fetch errors - Make authType and transport optional in connection types - Clean up connection state notifications for better UI updates 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
7872b5fdb7
commit
728fea3665
3 changed files with 65 additions and 193 deletions
|
|
@ -9,7 +9,6 @@ export function MCPTab() {
|
|||
const [showAddForm, setShowAddForm] = useState(false);
|
||||
const [newServerName, setNewServerName] = useState('');
|
||||
const [newServerUrl, setNewServerUrl] = useState('');
|
||||
const [authType, setAuthType] = useState<'none' | 'oauth'>('none');
|
||||
|
||||
// Collapsible section states
|
||||
const [showSummary, setShowSummary] = useState(true);
|
||||
|
|
@ -21,7 +20,6 @@ export function MCPTab() {
|
|||
await addMcpServer({
|
||||
name: 'Example Server',
|
||||
url: 'https://example-server.modelcontextprotocol.io/sse',
|
||||
authType: 'oauth',
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to add example server:', error);
|
||||
|
|
@ -36,13 +34,11 @@ export function MCPTab() {
|
|||
await addMcpServer({
|
||||
name: newServerName,
|
||||
url: newServerUrl,
|
||||
authType,
|
||||
});
|
||||
|
||||
// Reset form
|
||||
setNewServerName('');
|
||||
setNewServerUrl('');
|
||||
setAuthType('none');
|
||||
setShowAddForm(false);
|
||||
} catch (error) {
|
||||
console.error('Failed to add custom server:', error);
|
||||
|
|
@ -175,21 +171,6 @@ export function MCPTab() {
|
|||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-1">
|
||||
Authentication
|
||||
</label>
|
||||
<select
|
||||
value={authType}
|
||||
onChange={(e) => setAuthType(e.target.value as 'none' | 'oauth')}
|
||||
className="w-full px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
>
|
||||
<option value="none">None</option>
|
||||
<option value="oauth">OAuth</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isLoading || !newServerName.trim() || !newServerUrl.trim()}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|||
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
||||
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
||||
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
||||
import {
|
||||
import {
|
||||
auth,
|
||||
type OAuthClientProvider
|
||||
} from '@modelcontextprotocol/sdk/client/auth.js';
|
||||
|
|
@ -60,18 +60,18 @@ class MCPOAuthProvider implements OAuthClientProvider {
|
|||
pendingAuthorizationCode?: string;
|
||||
authError?: string;
|
||||
private onOAuthComplete?: () => void;
|
||||
|
||||
|
||||
constructor(connectionId: string, serverName: string, serverUrl: string, onOAuthComplete?: () => void) {
|
||||
this.connectionId = connectionId;
|
||||
this.serverName = serverName;
|
||||
this.serverUrl = serverUrl;
|
||||
this.onOAuthComplete = onOAuthComplete;
|
||||
}
|
||||
|
||||
|
||||
get redirectUrl(): string {
|
||||
return `${window.location.origin}/oauth/mcp/callback`;
|
||||
}
|
||||
|
||||
|
||||
get clientMetadata(): OAuthClientMetadata {
|
||||
return {
|
||||
redirect_uris: [this.redirectUrl],
|
||||
|
|
@ -81,13 +81,13 @@ class MCPOAuthProvider implements OAuthClientProvider {
|
|||
token_endpoint_auth_method: 'none', // Public client
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
state(): string {
|
||||
// Encode connection ID in the state parameter for callback identification
|
||||
const randomPart = this.generateRandomString(8);
|
||||
return `${this.connectionId}.${randomPart}`;
|
||||
}
|
||||
|
||||
|
||||
// Generate a consistent key for the server based on URL
|
||||
getServerKey(): string {
|
||||
try {
|
||||
|
|
@ -106,23 +106,23 @@ class MCPOAuthProvider implements OAuthClientProvider {
|
|||
const stored = localStorage.getItem(`mcp_oauth_client_${serverKey}`);
|
||||
return stored ? JSON.parse(stored) : undefined;
|
||||
}
|
||||
|
||||
|
||||
async saveClientInformation(clientInformation: OAuthClientInformation): Promise<void> {
|
||||
const serverKey = this.getServerKey();
|
||||
localStorage.setItem(`mcp_oauth_client_${serverKey}`, JSON.stringify(clientInformation));
|
||||
}
|
||||
|
||||
|
||||
tokens(): OAuthTokens | undefined {
|
||||
const stored = localStorage.getItem(`mcp_oauth_tokens_${this.connectionId}`);
|
||||
return stored ? JSON.parse(stored) : undefined;
|
||||
}
|
||||
|
||||
|
||||
async saveTokens(tokens: OAuthTokens): Promise<void> {
|
||||
localStorage.setItem(`mcp_oauth_tokens_${this.connectionId}`, JSON.stringify(tokens));
|
||||
}
|
||||
|
||||
|
||||
async redirectToAuthorization(authorizationUrl: URL): Promise<void> {
|
||||
|
||||
|
||||
// Open popup for OAuth flow
|
||||
const popup = window.open(
|
||||
authorizationUrl.toString(),
|
||||
|
|
@ -138,7 +138,7 @@ class MCPOAuthProvider implements OAuthClientProvider {
|
|||
const handleMessage = (event: MessageEvent) => {
|
||||
if (event.origin !== window.location.origin) return;
|
||||
if (event.data.type !== 'mcp_oauth_callback') return;
|
||||
|
||||
|
||||
// Extract connection ID from state parameter
|
||||
const state = event.data.state;
|
||||
if (!state || !state.startsWith(this.connectionId + '.')) return;
|
||||
|
|
@ -157,7 +157,7 @@ class MCPOAuthProvider implements OAuthClientProvider {
|
|||
|
||||
window.addEventListener('message', handleMessage);
|
||||
}
|
||||
|
||||
|
||||
async saveCodeVerifier(codeVerifier: string): Promise<void> {
|
||||
const oauthState: MCPOAuthState = {
|
||||
codeVerifier,
|
||||
|
|
@ -166,22 +166,22 @@ class MCPOAuthProvider implements OAuthClientProvider {
|
|||
};
|
||||
localStorage.setItem(`mcp_oauth_state_${this.connectionId}`, JSON.stringify(oauthState));
|
||||
}
|
||||
|
||||
|
||||
async codeVerifier(): Promise<string> {
|
||||
const stored = localStorage.getItem(`mcp_oauth_state_${this.connectionId}`);
|
||||
if (!stored) {
|
||||
throw new Error('No OAuth state found');
|
||||
}
|
||||
|
||||
|
||||
const oauthState: MCPOAuthState = JSON.parse(stored);
|
||||
if (Date.now() > oauthState.expiresAt) {
|
||||
localStorage.removeItem(`mcp_oauth_state_${this.connectionId}`);
|
||||
throw new Error('OAuth state expired');
|
||||
}
|
||||
|
||||
|
||||
return oauthState.codeVerifier;
|
||||
}
|
||||
|
||||
|
||||
private generateRandomString(length: number): string {
|
||||
const array = new Uint8Array(length);
|
||||
crypto.getRandomValues(array);
|
||||
|
|
@ -199,7 +199,7 @@ class MCPOAuthProvider implements OAuthClientProvider {
|
|||
serverUrl: this.serverUrl,
|
||||
authorizationCode,
|
||||
});
|
||||
|
||||
|
||||
if (result === 'AUTHORIZED') {
|
||||
this.notifyOAuthComplete();
|
||||
} else {
|
||||
|
|
@ -230,7 +230,7 @@ class MCPOAuthProvider implements OAuthClientProvider {
|
|||
export class MCPConnectionManager {
|
||||
private connection: MCPConnection;
|
||||
private client?: Client;
|
||||
private transport?: Transport;
|
||||
private transport?: Transport | StreamableHTTPClientTransport | SSEClientTransport;
|
||||
private reconnectTimeout?: NodeJS.Timeout;
|
||||
private healthCheckInterval?: NodeJS.Timeout;
|
||||
private oauthProvider?: MCPOAuthProvider;
|
||||
|
|
@ -243,22 +243,16 @@ export class MCPConnectionManager {
|
|||
name: config.name,
|
||||
url: config.url,
|
||||
status: 'disconnected',
|
||||
transport: 'streamable-http', // Will be determined during connection
|
||||
authType: config.authType || 'none',
|
||||
tools: [],
|
||||
resources: [],
|
||||
prompts: [],
|
||||
connectionAttempts: 0,
|
||||
config,
|
||||
};
|
||||
|
||||
// Initialize OAuth provider if auth is required
|
||||
if (this.connection.authType === 'oauth') {
|
||||
this.oauthProvider = new MCPOAuthProvider(id, config.name, config.url, () => {
|
||||
// Callback when OAuth completes successfully
|
||||
this.handleOAuthSuccess();
|
||||
});
|
||||
}
|
||||
this.oauthProvider = new MCPOAuthProvider(id, config.name, config.url, () => {
|
||||
// Callback when OAuth completes successfully
|
||||
this.handleOAuthSuccess();
|
||||
});
|
||||
}
|
||||
|
||||
getConnection(): MCPConnection {
|
||||
|
|
@ -272,7 +266,6 @@ export class MCPConnectionManager {
|
|||
|
||||
// Set callback for message monitoring
|
||||
setMessageCallback(callback: (connectionId: string, client: any, message: any, direction: 'sent' | 'received', extra?: any) => void): void {
|
||||
console.log(`Setting message callback for connection ${this.connection.id}`); // Debug log
|
||||
this.onMessage = callback;
|
||||
}
|
||||
|
||||
|
|
@ -286,61 +279,37 @@ export class MCPConnectionManager {
|
|||
async connect(): Promise<void> {
|
||||
this.connection.status = 'connecting';
|
||||
this.connection.error = undefined;
|
||||
|
||||
this.notifyConnectionUpdate();
|
||||
|
||||
try {
|
||||
|
||||
|
||||
// Clear any existing connections
|
||||
await this.disconnect();
|
||||
|
||||
// Determine transport strategy
|
||||
const transportPreference = this.connection.config.transport || 'auto';
|
||||
|
||||
if (transportPreference === 'auto' || transportPreference === 'streamable-http') {
|
||||
try {
|
||||
await this.tryStreamableHttp();
|
||||
this.connection.transport = 'streamable-http';
|
||||
} catch (error) {
|
||||
if (transportPreference === 'streamable-http') {
|
||||
throw error; // Don't fallback if explicitly requested
|
||||
}
|
||||
// Try SSE fallback
|
||||
await this.trySSE();
|
||||
this.connection.transport = 'sse';
|
||||
}
|
||||
} else if (transportPreference === 'sse') {
|
||||
|
||||
try {
|
||||
await this.tryStreamableHttp();
|
||||
this.connection.transport = 'streamable-http';
|
||||
} catch (error) {
|
||||
// TODO: jerome - if this is a TypeError: failed to fetch, then there is likely a CORS (or
|
||||
// Access-Control-Expose-Headers) issue with the server.
|
||||
await this.trySSE();
|
||||
this.connection.transport = 'sse';
|
||||
}
|
||||
|
||||
// Initialize client capabilities
|
||||
await this.initializeCapabilities();
|
||||
|
||||
|
||||
this.connection.status = 'connected';
|
||||
this.connection.lastConnected = new Date();
|
||||
this.connection.connectionAttempts = 0;
|
||||
|
||||
// Start health check monitoring
|
||||
this.startHealthCheck();
|
||||
|
||||
this.notifyConnectionUpdate();
|
||||
|
||||
} catch (error) {
|
||||
this.connection.status = 'failed';
|
||||
this.connection.error = error instanceof Error ? error.message : 'Unknown connection error';
|
||||
this.connection.connectionAttempts++;
|
||||
|
||||
// Schedule auto-reconnect if enabled
|
||||
if (this.connection.config.autoReconnect !== false &&
|
||||
this.connection.connectionAttempts < (this.connection.config.maxReconnectAttempts || 5)) {
|
||||
const delay = this.getBackoffDelay();
|
||||
this.reconnectTimeout = setTimeout(async () => {
|
||||
try {
|
||||
await this.reconnect();
|
||||
} catch (error) {
|
||||
console.error('Auto-reconnect failed:', error);
|
||||
// Don't throw here to prevent uncaught promise rejection
|
||||
}
|
||||
}, delay);
|
||||
}
|
||||
|
||||
this.notifyConnectionUpdate();
|
||||
|
||||
throw this.createMCPError('connection', this.connection.error, error);
|
||||
}
|
||||
}
|
||||
|
|
@ -350,12 +319,12 @@ export class MCPConnectionManager {
|
|||
clearTimeout(this.reconnectTimeout);
|
||||
this.reconnectTimeout = undefined;
|
||||
}
|
||||
|
||||
|
||||
if (this.healthCheckInterval) {
|
||||
clearInterval(this.healthCheckInterval);
|
||||
this.healthCheckInterval = undefined;
|
||||
}
|
||||
|
||||
|
||||
if (this.client) {
|
||||
try {
|
||||
await this.client.close();
|
||||
|
|
@ -364,7 +333,7 @@ export class MCPConnectionManager {
|
|||
}
|
||||
this.client = undefined;
|
||||
}
|
||||
|
||||
|
||||
if (this.transport) {
|
||||
try {
|
||||
await this.transport.close();
|
||||
|
|
@ -373,7 +342,7 @@ export class MCPConnectionManager {
|
|||
}
|
||||
this.transport = undefined;
|
||||
}
|
||||
|
||||
|
||||
this.connection.status = 'disconnected';
|
||||
this.connection.client = undefined;
|
||||
}
|
||||
|
|
@ -384,12 +353,12 @@ export class MCPConnectionManager {
|
|||
throw new Error('OAuth provider not initialized');
|
||||
}
|
||||
|
||||
|
||||
|
||||
try {
|
||||
// If we have an active transport, use its finishAuth method
|
||||
if (this.transport && typeof (this.transport as any).finishAuth === 'function') {
|
||||
await (this.transport as any).finishAuth(authorizationCode);
|
||||
|
||||
|
||||
// Now attempt to connect - the transport should be authenticated
|
||||
await this.connect();
|
||||
} else {
|
||||
|
|
@ -398,7 +367,7 @@ export class MCPConnectionManager {
|
|||
this.oauthProvider.pendingAuthorizationCode = authorizationCode;
|
||||
await this.connect();
|
||||
}
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.error('OAuth callback processing failed:', error);
|
||||
throw new Error(`OAuth callback failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
|
|
@ -411,7 +380,7 @@ export class MCPConnectionManager {
|
|||
// Clear connection-specific data
|
||||
localStorage.removeItem(`mcp_oauth_tokens_${this.connection.id}`);
|
||||
localStorage.removeItem(`mcp_oauth_state_${this.connection.id}`);
|
||||
|
||||
|
||||
// Note: We intentionally don't clear client information here since it's
|
||||
// shared across connections to the same server. Use clearSharedClientData()
|
||||
// if you need to clear the client registration for this server.
|
||||
|
|
@ -432,57 +401,36 @@ export class MCPConnectionManager {
|
|||
|
||||
// Handle successful OAuth completion
|
||||
private async handleOAuthSuccess(): Promise<void> {
|
||||
|
||||
|
||||
try {
|
||||
// Reset connection state and retry
|
||||
this.connection.status = 'connecting';
|
||||
this.connection.error = undefined;
|
||||
this.notifyConnectionUpdate();
|
||||
|
||||
// Attempt to connect now that we have valid tokens
|
||||
await this.connect();
|
||||
|
||||
this.notifyConnectionUpdate();
|
||||
} catch (error) {
|
||||
console.error('Post-OAuth connection failed:', error);
|
||||
this.connection.status = 'failed';
|
||||
this.connection.error = error instanceof Error ? error.message : 'Post-OAuth connection failed';
|
||||
this.notifyConnectionUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async tryStreamableHttp(): Promise<void> {
|
||||
try {
|
||||
|
||||
// Create transport options with OAuth provider if configured
|
||||
const transportOptions: any = {};
|
||||
|
||||
if (this.connection.authType === 'oauth' && this.oauthProvider) {
|
||||
transportOptions.authProvider = this.oauthProvider;
|
||||
}
|
||||
|
||||
const transport = new StreamableHTTPClientTransport(new URL(this.connection.url), transportOptions);
|
||||
const transport = new StreamableHTTPClientTransport(new URL(this.connection.url), {
|
||||
authProvider: this.oauthProvider
|
||||
});
|
||||
await this.initializeClient(transport);
|
||||
} catch (error) {
|
||||
console.error('StreamableHTTP connection failed:', error);
|
||||
console.log('StreamableHTTP connection failed:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private async trySSE(): Promise<void> {
|
||||
try {
|
||||
|
||||
// Create transport options with OAuth provider if configured
|
||||
const transportOptions: any = {};
|
||||
|
||||
if (this.connection.authType === 'oauth' && this.oauthProvider) {
|
||||
transportOptions.authProvider = this.oauthProvider;
|
||||
}
|
||||
|
||||
const transport = new SSEClientTransport(new URL(this.connection.url), transportOptions);
|
||||
const transport = new SSEClientTransport(new URL(this.connection.url), {
|
||||
authProvider: this.oauthProvider
|
||||
});
|
||||
await this.initializeClient(transport);
|
||||
} catch (error) {
|
||||
console.log('SSE connection failed:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
|
@ -491,7 +439,7 @@ export class MCPConnectionManager {
|
|||
try {
|
||||
const debugTransport = new DebugTransport(transport);
|
||||
this.transport = debugTransport;
|
||||
|
||||
|
||||
this.client = new Client(
|
||||
{
|
||||
name: 'example-remote-client',
|
||||
|
|
@ -501,20 +449,20 @@ export class MCPConnectionManager {
|
|||
capabilities: {},
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
// Set up message callbacks to broadcast to UI after client is created
|
||||
debugTransport.onsendmessage_ = async (message, options) => {
|
||||
if (this.onMessage && this.client) {
|
||||
this.onMessage(this.connection.id, this.client, message, 'sent', { options });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
debugTransport.onreceivemessage_ = (message, extra) => {
|
||||
if (this.onMessage && this.client) {
|
||||
this.onMessage(this.connection.id, this.client, message, 'received', extra);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
await this.client.connect(debugTransport);
|
||||
this.connection.client = this.client;
|
||||
} catch (error) {
|
||||
|
|
@ -530,7 +478,7 @@ export class MCPConnectionManager {
|
|||
try {
|
||||
// Discover tools
|
||||
this.connection.tools = await this.discoverTools();
|
||||
|
||||
|
||||
// Discover resources (if supported)
|
||||
try {
|
||||
this.connection.resources = await this.discoverResources();
|
||||
|
|
@ -538,7 +486,7 @@ export class MCPConnectionManager {
|
|||
// Resources not supported by this server
|
||||
this.connection.resources = [];
|
||||
}
|
||||
|
||||
|
||||
// Discover prompts (if supported)
|
||||
try {
|
||||
this.connection.prompts = await this.discoverPrompts();
|
||||
|
|
@ -546,7 +494,7 @@ export class MCPConnectionManager {
|
|||
// Prompts not supported by this server
|
||||
this.connection.prompts = [];
|
||||
}
|
||||
|
||||
|
||||
} catch (error) {
|
||||
throw this.createMCPError('protocol', 'Failed to initialize server capabilities', error);
|
||||
}
|
||||
|
|
@ -620,7 +568,7 @@ export class MCPConnectionManager {
|
|||
|
||||
// Remove the server prefix from the tool name (using double underscore separator)
|
||||
const normalizedServerName = normalizeServerName(this.connection.name);
|
||||
const unprefixedName = toolName.startsWith(`${normalizedServerName}__`)
|
||||
const unprefixedName = toolName.startsWith(`${normalizedServerName}__`)
|
||||
? toolName.slice(normalizedServerName.length + 2)
|
||||
: toolName;
|
||||
|
||||
|
|
@ -640,61 +588,6 @@ export class MCPConnectionManager {
|
|||
return this.connection.status;
|
||||
}
|
||||
|
||||
private getBackoffDelay(): number {
|
||||
// Exponential backoff: 1s, 2s, 4s, 8s, 16s
|
||||
return Math.min(1000 * Math.pow(2, this.connection.connectionAttempts - 1), 16000);
|
||||
}
|
||||
|
||||
private startHealthCheck(): void {
|
||||
// Clear any existing health check
|
||||
if (this.healthCheckInterval) {
|
||||
clearInterval(this.healthCheckInterval);
|
||||
}
|
||||
|
||||
// Start health check every 30 seconds
|
||||
this.healthCheckInterval = setInterval(async () => {
|
||||
await this.performHealthCheck();
|
||||
}, 10 * 60 * 1000); // 10 minutes
|
||||
|
||||
}
|
||||
|
||||
private async performHealthCheck(): Promise<void> {
|
||||
// Only check if we're supposed to be connected
|
||||
if (this.connection.status !== 'connected' || !this.client) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Try to list tools as a health check - this is a lightweight operation
|
||||
await this.client.ping();
|
||||
} catch (error) {
|
||||
console.warn(`Health check failed for ${this.connection.name}:`, error);
|
||||
await this.handleHealthCheckFailure(error);
|
||||
}
|
||||
}
|
||||
|
||||
private async handleHealthCheckFailure(error: any): Promise<void> {
|
||||
|
||||
// Stop health check during reconnection attempt
|
||||
if (this.healthCheckInterval) {
|
||||
clearInterval(this.healthCheckInterval);
|
||||
this.healthCheckInterval = undefined;
|
||||
}
|
||||
|
||||
// Mark as disconnected and attempt reconnection
|
||||
this.connection.status = 'connecting';
|
||||
this.connection.error = error instanceof Error ? error.message : 'Health check failed';
|
||||
this.notifyConnectionUpdate();
|
||||
|
||||
try {
|
||||
// Attempt reconnection
|
||||
await this.connect();
|
||||
} catch (reconnectError) {
|
||||
console.error(`Health check reconnection failed for ${this.connection.name}:`, reconnectError);
|
||||
// The connect method will handle scheduling retry attempts
|
||||
}
|
||||
}
|
||||
|
||||
private createMCPError(type: MCPError['type'], message: string, details?: any): MCPError {
|
||||
return {
|
||||
type,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import type { TransportSendOptions } from '@modelcontextprotocol/sdk/shared/tran
|
|||
export interface MCPServerConfig {
|
||||
name: string; // User-provided display name
|
||||
url: string; // Server endpoint URL
|
||||
transport?: 'sse' | 'streamable-http' | 'auto'; // Default: auto-detect
|
||||
authType?: 'none' | 'oauth'; // Default: none
|
||||
oauthConfig?: {
|
||||
clientId?: string;
|
||||
|
|
@ -18,7 +17,6 @@ export interface MCPServerConfig {
|
|||
scope?: string; // OAuth scope
|
||||
redirectUri?: string; // Override default redirect URI
|
||||
};
|
||||
autoReconnect?: boolean; // Default: true
|
||||
maxReconnectAttempts?: number; // Default: 5
|
||||
}
|
||||
|
||||
|
|
@ -75,8 +73,8 @@ export interface MCPConnection {
|
|||
url: string; // Server URL
|
||||
status: 'connecting' | 'connected' | 'failed' | 'disconnected';
|
||||
client?: Client; // MCP SDK client instance
|
||||
transport: 'sse' | 'streamable-http';
|
||||
authType: 'none' | 'oauth';
|
||||
transport?: 'sse' | 'streamable-http';
|
||||
authType?: 'none' | 'oauth';
|
||||
|
||||
// Available capabilities
|
||||
tools: Tool[]; // Tools with name-prefixed identifiers
|
||||
|
|
|
|||
Loading…
Reference in a new issue