mcp-jokes / node_modules /@modelcontextprotocol /sdk /dist /cjs /examples /client /simpleStreamableHttp.js
| ; | |
| Object.defineProperty(exports, "__esModule", { value: true }); | |
| const index_js_1 = require("../../client/index.js"); | |
| const streamableHttp_js_1 = require("../../client/streamableHttp.js"); | |
| const node_readline_1 = require("node:readline"); | |
| const types_js_1 = require("../../types.js"); | |
| // Create readline interface for user input | |
| const readline = (0, node_readline_1.createInterface)({ | |
| input: process.stdin, | |
| output: process.stdout | |
| }); | |
| // Track received notifications for debugging resumability | |
| let notificationCount = 0; | |
| // Global client and transport for interactive commands | |
| let client = null; | |
| let transport = null; | |
| let serverUrl = 'http://localhost:3000/mcp'; | |
| let notificationsToolLastEventId = undefined; | |
| let sessionId = undefined; | |
| async function main() { | |
| console.log('MCP Interactive Client'); | |
| console.log('====================='); | |
| // Connect to server immediately with default settings | |
| await connect(); | |
| // Print help and start the command loop | |
| printHelp(); | |
| commandLoop(); | |
| } | |
| function printHelp() { | |
| console.log('\nAvailable commands:'); | |
| console.log(' connect [url] - Connect to MCP server (default: http://localhost:3000/mcp)'); | |
| console.log(' disconnect - Disconnect from server'); | |
| console.log(' terminate-session - Terminate the current session'); | |
| console.log(' reconnect - Reconnect to the server'); | |
| console.log(' list-tools - List available tools'); | |
| console.log(' call-tool <name> [args] - Call a tool with optional JSON arguments'); | |
| console.log(' greet [name] - Call the greet tool'); | |
| console.log(' multi-greet [name] - Call the multi-greet tool with notifications'); | |
| console.log(' start-notifications [interval] [count] - Start periodic notifications'); | |
| console.log(' list-prompts - List available prompts'); | |
| console.log(' get-prompt [name] [args] - Get a prompt with optional JSON arguments'); | |
| console.log(' list-resources - List available resources'); | |
| console.log(' help - Show this help'); | |
| console.log(' quit - Exit the program'); | |
| } | |
| function commandLoop() { | |
| readline.question('\n> ', async (input) => { | |
| var _a; | |
| const args = input.trim().split(/\s+/); | |
| const command = (_a = args[0]) === null || _a === void 0 ? void 0 : _a.toLowerCase(); | |
| try { | |
| switch (command) { | |
| case 'connect': | |
| await connect(args[1]); | |
| break; | |
| case 'disconnect': | |
| await disconnect(); | |
| break; | |
| case 'terminate-session': | |
| await terminateSession(); | |
| break; | |
| case 'reconnect': | |
| await reconnect(); | |
| break; | |
| case 'list-tools': | |
| await listTools(); | |
| break; | |
| case 'call-tool': | |
| if (args.length < 2) { | |
| console.log('Usage: call-tool <name> [args]'); | |
| } | |
| else { | |
| const toolName = args[1]; | |
| let toolArgs = {}; | |
| if (args.length > 2) { | |
| try { | |
| toolArgs = JSON.parse(args.slice(2).join(' ')); | |
| } | |
| catch (_b) { | |
| console.log('Invalid JSON arguments. Using empty args.'); | |
| } | |
| } | |
| await callTool(toolName, toolArgs); | |
| } | |
| break; | |
| case 'greet': | |
| await callGreetTool(args[1] || 'MCP User'); | |
| break; | |
| case 'multi-greet': | |
| await callMultiGreetTool(args[1] || 'MCP User'); | |
| break; | |
| case 'start-notifications': { | |
| const interval = args[1] ? parseInt(args[1], 10) : 2000; | |
| const count = args[2] ? parseInt(args[2], 10) : 10; | |
| await startNotifications(interval, count); | |
| break; | |
| } | |
| case 'list-prompts': | |
| await listPrompts(); | |
| break; | |
| case 'get-prompt': | |
| if (args.length < 2) { | |
| console.log('Usage: get-prompt <name> [args]'); | |
| } | |
| else { | |
| const promptName = args[1]; | |
| let promptArgs = {}; | |
| if (args.length > 2) { | |
| try { | |
| promptArgs = JSON.parse(args.slice(2).join(' ')); | |
| } | |
| catch (_c) { | |
| console.log('Invalid JSON arguments. Using empty args.'); | |
| } | |
| } | |
| await getPrompt(promptName, promptArgs); | |
| } | |
| break; | |
| case 'list-resources': | |
| await listResources(); | |
| break; | |
| case 'help': | |
| printHelp(); | |
| break; | |
| case 'quit': | |
| case 'exit': | |
| await cleanup(); | |
| return; | |
| default: | |
| if (command) { | |
| console.log(`Unknown command: ${command}`); | |
| } | |
| break; | |
| } | |
| } | |
| catch (error) { | |
| console.error(`Error executing command: ${error}`); | |
| } | |
| // Continue the command loop | |
| commandLoop(); | |
| }); | |
| } | |
| async function connect(url) { | |
| if (client) { | |
| console.log('Already connected. Disconnect first.'); | |
| return; | |
| } | |
| if (url) { | |
| serverUrl = url; | |
| } | |
| console.log(`Connecting to ${serverUrl}...`); | |
| try { | |
| // Create a new client | |
| client = new index_js_1.Client({ | |
| name: 'example-client', | |
| version: '1.0.0' | |
| }); | |
| client.onerror = (error) => { | |
| console.error('\x1b[31mClient error:', error, '\x1b[0m'); | |
| }; | |
| transport = new streamableHttp_js_1.StreamableHTTPClientTransport(new URL(serverUrl), { | |
| sessionId: sessionId | |
| }); | |
| // Set up notification handlers | |
| client.setNotificationHandler(types_js_1.LoggingMessageNotificationSchema, (notification) => { | |
| notificationCount++; | |
| console.log(`\nNotification #${notificationCount}: ${notification.params.level} - ${notification.params.data}`); | |
| // Re-display the prompt | |
| process.stdout.write('> '); | |
| }); | |
| client.setNotificationHandler(types_js_1.ResourceListChangedNotificationSchema, async (_) => { | |
| console.log(`\nResource list changed notification received!`); | |
| try { | |
| if (!client) { | |
| console.log('Client disconnected, cannot fetch resources'); | |
| return; | |
| } | |
| const resourcesResult = await client.request({ | |
| method: 'resources/list', | |
| params: {} | |
| }, types_js_1.ListResourcesResultSchema); | |
| console.log('Available resources count:', resourcesResult.resources.length); | |
| } | |
| catch (_a) { | |
| console.log('Failed to list resources after change notification'); | |
| } | |
| // Re-display the prompt | |
| process.stdout.write('> '); | |
| }); | |
| // Connect the client | |
| await client.connect(transport); | |
| sessionId = transport.sessionId; | |
| console.log('Transport created with session ID:', sessionId); | |
| console.log('Connected to MCP server'); | |
| } | |
| catch (error) { | |
| console.error('Failed to connect:', error); | |
| client = null; | |
| transport = null; | |
| } | |
| } | |
| async function disconnect() { | |
| if (!client || !transport) { | |
| console.log('Not connected.'); | |
| return; | |
| } | |
| try { | |
| await transport.close(); | |
| console.log('Disconnected from MCP server'); | |
| client = null; | |
| transport = null; | |
| } | |
| catch (error) { | |
| console.error('Error disconnecting:', error); | |
| } | |
| } | |
| async function terminateSession() { | |
| if (!client || !transport) { | |
| console.log('Not connected.'); | |
| return; | |
| } | |
| try { | |
| console.log('Terminating session with ID:', transport.sessionId); | |
| await transport.terminateSession(); | |
| console.log('Session terminated successfully'); | |
| // Check if sessionId was cleared after termination | |
| if (!transport.sessionId) { | |
| console.log('Session ID has been cleared'); | |
| sessionId = undefined; | |
| // Also close the transport and clear client objects | |
| await transport.close(); | |
| console.log('Transport closed after session termination'); | |
| client = null; | |
| transport = null; | |
| } | |
| else { | |
| console.log('Server responded with 405 Method Not Allowed (session termination not supported)'); | |
| console.log('Session ID is still active:', transport.sessionId); | |
| } | |
| } | |
| catch (error) { | |
| console.error('Error terminating session:', error); | |
| } | |
| } | |
| async function reconnect() { | |
| if (client) { | |
| await disconnect(); | |
| } | |
| await connect(); | |
| } | |
| async function listTools() { | |
| if (!client) { | |
| console.log('Not connected to server.'); | |
| return; | |
| } | |
| try { | |
| const toolsRequest = { | |
| method: 'tools/list', | |
| params: {} | |
| }; | |
| const toolsResult = await client.request(toolsRequest, types_js_1.ListToolsResultSchema); | |
| console.log('Available tools:'); | |
| if (toolsResult.tools.length === 0) { | |
| console.log(' No tools available'); | |
| } | |
| else { | |
| for (const tool of toolsResult.tools) { | |
| console.log(` - ${tool.name}: ${tool.description}`); | |
| } | |
| } | |
| } | |
| catch (error) { | |
| console.log(`Tools not supported by this server (${error})`); | |
| } | |
| } | |
| async function callTool(name, args) { | |
| if (!client) { | |
| console.log('Not connected to server.'); | |
| return; | |
| } | |
| try { | |
| const request = { | |
| method: 'tools/call', | |
| params: { | |
| name, | |
| arguments: args | |
| } | |
| }; | |
| console.log(`Calling tool '${name}' with args:`, args); | |
| const onLastEventIdUpdate = (event) => { | |
| notificationsToolLastEventId = event; | |
| }; | |
| const result = await client.request(request, types_js_1.CallToolResultSchema, { | |
| resumptionToken: notificationsToolLastEventId, onresumptiontoken: onLastEventIdUpdate | |
| }); | |
| console.log('Tool result:'); | |
| result.content.forEach(item => { | |
| if (item.type === 'text') { | |
| console.log(` ${item.text}`); | |
| } | |
| else { | |
| console.log(` ${item.type} content:`, item); | |
| } | |
| }); | |
| } | |
| catch (error) { | |
| console.log(`Error calling tool ${name}: ${error}`); | |
| } | |
| } | |
| async function callGreetTool(name) { | |
| await callTool('greet', { name }); | |
| } | |
| async function callMultiGreetTool(name) { | |
| console.log('Calling multi-greet tool with notifications...'); | |
| await callTool('multi-greet', { name }); | |
| } | |
| async function startNotifications(interval, count) { | |
| console.log(`Starting notification stream: interval=${interval}ms, count=${count || 'unlimited'}`); | |
| await callTool('start-notification-stream', { interval, count }); | |
| } | |
| async function listPrompts() { | |
| if (!client) { | |
| console.log('Not connected to server.'); | |
| return; | |
| } | |
| try { | |
| const promptsRequest = { | |
| method: 'prompts/list', | |
| params: {} | |
| }; | |
| const promptsResult = await client.request(promptsRequest, types_js_1.ListPromptsResultSchema); | |
| console.log('Available prompts:'); | |
| if (promptsResult.prompts.length === 0) { | |
| console.log(' No prompts available'); | |
| } | |
| else { | |
| for (const prompt of promptsResult.prompts) { | |
| console.log(` - ${prompt.name}: ${prompt.description}`); | |
| } | |
| } | |
| } | |
| catch (error) { | |
| console.log(`Prompts not supported by this server (${error})`); | |
| } | |
| } | |
| async function getPrompt(name, args) { | |
| if (!client) { | |
| console.log('Not connected to server.'); | |
| return; | |
| } | |
| try { | |
| const promptRequest = { | |
| method: 'prompts/get', | |
| params: { | |
| name, | |
| arguments: args | |
| } | |
| }; | |
| const promptResult = await client.request(promptRequest, types_js_1.GetPromptResultSchema); | |
| console.log('Prompt template:'); | |
| promptResult.messages.forEach((msg, index) => { | |
| console.log(` [${index + 1}] ${msg.role}: ${msg.content.text}`); | |
| }); | |
| } | |
| catch (error) { | |
| console.log(`Error getting prompt ${name}: ${error}`); | |
| } | |
| } | |
| async function listResources() { | |
| if (!client) { | |
| console.log('Not connected to server.'); | |
| return; | |
| } | |
| try { | |
| const resourcesRequest = { | |
| method: 'resources/list', | |
| params: {} | |
| }; | |
| const resourcesResult = await client.request(resourcesRequest, types_js_1.ListResourcesResultSchema); | |
| console.log('Available resources:'); | |
| if (resourcesResult.resources.length === 0) { | |
| console.log(' No resources available'); | |
| } | |
| else { | |
| for (const resource of resourcesResult.resources) { | |
| console.log(` - ${resource.name}: ${resource.uri}`); | |
| } | |
| } | |
| } | |
| catch (error) { | |
| console.log(`Resources not supported by this server (${error})`); | |
| } | |
| } | |
| async function cleanup() { | |
| if (client && transport) { | |
| try { | |
| // First try to terminate the session gracefully | |
| if (transport.sessionId) { | |
| try { | |
| console.log('Terminating session before exit...'); | |
| await transport.terminateSession(); | |
| console.log('Session terminated successfully'); | |
| } | |
| catch (error) { | |
| console.error('Error terminating session:', error); | |
| } | |
| } | |
| // Then close the transport | |
| await transport.close(); | |
| } | |
| catch (error) { | |
| console.error('Error closing transport:', error); | |
| } | |
| } | |
| process.stdin.setRawMode(false); | |
| readline.close(); | |
| console.log('\nGoodbye!'); | |
| process.exit(0); | |
| } | |
| // Set up raw mode for keyboard input to capture Escape key | |
| process.stdin.setRawMode(true); | |
| process.stdin.on('data', async (data) => { | |
| // Check for Escape key (27) | |
| if (data.length === 1 && data[0] === 27) { | |
| console.log('\nESC key pressed. Disconnecting from server...'); | |
| // Abort current operation and disconnect from server | |
| if (client && transport) { | |
| await disconnect(); | |
| console.log('Disconnected. Press Enter to continue.'); | |
| } | |
| else { | |
| console.log('Not connected to server.'); | |
| } | |
| // Re-display the prompt | |
| process.stdout.write('> '); | |
| } | |
| }); | |
| // Handle Ctrl+C | |
| process.on('SIGINT', async () => { | |
| console.log('\nReceived SIGINT. Cleaning up...'); | |
| await cleanup(); | |
| }); | |
| // Start the interactive client | |
| main().catch((error) => { | |
| console.error('Error running MCP client:', error); | |
| process.exit(1); | |
| }); | |
| //# sourceMappingURL=simpleStreamableHttp.js.map |