Last active
November 29, 2025 14:45
-
-
Save abhiaiyer91/5bf4569ab97fe9b0aeb5cb8c79e625c3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Reproduction script for issue #10668 - FIXED VERSION | |
| * | |
| * This shows the correct way to handle elicitation requests | |
| */ | |
| import { MCPClient } from './src/client/configuration'; | |
| import { MCPServer } from './src/server/server'; | |
| import http from 'http'; | |
| import { z } from 'zod'; | |
| const PORT = 9700 + Math.floor(Math.random() * 1000); | |
| const SERVER_URL = `http://localhost:${PORT}/http`; | |
| // Create the MCP server with a tool that uses elicitation | |
| const server = new MCPServer({ | |
| name: 'TestElicitationServer', | |
| version: '1.0.0', | |
| tools: { | |
| confirmAction: { | |
| description: 'A tool that requires user confirmation via elicitation', | |
| parameters: z.object({ | |
| action: z.string().describe('The action to confirm'), | |
| }), | |
| execute: async (inputData, context) => { | |
| try { | |
| const userApproval = await context?.mcp?.elicitation?.sendRequest({ | |
| mode: 'form', | |
| message: 'Are you sure?', | |
| requestedSchema: { | |
| type: 'object', | |
| properties: { | |
| userConfirmation: { | |
| type: 'boolean', | |
| title: 'User Confirmation', | |
| description: 'Are you sure?', | |
| }, | |
| }, | |
| required: ['userConfirmation'], | |
| }, | |
| }); | |
| if (userApproval.action !== 'accept') { | |
| console.log('User did not accept!'); | |
| return { | |
| content: [ | |
| { | |
| type: 'text' as const, | |
| text: 'User did not accept!', | |
| }, | |
| ], | |
| }; | |
| } | |
| return { | |
| content: [ | |
| { | |
| type: 'text' as const, | |
| text: 'User has given confirmation!', | |
| }, | |
| ], | |
| }; | |
| } catch (error) { | |
| console.error('Error in tool execution:', error); | |
| return { | |
| content: [ | |
| { | |
| type: 'text' as const, | |
| text: `Error: ${error instanceof Error ? error.message : String(error)}`, | |
| }, | |
| ], | |
| isError: true, | |
| }; | |
| } | |
| }, | |
| }, | |
| }, | |
| }); | |
| // Start HTTP server | |
| const httpServer = http.createServer(async (req, res) => { | |
| const url = new URL(req.url || '', `http://localhost:${PORT}`); | |
| await server.startHTTP({ | |
| url, | |
| httpPath: '/http', | |
| req, | |
| res, | |
| }); | |
| }); | |
| await new Promise<void>(resolve => { | |
| httpServer.listen(PORT, () => { | |
| console.log(`β Server started on port ${PORT}`); | |
| resolve(); | |
| }); | |
| }); | |
| // Simulate user's exact initialization pattern | |
| const config = { | |
| mcps: [ | |
| { | |
| type: 'http' as const, | |
| name: 'BASIC_MCP', | |
| url: SERVER_URL, | |
| timeout: 15000, | |
| headers: undefined as Record<string, string> | undefined, | |
| }, | |
| ], | |
| }; | |
| const servers: Record<string, any> = {}; | |
| for (const mcp of config.mcps) { | |
| if (mcp.type === 'http') { | |
| const serverConfig: any = { | |
| url: new URL(mcp.url), | |
| timeout: mcp.timeout || 15000, | |
| capabilities: { | |
| elicitation: { | |
| form: {}, | |
| }, | |
| }, | |
| }; | |
| if (mcp.headers) { | |
| serverConfig.requestInit = { | |
| headers: mcp.headers, | |
| }; | |
| } | |
| servers[mcp.name] = serverConfig; | |
| } | |
| } | |
| const mcpClient = new MCPClient({ servers: servers, timeout: 15000 }); | |
| // β FIXED: Handler returns content that matches the schema | |
| if (config.mcps && mcpClient) { | |
| for (const mcp of config.mcps) { | |
| switch (mcp.name) { | |
| case 'BASIC_MCP': | |
| console.log('Setting up elicitation handler for BASIC_MCP'); | |
| await mcpClient.elicitation.onRequest('BASIC_MCP', async request => { | |
| console.log('π Elicitation request received for BASIC_MCP'); | |
| const schema: any = request.requestedSchema; | |
| const properties = schema.properties || {}; | |
| const required = (schema.required as string[]) || []; | |
| // β FIX: Return content that matches the schema | |
| // Build the response based on the schema | |
| const content: Record<string, any> = {}; | |
| for (const fieldName of Object.keys(properties)) { | |
| const field = properties[fieldName]; | |
| // For boolean fields, default to true (or prompt user) | |
| if (field.type === 'boolean') { | |
| content[fieldName] = true; // Or get from user input | |
| } else if (field.type === 'string') { | |
| content[fieldName] = ''; // Or get from user input | |
| } else if (field.type === 'number') { | |
| content[fieldName] = 0; // Or get from user input | |
| } | |
| } | |
| console.log('β Returning content that matches schema:', content); | |
| return { action: 'accept', content }; | |
| }); | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| } | |
| // Get tools and execute | |
| const tools = await mcpClient.listTools(); | |
| const tool = tools['BASIC_MCP_confirmAction']; | |
| console.log('\nπ Executing tool with FIXED handler...'); | |
| const result = await tool.execute({ | |
| action: 'test action', | |
| }); | |
| console.log('\nπ Result:'); | |
| console.log(JSON.stringify(result, null, 2)); | |
| if (result.isError) { | |
| console.log('\nβ Tool execution resulted in error'); | |
| } else { | |
| const resultText = result.content[0]?.text; | |
| if (resultText && resultText.includes('User has given confirmation')) { | |
| console.log('\nβ SUCCESS! Handler worked correctly!'); | |
| } | |
| } | |
| // Cleanup | |
| await mcpClient.disconnect(); | |
| await server.close(); | |
| httpServer.closeAllConnections?.(); | |
| await new Promise<void>(resolve => { | |
| httpServer.close(() => resolve()); | |
| }); | |
| console.log('\nβ Done'); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment