UNPKG

@measey/mycoder-agent

Version:

Agent module for mycoder - an AI-powered software development assistant

292 lines 12 kB
/** * MCP Tool for MyCoder Agent * * This tool allows the agent to interact with Model Context Protocol (MCP) servers * to retrieve resources and use tools provided by those servers. * * Uses the official MCP SDK: https://www.npmjs.com/package/@modelcontextprotocol/sdk */ import { z } from 'zod'; import { zodToJsonSchema } from 'zod-to-json-schema'; // Parameters for listResources method const listResourcesSchema = z.object({ server: z .string() .optional() .describe('Optional server name to filter resources by'), }); // Parameters for getResource method const getResourceSchema = z.object({ uri: z .string() .describe('URI of the resource to fetch in the format "scheme://path"'), }); // Parameters for listTools method const listToolsSchema = z.object({ server: z .string() .optional() .describe('Optional server name to filter tools by'), }); // Parameters for executeTool method const executeToolSchema = z.object({ uri: z .string() .describe('URI of the tool to execute in the format "scheme://path"'), params: z .record(z.unknown()) .optional() .describe('Parameters to pass to the tool'), }); // Return type for listResources const listResourcesReturnSchema = z.array(z.object({ uri: z.string(), metadata: z.record(z.unknown()).optional(), })); // Return type for getResource const getResourceReturnSchema = z.string(); // Return type for listTools const listToolsReturnSchema = z.array(z.object({ uri: z.string(), name: z.string(), description: z.string().optional(), parameters: z.record(z.unknown()).optional(), returns: z.record(z.unknown()).optional(), })); // Return type for executeTool - can be any JSON value const executeToolReturnSchema = z.unknown(); // Map to store MCP clients const mcpClients = new Map(); /** * Create a new MCP tool with the specified configuration * @param config MCP configuration * @returns The MCP tool */ export function createMcpTool(config) { // We'll import the MCP SDK dynamically to avoid TypeScript errors // This is a temporary solution until we can properly add type declarations const mcpSdk = require('@modelcontextprotocol/sdk'); // Initialize MCP clients for each configured server mcpClients.clear(); if (config.servers && config.servers.length > 0) { for (const server of config.servers) { try { let clientOptions = { baseURL: server.url, }; // Add authentication if configured if (server.auth && server.auth.type === 'bearer') { clientOptions = { ...clientOptions, headers: { Authorization: `Bearer ${server.auth.token}`, }, }; } const client = new mcpSdk.Client(clientOptions); mcpClients.set(server.name, client); } catch (error) { console.error(`Failed to initialize MCP client for server ${server.name}:`, error); } } } // Define the MCP tool return { name: 'mcp', description: 'Interact with Model Context Protocol (MCP) servers to retrieve resources and execute tools', parameters: z.discriminatedUnion('method', [ z.object({ method: z.literal('listResources'), params: listResourcesSchema.optional(), }), z.object({ method: z.literal('getResource'), params: getResourceSchema, }), z.object({ method: z.literal('listTools'), params: listToolsSchema.optional(), }), z.object({ method: z.literal('executeTool'), params: executeToolSchema, }), ]), parametersJsonSchema: zodToJsonSchema(z.discriminatedUnion('method', [ z.object({ method: z.literal('listResources'), params: listResourcesSchema.optional(), }), z.object({ method: z.literal('getResource'), params: getResourceSchema, }), z.object({ method: z.literal('listTools'), params: listToolsSchema.optional(), }), z.object({ method: z.literal('executeTool'), params: executeToolSchema, }), ])), returns: z.union([ listResourcesReturnSchema, getResourceReturnSchema, listToolsReturnSchema, executeToolReturnSchema, ]), returnsJsonSchema: zodToJsonSchema(z.union([ listResourcesReturnSchema, getResourceReturnSchema, listToolsReturnSchema, executeToolReturnSchema, ])), execute: async ({ method, params }, { logger }) => { // Extract the server name from a URI (resource or tool) function getServerNameFromUri(uri) { const match = uri.match(/^([^:]+):\/\//); return match ? match[1] : undefined; } if (method === 'listResources') { // List available resources from MCP servers const resources = []; const serverFilter = params?.server; // If a specific server is requested, only check that server if (serverFilter) { const client = mcpClients.get(serverFilter); if (client) { try { logger.debug(`Fetching resources from server: ${serverFilter}`); const serverResources = await client.resources(); resources.push(...serverResources); } catch (error) { logger.error(`Failed to fetch resources from server ${serverFilter}:`, error); } } else { logger.warn(`Server not found: ${serverFilter}`); } } else { // Otherwise, check all servers for (const [serverName, client] of mcpClients.entries()) { try { logger.debug(`Fetching resources from server: ${serverName}`); const serverResources = await client.resources(); resources.push(...serverResources); } catch (error) { logger.error(`Failed to fetch resources from server ${serverName}:`, error); } } } return resources; } else if (method === 'getResource') { // Fetch a resource from an MCP server const uri = params.uri; // Parse the URI to determine which server to use const serverName = getServerNameFromUri(uri); if (!serverName) { throw new Error(`Could not determine server from URI: ${uri}`); } const client = mcpClients.get(serverName); if (!client) { throw new Error(`Server not found: ${serverName}`); } // Use the MCP SDK to fetch the resource logger.debug(`Fetching resource: ${uri}`); const resource = await client.resource(uri); return resource.content; } else if (method === 'listTools') { // List available tools from MCP servers const tools = []; const serverFilter = params?.server; // If a specific server is requested, only check that server if (serverFilter) { const client = mcpClients.get(serverFilter); if (client) { try { logger.debug(`Fetching tools from server: ${serverFilter}`); const serverTools = await client.tools(); tools.push(...serverTools); } catch (error) { logger.error(`Failed to fetch tools from server ${serverFilter}:`, error); } } else { logger.warn(`Server not found: ${serverFilter}`); } } else { // Otherwise, check all servers for (const [serverName, client] of mcpClients.entries()) { try { logger.debug(`Fetching tools from server: ${serverName}`); const serverTools = await client.tools(); tools.push(...serverTools); } catch (error) { logger.error(`Failed to fetch tools from server ${serverName}:`, error); } } } return tools; } else if (method === 'executeTool') { // Execute a tool from an MCP server const { uri, params: toolParams = {} } = params; // Parse the URI to determine which server to use const serverName = getServerNameFromUri(uri); if (!serverName) { throw new Error(`Could not determine server from URI: ${uri}`); } const client = mcpClients.get(serverName); if (!client) { throw new Error(`Server not found: ${serverName}`); } // Use the MCP SDK to execute the tool logger.debug(`Executing tool: ${uri} with params:`, toolParams); const result = await client.tool(uri, toolParams); return result; } throw new Error(`Unknown method: ${method}`); }, logParameters: (params, { logger }) => { if (params.method === 'listResources') { logger.debug(`Listing MCP resources${params.params?.server ? ` from server: ${params.params.server}` : ''}`); } else if (params.method === 'getResource') { logger.debug(`Fetching MCP resource: ${params.params.uri}`); } else if (params.method === 'listTools') { logger.debug(`Listing MCP tools${params.params?.server ? ` from server: ${params.params.server}` : ''}`); } else if (params.method === 'executeTool') { logger.debug(`Executing MCP tool: ${params.params.uri}`); } }, logReturns: (result, { logger }) => { if (Array.isArray(result)) { if (result.length > 0 && 'description' in result[0]) { logger.debug(`Found ${result.length} MCP tools`); } else { logger.debug(`Found ${result.length} MCP resources`); } } else if (typeof result === 'string') { logger.debug(`Retrieved MCP resource content (${result.length} characters)`); } else { logger.debug(`Executed MCP tool and received result`); } }, }; } //# sourceMappingURL=mcp.js.map