Go full lazy with http transport

This also means we need to default to sse-first
This commit is contained in:
Glen Maddern 2025-04-24 22:16:50 +10:00
parent 1908c203ff
commit 02305b3d1c
3 changed files with 31 additions and 39 deletions

View file

@ -31,7 +31,7 @@ async function runClient(
serverUrl: string,
callbackPort: number,
headers: Record<string, string>,
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) => {

View file

@ -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<string, string>,
authInitializer: AuthInitializer,
transportStrategy: TransportStrategy = 'http-first',
transportStrategy: TransportStrategy = 'sse-first',
recursionReasons: Set<string> = new Set(),
): Promise<Transport> {
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]

View file

@ -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<string, string>, transportStrategy: TransportStrategy = 'http-first') {
async function runProxy(
serverUrl: string,
callbackPort: number,
headers: Record<string, string>,
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