Skip to content

Commit 0eafe01

Browse files
authored
Merge branch 'main' into feature/403-upscoping
2 parents 48f4470 + fc4a6ec commit 0eafe01

File tree

2 files changed

+54
-46
lines changed

2 files changed

+54
-46
lines changed

src/examples/client/elicitationUrlExample.ts

Lines changed: 51 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,42 @@ async function waitForOAuthCallback(): Promise<string> {
477477
});
478478
}
479479

480+
/**
481+
* Attempts to connect to the MCP server with OAuth authentication.
482+
* Handles OAuth flow recursively if authorization is required.
483+
*/
484+
async function attemptConnection(oauthProvider: InMemoryOAuthClientProvider): Promise<void> {
485+
console.log('🚢 Creating transport with OAuth provider...');
486+
const baseUrl = new URL(serverUrl);
487+
transport = new StreamableHTTPClientTransport(baseUrl, {
488+
sessionId: sessionId,
489+
authProvider: oauthProvider
490+
});
491+
console.log('🚢 Transport created');
492+
493+
try {
494+
console.log('🔌 Attempting connection (this will trigger OAuth redirect if needed)...');
495+
await client!.connect(transport);
496+
sessionId = transport.sessionId;
497+
console.log('Transport created with session ID:', sessionId);
498+
console.log('✅ Connected successfully');
499+
} catch (error) {
500+
if (error instanceof UnauthorizedError) {
501+
console.log('🔐 OAuth required - waiting for authorization...');
502+
const callbackPromise = waitForOAuthCallback();
503+
const authCode = await callbackPromise;
504+
await transport.finishAuth(authCode);
505+
console.log('🔐 Authorization code received:', authCode);
506+
console.log('🔌 Reconnecting with authenticated transport...');
507+
// Recursively retry connection after OAuth completion
508+
await attemptConnection(oauthProvider);
509+
} else {
510+
console.error('❌ Connection failed with non-auth error:', error);
511+
throw error;
512+
}
513+
}
514+
}
515+
480516
async function connect(url?: string): Promise<void> {
481517
if (client) {
482518
console.log('Already connected. Disconnect first.');
@@ -487,7 +523,10 @@ async function connect(url?: string): Promise<void> {
487523
serverUrl = url;
488524
}
489525

526+
console.log(`🔗 Attempting to connect to ${serverUrl}...`);
527+
490528
// Create a new client with elicitation capability
529+
console.log('👤 Creating MCP client...');
491530
client = new Client(
492531
{
493532
name: 'example-client',
@@ -503,19 +542,7 @@ async function connect(url?: string): Promise<void> {
503542
}
504543
}
505544
);
506-
if (!transport) {
507-
// Only create a new transport if one doesn't exist
508-
transport = new StreamableHTTPClientTransport(new URL(serverUrl), {
509-
sessionId: sessionId,
510-
authProvider: oauthProvider,
511-
requestInit: {
512-
headers: {
513-
'Content-Type': 'application/json',
514-
Accept: 'application/json, text/event-stream'
515-
}
516-
}
517-
});
518-
}
545+
console.log('👤 Client created');
519546

520547
// Set up elicitation request handler with proper validation
521548
client.setRequestHandler(ElicitRequestSchema, elicitationRequestHandler);
@@ -536,42 +563,20 @@ async function connect(url?: string): Promise<void> {
536563
});
537564

538565
try {
539-
console.log(`Connecting to ${serverUrl}...`);
540-
// Connect the client
541-
await client.connect(transport);
542-
sessionId = transport.sessionId;
543-
console.log('Transport created with session ID:', sessionId);
566+
console.log('🔐 Starting OAuth flow...');
567+
await attemptConnection(oauthProvider!);
544568
console.log('Connected to MCP server');
569+
570+
// Set up error handler after connection is established so we don't double log errors
571+
client.onerror = error => {
572+
console.error('\x1b[31mClient error:', error, '\x1b[0m');
573+
};
545574
} catch (error) {
546-
if (error instanceof UnauthorizedError) {
547-
console.log('OAuth required - waiting for authorization...');
548-
const callbackPromise = waitForOAuthCallback();
549-
const authCode = await callbackPromise;
550-
await transport.finishAuth(authCode);
551-
console.log('🔐 Authorization code received:', authCode);
552-
console.log('🔌 Reconnecting with authenticated transport...');
553-
transport = new StreamableHTTPClientTransport(new URL(serverUrl), {
554-
sessionId: sessionId,
555-
authProvider: oauthProvider,
556-
requestInit: {
557-
headers: {
558-
'Content-Type': 'application/json',
559-
Accept: 'application/json, text/event-stream'
560-
}
561-
}
562-
});
563-
await client.connect(transport);
564-
} else {
565-
console.error('Failed to connect:', error);
566-
client = null;
567-
transport = null;
568-
return;
569-
}
575+
console.error('Failed to connect:', error);
576+
client = null;
577+
transport = null;
578+
return;
570579
}
571-
// Set up error handler after connection is established so we don't double log errors
572-
client.onerror = error => {
573-
console.error('\x1b[31mClient error:', error, '\x1b[0m');
574-
};
575580
}
576581

577582
async function disconnect(): Promise<void> {

src/server/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,11 +345,14 @@ export class Server<
345345
async elicitInput(params: ElicitRequestURLParams, options?: RequestOptions): Promise<ElicitResult>;
346346
/**
347347
* Creates an elicitation request for the given parameters.
348+
* @deprecated Use the overloads with explicit `mode: 'form' | 'url'` instead.
348349
* @param params The parameters for the form elicitation request (legacy signature without mode).
349350
* @param options Optional request options.
350351
* @returns The result of the elicitation request.
351352
*/
352353
async elicitInput(params: LegacyElicitRequestFormParams, options?: RequestOptions): Promise<ElicitResult>;
354+
355+
// Implementation (not visible to callers)
353356
async elicitInput(
354357
params: LegacyElicitRequestFormParams | ElicitRequestFormParams | ElicitRequestURLParams,
355358
options?: RequestOptions

0 commit comments

Comments
 (0)