diff --git a/src/lib/node-oauth-client-provider.ts b/src/lib/node-oauth-client-provider.ts index 806f3af..e074e13 100644 --- a/src/lib/node-oauth-client-provider.ts +++ b/src/lib/node-oauth-client-provider.ts @@ -22,18 +22,23 @@ export class NodeOAuthClientProvider implements OAuthClientProvider { private clientUri: string private softwareId: string private softwareVersion: string - + private clientId: string | undefined + private clientSecret: string | undefined + private scope: string | undefined /** * Creates a new NodeOAuthClientProvider * @param options Configuration options for the provider */ - constructor(readonly options: OAuthProviderOptions) { + constructor(readonly options: OAuthProviderOptions & { clientId?: string, clientSecret?: string, scope?: string }) { this.serverUrlHash = getServerUrlHash(options.serverUrl) this.callbackPath = options.callbackPath || '/oauth/callback' this.clientName = options.clientName || 'MCP CLI Client' this.clientUri = options.clientUri || 'https://github.com/modelcontextprotocol/mcp-cli' this.softwareId = options.softwareId || '2e6dc280-f3c3-4e01-99a7-8181dbd1d23d' this.softwareVersion = options.softwareVersion || MCP_REMOTE_VERSION + this.clientId = options.clientId + this.clientSecret = options.clientSecret + this.scope = options.scope } get redirectUrl(): string { @@ -50,6 +55,7 @@ export class NodeOAuthClientProvider implements OAuthClientProvider { client_uri: this.clientUri, software_id: this.softwareId, software_version: this.softwareVersion, + scope: this.scope, } } @@ -59,6 +65,12 @@ export class NodeOAuthClientProvider implements OAuthClientProvider { */ async clientInformation(): Promise { // log('Reading client info') + if (this.clientId) { + return { + client_id: this.clientId, + client_secret: this.clientSecret, + } + } return readJsonFile(this.serverUrlHash, 'client_info.json', OAuthClientInformationSchema) } diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 572550c..d9108f3 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -392,9 +392,49 @@ export async function findAvailablePort(preferredPort?: number): Promise export async function parseCommandLineArgs(args: string[], defaultPort: number, usage: string) { // Process headers const headers: Record = {} + let clientId: string | undefined = undefined + let clientSecret: string | undefined = undefined + let callbackPath: string | undefined = undefined + let scope: string | undefined = undefined let i = 0 while (i < args.length) { - if (args[i] === '--header' && i < args.length - 1) { + if (args[i] === '--client-id' && i < args.length - 1) { + const value = args[i + 1] + if (value) { + clientId = value + } else { + log('Warning: ignoring empty client-id argument') + } + args.splice(i, 2) + continue + } else if (args[i] === '--client-secret' && i < args.length - 1) { + const value = args[i + 1] + if (value) { + clientSecret = value + } else { + log('Warning: ignoring empty client-secret argument') + } + args.splice(i, 2) + continue + } else if (args[i] === '--callback-path' && i < args.length - 1) { + const value = args[i + 1] + if (value) { + callbackPath = value + } else { + log('Warning: ignoring empty callback-path argument') + } + args.splice(i, 2) + continue + } else if (args[i] === '--scope' && i < args.length - 1) { + const value = args[i + 1] + if (value) { + scope = value + } else { + log('Warning: ignoring empty scope argument') + } + args.splice(i, 2) + continue + } else if (args[i] === '--header' && i < args.length - 1) { const value = args[i + 1] const match = value.match(/^([A-Za-z0-9_-]+):(.*)$/) if (match) { @@ -468,7 +508,7 @@ export async function parseCommandLineArgs(args: string[], defaultPort: number, }) } - return { serverUrl, callbackPort, headers, transportStrategy } + return { serverUrl, callbackPort, headers, transportStrategy, callbackPath, clientId, clientSecret, scope } } /** diff --git a/src/proxy.ts b/src/proxy.ts index 7263a95..94cf1c5 100644 --- a/src/proxy.ts +++ b/src/proxy.ts @@ -32,6 +32,10 @@ async function runProxy( callbackPort: number, headers: Record, transportStrategy: TransportStrategy = 'http-first', + callbackPath?: string, + clientId?: string, + clientSecret?: string, + scope?: string, ) { // Set up event emitter for auth flow const events = new EventEmitter() @@ -46,7 +50,11 @@ async function runProxy( const authProvider = new NodeOAuthClientProvider({ serverUrl, callbackPort, + callbackPath, clientName: 'MCP CLI Proxy', + clientId: clientId, + clientSecret: clientSecret, + scope: scope, }) // Create the STDIO transport for local connections @@ -136,8 +144,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), 3334, 'Usage: npx tsx proxy.ts [callback-port]') - .then(({ serverUrl, callbackPort, headers, transportStrategy }) => { - return runProxy(serverUrl, callbackPort, headers, transportStrategy) + .then(({ serverUrl, callbackPort, headers, transportStrategy, callbackPath, clientId, clientSecret, scope }) => { + return runProxy(serverUrl, callbackPort, headers, transportStrategy, callbackPath, clientId, clientSecret, scope) }) .catch((error) => { log('Fatal error:', error)