UNPKG

@modelcontextprotocol/sdk

Version:

Model Context Protocol implementation for TypeScript

156 lines 6.37 kB
/** * Simple interactive task client demonstrating elicitation and sampling responses. * * This client connects to simpleTaskInteractive.ts server and demonstrates: * - Handling elicitation requests (y/n confirmation) * - Handling sampling requests (returns a hardcoded haiku) * - Using task-based tool execution with streaming */ import { Client } from '../../client/index.js'; import { StreamableHTTPClientTransport } from '../../client/streamableHttp.js'; import { createInterface } from 'node:readline'; import { CallToolResultSchema, ElicitRequestSchema, CreateMessageRequestSchema, ErrorCode, McpError } from '../../types.js'; // Create readline interface for user input const readline = createInterface({ input: process.stdin, output: process.stdout }); function question(prompt) { return new Promise(resolve => { readline.question(prompt, answer => { resolve(answer.trim()); }); }); } function getTextContent(result) { var _a; const textContent = result.content.find((c) => c.type === 'text'); return (_a = textContent === null || textContent === void 0 ? void 0 : textContent.text) !== null && _a !== void 0 ? _a : '(no text)'; } async function elicitationCallback(params) { console.log(`\n[Elicitation] Server asks: ${params.message}`); // Simple terminal prompt for y/n const response = await question('Your response (y/n): '); const confirmed = ['y', 'yes', 'true', '1'].includes(response.toLowerCase()); console.log(`[Elicitation] Responding with: confirm=${confirmed}`); return { action: 'accept', content: { confirm: confirmed } }; } async function samplingCallback(params) { // Get the prompt from the first message let prompt = 'unknown'; if (params.messages && params.messages.length > 0) { const firstMessage = params.messages[0]; const content = firstMessage.content; if (typeof content === 'object' && !Array.isArray(content) && content.type === 'text' && 'text' in content) { prompt = content.text; } else if (Array.isArray(content)) { const textPart = content.find(c => c.type === 'text' && 'text' in c); if (textPart && 'text' in textPart) { prompt = textPart.text; } } } console.log(`\n[Sampling] Server requests LLM completion for: ${prompt}`); // Return a hardcoded haiku (in real use, call your LLM here) const haiku = `Cherry blossoms fall Softly on the quiet pond Spring whispers goodbye`; console.log('[Sampling] Responding with haiku'); return { model: 'mock-haiku-model', role: 'assistant', content: { type: 'text', text: haiku } }; } async function run(url) { console.log('Simple Task Interactive Client'); console.log('=============================='); console.log(`Connecting to ${url}...`); // Create client with elicitation and sampling capabilities const client = new Client({ name: 'simple-task-interactive-client', version: '1.0.0' }, { capabilities: { elicitation: { form: {} }, sampling: {} } }); // Set up elicitation request handler client.setRequestHandler(ElicitRequestSchema, async (request) => { if (request.params.mode && request.params.mode !== 'form') { throw new McpError(ErrorCode.InvalidParams, `Unsupported elicitation mode: ${request.params.mode}`); } return elicitationCallback(request.params); }); // Set up sampling request handler client.setRequestHandler(CreateMessageRequestSchema, async (request) => { return samplingCallback(request.params); }); // Connect to server const transport = new StreamableHTTPClientTransport(new URL(url)); await client.connect(transport); console.log('Connected!\n'); // List tools const toolsResult = await client.listTools(); console.log(`Available tools: ${toolsResult.tools.map(t => t.name).join(', ')}`); // Demo 1: Elicitation (confirm_delete) console.log('\n--- Demo 1: Elicitation ---'); console.log('Calling confirm_delete tool...'); const confirmStream = client.experimental.tasks.callToolStream({ name: 'confirm_delete', arguments: { filename: 'important.txt' } }, CallToolResultSchema, { task: { ttl: 60000 } }); for await (const message of confirmStream) { switch (message.type) { case 'taskCreated': console.log(`Task created: ${message.task.taskId}`); break; case 'taskStatus': console.log(`Task status: ${message.task.status}`); break; case 'result': console.log(`Result: ${getTextContent(message.result)}`); break; case 'error': console.error(`Error: ${message.error}`); break; } } // Demo 2: Sampling (write_haiku) console.log('\n--- Demo 2: Sampling ---'); console.log('Calling write_haiku tool...'); const haikuStream = client.experimental.tasks.callToolStream({ name: 'write_haiku', arguments: { topic: 'autumn leaves' } }, CallToolResultSchema, { task: { ttl: 60000 } }); for await (const message of haikuStream) { switch (message.type) { case 'taskCreated': console.log(`Task created: ${message.task.taskId}`); break; case 'taskStatus': console.log(`Task status: ${message.task.status}`); break; case 'result': console.log(`Result:\n${getTextContent(message.result)}`); break; case 'error': console.error(`Error: ${message.error}`); break; } } // Cleanup console.log('\nDemo complete. Closing connection...'); await transport.close(); readline.close(); } // Parse command line arguments const args = process.argv.slice(2); let url = 'http://localhost:8000/mcp'; for (let i = 0; i < args.length; i++) { if (args[i] === '--url' && args[i + 1]) { url = args[i + 1]; i++; } } // Run the client run(url).catch(error => { console.error('Error running client:', error); process.exit(1); }); //# sourceMappingURL=simpleTaskInteractiveClient.js.map