From 02305b3d1cd4d0c1c29829e6f4e13a1b16ca178e Mon Sep 17 00:00:00 2001 From: Glen Maddern Date: Thu, 24 Apr 2025 22:16:50 +1000 Subject: [PATCH] Go full lazy with http transport This also means we need to default to sse-first --- src/client.ts | 25 +++++++++---------------- src/lib/utils.ts | 12 ++++++++---- src/proxy.ts | 33 ++++++++++++++------------------- 3 files changed, 31 insertions(+), 39 deletions(-) diff --git a/src/client.ts b/src/client.ts index ae14109..40ac822 100644 --- a/src/client.ts +++ b/src/client.ts @@ -31,7 +31,7 @@ async function runClient( serverUrl: string, callbackPort: number, headers: Record, - transportStrategy: TransportStrategy = 'http-first', + transportStrategy: TransportStrategy = 'sse-first', ) { // Set up event emitter for auth flow const events = new EventEmitter() @@ -66,10 +66,10 @@ async function runClient( // Define an auth initializer function const authInitializer = async () => { const authState = await authCoordinator.initializeAuth() - + // Store server in outer scope for cleanup server = authState.server - + // If auth was completed by another instance, just log that we'll use the auth from disk if (authState.skipBrowserAuth) { log('Authentication was completed by another instance - will use tokens from disk...') @@ -77,23 +77,16 @@ async function runClient( // so we're slightly too early await new Promise((res) => setTimeout(res, 1_000)) } - - return { - waitForAuthCode: authState.waitForAuthCode, - skipBrowserAuth: authState.skipBrowserAuth + + return { + waitForAuthCode: authState.waitForAuthCode, + skipBrowserAuth: authState.skipBrowserAuth, } } - + try { // Connect to remote server with lazy authentication - const transport = await connectToRemoteServer( - client, - serverUrl, - authProvider, - headers, - authInitializer, - transportStrategy, - ) + const transport = await connectToRemoteServer(client, serverUrl, authProvider, headers, authInitializer, transportStrategy) // Set up message and error handlers transport.onmessage = (message) => { diff --git a/src/lib/utils.ts b/src/lib/utils.ts index f905ad4..6bba9d5 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -93,12 +93,12 @@ export type AuthInitializer = () => Promise<{ * @returns The connected transport */ export async function connectToRemoteServer( - client: Client, + client: Client | null, serverUrl: string, authProvider: OAuthClientProvider, headers: Record, authInitializer: AuthInitializer, - transportStrategy: TransportStrategy = 'http-first', + transportStrategy: TransportStrategy = 'sse-first', recursionReasons: Set = new Set(), ): Promise { log(`[${pid}] Connecting to remote server: ${serverUrl}`) @@ -140,7 +140,11 @@ export async function connectToRemoteServer( }) try { - await client.connect(transport) + if (client) { + await client.connect(transport) + } else { + await transport.start() + } log(`Connected to remote server using ${transport.constructor.name}`) return transport @@ -380,7 +384,7 @@ export async function parseCommandLineArgs(args: string[], defaultPort: number, const allowHttp = args.includes('--allow-http') // Parse transport strategy - let transportStrategy: TransportStrategy = 'http-first' // Default + let transportStrategy: TransportStrategy = 'sse-first' // Default const transportIndex = args.indexOf('--transport') if (transportIndex !== -1 && transportIndex < args.length - 1) { const strategy = args[transportIndex + 1] diff --git a/src/proxy.ts b/src/proxy.ts index 59c2e48..e90d7a5 100644 --- a/src/proxy.ts +++ b/src/proxy.ts @@ -23,12 +23,16 @@ import { } from './lib/utils' import { NodeOAuthClientProvider } from './lib/node-oauth-client-provider' import { createLazyAuthCoordinator } from './lib/coordination' -import { Client } from '@modelcontextprotocol/sdk/client/index.js' /** * Main function to run the proxy */ -async function runProxy(serverUrl: string, callbackPort: number, headers: Record, transportStrategy: TransportStrategy = 'http-first') { +async function runProxy( + serverUrl: string, + callbackPort: number, + headers: Record, + transportStrategy: TransportStrategy = 'sse-first', +) { // Set up event emitter for auth flow const events = new EventEmitter() @@ -54,10 +58,10 @@ async function runProxy(serverUrl: string, callbackPort: number, headers: Record // Define an auth initializer function const authInitializer = async () => { const authState = await authCoordinator.initializeAuth() - + // Store server in outer scope for cleanup server = authState.server - + // If auth was completed by another instance, just log that we'll use the auth from disk if (authState.skipBrowserAuth) { log('Authentication was completed by another instance - will use tokens from disk') @@ -65,25 +69,16 @@ async function runProxy(serverUrl: string, callbackPort: number, headers: Record // so we're slightly too early await new Promise((res) => setTimeout(res, 1_000)) } - - return { - waitForAuthCode: authState.waitForAuthCode, - skipBrowserAuth: authState.skipBrowserAuth + + return { + waitForAuthCode: authState.waitForAuthCode, + skipBrowserAuth: authState.skipBrowserAuth, } } try { - const client = new Client( - { - name: 'mcp-remote', - version: MCP_REMOTE_VERSION, - }, - { - capabilities: {}, - }, - ) // Connect to remote server with lazy authentication - const remoteTransport = await connectToRemoteServer(client, serverUrl, authProvider, headers, authInitializer, transportStrategy) + const remoteTransport = await connectToRemoteServer(null, serverUrl, authProvider, headers, authInitializer, transportStrategy) // Set up bidirectional proxy between local and remote transports mcpProxy({ @@ -94,7 +89,7 @@ async function runProxy(serverUrl: string, callbackPort: number, headers: Record // Start the local STDIO server await localTransport.start() log('Local STDIO server running') - log('Proxy established successfully between local STDIO and remote SSE') + log(`Proxy established successfully between local STDIO and remote ${remoteTransport.constructor.name}`) log('Press Ctrl+C to exit') // Setup cleanup handler