most of the implementation looking ok but sharing the token the wrong way

This commit is contained in:
Glen Maddern 2025-03-31 19:38:52 +11:00
parent 743b6b207f
commit 412b5d9486
6 changed files with 364 additions and 62 deletions

View file

@ -18,7 +18,8 @@ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'
import { ListResourcesResultSchema, ListToolsResultSchema } from '@modelcontextprotocol/sdk/types.js'
import { UnauthorizedError } from '@modelcontextprotocol/sdk/client/auth.js'
import { NodeOAuthClientProvider } from './lib/node-oauth-client-provider'
import { parseCommandLineArgs, setupOAuthCallbackServer, setupSignalHandlers, MCP_REMOTE_VERSION } from './lib/utils'
import { parseCommandLineArgs, setupSignalHandlers, log, MCP_REMOTE_VERSION, getServerUrlHash } from './lib/utils'
import { coordinateAuth } from './lib/coordination'
/**
* Main function to run the client
@ -27,6 +28,12 @@ async function runClient(serverUrl: string, callbackPort: number, clean: boolean
// Set up event emitter for auth flow
const events = new EventEmitter()
// Get the server URL hash for lockfile operations
const serverUrlHash = getServerUrlHash(serverUrl)
// Coordinate authentication with other instances
const { server, waitForAuthCode, skipBrowserAuth } = await coordinateAuth(serverUrlHash, callbackPort, events)
// Create the OAuth client provider
const authProvider = new NodeOAuthClientProvider({
serverUrl,
@ -35,6 +42,11 @@ async function runClient(serverUrl: string, callbackPort: number, clean: boolean
clean,
})
// If we got auth from another instance, pre-populate with the received code
if (skipBrowserAuth) {
log('Using auth code from another instance')
}
// Create the client
const client = new Client(
{
@ -53,15 +65,15 @@ async function runClient(serverUrl: string, callbackPort: number, clean: boolean
// Set up message and error handlers
transport.onmessage = (message) => {
console.log('Received message:', JSON.stringify(message, null, 2))
log('Received message:', JSON.stringify(message, null, 2))
}
transport.onerror = (error) => {
console.error('Transport error:', error)
log('Transport error:', error)
}
transport.onclose = () => {
console.log('Connection closed.')
log('Connection closed.')
process.exit(0)
}
return transport
@ -69,16 +81,9 @@ async function runClient(serverUrl: string, callbackPort: number, clean: boolean
const transport = initTransport()
// Set up an HTTP server to handle OAuth callback
const { server, waitForAuthCode } = setupOAuthCallbackServer({
port: callbackPort,
path: '/oauth/callback',
events,
})
// Set up cleanup handler
const cleanup = async () => {
console.log('\nClosing connection...')
log('\nClosing connection...')
await client.close()
server.close()
}
@ -86,44 +91,44 @@ async function runClient(serverUrl: string, callbackPort: number, clean: boolean
// Try to connect
try {
console.log('Connecting to server...')
log('Connecting to server...')
await client.connect(transport)
console.log('Connected successfully!')
log('Connected successfully!')
} catch (error) {
if (error instanceof UnauthorizedError || (error instanceof Error && error.message.includes('Unauthorized'))) {
console.log('Authentication required. Waiting for authorization...')
log('Authentication required. Waiting for authorization...')
// Wait for the authorization code from the callback
// Wait for the authorization code from the callback or another instance
const code = await waitForAuthCode()
try {
console.log('Completing authorization...')
log('Completing authorization...')
await transport.finishAuth(code)
// Reconnect after authorization with a new transport
console.log('Connecting after authorization...')
log('Connecting after authorization...')
await client.connect(initTransport())
console.log('Connected successfully!')
log('Connected successfully!')
// Request tools list after auth
console.log('Requesting tools list...')
log('Requesting tools list...')
const tools = await client.request({ method: 'tools/list' }, ListToolsResultSchema)
console.log('Tools:', JSON.stringify(tools, null, 2))
log('Tools:', JSON.stringify(tools, null, 2))
// Request resources list after auth
console.log('Requesting resource list...')
log('Requesting resource list...')
const resources = await client.request({ method: 'resources/list' }, ListResourcesResultSchema)
console.log('Resources:', JSON.stringify(resources, null, 2))
log('Resources:', JSON.stringify(resources, null, 2))
console.log('Listening for messages. Press Ctrl+C to exit.')
log('Listening for messages. Press Ctrl+C to exit.')
} catch (authError) {
console.error('Authorization error:', authError)
log('Authorization error:', authError)
server.close()
process.exit(1)
}
} else {
console.error('Connection error:', error)
log('Connection error:', error)
server.close()
process.exit(1)
}
@ -131,23 +136,23 @@ async function runClient(serverUrl: string, callbackPort: number, clean: boolean
try {
// Request tools list
console.log('Requesting tools list...')
log('Requesting tools list...')
const tools = await client.request({ method: 'tools/list' }, ListToolsResultSchema)
console.log('Tools:', JSON.stringify(tools, null, 2))
log('Tools:', JSON.stringify(tools, null, 2))
} catch (e) {
console.log('Error requesting tools list:', e)
log('Error requesting tools list:', e)
}
try {
// Request resources list
console.log('Requesting resource list...')
log('Requesting resource list...')
const resources = await client.request({ method: 'resources/list' }, ListResourcesResultSchema)
console.log('Resources:', JSON.stringify(resources, null, 2))
log('Resources:', JSON.stringify(resources, null, 2))
} catch (e) {
console.log('Error requesting resources list:', e)
log('Error requesting resources list:', e)
}
console.log('Listening for messages. Press Ctrl+C to exit.')
log('Listening for messages. Press Ctrl+C to exit.')
}
// Parse command-line arguments and run the client