UNPKG

@copilotkit/runtime

Version:

<div align="center"> <a href="https://copilotkit.ai" target="_blank"> <img src="https://github.com/copilotkit/copilotkit/raw/main/assets/banner.png" alt="CopilotKit Logo"> </a>

189 lines (162 loc) 6.32 kB
import { Action, Parameter } from "@copilotkit/shared"; /** * Represents a tool provided by an MCP server. */ export interface MCPTool { description?: string; /** Schema defining parameters, mirroring the MCP structure. */ schema?: { parameters?: { properties?: Record<string, any>; required?: string[]; jsonSchema?: Record<string, any>; }; }; /** The function to call to execute the tool on the MCP server. */ execute(options: { params: any }): Promise<any>; } /** * Defines the contract for *any* MCP client implementation the user might provide. */ export interface MCPClient { /** A method that returns a map of tool names to MCPTool objects available from the connected MCP server. */ tools(): Promise<Record<string, MCPTool>>; /** An optional method for cleanup if the underlying client requires explicit disconnection. */ close?(): Promise<void>; } /** * Configuration for connecting to an MCP endpoint. */ export interface MCPEndpointConfig { endpoint: string; apiKey?: string; } /** * Extracts CopilotKit-compatible parameters from an MCP tool schema. * @param toolOrSchema The schema object from an MCPTool or the full MCPTool object. * @returns An array of Parameter objects. */ export function extractParametersFromSchema( toolOrSchema?: MCPTool | MCPTool["schema"], ): Parameter[] { const parameters: Parameter[] = []; // Handle either full tool object or just schema const schema = "schema" in (toolOrSchema || {}) ? (toolOrSchema as MCPTool).schema : (toolOrSchema as MCPTool["schema"]); const toolParameters = schema?.parameters || schema?.parameters?.jsonSchema; const properties = toolParameters?.properties; const requiredParams = new Set(toolParameters?.required || []); if (!properties) { return parameters; } for (const paramName in properties) { if (Object.prototype.hasOwnProperty.call(properties, paramName)) { const paramDef = properties[paramName]; parameters.push({ name: paramName, // Infer type, default to string. MCP schemas might have more complex types. // This might need refinement based on common MCP schema practices. type: paramDef.type || "string", description: paramDef.description, required: requiredParams.has(paramName), // Attributes might not directly map, handle if necessary // attributes: paramDef.attributes || undefined, }); } } return parameters; } /** * Converts a map of MCPTools into an array of CopilotKit Actions. * @param mcpTools A record mapping tool names to MCPTool objects. * @param mcpEndpoint The endpoint URL from which these tools were fetched. * @returns An array of Action<any> objects. */ export function convertMCPToolsToActions( mcpTools: Record<string, MCPTool>, mcpEndpoint: string, ): Action<any>[] { const actions: Action<any>[] = []; for (const [toolName, tool] of Object.entries(mcpTools)) { const parameters = extractParametersFromSchema(tool); const handler = async (params: any): Promise<any> => { try { const result = await tool.execute({ params }); // Ensure the result is a string or stringify it, as required by many LLMs. // This might need adjustment depending on how different LLMs handle tool results. return typeof result === "string" ? result : JSON.stringify(result); } catch (error) { console.error( `Error executing MCP tool '${toolName}' from endpoint ${mcpEndpoint}:`, error, ); // Re-throw or format the error for the LLM throw new Error( `Execution failed for MCP tool '${toolName}': ${ error instanceof Error ? error.message : String(error) }`, ); } }; actions.push({ name: toolName, description: tool.description || `MCP tool: ${toolName} (from ${mcpEndpoint})`, parameters: parameters, handler: handler, // Add metadata for easier identification/debugging _isMCPTool: true, _mcpEndpoint: mcpEndpoint, } as Action<any> & { _isMCPTool: boolean; _mcpEndpoint: string }); // Type assertion for metadata } return actions; } /** * Generate better instructions for using MCP tools * This is used to enhance the system prompt with tool documentation */ export function generateMcpToolInstructions(toolsMap: Record<string, MCPTool>): string { if (!toolsMap || Object.keys(toolsMap).length === 0) { return ""; } const toolEntries = Object.entries(toolsMap); // Generate documentation for each tool const toolsDoc = toolEntries .map(([name, tool]) => { // Extract schema information if available let paramsDoc = " No parameters required"; try { if (tool.schema && typeof tool.schema === "object") { const schema = tool.schema as any; // Extract parameters from JSON Schema if (schema.properties) { const requiredParams = schema.required || []; // Build parameter documentation from properties const paramsList = Object.entries(schema.properties).map(([paramName, propSchema]) => { const propDetails = propSchema as any; const requiredMark = requiredParams.includes(paramName) ? "*" : ""; const typeInfo = propDetails.type || "any"; const description = propDetails.description ? ` - ${propDetails.description}` : ""; return ` - ${paramName}${requiredMark} (${typeInfo})${description}`; }); if (paramsList.length > 0) { paramsDoc = paramsList.join("\n"); } } } } catch (e) { console.error(`Error parsing schema for tool ${name}:`, e); } return `- ${name}: ${tool.description || ""} ${paramsDoc}`; }) .join("\n\n"); return `You have access to the following external tools provided by Model Context Protocol (MCP) servers: ${toolsDoc} When using these tools: 1. Only provide valid parameters according to their type requirements 2. Required parameters are marked with * 3. Format API calls correctly with the expected parameter structure 4. Always check tool responses to determine your next action`; }