Picking a default port based on the server hash
This commit is contained in:
parent
6f2399bbfb
commit
b1dfa9fe5b
3 changed files with 21 additions and 14 deletions
|
@ -151,7 +151,7 @@ async function runClient(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse command-line arguments and run the client
|
// Parse command-line arguments and run the client
|
||||||
parseCommandLineArgs(process.argv.slice(2), 3333, 'Usage: npx tsx client.ts <https://server-url> [callback-port]')
|
parseCommandLineArgs(process.argv.slice(2), 'Usage: npx tsx client.ts <https://server-url> [callback-port]')
|
||||||
.then(({ serverUrl, callbackPort, headers, transportStrategy }) => {
|
.then(({ serverUrl, callbackPort, headers, transportStrategy }) => {
|
||||||
return runClient(serverUrl, callbackPort, headers, transportStrategy)
|
return runClient(serverUrl, callbackPort, headers, transportStrategy)
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,6 +4,12 @@ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'
|
||||||
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
|
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'
|
||||||
import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'
|
import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'
|
||||||
import { OAuthClientInformationFull, OAuthClientInformationFullSchema } from '@modelcontextprotocol/sdk/shared/auth.js'
|
import { OAuthClientInformationFull, OAuthClientInformationFullSchema } from '@modelcontextprotocol/sdk/shared/auth.js'
|
||||||
|
import { OAuthCallbackServerOptions } from './types'
|
||||||
|
import { getConfigFilePath, readJsonFile } from './mcp-auth-config'
|
||||||
|
import express from 'express'
|
||||||
|
import net from 'net'
|
||||||
|
import crypto from 'crypto'
|
||||||
|
import fs from 'fs/promises'
|
||||||
|
|
||||||
// Connection constants
|
// Connection constants
|
||||||
export const REASON_AUTH_NEEDED = 'authentication-needed'
|
export const REASON_AUTH_NEEDED = 'authentication-needed'
|
||||||
|
@ -11,12 +17,6 @@ export const REASON_TRANSPORT_FALLBACK = 'falling-back-to-alternate-transport'
|
||||||
|
|
||||||
// Transport strategy types
|
// Transport strategy types
|
||||||
export type TransportStrategy = 'sse-only' | 'http-only' | 'sse-first' | 'http-first'
|
export type TransportStrategy = 'sse-only' | 'http-only' | 'sse-first' | 'http-first'
|
||||||
import { OAuthCallbackServerOptions } from './types'
|
|
||||||
import { getConfigFilePath, readJsonFile } from './mcp-auth-config'
|
|
||||||
import express from 'express'
|
|
||||||
import net from 'net'
|
|
||||||
import crypto from 'crypto'
|
|
||||||
import fs from 'fs/promises'
|
|
||||||
|
|
||||||
// Package version from package.json
|
// Package version from package.json
|
||||||
export const MCP_REMOTE_VERSION = require('../../package.json').version
|
export const MCP_REMOTE_VERSION = require('../../package.json').version
|
||||||
|
@ -355,8 +355,7 @@ export function setupOAuthCallbackServer(options: OAuthCallbackServerOptions) {
|
||||||
return { server, authCode, waitForAuthCode }
|
return { server, authCode, waitForAuthCode }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findExistingClientPort(serverUrl: string): Promise<number | undefined> {
|
async function findExistingClientPort(serverUrlHash: string): Promise<number | undefined> {
|
||||||
const serverUrlHash = getServerUrlHash(serverUrl)
|
|
||||||
const clientInfo = await readJsonFile<OAuthClientInformationFull>(serverUrlHash, 'client_info.json', OAuthClientInformationFullSchema)
|
const clientInfo = await readJsonFile<OAuthClientInformationFull>(serverUrlHash, 'client_info.json', OAuthClientInformationFullSchema)
|
||||||
if (!clientInfo) {
|
if (!clientInfo) {
|
||||||
return undefined
|
return undefined
|
||||||
|
@ -370,6 +369,13 @@ async function findExistingClientPort(serverUrl: string): Promise<number | undef
|
||||||
return parseInt(localhostRedirectUri.port)
|
return parseInt(localhostRedirectUri.port)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function calculateDefaultPort(serverUrlHash: string): number {
|
||||||
|
// Convert the first 4 bytes of the serverUrlHash into a port offset
|
||||||
|
const offset = parseInt(serverUrlHash.substring(0, 4), 16)
|
||||||
|
// Pick a consistent but random-seeming port from 3335 to 49151
|
||||||
|
return 3335 + (offset % 45816)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds an available port on the local machine
|
* Finds an available port on the local machine
|
||||||
* @param preferredPort Optional preferred port to try first
|
* @param preferredPort Optional preferred port to try first
|
||||||
|
@ -403,11 +409,10 @@ export async function findAvailablePort(preferredPort?: number): Promise<number>
|
||||||
/**
|
/**
|
||||||
* Parses command line arguments for MCP clients and proxies
|
* Parses command line arguments for MCP clients and proxies
|
||||||
* @param args Command line arguments
|
* @param args Command line arguments
|
||||||
* @param defaultPort Default port for the callback server if specified port is unavailable
|
|
||||||
* @param usage Usage message to show on error
|
* @param usage Usage message to show on error
|
||||||
* @returns A promise that resolves to an object with parsed serverUrl, callbackPort and headers
|
* @returns A promise that resolves to an object with parsed serverUrl, callbackPort and headers
|
||||||
*/
|
*/
|
||||||
export async function parseCommandLineArgs(args: string[], defaultPort: number, usage: string) {
|
export async function parseCommandLineArgs(args: string[], usage: string) {
|
||||||
// Process headers
|
// Process headers
|
||||||
const headers: Record<string, string> = {}
|
const headers: Record<string, string> = {}
|
||||||
let i = 0
|
let i = 0
|
||||||
|
@ -457,9 +462,11 @@ export async function parseCommandLineArgs(args: string[], defaultPort: number,
|
||||||
log(usage)
|
log(usage)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
const serverUrlHash = getServerUrlHash(serverUrl)
|
||||||
|
const defaultPort = calculateDefaultPort(serverUrlHash)
|
||||||
|
|
||||||
// Use the specified port, or the existing client port or fallback to find an available one
|
// Use the specified port, or the existing client port or fallback to find an available one
|
||||||
const [existingClientPort, availablePort] = await Promise.all([findExistingClientPort(serverUrl), findAvailablePort(defaultPort)])
|
const [existingClientPort, availablePort] = await Promise.all([findExistingClientPort(serverUrlHash), findAvailablePort(defaultPort)])
|
||||||
let callbackPort: number
|
let callbackPort: number
|
||||||
|
|
||||||
if (specifiedPort) {
|
if (specifiedPort) {
|
||||||
|
@ -467,7 +474,7 @@ export async function parseCommandLineArgs(args: string[], defaultPort: number,
|
||||||
log(
|
log(
|
||||||
`Warning! Specified callback port of ${specifiedPort}, which conflicts with existing client registration port ${existingClientPort}. Deleting existing client data to force reregistration.`,
|
`Warning! Specified callback port of ${specifiedPort}, which conflicts with existing client registration port ${existingClientPort}. Deleting existing client data to force reregistration.`,
|
||||||
)
|
)
|
||||||
await fs.rm(getConfigFilePath(getServerUrlHash(serverUrl), 'client_info.json'))
|
await fs.rm(getConfigFilePath(serverUrlHash, 'client_info.json'))
|
||||||
}
|
}
|
||||||
log(`Using specified callback port: ${specifiedPort}`)
|
log(`Using specified callback port: ${specifiedPort}`)
|
||||||
callbackPort = specifiedPort
|
callbackPort = specifiedPort
|
||||||
|
|
|
@ -135,7 +135,7 @@ to the CA certificate file. If using claude_desktop_config.json, this might look
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse command-line arguments and run the proxy
|
// Parse command-line arguments and run the proxy
|
||||||
parseCommandLineArgs(process.argv.slice(2), 3334, 'Usage: npx tsx proxy.ts <https://server-url> [callback-port]')
|
parseCommandLineArgs(process.argv.slice(2), 'Usage: npx tsx proxy.ts <https://server-url> [callback-port]')
|
||||||
.then(({ serverUrl, callbackPort, headers, transportStrategy }) => {
|
.then(({ serverUrl, callbackPort, headers, transportStrategy }) => {
|
||||||
return runProxy(serverUrl, callbackPort, headers, transportStrategy)
|
return runProxy(serverUrl, callbackPort, headers, transportStrategy)
|
||||||
})
|
})
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue