diff --git a/README.md b/README.md index bc0a4a7..5e2fba1 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,17 @@ To bypass authentication, or to emit custom headers on all requests to your remo ] ``` +* To change which host `mcp-remote` registers as the OAuth callback URL (by default `localhost`), add the `--host` flag. + +```json + "args": [ + "mcp-remote", + "https://remote.mcp.server/sse", + "--host", + "127.0.0.1" + ] +``` + * To allow HTTP connections in trusted private networks, add the `--allow-http` flag. Note: This should only be used in secure private networks where traffic cannot be intercepted. ```json diff --git a/src/client.ts b/src/client.ts index d87599c..2fbcc80 100644 --- a/src/client.ts +++ b/src/client.ts @@ -32,6 +32,7 @@ async function runClient( callbackPort: number, headers: Record, transportStrategy: TransportStrategy = 'http-first', + host: string, ) { // Set up event emitter for auth flow const events = new EventEmitter() @@ -46,6 +47,7 @@ async function runClient( const authProvider = new NodeOAuthClientProvider({ serverUrl, callbackPort, + host, clientName: 'MCP CLI Client', }) @@ -152,8 +154,8 @@ async function runClient( // Parse command-line arguments and run the client parseCommandLineArgs(process.argv.slice(2), 'Usage: npx tsx client.ts [callback-port]') - .then(({ serverUrl, callbackPort, headers, transportStrategy }) => { - return runClient(serverUrl, callbackPort, headers, transportStrategy) + .then(({ serverUrl, callbackPort, headers, transportStrategy, host }) => { + return runClient(serverUrl, callbackPort, headers, transportStrategy, host) }) .catch((error) => { console.error('Fatal error:', error) diff --git a/src/lib/node-oauth-client-provider.ts b/src/lib/node-oauth-client-provider.ts index 0826844..4268e86 100644 --- a/src/lib/node-oauth-client-provider.ts +++ b/src/lib/node-oauth-client-provider.ts @@ -36,7 +36,7 @@ export class NodeOAuthClientProvider implements OAuthClientProvider { } get redirectUrl(): string { - return `http://localhost:${this.options.callbackPort}${this.callbackPath}` + return `http://${this.options.host}:${this.options.callbackPort}${this.callbackPath}`; } get clientMetadata() { diff --git a/src/lib/types.ts b/src/lib/types.ts index 723b93f..3f4462c 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -8,6 +8,8 @@ export interface OAuthProviderOptions { serverUrl: string /** Port for the OAuth callback server */ callbackPort: number + /** Desired hostname for the OAuth callback server */ + host: string /** Path for the OAuth callback endpoint */ callbackPath?: string /** Directory to store OAuth credentials */ diff --git a/src/lib/utils.ts b/src/lib/utils.ts index a0a60dc..45ec8fd 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -361,7 +361,7 @@ async function findExistingClientPort(serverUrlHash: string): Promise new URL(uri)).find(({ hostname }) => hostname === 'localhost') + const localhostRedirectUri = clientInfo.redirect_uris.map((uri) => new URL(uri)).find(({ hostname }) => hostname === 'localhost' || hostname === '127.0.0.1') if (!localhostRedirectUri) { throw new Error('Cannot find localhost callback URI from existing client information') } @@ -449,6 +449,14 @@ export async function parseCommandLineArgs(args: string[], usage: string) { } } + // Parse host + let host = 'localhost' // Default + const hostIndex = args.indexOf('--host') + if (hostIndex !== -1 && hostIndex < args.length - 1) { + host = args[hostIndex + 1] + log(`Using callback hostname: ${host}`) + } + if (!serverUrl) { log(usage) process.exit(1) @@ -505,7 +513,7 @@ export async function parseCommandLineArgs(args: string[], usage: string) { }) } - return { serverUrl, callbackPort, headers, transportStrategy } + return { serverUrl, callbackPort, headers, transportStrategy, host } } /** diff --git a/src/proxy.ts b/src/proxy.ts index 535bfe2..2520f8a 100644 --- a/src/proxy.ts +++ b/src/proxy.ts @@ -32,6 +32,7 @@ async function runProxy( callbackPort: number, headers: Record, transportStrategy: TransportStrategy = 'http-first', + host: string, ) { // Set up event emitter for auth flow const events = new EventEmitter() @@ -46,6 +47,7 @@ async function runProxy( const authProvider = new NodeOAuthClientProvider({ serverUrl, callbackPort, + host, clientName: 'MCP CLI Proxy', }) @@ -136,8 +138,8 @@ to the CA certificate file. If using claude_desktop_config.json, this might look // Parse command-line arguments and run the proxy parseCommandLineArgs(process.argv.slice(2), 'Usage: npx tsx proxy.ts [callback-port]') - .then(({ serverUrl, callbackPort, headers, transportStrategy }) => { - return runProxy(serverUrl, callbackPort, headers, transportStrategy) + .then(({ serverUrl, callbackPort, headers, transportStrategy, host }) => { + return runProxy(serverUrl, callbackPort, headers, transportStrategy, host) }) .catch((error) => { log('Fatal error:', error)