@modelcontextprotocol/sdk 1.22.0 ā 1.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -11
- package/dist/cjs/client/auth.d.ts +11 -1
- package/dist/cjs/client/auth.d.ts.map +1 -1
- package/dist/cjs/client/auth.js +71 -19
- package/dist/cjs/client/auth.js.map +1 -1
- package/dist/cjs/client/index.d.ts +122 -662
- package/dist/cjs/client/index.d.ts.map +1 -1
- package/dist/cjs/client/index.js +72 -15
- package/dist/cjs/client/index.js.map +1 -1
- package/dist/cjs/client/sse.d.ts +1 -0
- package/dist/cjs/client/sse.d.ts.map +1 -1
- package/dist/cjs/client/sse.js +5 -3
- package/dist/cjs/client/sse.js.map +1 -1
- package/dist/cjs/client/streamableHttp.d.ts +14 -2
- package/dist/cjs/client/streamableHttp.d.ts.map +1 -1
- package/dist/cjs/client/streamableHttp.js +79 -19
- package/dist/cjs/client/streamableHttp.js.map +1 -1
- package/dist/cjs/examples/client/elicitationUrlExample.d.ts +2 -0
- package/dist/cjs/examples/client/elicitationUrlExample.d.ts.map +1 -0
- package/dist/cjs/examples/client/elicitationUrlExample.js +693 -0
- package/dist/cjs/examples/client/elicitationUrlExample.js.map +1 -0
- package/dist/cjs/examples/client/simpleOAuthClient.js +13 -52
- package/dist/cjs/examples/client/simpleOAuthClient.js.map +1 -1
- package/dist/cjs/examples/client/simpleOAuthClientProvider.d.ts +26 -0
- package/dist/cjs/examples/client/simpleOAuthClientProvider.d.ts.map +1 -0
- package/dist/cjs/examples/client/simpleOAuthClientProvider.js +51 -0
- package/dist/cjs/examples/client/simpleOAuthClientProvider.js.map +1 -0
- package/dist/cjs/examples/client/simpleStreamableHttp.js +10 -5
- package/dist/cjs/examples/client/simpleStreamableHttp.js.map +1 -1
- package/dist/cjs/examples/client/ssePollingClient.d.ts +2 -0
- package/dist/cjs/examples/client/ssePollingClient.d.ts.map +1 -0
- package/dist/cjs/examples/client/ssePollingClient.js +95 -0
- package/dist/cjs/examples/client/ssePollingClient.js.map +1 -0
- package/dist/cjs/examples/server/demoInMemoryOAuthProvider.d.ts +4 -4
- package/dist/cjs/examples/server/demoInMemoryOAuthProvider.d.ts.map +1 -1
- package/dist/cjs/examples/server/demoInMemoryOAuthProvider.js +16 -0
- package/dist/cjs/examples/server/demoInMemoryOAuthProvider.js.map +1 -1
- package/dist/cjs/examples/server/elicitationFormExample.d.ts +2 -0
- package/dist/cjs/examples/server/elicitationFormExample.d.ts.map +1 -0
- package/dist/cjs/examples/server/{elicitationExample.js ā elicitationFormExample.js} +14 -8
- package/dist/cjs/examples/server/elicitationFormExample.js.map +1 -0
- package/dist/cjs/examples/server/elicitationUrlExample.d.ts +2 -0
- package/dist/cjs/examples/server/elicitationUrlExample.d.ts.map +1 -0
- package/dist/cjs/examples/server/elicitationUrlExample.js +655 -0
- package/dist/cjs/examples/server/elicitationUrlExample.js.map +1 -0
- package/dist/cjs/examples/server/jsonResponseStreamableHttp.js +26 -3
- package/dist/cjs/examples/server/jsonResponseStreamableHttp.js.map +1 -1
- package/dist/cjs/examples/server/mcpServerOutputSchema.js +34 -11
- package/dist/cjs/examples/server/mcpServerOutputSchema.js.map +1 -1
- package/dist/cjs/examples/server/simpleSseServer.js +26 -3
- package/dist/cjs/examples/server/simpleSseServer.js.map +1 -1
- package/dist/cjs/examples/server/simpleStatelessStreamableHttp.js +27 -4
- package/dist/cjs/examples/server/simpleStatelessStreamableHttp.js.map +1 -1
- package/dist/cjs/examples/server/simpleStreamableHttp.js +34 -10
- package/dist/cjs/examples/server/simpleStreamableHttp.js.map +1 -1
- package/dist/cjs/examples/server/sseAndStreamableHttpCompatibleServer.js +26 -3
- package/dist/cjs/examples/server/sseAndStreamableHttpCompatibleServer.js.map +1 -1
- package/dist/cjs/examples/server/ssePollingExample.d.ts +2 -0
- package/dist/cjs/examples/server/ssePollingExample.d.ts.map +1 -0
- package/dist/cjs/examples/server/ssePollingExample.js +116 -0
- package/dist/cjs/examples/server/ssePollingExample.js.map +1 -0
- package/dist/cjs/examples/server/toolWithSampleServer.js +30 -8
- package/dist/cjs/examples/server/toolWithSampleServer.js.map +1 -1
- package/dist/cjs/server/auth/handlers/authorize.js +34 -11
- package/dist/cjs/server/auth/handlers/authorize.js.map +1 -1
- package/dist/cjs/server/auth/handlers/token.js +35 -12
- package/dist/cjs/server/auth/handlers/token.js.map +1 -1
- package/dist/cjs/server/auth/middleware/bearerAuth.d.ts.map +1 -1
- package/dist/cjs/server/auth/middleware/bearerAuth.js +13 -8
- package/dist/cjs/server/auth/middleware/bearerAuth.js.map +1 -1
- package/dist/cjs/server/auth/middleware/clientAuth.js +27 -4
- package/dist/cjs/server/auth/middleware/clientAuth.js.map +1 -1
- package/dist/cjs/server/auth/router.js +1 -1
- package/dist/cjs/server/auth/router.js.map +1 -1
- package/dist/cjs/server/completable.d.ts +30 -16
- package/dist/cjs/server/completable.d.ts.map +1 -1
- package/dist/cjs/server/completable.js +38 -55
- package/dist/cjs/server/completable.js.map +1 -1
- package/dist/cjs/server/index.d.ts +176 -50
- package/dist/cjs/server/index.d.ts.map +1 -1
- package/dist/cjs/server/index.js +99 -13
- package/dist/cjs/server/index.js.map +1 -1
- package/dist/cjs/server/mcp.d.ts +14 -16
- package/dist/cjs/server/mcp.d.ts.map +1 -1
- package/dist/cjs/server/mcp.js +83 -40
- package/dist/cjs/server/mcp.js.map +1 -1
- package/dist/cjs/server/streamableHttp.d.ts +27 -0
- package/dist/cjs/server/streamableHttp.d.ts.map +1 -1
- package/dist/cjs/server/streamableHttp.js +70 -5
- package/dist/cjs/server/streamableHttp.js.map +1 -1
- package/dist/cjs/server/zod-compat.d.ts +82 -0
- package/dist/cjs/server/zod-compat.d.ts.map +1 -0
- package/dist/cjs/server/zod-compat.js +252 -0
- package/dist/cjs/server/zod-compat.js.map +1 -0
- package/dist/cjs/server/zod-json-schema-compat.d.ts +12 -0
- package/dist/cjs/server/zod-json-schema-compat.d.ts.map +1 -0
- package/dist/cjs/server/zod-json-schema-compat.js +80 -0
- package/dist/cjs/server/zod-json-schema-compat.js.map +1 -0
- package/dist/cjs/shared/auth.d.ts +116 -502
- package/dist/cjs/shared/auth.d.ts.map +1 -1
- package/dist/cjs/shared/auth.js +133 -112
- package/dist/cjs/shared/auth.js.map +1 -1
- package/dist/cjs/shared/protocol.d.ts +5 -9
- package/dist/cjs/shared/protocol.d.ts.map +1 -1
- package/dist/cjs/shared/protocol.js +24 -10
- package/dist/cjs/shared/protocol.js.map +1 -1
- package/dist/cjs/shared/transport.d.ts +14 -0
- package/dist/cjs/shared/transport.d.ts.map +1 -1
- package/dist/cjs/shared/transport.js +40 -0
- package/dist/cjs/shared/transport.js.map +1 -1
- package/dist/cjs/shared/zodTestMatrix.d.ts +16 -0
- package/dist/cjs/shared/zodTestMatrix.d.ts.map +1 -0
- package/dist/cjs/shared/zodTestMatrix.js +43 -0
- package/dist/cjs/shared/zodTestMatrix.js.map +1 -0
- package/dist/cjs/spec.types.d.ts +393 -70
- package/dist/cjs/spec.types.d.ts.map +1 -1
- package/dist/cjs/spec.types.js +5 -7
- package/dist/cjs/spec.types.js.map +1 -1
- package/dist/cjs/types.d.ts +3580 -23866
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/cjs/types.js +574 -340
- package/dist/cjs/types.js.map +1 -1
- package/dist/cjs/validation/cfworker-provider.d.ts +0 -1
- package/dist/cjs/validation/cfworker-provider.d.ts.map +1 -1
- package/dist/cjs/validation/cfworker-provider.js +0 -1
- package/dist/cjs/validation/cfworker-provider.js.map +1 -1
- package/dist/esm/client/auth.d.ts +11 -1
- package/dist/esm/client/auth.d.ts.map +1 -1
- package/dist/esm/client/auth.js +71 -20
- package/dist/esm/client/auth.js.map +1 -1
- package/dist/esm/client/index.d.ts +122 -662
- package/dist/esm/client/index.d.ts.map +1 -1
- package/dist/esm/client/index.js +71 -15
- package/dist/esm/client/index.js.map +1 -1
- package/dist/esm/client/sse.d.ts +1 -0
- package/dist/esm/client/sse.d.ts.map +1 -1
- package/dist/esm/client/sse.js +5 -3
- package/dist/esm/client/sse.js.map +1 -1
- package/dist/esm/client/streamableHttp.d.ts +14 -2
- package/dist/esm/client/streamableHttp.d.ts.map +1 -1
- package/dist/esm/client/streamableHttp.js +79 -19
- package/dist/esm/client/streamableHttp.js.map +1 -1
- package/dist/esm/examples/client/elicitationUrlExample.d.ts +2 -0
- package/dist/esm/examples/client/elicitationUrlExample.d.ts.map +1 -0
- package/dist/esm/examples/client/elicitationUrlExample.js +691 -0
- package/dist/esm/examples/client/elicitationUrlExample.js.map +1 -0
- package/dist/esm/examples/client/simpleOAuthClient.js +12 -51
- package/dist/esm/examples/client/simpleOAuthClient.js.map +1 -1
- package/dist/esm/examples/client/simpleOAuthClientProvider.d.ts +26 -0
- package/dist/esm/examples/client/simpleOAuthClientProvider.d.ts.map +1 -0
- package/dist/esm/examples/client/simpleOAuthClientProvider.js +47 -0
- package/dist/esm/examples/client/simpleOAuthClientProvider.js.map +1 -0
- package/dist/esm/examples/client/simpleStreamableHttp.js +11 -6
- package/dist/esm/examples/client/simpleStreamableHttp.js.map +1 -1
- package/dist/esm/examples/client/ssePollingClient.d.ts +2 -0
- package/dist/esm/examples/client/ssePollingClient.d.ts.map +1 -0
- package/dist/esm/examples/client/ssePollingClient.js +93 -0
- package/dist/esm/examples/client/ssePollingClient.js.map +1 -0
- package/dist/esm/examples/server/demoInMemoryOAuthProvider.d.ts +4 -4
- package/dist/esm/examples/server/demoInMemoryOAuthProvider.d.ts.map +1 -1
- package/dist/esm/examples/server/demoInMemoryOAuthProvider.js +16 -0
- package/dist/esm/examples/server/demoInMemoryOAuthProvider.js.map +1 -1
- package/dist/esm/examples/server/elicitationFormExample.d.ts +2 -0
- package/dist/esm/examples/server/elicitationFormExample.d.ts.map +1 -0
- package/dist/esm/examples/server/{elicitationExample.js ā elicitationFormExample.js} +14 -8
- package/dist/esm/examples/server/elicitationFormExample.js.map +1 -0
- package/dist/esm/examples/server/elicitationUrlExample.d.ts +2 -0
- package/dist/esm/examples/server/elicitationUrlExample.d.ts.map +1 -0
- package/dist/esm/examples/server/elicitationUrlExample.js +650 -0
- package/dist/esm/examples/server/elicitationUrlExample.js.map +1 -0
- package/dist/esm/examples/server/jsonResponseStreamableHttp.js +1 -1
- package/dist/esm/examples/server/jsonResponseStreamableHttp.js.map +1 -1
- package/dist/esm/examples/server/mcpServerOutputSchema.js +1 -1
- package/dist/esm/examples/server/mcpServerOutputSchema.js.map +1 -1
- package/dist/esm/examples/server/simpleSseServer.js +1 -1
- package/dist/esm/examples/server/simpleSseServer.js.map +1 -1
- package/dist/esm/examples/server/simpleStatelessStreamableHttp.js +1 -1
- package/dist/esm/examples/server/simpleStatelessStreamableHttp.js.map +1 -1
- package/dist/esm/examples/server/simpleStreamableHttp.js +4 -3
- package/dist/esm/examples/server/simpleStreamableHttp.js.map +1 -1
- package/dist/esm/examples/server/sseAndStreamableHttpCompatibleServer.js +1 -1
- package/dist/esm/examples/server/sseAndStreamableHttpCompatibleServer.js.map +1 -1
- package/dist/esm/examples/server/ssePollingExample.d.ts +2 -0
- package/dist/esm/examples/server/ssePollingExample.d.ts.map +1 -0
- package/dist/esm/examples/server/ssePollingExample.js +111 -0
- package/dist/esm/examples/server/ssePollingExample.js.map +1 -0
- package/dist/esm/examples/server/toolWithSampleServer.js +6 -7
- package/dist/esm/examples/server/toolWithSampleServer.js.map +1 -1
- package/dist/esm/server/auth/handlers/authorize.js +1 -1
- package/dist/esm/server/auth/handlers/authorize.js.map +1 -1
- package/dist/esm/server/auth/handlers/token.js +1 -1
- package/dist/esm/server/auth/handlers/token.js.map +1 -1
- package/dist/esm/server/auth/middleware/bearerAuth.d.ts.map +1 -1
- package/dist/esm/server/auth/middleware/bearerAuth.js +13 -8
- package/dist/esm/server/auth/middleware/bearerAuth.js.map +1 -1
- package/dist/esm/server/auth/middleware/clientAuth.js +1 -1
- package/dist/esm/server/auth/middleware/clientAuth.js.map +1 -1
- package/dist/esm/server/auth/router.js +1 -1
- package/dist/esm/server/auth/router.js.map +1 -1
- package/dist/esm/server/completable.d.ts +30 -16
- package/dist/esm/server/completable.d.ts.map +1 -1
- package/dist/esm/server/completable.js +34 -53
- package/dist/esm/server/completable.js.map +1 -1
- package/dist/esm/server/index.d.ts +176 -50
- package/dist/esm/server/index.d.ts.map +1 -1
- package/dist/esm/server/index.js +99 -13
- package/dist/esm/server/index.js.map +1 -1
- package/dist/esm/server/mcp.d.ts +14 -16
- package/dist/esm/server/mcp.d.ts.map +1 -1
- package/dist/esm/server/mcp.js +84 -41
- package/dist/esm/server/mcp.js.map +1 -1
- package/dist/esm/server/streamableHttp.d.ts +27 -0
- package/dist/esm/server/streamableHttp.d.ts.map +1 -1
- package/dist/esm/server/streamableHttp.js +70 -5
- package/dist/esm/server/streamableHttp.js.map +1 -1
- package/dist/esm/server/zod-compat.d.ts +82 -0
- package/dist/esm/server/zod-compat.d.ts.map +1 -0
- package/dist/esm/server/zod-compat.js +217 -0
- package/dist/esm/server/zod-compat.js.map +1 -0
- package/dist/esm/server/zod-json-schema-compat.d.ts +12 -0
- package/dist/esm/server/zod-json-schema-compat.d.ts.map +1 -0
- package/dist/esm/server/zod-json-schema-compat.js +52 -0
- package/dist/esm/server/zod-json-schema-compat.js.map +1 -0
- package/dist/esm/shared/auth.d.ts +116 -502
- package/dist/esm/shared/auth.d.ts.map +1 -1
- package/dist/esm/shared/auth.js +17 -19
- package/dist/esm/shared/auth.js.map +1 -1
- package/dist/esm/shared/protocol.d.ts +5 -9
- package/dist/esm/shared/protocol.d.ts.map +1 -1
- package/dist/esm/shared/protocol.js +24 -10
- package/dist/esm/shared/protocol.js.map +1 -1
- package/dist/esm/shared/transport.d.ts +14 -0
- package/dist/esm/shared/transport.d.ts.map +1 -1
- package/dist/esm/shared/transport.js +38 -1
- package/dist/esm/shared/transport.js.map +1 -1
- package/dist/esm/shared/zodTestMatrix.d.ts +16 -0
- package/dist/esm/shared/zodTestMatrix.d.ts.map +1 -0
- package/dist/esm/shared/zodTestMatrix.js +17 -0
- package/dist/esm/shared/zodTestMatrix.js.map +1 -0
- package/dist/esm/spec.types.d.ts +393 -70
- package/dist/esm/spec.types.d.ts.map +1 -1
- package/dist/esm/spec.types.js +4 -6
- package/dist/esm/spec.types.js.map +1 -1
- package/dist/esm/types.d.ts +3580 -23866
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/esm/types.js +282 -72
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/validation/cfworker-provider.d.ts +0 -1
- package/dist/esm/validation/cfworker-provider.d.ts.map +1 -1
- package/dist/esm/validation/cfworker-provider.js +0 -1
- package/dist/esm/validation/cfworker-provider.js.map +1 -1
- package/package.json +13 -12
- package/dist/cjs/examples/server/elicitationExample.d.ts +0 -2
- package/dist/cjs/examples/server/elicitationExample.d.ts.map +0 -1
- package/dist/cjs/examples/server/elicitationExample.js.map +0 -1
- package/dist/esm/examples/server/elicitationExample.d.ts +0 -2
- package/dist/esm/examples/server/elicitationExample.d.ts.map +0 -1
- package/dist/esm/examples/server/elicitationExample.js.map +0 -1
|
@@ -0,0 +1,693 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Run with: npx tsx src/examples/client/elicitationUrlExample.ts
|
|
3
|
+
//
|
|
4
|
+
// This example demonstrates how to use URL elicitation to securely
|
|
5
|
+
// collect user input in a remote (HTTP) server.
|
|
6
|
+
// URL elicitation allows servers to prompt the end-user to open a URL in their browser
|
|
7
|
+
// to collect sensitive information.
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
const index_js_1 = require("../../client/index.js");
|
|
10
|
+
const streamableHttp_js_1 = require("../../client/streamableHttp.js");
|
|
11
|
+
const node_readline_1 = require("node:readline");
|
|
12
|
+
const types_js_1 = require("../../types.js");
|
|
13
|
+
const metadataUtils_js_1 = require("../../shared/metadataUtils.js");
|
|
14
|
+
const node_child_process_1 = require("node:child_process");
|
|
15
|
+
const simpleOAuthClientProvider_js_1 = require("./simpleOAuthClientProvider.js");
|
|
16
|
+
const auth_js_1 = require("../../client/auth.js");
|
|
17
|
+
const node_http_1 = require("node:http");
|
|
18
|
+
// Set up OAuth (required for this example)
|
|
19
|
+
const OAUTH_CALLBACK_PORT = 8090; // Use different port than auth server (3001)
|
|
20
|
+
const OAUTH_CALLBACK_URL = `http://localhost:${OAUTH_CALLBACK_PORT}/callback`;
|
|
21
|
+
let oauthProvider = undefined;
|
|
22
|
+
console.log('Getting OAuth token...');
|
|
23
|
+
const clientMetadata = {
|
|
24
|
+
client_name: 'Elicitation MCP Client',
|
|
25
|
+
redirect_uris: [OAUTH_CALLBACK_URL],
|
|
26
|
+
grant_types: ['authorization_code', 'refresh_token'],
|
|
27
|
+
response_types: ['code'],
|
|
28
|
+
token_endpoint_auth_method: 'client_secret_post',
|
|
29
|
+
scope: 'mcp:tools'
|
|
30
|
+
};
|
|
31
|
+
oauthProvider = new simpleOAuthClientProvider_js_1.InMemoryOAuthClientProvider(OAUTH_CALLBACK_URL, clientMetadata, (redirectUrl) => {
|
|
32
|
+
console.log(`š Opening browser for OAuth redirect: ${redirectUrl.toString()}`);
|
|
33
|
+
openBrowser(redirectUrl.toString());
|
|
34
|
+
});
|
|
35
|
+
// Create readline interface for user input
|
|
36
|
+
const readline = (0, node_readline_1.createInterface)({
|
|
37
|
+
input: process.stdin,
|
|
38
|
+
output: process.stdout
|
|
39
|
+
});
|
|
40
|
+
let abortCommand = new AbortController();
|
|
41
|
+
// Global client and transport for interactive commands
|
|
42
|
+
let client = null;
|
|
43
|
+
let transport = null;
|
|
44
|
+
let serverUrl = 'http://localhost:3000/mcp';
|
|
45
|
+
let sessionId = undefined;
|
|
46
|
+
let isProcessingCommand = false;
|
|
47
|
+
let isProcessingElicitations = false;
|
|
48
|
+
const elicitationQueue = [];
|
|
49
|
+
let elicitationQueueSignal = null;
|
|
50
|
+
let elicitationsCompleteSignal = null;
|
|
51
|
+
// Map to track pending URL elicitations waiting for completion notifications
|
|
52
|
+
const pendingURLElicitations = new Map();
|
|
53
|
+
async function main() {
|
|
54
|
+
console.log('MCP Interactive Client');
|
|
55
|
+
console.log('=====================');
|
|
56
|
+
// Connect to server immediately with default settings
|
|
57
|
+
await connect();
|
|
58
|
+
// Start the elicitation loop in the background
|
|
59
|
+
elicitationLoop().catch(error => {
|
|
60
|
+
console.error('Unexpected error in elicitation loop:', error);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
});
|
|
63
|
+
// Short delay allowing the server to send any SSE elicitations on connection
|
|
64
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
65
|
+
// Wait until we are done processing any initial elicitations
|
|
66
|
+
await waitForElicitationsToComplete();
|
|
67
|
+
// Print help and start the command loop
|
|
68
|
+
printHelp();
|
|
69
|
+
await commandLoop();
|
|
70
|
+
}
|
|
71
|
+
async function waitForElicitationsToComplete() {
|
|
72
|
+
// Wait until the queue is empty and nothing is being processed
|
|
73
|
+
while (elicitationQueue.length > 0 || isProcessingElicitations) {
|
|
74
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function printHelp() {
|
|
78
|
+
console.log('\nAvailable commands:');
|
|
79
|
+
console.log(' connect [url] - Connect to MCP server (default: http://localhost:3000/mcp)');
|
|
80
|
+
console.log(' disconnect - Disconnect from server');
|
|
81
|
+
console.log(' terminate-session - Terminate the current session');
|
|
82
|
+
console.log(' reconnect - Reconnect to the server');
|
|
83
|
+
console.log(' list-tools - List available tools');
|
|
84
|
+
console.log(' call-tool <name> [args] - Call a tool with optional JSON arguments');
|
|
85
|
+
console.log(' payment-confirm - Test URL elicitation via error response with payment-confirm tool');
|
|
86
|
+
console.log(' third-party-auth - Test tool that requires third-party OAuth credentials');
|
|
87
|
+
console.log(' help - Show this help');
|
|
88
|
+
console.log(' quit - Exit the program');
|
|
89
|
+
}
|
|
90
|
+
async function commandLoop() {
|
|
91
|
+
await new Promise(resolve => {
|
|
92
|
+
if (!isProcessingElicitations) {
|
|
93
|
+
resolve();
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
elicitationsCompleteSignal = resolve;
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
readline.question('\n> ', { signal: abortCommand.signal }, async (input) => {
|
|
100
|
+
var _a;
|
|
101
|
+
isProcessingCommand = true;
|
|
102
|
+
const args = input.trim().split(/\s+/);
|
|
103
|
+
const command = (_a = args[0]) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
104
|
+
try {
|
|
105
|
+
switch (command) {
|
|
106
|
+
case 'connect':
|
|
107
|
+
await connect(args[1]);
|
|
108
|
+
break;
|
|
109
|
+
case 'disconnect':
|
|
110
|
+
await disconnect();
|
|
111
|
+
break;
|
|
112
|
+
case 'terminate-session':
|
|
113
|
+
await terminateSession();
|
|
114
|
+
break;
|
|
115
|
+
case 'reconnect':
|
|
116
|
+
await reconnect();
|
|
117
|
+
break;
|
|
118
|
+
case 'list-tools':
|
|
119
|
+
await listTools();
|
|
120
|
+
break;
|
|
121
|
+
case 'call-tool':
|
|
122
|
+
if (args.length < 2) {
|
|
123
|
+
console.log('Usage: call-tool <name> [args]');
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
const toolName = args[1];
|
|
127
|
+
let toolArgs = {};
|
|
128
|
+
if (args.length > 2) {
|
|
129
|
+
try {
|
|
130
|
+
toolArgs = JSON.parse(args.slice(2).join(' '));
|
|
131
|
+
}
|
|
132
|
+
catch (_b) {
|
|
133
|
+
console.log('Invalid JSON arguments. Using empty args.');
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
await callTool(toolName, toolArgs);
|
|
137
|
+
}
|
|
138
|
+
break;
|
|
139
|
+
case 'payment-confirm':
|
|
140
|
+
await callPaymentConfirmTool();
|
|
141
|
+
break;
|
|
142
|
+
case 'third-party-auth':
|
|
143
|
+
await callThirdPartyAuthTool();
|
|
144
|
+
break;
|
|
145
|
+
case 'help':
|
|
146
|
+
printHelp();
|
|
147
|
+
break;
|
|
148
|
+
case 'quit':
|
|
149
|
+
case 'exit':
|
|
150
|
+
await cleanup();
|
|
151
|
+
return;
|
|
152
|
+
default:
|
|
153
|
+
if (command) {
|
|
154
|
+
console.log(`Unknown command: ${command}`);
|
|
155
|
+
}
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
console.error(`Error executing command: ${error}`);
|
|
161
|
+
}
|
|
162
|
+
finally {
|
|
163
|
+
isProcessingCommand = false;
|
|
164
|
+
}
|
|
165
|
+
// Process another command after we've processed the this one
|
|
166
|
+
await commandLoop();
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
async function elicitationLoop() {
|
|
170
|
+
while (true) {
|
|
171
|
+
// Wait until we have elicitations to process
|
|
172
|
+
await new Promise(resolve => {
|
|
173
|
+
if (elicitationQueue.length > 0) {
|
|
174
|
+
resolve();
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
elicitationQueueSignal = resolve;
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
isProcessingElicitations = true;
|
|
181
|
+
abortCommand.abort(); // Abort the command loop if it's running
|
|
182
|
+
// Process all queued elicitations
|
|
183
|
+
while (elicitationQueue.length > 0) {
|
|
184
|
+
const queued = elicitationQueue.shift();
|
|
185
|
+
console.log(`š¤ Processing queued elicitation (${elicitationQueue.length} remaining)`);
|
|
186
|
+
try {
|
|
187
|
+
const result = await handleElicitationRequest(queued.request);
|
|
188
|
+
queued.resolve(result);
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
queued.reject(error instanceof Error ? error : new Error(String(error)));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
console.log('ā
All queued elicitations processed. Resuming command loop...\n');
|
|
195
|
+
isProcessingElicitations = false;
|
|
196
|
+
// Reset the abort controller for the next command loop
|
|
197
|
+
abortCommand = new AbortController();
|
|
198
|
+
// Resume the command loop
|
|
199
|
+
if (elicitationsCompleteSignal) {
|
|
200
|
+
elicitationsCompleteSignal();
|
|
201
|
+
elicitationsCompleteSignal = null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
async function openBrowser(url) {
|
|
206
|
+
const command = `open "${url}"`;
|
|
207
|
+
(0, node_child_process_1.exec)(command, error => {
|
|
208
|
+
if (error) {
|
|
209
|
+
console.error(`Failed to open browser: ${error.message}`);
|
|
210
|
+
console.log(`Please manually open: ${url}`);
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Enqueues an elicitation request and returns the result.
|
|
216
|
+
*
|
|
217
|
+
* This function is used so that our CLI (which can only handle one input request at a time)
|
|
218
|
+
* can handle elicitation requests and the command loop.
|
|
219
|
+
*
|
|
220
|
+
* @param request - The elicitation request to be handled
|
|
221
|
+
* @returns The elicitation result
|
|
222
|
+
*/
|
|
223
|
+
async function elicitationRequestHandler(request) {
|
|
224
|
+
// If we are processing a command, handle this elicitation immediately
|
|
225
|
+
if (isProcessingCommand) {
|
|
226
|
+
console.log('š Processing elicitation immediately (during command execution)');
|
|
227
|
+
return await handleElicitationRequest(request);
|
|
228
|
+
}
|
|
229
|
+
// Otherwise, queue the request to be handled by the elicitation loop
|
|
230
|
+
console.log(`š„ Queueing elicitation request (queue size will be: ${elicitationQueue.length + 1})`);
|
|
231
|
+
return new Promise((resolve, reject) => {
|
|
232
|
+
elicitationQueue.push({
|
|
233
|
+
request,
|
|
234
|
+
resolve,
|
|
235
|
+
reject
|
|
236
|
+
});
|
|
237
|
+
// Signal the elicitation loop that there's work to do
|
|
238
|
+
if (elicitationQueueSignal) {
|
|
239
|
+
elicitationQueueSignal();
|
|
240
|
+
elicitationQueueSignal = null;
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Handles an elicitation request.
|
|
246
|
+
*
|
|
247
|
+
* This function is used to handle the elicitation request and return the result.
|
|
248
|
+
*
|
|
249
|
+
* @param request - The elicitation request to be handled
|
|
250
|
+
* @returns The elicitation result
|
|
251
|
+
*/
|
|
252
|
+
async function handleElicitationRequest(request) {
|
|
253
|
+
const mode = request.params.mode;
|
|
254
|
+
console.log('\nš Elicitation Request Received:');
|
|
255
|
+
console.log(`Mode: ${mode}`);
|
|
256
|
+
if (mode === 'url') {
|
|
257
|
+
return {
|
|
258
|
+
action: await handleURLElicitation(request.params)
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
// Should not happen because the client declares its capabilities to the server,
|
|
263
|
+
// but being defensive is a good practice:
|
|
264
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Unsupported elicitation mode: ${mode}`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Handles a URL elicitation by opening the URL in the browser.
|
|
269
|
+
*
|
|
270
|
+
* Note: This is a shared code for both request handlers and error handlers.
|
|
271
|
+
* As a result of sharing schema, there is no big forking of logic for the client.
|
|
272
|
+
*
|
|
273
|
+
* @param params - The URL elicitation request parameters
|
|
274
|
+
* @returns The action to take (accept, cancel, or decline)
|
|
275
|
+
*/
|
|
276
|
+
async function handleURLElicitation(params) {
|
|
277
|
+
const url = params.url;
|
|
278
|
+
const elicitationId = params.elicitationId;
|
|
279
|
+
const message = params.message;
|
|
280
|
+
console.log(`š Elicitation ID: ${elicitationId}`); // Print for illustration
|
|
281
|
+
// Parse URL to show domain for security
|
|
282
|
+
let domain = 'unknown domain';
|
|
283
|
+
try {
|
|
284
|
+
const parsedUrl = new URL(url);
|
|
285
|
+
domain = parsedUrl.hostname;
|
|
286
|
+
}
|
|
287
|
+
catch (_a) {
|
|
288
|
+
console.error('Invalid URL provided by server');
|
|
289
|
+
return 'decline';
|
|
290
|
+
}
|
|
291
|
+
// Example security warning to help prevent phishing attacks
|
|
292
|
+
console.log('\nā ļø \x1b[33mSECURITY WARNING\x1b[0m ā ļø');
|
|
293
|
+
console.log('\x1b[33mThe server is requesting you to open an external URL.\x1b[0m');
|
|
294
|
+
console.log('\x1b[33mOnly proceed if you trust this server and understand why it needs this.\x1b[0m\n');
|
|
295
|
+
console.log(`š Target domain: \x1b[36m${domain}\x1b[0m`);
|
|
296
|
+
console.log(`š Full URL: \x1b[36m${url}\x1b[0m`);
|
|
297
|
+
console.log(`\nā¹ļø Server's reason:\n\n\x1b[36m${message}\x1b[0m\n`);
|
|
298
|
+
// 1. Ask for user consent to open the URL
|
|
299
|
+
const consent = await new Promise(resolve => {
|
|
300
|
+
readline.question('\nDo you want to open this URL in your browser? (y/n): ', input => {
|
|
301
|
+
resolve(input.trim().toLowerCase());
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
// 2. If user did not consent, return appropriate result
|
|
305
|
+
if (consent === 'no' || consent === 'n') {
|
|
306
|
+
console.log('ā URL navigation declined.');
|
|
307
|
+
return 'decline';
|
|
308
|
+
}
|
|
309
|
+
else if (consent !== 'yes' && consent !== 'y') {
|
|
310
|
+
console.log('š« Invalid response. Cancelling elicitation.');
|
|
311
|
+
return 'cancel';
|
|
312
|
+
}
|
|
313
|
+
// 3. Wait for completion notification in the background
|
|
314
|
+
const completionPromise = new Promise((resolve, reject) => {
|
|
315
|
+
const timeout = setTimeout(() => {
|
|
316
|
+
pendingURLElicitations.delete(elicitationId);
|
|
317
|
+
console.log(`\x1b[31mā Elicitation ${elicitationId} timed out waiting for completion.\x1b[0m`);
|
|
318
|
+
reject(new Error('Elicitation completion timeout'));
|
|
319
|
+
}, 5 * 60 * 1000); // 5 minute timeout
|
|
320
|
+
pendingURLElicitations.set(elicitationId, {
|
|
321
|
+
resolve: () => {
|
|
322
|
+
clearTimeout(timeout);
|
|
323
|
+
resolve();
|
|
324
|
+
},
|
|
325
|
+
reject,
|
|
326
|
+
timeout
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
completionPromise.catch(error => {
|
|
330
|
+
console.error('Background completion wait failed:', error);
|
|
331
|
+
});
|
|
332
|
+
// 4. Open the URL in the browser
|
|
333
|
+
console.log(`\nš Opening browser to: ${url}`);
|
|
334
|
+
await openBrowser(url);
|
|
335
|
+
console.log('\nā³ Waiting for you to complete the interaction in your browser...');
|
|
336
|
+
console.log(' The server will send a notification once you complete the action.');
|
|
337
|
+
// 5. Acknowledge the user accepted the elicitation
|
|
338
|
+
return 'accept';
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Example OAuth callback handler - in production, use a more robust approach
|
|
342
|
+
* for handling callbacks and storing tokens
|
|
343
|
+
*/
|
|
344
|
+
/**
|
|
345
|
+
* Starts a temporary HTTP server to receive the OAuth callback
|
|
346
|
+
*/
|
|
347
|
+
async function waitForOAuthCallback() {
|
|
348
|
+
return new Promise((resolve, reject) => {
|
|
349
|
+
const server = (0, node_http_1.createServer)((req, res) => {
|
|
350
|
+
// Ignore favicon requests
|
|
351
|
+
if (req.url === '/favicon.ico') {
|
|
352
|
+
res.writeHead(404);
|
|
353
|
+
res.end();
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
console.log(`š„ Received callback: ${req.url}`);
|
|
357
|
+
const parsedUrl = new URL(req.url || '', 'http://localhost');
|
|
358
|
+
const code = parsedUrl.searchParams.get('code');
|
|
359
|
+
const error = parsedUrl.searchParams.get('error');
|
|
360
|
+
if (code) {
|
|
361
|
+
console.log(`ā
Authorization code received: ${code === null || code === void 0 ? void 0 : code.substring(0, 10)}...`);
|
|
362
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
363
|
+
res.end(`
|
|
364
|
+
<html>
|
|
365
|
+
<body>
|
|
366
|
+
<h1>Authorization Successful!</h1>
|
|
367
|
+
<p>This simulates successful authorization of the MCP client, which now has an access token for the MCP server.</p>
|
|
368
|
+
<p>This window will close automatically in 10 seconds.</p>
|
|
369
|
+
<script>setTimeout(() => window.close(), 10000);</script>
|
|
370
|
+
</body>
|
|
371
|
+
</html>
|
|
372
|
+
`);
|
|
373
|
+
resolve(code);
|
|
374
|
+
setTimeout(() => server.close(), 15000);
|
|
375
|
+
}
|
|
376
|
+
else if (error) {
|
|
377
|
+
console.log(`ā Authorization error: ${error}`);
|
|
378
|
+
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
379
|
+
res.end(`
|
|
380
|
+
<html>
|
|
381
|
+
<body>
|
|
382
|
+
<h1>Authorization Failed</h1>
|
|
383
|
+
<p>Error: ${error}</p>
|
|
384
|
+
</body>
|
|
385
|
+
</html>
|
|
386
|
+
`);
|
|
387
|
+
reject(new Error(`OAuth authorization failed: ${error}`));
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
console.log(`ā No authorization code or error in callback`);
|
|
391
|
+
res.writeHead(400);
|
|
392
|
+
res.end('Bad request');
|
|
393
|
+
reject(new Error('No authorization code provided'));
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
server.listen(OAUTH_CALLBACK_PORT, () => {
|
|
397
|
+
console.log(`OAuth callback server started on http://localhost:${OAUTH_CALLBACK_PORT}`);
|
|
398
|
+
});
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Attempts to connect to the MCP server with OAuth authentication.
|
|
403
|
+
* Handles OAuth flow recursively if authorization is required.
|
|
404
|
+
*/
|
|
405
|
+
async function attemptConnection(oauthProvider) {
|
|
406
|
+
console.log('š¢ Creating transport with OAuth provider...');
|
|
407
|
+
const baseUrl = new URL(serverUrl);
|
|
408
|
+
transport = new streamableHttp_js_1.StreamableHTTPClientTransport(baseUrl, {
|
|
409
|
+
sessionId: sessionId,
|
|
410
|
+
authProvider: oauthProvider
|
|
411
|
+
});
|
|
412
|
+
console.log('š¢ Transport created');
|
|
413
|
+
try {
|
|
414
|
+
console.log('š Attempting connection (this will trigger OAuth redirect if needed)...');
|
|
415
|
+
await client.connect(transport);
|
|
416
|
+
sessionId = transport.sessionId;
|
|
417
|
+
console.log('Transport created with session ID:', sessionId);
|
|
418
|
+
console.log('ā
Connected successfully');
|
|
419
|
+
}
|
|
420
|
+
catch (error) {
|
|
421
|
+
if (error instanceof auth_js_1.UnauthorizedError) {
|
|
422
|
+
console.log('š OAuth required - waiting for authorization...');
|
|
423
|
+
const callbackPromise = waitForOAuthCallback();
|
|
424
|
+
const authCode = await callbackPromise;
|
|
425
|
+
await transport.finishAuth(authCode);
|
|
426
|
+
console.log('š Authorization code received:', authCode);
|
|
427
|
+
console.log('š Reconnecting with authenticated transport...');
|
|
428
|
+
// Recursively retry connection after OAuth completion
|
|
429
|
+
await attemptConnection(oauthProvider);
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
console.error('ā Connection failed with non-auth error:', error);
|
|
433
|
+
throw error;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
async function connect(url) {
|
|
438
|
+
if (client) {
|
|
439
|
+
console.log('Already connected. Disconnect first.');
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
if (url) {
|
|
443
|
+
serverUrl = url;
|
|
444
|
+
}
|
|
445
|
+
console.log(`š Attempting to connect to ${serverUrl}...`);
|
|
446
|
+
// Create a new client with elicitation capability
|
|
447
|
+
console.log('š¤ Creating MCP client...');
|
|
448
|
+
client = new index_js_1.Client({
|
|
449
|
+
name: 'example-client',
|
|
450
|
+
version: '1.0.0'
|
|
451
|
+
}, {
|
|
452
|
+
capabilities: {
|
|
453
|
+
elicitation: {
|
|
454
|
+
// Only URL elicitation is supported in this demo
|
|
455
|
+
// (see server/elicitationExample.ts for a demo of form mode elicitation)
|
|
456
|
+
url: {}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
console.log('š¤ Client created');
|
|
461
|
+
// Set up elicitation request handler with proper validation
|
|
462
|
+
client.setRequestHandler(types_js_1.ElicitRequestSchema, elicitationRequestHandler);
|
|
463
|
+
// Set up notification handler for elicitation completion
|
|
464
|
+
client.setNotificationHandler(types_js_1.ElicitationCompleteNotificationSchema, notification => {
|
|
465
|
+
const { elicitationId } = notification.params;
|
|
466
|
+
const pending = pendingURLElicitations.get(elicitationId);
|
|
467
|
+
if (pending) {
|
|
468
|
+
clearTimeout(pending.timeout);
|
|
469
|
+
pendingURLElicitations.delete(elicitationId);
|
|
470
|
+
console.log(`\x1b[32mā
Elicitation ${elicitationId} completed!\x1b[0m`);
|
|
471
|
+
pending.resolve();
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
// Shouldn't happen - discard it!
|
|
475
|
+
console.warn(`Received completion notification for unknown elicitation: ${elicitationId}`);
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
try {
|
|
479
|
+
console.log('š Starting OAuth flow...');
|
|
480
|
+
await attemptConnection(oauthProvider);
|
|
481
|
+
console.log('Connected to MCP server');
|
|
482
|
+
// Set up error handler after connection is established so we don't double log errors
|
|
483
|
+
client.onerror = error => {
|
|
484
|
+
console.error('\x1b[31mClient error:', error, '\x1b[0m');
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
catch (error) {
|
|
488
|
+
console.error('Failed to connect:', error);
|
|
489
|
+
client = null;
|
|
490
|
+
transport = null;
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
async function disconnect() {
|
|
495
|
+
if (!client || !transport) {
|
|
496
|
+
console.log('Not connected.');
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
try {
|
|
500
|
+
await transport.close();
|
|
501
|
+
console.log('Disconnected from MCP server');
|
|
502
|
+
client = null;
|
|
503
|
+
transport = null;
|
|
504
|
+
}
|
|
505
|
+
catch (error) {
|
|
506
|
+
console.error('Error disconnecting:', error);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
async function terminateSession() {
|
|
510
|
+
if (!client || !transport) {
|
|
511
|
+
console.log('Not connected.');
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
try {
|
|
515
|
+
console.log('Terminating session with ID:', transport.sessionId);
|
|
516
|
+
await transport.terminateSession();
|
|
517
|
+
console.log('Session terminated successfully');
|
|
518
|
+
// Check if sessionId was cleared after termination
|
|
519
|
+
if (!transport.sessionId) {
|
|
520
|
+
console.log('Session ID has been cleared');
|
|
521
|
+
sessionId = undefined;
|
|
522
|
+
// Also close the transport and clear client objects
|
|
523
|
+
await transport.close();
|
|
524
|
+
console.log('Transport closed after session termination');
|
|
525
|
+
client = null;
|
|
526
|
+
transport = null;
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
console.log('Server responded with 405 Method Not Allowed (session termination not supported)');
|
|
530
|
+
console.log('Session ID is still active:', transport.sessionId);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
catch (error) {
|
|
534
|
+
console.error('Error terminating session:', error);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
async function reconnect() {
|
|
538
|
+
if (client) {
|
|
539
|
+
await disconnect();
|
|
540
|
+
}
|
|
541
|
+
await connect();
|
|
542
|
+
}
|
|
543
|
+
async function listTools() {
|
|
544
|
+
if (!client) {
|
|
545
|
+
console.log('Not connected to server.');
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
try {
|
|
549
|
+
const toolsRequest = {
|
|
550
|
+
method: 'tools/list',
|
|
551
|
+
params: {}
|
|
552
|
+
};
|
|
553
|
+
const toolsResult = await client.request(toolsRequest, types_js_1.ListToolsResultSchema);
|
|
554
|
+
console.log('Available tools:');
|
|
555
|
+
if (toolsResult.tools.length === 0) {
|
|
556
|
+
console.log(' No tools available');
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
for (const tool of toolsResult.tools) {
|
|
560
|
+
console.log(` - id: ${tool.name}, name: ${(0, metadataUtils_js_1.getDisplayName)(tool)}, description: ${tool.description}`);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
catch (error) {
|
|
565
|
+
console.log(`Tools not supported by this server (${error})`);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
async function callTool(name, args) {
|
|
569
|
+
if (!client) {
|
|
570
|
+
console.log('Not connected to server.');
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
try {
|
|
574
|
+
const request = {
|
|
575
|
+
method: 'tools/call',
|
|
576
|
+
params: {
|
|
577
|
+
name,
|
|
578
|
+
arguments: args
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
console.log(`Calling tool '${name}' with args:`, args);
|
|
582
|
+
const result = await client.request(request, types_js_1.CallToolResultSchema);
|
|
583
|
+
console.log('Tool result:');
|
|
584
|
+
const resourceLinks = [];
|
|
585
|
+
result.content.forEach(item => {
|
|
586
|
+
if (item.type === 'text') {
|
|
587
|
+
console.log(` ${item.text}`);
|
|
588
|
+
}
|
|
589
|
+
else if (item.type === 'resource_link') {
|
|
590
|
+
const resourceLink = item;
|
|
591
|
+
resourceLinks.push(resourceLink);
|
|
592
|
+
console.log(` š Resource Link: ${resourceLink.name}`);
|
|
593
|
+
console.log(` URI: ${resourceLink.uri}`);
|
|
594
|
+
if (resourceLink.mimeType) {
|
|
595
|
+
console.log(` Type: ${resourceLink.mimeType}`);
|
|
596
|
+
}
|
|
597
|
+
if (resourceLink.description) {
|
|
598
|
+
console.log(` Description: ${resourceLink.description}`);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
else if (item.type === 'resource') {
|
|
602
|
+
console.log(` [Embedded Resource: ${item.resource.uri}]`);
|
|
603
|
+
}
|
|
604
|
+
else if (item.type === 'image') {
|
|
605
|
+
console.log(` [Image: ${item.mimeType}]`);
|
|
606
|
+
}
|
|
607
|
+
else if (item.type === 'audio') {
|
|
608
|
+
console.log(` [Audio: ${item.mimeType}]`);
|
|
609
|
+
}
|
|
610
|
+
else {
|
|
611
|
+
console.log(` [Unknown content type]:`, item);
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
// Offer to read resource links
|
|
615
|
+
if (resourceLinks.length > 0) {
|
|
616
|
+
console.log(`\nFound ${resourceLinks.length} resource link(s). Use 'read-resource <uri>' to read their content.`);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
catch (error) {
|
|
620
|
+
if (error instanceof types_js_1.UrlElicitationRequiredError) {
|
|
621
|
+
console.log('\nš Elicitation Required Error Received:');
|
|
622
|
+
console.log(`Message: ${error.message}`);
|
|
623
|
+
for (const e of error.elicitations) {
|
|
624
|
+
await handleURLElicitation(e); // For the error handler, we discard the action result because we don't respond to an error response
|
|
625
|
+
}
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
console.log(`Error calling tool ${name}: ${error}`);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
async function cleanup() {
|
|
632
|
+
if (client && transport) {
|
|
633
|
+
try {
|
|
634
|
+
// First try to terminate the session gracefully
|
|
635
|
+
if (transport.sessionId) {
|
|
636
|
+
try {
|
|
637
|
+
console.log('Terminating session before exit...');
|
|
638
|
+
await transport.terminateSession();
|
|
639
|
+
console.log('Session terminated successfully');
|
|
640
|
+
}
|
|
641
|
+
catch (error) {
|
|
642
|
+
console.error('Error terminating session:', error);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
// Then close the transport
|
|
646
|
+
await transport.close();
|
|
647
|
+
}
|
|
648
|
+
catch (error) {
|
|
649
|
+
console.error('Error closing transport:', error);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
process.stdin.setRawMode(false);
|
|
653
|
+
readline.close();
|
|
654
|
+
console.log('\nGoodbye!');
|
|
655
|
+
process.exit(0);
|
|
656
|
+
}
|
|
657
|
+
async function callPaymentConfirmTool() {
|
|
658
|
+
console.log('Calling payment-confirm tool...');
|
|
659
|
+
await callTool('payment-confirm', { cartId: 'cart_123' });
|
|
660
|
+
}
|
|
661
|
+
async function callThirdPartyAuthTool() {
|
|
662
|
+
console.log('Calling third-party-auth tool...');
|
|
663
|
+
await callTool('third-party-auth', { param1: 'test' });
|
|
664
|
+
}
|
|
665
|
+
// Set up raw mode for keyboard input to capture Escape key
|
|
666
|
+
process.stdin.setRawMode(true);
|
|
667
|
+
process.stdin.on('data', async (data) => {
|
|
668
|
+
// Check for Escape key (27)
|
|
669
|
+
if (data.length === 1 && data[0] === 27) {
|
|
670
|
+
console.log('\nESC key pressed. Disconnecting from server...');
|
|
671
|
+
// Abort current operation and disconnect from server
|
|
672
|
+
if (client && transport) {
|
|
673
|
+
await disconnect();
|
|
674
|
+
console.log('Disconnected. Press Enter to continue.');
|
|
675
|
+
}
|
|
676
|
+
else {
|
|
677
|
+
console.log('Not connected to server.');
|
|
678
|
+
}
|
|
679
|
+
// Re-display the prompt
|
|
680
|
+
process.stdout.write('> ');
|
|
681
|
+
}
|
|
682
|
+
});
|
|
683
|
+
// Handle Ctrl+C
|
|
684
|
+
process.on('SIGINT', async () => {
|
|
685
|
+
console.log('\nReceived SIGINT. Cleaning up...');
|
|
686
|
+
await cleanup();
|
|
687
|
+
});
|
|
688
|
+
// Start the interactive client
|
|
689
|
+
main().catch((error) => {
|
|
690
|
+
console.error('Error running MCP client:', error);
|
|
691
|
+
process.exit(1);
|
|
692
|
+
});
|
|
693
|
+
//# sourceMappingURL=elicitationUrlExample.js.map
|