avancement planning

This commit is contained in:
2026-05-26 11:58:39 +02:00
parent 619a2b240a
commit 150b97cd2e
4892 changed files with 99214 additions and 429382 deletions
@@ -1,9 +1,9 @@
import { Client } from '../../client/index.js';
import { StreamableHTTPClientTransport } from '../../client/streamableHttp.js';
import { createInterface } from 'node:readline';
import { ListToolsResultSchema, CallToolResultSchema, ListPromptsResultSchema, GetPromptResultSchema, ListResourcesResultSchema, LoggingMessageNotificationSchema, ResourceListChangedNotificationSchema, ElicitRequestSchema, ReadResourceResultSchema, } from '../../types.js';
import { ListToolsResultSchema, CallToolResultSchema, ListPromptsResultSchema, GetPromptResultSchema, ListResourcesResultSchema, LoggingMessageNotificationSchema, ResourceListChangedNotificationSchema, ElicitRequestSchema, ReadResourceResultSchema, RELATED_TASK_META_KEY, ErrorCode, McpError } from '../../types.js';
import { getDisplayName } from '../../shared/metadataUtils.js';
import Ajv from "ajv";
import { Ajv } from 'ajv';
// Create readline interface for user input
const readline = createInterface({
input: process.stdin,
@@ -34,9 +34,10 @@ function printHelp() {
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(' call-tool-task <name> [args] - Call a tool with task-based execution (example: call-tool-task delay {"duration":3000})');
console.log(' greet [name] - Call the greet tool');
console.log(' multi-greet [name] - Call the multi-greet tool with notifications');
console.log(' collect-info [type] - Test elicitation with collect-user-info tool (contact/preferences/feedback)');
console.log(' collect-info [type] - Test form elicitation with collect-user-info tool (contact/preferences/feedback)');
console.log(' start-notifications [interval] [count] - Start periodic notifications');
console.log(' run-notifications-tool-with-resumability [interval] [count] - Run notification tool with resumability');
console.log(' list-prompts - List available prompts');
@@ -48,9 +49,8 @@ function printHelp() {
}
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();
const command = args[0]?.toLowerCase();
try {
switch (command) {
case 'connect':
@@ -79,7 +79,7 @@ function commandLoop() {
try {
toolArgs = JSON.parse(args.slice(2).join(' '));
}
catch (_b) {
catch {
console.log('Invalid JSON arguments. Using empty args.');
}
}
@@ -107,6 +107,24 @@ function commandLoop() {
await runNotificationsToolWithResumability(interval, count);
break;
}
case 'call-tool-task':
if (args.length < 2) {
console.log('Usage: call-tool-task <name> [args]');
}
else {
const toolName = args[1];
let toolArgs = {};
if (args.length > 2) {
try {
toolArgs = JSON.parse(args.slice(2).join(' '));
}
catch {
console.log('Invalid JSON arguments. Using empty args.');
}
}
await callToolTask(toolName, toolArgs);
}
break;
case 'list-prompts':
await listPrompts();
break;
@@ -121,7 +139,7 @@ function commandLoop() {
try {
promptArgs = JSON.parse(args.slice(2).join(' '));
}
catch (_c) {
catch {
console.log('Invalid JSON arguments. Using empty args.');
}
}
@@ -170,23 +188,28 @@ async function connect(url) {
}
console.log(`Connecting to ${serverUrl}...`);
try {
// Create a new client with elicitation capability
// Create a new client with form elicitation capability
client = new Client({
name: 'example-client',
version: '1.0.0'
}, {
capabilities: {
elicitation: {},
},
elicitation: {
form: {}
}
}
});
client.onerror = (error) => {
client.onerror = error => {
console.error('\x1b[31mClient error:', error, '\x1b[0m');
};
// Set up elicitation request handler with proper validation
client.setRequestHandler(ElicitRequestSchema, async (request) => {
var _a;
console.log('\n🔔 Elicitation Request Received:');
if (request.params.mode !== 'form') {
throw new McpError(ErrorCode.InvalidParams, `Unsupported elicitation mode: ${request.params.mode}`);
}
console.log('\n🔔 Elicitation (form) Request Received:');
console.log(`Message: ${request.params.message}`);
console.log(`Related Task: ${request.params._meta?.[RELATED_TASK_META_KEY]?.taskId}`);
console.log('Requested Schema:');
console.log(JSON.stringify(request.params.requestedSchema, null, 2));
const schema = request.params.requestedSchema;
@@ -235,8 +258,8 @@ async function connect(url) {
prompt += ` [default: ${field.default}]`;
}
prompt += ': ';
const answer = await new Promise((resolve) => {
readline.question(prompt, (input) => {
const answer = await new Promise(resolve => {
readline.question(prompt, input => {
resolve(input.trim());
});
});
@@ -297,7 +320,8 @@ async function connect(url) {
return { action: 'cancel' };
}
// If we didn't complete all fields due to an error, try again
if (Object.keys(content).length !== Object.keys(properties).filter(name => required.includes(name) || content[name] !== undefined).length) {
if (Object.keys(content).length !==
Object.keys(properties).filter(name => required.includes(name) || content[name] !== undefined).length) {
if (attempts < maxAttempts) {
console.log('Please try again...');
continue;
@@ -311,8 +335,8 @@ async function connect(url) {
const isValid = validate(content);
if (!isValid) {
console.log('❌ Validation errors:');
(_a = validate.errors) === null || _a === void 0 ? void 0 : _a.forEach(error => {
console.log(` - ${error.dataPath || 'root'}: ${error.message}`);
validate.errors?.forEach(error => {
console.log(` - ${error.instancePath || 'root'}: ${error.message}`);
});
if (attempts < maxAttempts) {
console.log('Please correct the errors and try again...');
@@ -326,15 +350,15 @@ async function connect(url) {
// Show the collected data and ask for confirmation
console.log('\n✅ Collected data:');
console.log(JSON.stringify(content, null, 2));
const confirmAnswer = await new Promise((resolve) => {
readline.question('\nSubmit this information? (yes/no/cancel): ', (input) => {
const confirmAnswer = await new Promise(resolve => {
readline.question('\nSubmit this information? (yes/no/cancel): ', input => {
resolve(input.trim().toLowerCase());
});
});
if (confirmAnswer === 'yes' || confirmAnswer === 'y') {
return {
action: 'accept',
content,
content
};
}
else if (confirmAnswer === 'cancel' || confirmAnswer === 'c') {
@@ -357,7 +381,7 @@ async function connect(url) {
sessionId: sessionId
});
// Set up notification handlers
client.setNotificationHandler(LoggingMessageNotificationSchema, (notification) => {
client.setNotificationHandler(LoggingMessageNotificationSchema, notification => {
notificationCount++;
console.log(`\nNotification #${notificationCount}: ${notification.params.level} - ${notification.params.data}`);
// Re-display the prompt
@@ -376,7 +400,7 @@ async function connect(url) {
}, ListResourcesResultSchema);
console.log('Available resources count:', resourcesResult.resources.length);
}
catch (_a) {
catch {
console.log('Failed to list resources after change notification');
}
// Re-display the prompt
@@ -531,7 +555,7 @@ async function callMultiGreetTool(name) {
await callTool('multi-greet', { name });
}
async function callCollectInfoTool(infoType) {
console.log(`Testing elicitation with collect-user-info tool (${infoType})...`);
console.log(`Testing form elicitation with collect-user-info tool (${infoType})...`);
await callTool('collect-user-info', { infoType });
}
async function startNotifications(interval, count) {
@@ -616,7 +640,7 @@ async function getPrompt(name, args) {
const promptResult = await client.request(promptRequest, GetPromptResultSchema);
console.log('Prompt template:');
promptResult.messages.forEach((msg, index) => {
console.log(` [${index + 1}] ${msg.role}: ${msg.content.text}`);
console.log(` [${index + 1}] ${msg.role}: ${msg.content.type === 'text' ? msg.content.text : JSON.stringify(msg.content)}`);
});
}
catch (error) {
@@ -669,7 +693,10 @@ async function readResource(uri) {
if ('text' in content && typeof content.text === 'string') {
console.log(' Content:');
console.log(' ---');
console.log(content.text.split('\n').map((line) => ' ' + line).join('\n'));
console.log(content.text
.split('\n')
.map((line) => ' ' + line)
.join('\n'));
console.log(' ---');
}
else if ('blob' in content && typeof content.blob === 'string') {
@@ -681,6 +708,57 @@ async function readResource(uri) {
console.log(`Error reading resource ${uri}: ${error}`);
}
}
async function callToolTask(name, args) {
if (!client) {
console.log('Not connected to server.');
return;
}
console.log(`Calling tool '${name}' with task-based execution...`);
console.log('Arguments:', args);
// Use task-based execution - call now, fetch later
// Using the experimental tasks API - WARNING: may change without notice
console.log('This will return immediately while processing continues in the background...');
try {
// Call the tool with task metadata using streaming API
const stream = client.experimental.tasks.callToolStream({
name,
arguments: args
}, CallToolResultSchema, {
task: {
ttl: 60000 // Keep results for 60 seconds
}
});
console.log('Waiting for task completion...');
let lastStatus = '';
for await (const message of stream) {
switch (message.type) {
case 'taskCreated':
console.log('Task created successfully with ID:', message.task.taskId);
break;
case 'taskStatus':
if (lastStatus !== message.task.status) {
console.log(` ${message.task.status}${message.task.statusMessage ? ` - ${message.task.statusMessage}` : ''}`);
}
lastStatus = message.task.status;
break;
case 'result':
console.log('Task completed!');
console.log('Tool result:');
message.result.content.forEach(item => {
if (item.type === 'text') {
console.log(` ${item.text}`);
}
});
break;
case 'error':
throw message.error;
}
}
}
catch (error) {
console.log(`Error with task-based execution: ${error}`);
}
}
async function cleanup() {
if (client && transport) {
try {