wip, client now using the util with fallback
This commit is contained in:
parent
f80c6c4850
commit
0bf84d5d22
1 changed files with 49 additions and 78 deletions
127
src/client.ts
127
src/client.ts
|
@ -11,17 +11,28 @@
|
||||||
|
|
||||||
import { EventEmitter } from 'events'
|
import { EventEmitter } from 'events'
|
||||||
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
|
||||||
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'
|
|
||||||
import { ListResourcesResultSchema, ListToolsResultSchema } from '@modelcontextprotocol/sdk/types.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 { NodeOAuthClientProvider } from './lib/node-oauth-client-provider'
|
||||||
import { parseCommandLineArgs, setupSignalHandlers, log, MCP_REMOTE_VERSION, getServerUrlHash } from './lib/utils'
|
import {
|
||||||
|
parseCommandLineArgs,
|
||||||
|
setupSignalHandlers,
|
||||||
|
log,
|
||||||
|
MCP_REMOTE_VERSION,
|
||||||
|
getServerUrlHash,
|
||||||
|
connectToRemoteServer,
|
||||||
|
TransportStrategy,
|
||||||
|
} from './lib/utils'
|
||||||
import { coordinateAuth } from './lib/coordination'
|
import { coordinateAuth } from './lib/coordination'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main function to run the client
|
* Main function to run the client
|
||||||
*/
|
*/
|
||||||
async function runClient(serverUrl: string, callbackPort: number, headers: Record<string, string>) {
|
async function runClient(
|
||||||
|
serverUrl: string,
|
||||||
|
callbackPort: number,
|
||||||
|
headers: Record<string, string>,
|
||||||
|
transportStrategy: TransportStrategy = 'http-first',
|
||||||
|
) {
|
||||||
// Set up event emitter for auth flow
|
// Set up event emitter for auth flow
|
||||||
const events = new EventEmitter()
|
const events = new EventEmitter()
|
||||||
|
|
||||||
|
@ -57,10 +68,9 @@ async function runClient(serverUrl: string, callbackPort: number, headers: Recor
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create the transport factory
|
try {
|
||||||
const url = new URL(serverUrl)
|
// Connect to remote server with authentication
|
||||||
function initTransport() {
|
const transport = await connectToRemoteServer(serverUrl, authProvider, headers, waitForAuthCode, skipBrowserAuth, transportStrategy)
|
||||||
const transport = new SSEClientTransport(url, { authProvider, requestInit: { headers } })
|
|
||||||
|
|
||||||
// Set up message and error handlers
|
// Set up message and error handlers
|
||||||
transport.onmessage = (message) => {
|
transport.onmessage = (message) => {
|
||||||
|
@ -75,89 +85,50 @@ async function runClient(serverUrl: string, callbackPort: number, headers: Recor
|
||||||
log('Connection closed.')
|
log('Connection closed.')
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
}
|
}
|
||||||
return transport
|
|
||||||
}
|
|
||||||
|
|
||||||
const transport = initTransport()
|
// Set up cleanup handler
|
||||||
|
const cleanup = async () => {
|
||||||
|
log('\nClosing connection...')
|
||||||
|
await client.close()
|
||||||
|
server.close()
|
||||||
|
}
|
||||||
|
setupSignalHandlers(cleanup)
|
||||||
|
|
||||||
// Set up cleanup handler
|
// Connect the client
|
||||||
const cleanup = async () => {
|
log('Connecting client...')
|
||||||
log('\nClosing connection...')
|
|
||||||
await client.close()
|
|
||||||
server.close()
|
|
||||||
}
|
|
||||||
setupSignalHandlers(cleanup)
|
|
||||||
|
|
||||||
// Try to connect
|
|
||||||
try {
|
|
||||||
log('Connecting to server...')
|
|
||||||
await client.connect(transport)
|
await client.connect(transport)
|
||||||
log('Connected successfully!')
|
log('Connected successfully!')
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof UnauthorizedError || (error instanceof Error && error.message.includes('Unauthorized'))) {
|
|
||||||
log('Authentication required. Waiting for authorization...')
|
|
||||||
|
|
||||||
// Wait for the authorization code from the callback or another instance
|
try {
|
||||||
const code = await waitForAuthCode()
|
// Request tools list
|
||||||
|
log('Requesting tools list...')
|
||||||
try {
|
const tools = await client.request({ method: 'tools/list' }, ListToolsResultSchema)
|
||||||
log('Completing authorization...')
|
log('Tools:', JSON.stringify(tools, null, 2))
|
||||||
await transport.finishAuth(code)
|
} catch (e) {
|
||||||
|
log('Error requesting tools list:', e)
|
||||||
// Reconnect after authorization with a new transport
|
|
||||||
log('Connecting after authorization...')
|
|
||||||
await client.connect(initTransport())
|
|
||||||
|
|
||||||
log('Connected successfully!')
|
|
||||||
|
|
||||||
// Request tools list after auth
|
|
||||||
log('Requesting tools list...')
|
|
||||||
const tools = await client.request({ method: 'tools/list' }, ListToolsResultSchema)
|
|
||||||
log('Tools:', JSON.stringify(tools, null, 2))
|
|
||||||
|
|
||||||
// Request resources list after auth
|
|
||||||
log('Requesting resource list...')
|
|
||||||
const resources = await client.request({ method: 'resources/list' }, ListResourcesResultSchema)
|
|
||||||
log('Resources:', JSON.stringify(resources, null, 2))
|
|
||||||
|
|
||||||
log('Listening for messages. Press Ctrl+C to exit.')
|
|
||||||
} catch (authError) {
|
|
||||||
log('Authorization error:', authError)
|
|
||||||
server.close()
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log('Connection error:', error)
|
|
||||||
server.close()
|
|
||||||
process.exit(1)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Request tools list
|
// Request resources list
|
||||||
log('Requesting tools list...')
|
log('Requesting resource list...')
|
||||||
const tools = await client.request({ method: 'tools/list' }, ListToolsResultSchema)
|
const resources = await client.request({ method: 'resources/list' }, ListResourcesResultSchema)
|
||||||
log('Tools:', JSON.stringify(tools, null, 2))
|
log('Resources:', JSON.stringify(resources, null, 2))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('Error requesting tools list:', e)
|
log('Error requesting resources list:', e)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
log('Listening for messages. Press Ctrl+C to exit.')
|
||||||
// Request resources list
|
} catch (error) {
|
||||||
log('Requesting resource list...')
|
log('Fatal error:', error)
|
||||||
const resources = await client.request({ method: 'resources/list' }, ListResourcesResultSchema)
|
server.close()
|
||||||
log('Resources:', JSON.stringify(resources, null, 2))
|
process.exit(1)
|
||||||
} catch (e) {
|
|
||||||
log('Error requesting resources list:', e)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log('Listening for messages. Press Ctrl+C to exit.')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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), 3333, 'Usage: npx tsx client.ts <https://server-url> [callback-port]')
|
||||||
.then(({ serverUrl, callbackPort, headers }) => {
|
.then(({ serverUrl, callbackPort, headers, transportStrategy }) => {
|
||||||
return runClient(serverUrl, callbackPort, headers)
|
return runClient(serverUrl, callbackPort, headers, transportStrategy)
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('Fatal error:', error)
|
console.error('Fatal error:', error)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue