File size: 6,533 Bytes
a5d718a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | "use strict";
/**
* 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
*/
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
});
function question(prompt) {
return new Promise(resolve => {
readline.question(prompt, answer => {
resolve(answer.trim());
});
});
}
function getTextContent(result) {
const textContent = result.content.find((c) => c.type === 'text');
return textContent?.text ?? '(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 index_js_1.Client({ name: 'simple-task-interactive-client', version: '1.0.0' }, {
capabilities: {
elicitation: { form: {} },
sampling: {}
}
});
// Set up elicitation request handler
client.setRequestHandler(types_js_1.ElicitRequestSchema, async (request) => {
if (request.params.mode && request.params.mode !== 'form') {
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidParams, `Unsupported elicitation mode: ${request.params.mode}`);
}
return elicitationCallback(request.params);
});
// Set up sampling request handler
client.setRequestHandler(types_js_1.CreateMessageRequestSchema, async (request) => {
return samplingCallback(request.params);
});
// Connect to server
const transport = new streamableHttp_js_1.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' } }, types_js_1.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' } }, types_js_1.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 |