Skip to content

Commit b1dfa9f

Browse files
committed
Picking a default port based on the server hash
1 parent 6f2399b commit b1dfa9f

File tree

3 files changed

+21
-14
lines changed

3 files changed

+21
-14
lines changed

src/client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ async function runClient(
151151
}
152152

153153
// Parse command-line arguments and run the client
154-
parseCommandLineArgs(process.argv.slice(2), 3333, 'Usage: npx tsx client.ts <https://server-url> [callback-port]')
154+
parseCommandLineArgs(process.argv.slice(2), 'Usage: npx tsx client.ts <https://server-url> [callback-port]')
155155
.then(({ serverUrl, callbackPort, headers, transportStrategy }) => {
156156
return runClient(serverUrl, callbackPort, headers, transportStrategy)
157157
})

src/lib/utils.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,19 @@ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'
44
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
55
import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'
66
import { OAuthClientInformationFull, OAuthClientInformationFullSchema } from '@modelcontextprotocol/sdk/shared/auth.js'
7+
import { OAuthCallbackServerOptions } from './types'
8+
import { getConfigFilePath, readJsonFile } from './mcp-auth-config'
9+
import express from 'express'
10+
import net from 'net'
11+
import crypto from 'crypto'
12+
import fs from 'fs/promises'
713

814
// Connection constants
915
export const REASON_AUTH_NEEDED = 'authentication-needed'
1016
export const REASON_TRANSPORT_FALLBACK = 'falling-back-to-alternate-transport'
1117

1218
// Transport strategy types
1319
export type TransportStrategy = 'sse-only' | 'http-only' | 'sse-first' | 'http-first'
14-
import { OAuthCallbackServerOptions } from './types'
15-
import { getConfigFilePath, readJsonFile } from './mcp-auth-config'
16-
import express from 'express'
17-
import net from 'net'
18-
import crypto from 'crypto'
19-
import fs from 'fs/promises'
2020

2121
// Package version from package.json
2222
export const MCP_REMOTE_VERSION = require('../../package.json').version
@@ -355,8 +355,7 @@ export function setupOAuthCallbackServer(options: OAuthCallbackServerOptions) {
355355
return { server, authCode, waitForAuthCode }
356356
}
357357

358-
async function findExistingClientPort(serverUrl: string): Promise<number | undefined> {
359-
const serverUrlHash = getServerUrlHash(serverUrl)
358+
async function findExistingClientPort(serverUrlHash: string): Promise<number | undefined> {
360359
const clientInfo = await readJsonFile<OAuthClientInformationFull>(serverUrlHash, 'client_info.json', OAuthClientInformationFullSchema)
361360
if (!clientInfo) {
362361
return undefined
@@ -370,6 +369,13 @@ async function findExistingClientPort(serverUrl: string): Promise<number | undef
370369
return parseInt(localhostRedirectUri.port)
371370
}
372371

372+
function calculateDefaultPort(serverUrlHash: string): number {
373+
// Convert the first 4 bytes of the serverUrlHash into a port offset
374+
const offset = parseInt(serverUrlHash.substring(0, 4), 16)
375+
// Pick a consistent but random-seeming port from 3335 to 49151
376+
return 3335 + (offset % 45816)
377+
}
378+
373379
/**
374380
* Finds an available port on the local machine
375381
* @param preferredPort Optional preferred port to try first
@@ -403,11 +409,10 @@ export async function findAvailablePort(preferredPort?: number): Promise<number>
403409
/**
404410
* Parses command line arguments for MCP clients and proxies
405411
* @param args Command line arguments
406-
* @param defaultPort Default port for the callback server if specified port is unavailable
407412
* @param usage Usage message to show on error
408413
* @returns A promise that resolves to an object with parsed serverUrl, callbackPort and headers
409414
*/
410-
export async function parseCommandLineArgs(args: string[], defaultPort: number, usage: string) {
415+
export async function parseCommandLineArgs(args: string[], usage: string) {
411416
// Process headers
412417
const headers: Record<string, string> = {}
413418
let i = 0
@@ -457,17 +462,19 @@ export async function parseCommandLineArgs(args: string[], defaultPort: number,
457462
log(usage)
458463
process.exit(1)
459464
}
465+
const serverUrlHash = getServerUrlHash(serverUrl)
466+
const defaultPort = calculateDefaultPort(serverUrlHash)
460467

461468
// Use the specified port, or the existing client port or fallback to find an available one
462-
const [existingClientPort, availablePort] = await Promise.all([findExistingClientPort(serverUrl), findAvailablePort(defaultPort)])
469+
const [existingClientPort, availablePort] = await Promise.all([findExistingClientPort(serverUrlHash), findAvailablePort(defaultPort)])
463470
let callbackPort: number
464471

465472
if (specifiedPort) {
466473
if (existingClientPort && specifiedPort !== existingClientPort) {
467474
log(
468475
`Warning! Specified callback port of ${specifiedPort}, which conflicts with existing client registration port ${existingClientPort}. Deleting existing client data to force reregistration.`,
469476
)
470-
await fs.rm(getConfigFilePath(getServerUrlHash(serverUrl), 'client_info.json'))
477+
await fs.rm(getConfigFilePath(serverUrlHash, 'client_info.json'))
471478
}
472479
log(`Using specified callback port: ${specifiedPort}`)
473480
callbackPort = specifiedPort

src/proxy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ to the CA certificate file. If using claude_desktop_config.json, this might look
135135
}
136136

137137
// Parse command-line arguments and run the proxy
138-
parseCommandLineArgs(process.argv.slice(2), 3334, 'Usage: npx tsx proxy.ts <https://server-url> [callback-port]')
138+
parseCommandLineArgs(process.argv.slice(2), 'Usage: npx tsx proxy.ts <https://server-url> [callback-port]')
139139
.then(({ serverUrl, callbackPort, headers, transportStrategy }) => {
140140
return runProxy(serverUrl, callbackPort, headers, transportStrategy)
141141
})

0 commit comments

Comments
 (0)