UNPKG

@juspay/neurolink

Version:

Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio

357 lines 12.1 kB
/** * Agent and Workflow Exposure as MCP Tools * * Enables exposing NeuroLink agents and workflows as MCP tools, * allowing external MCP clients to invoke complex AI operations * through the standardized MCP protocol. * * @module mcp/agentExposure * @since 8.39.0 */ import { logger } from "../utils/logger.js"; import { withTimeout } from "../utils/async/withTimeout.js"; import { ErrorFactory } from "../utils/errorHandling.js"; /** * Expose an agent as an MCP tool */ export function exposeAgentAsTool(agent, options = {}) { const { prefix = "agent", defaultAnnotations = {}, includeMetadataInDescription = true, nameTransformer = (name) => name.toLowerCase().replace(/\s+/g, "_"), wrapWithContext = true, executionTimeout = 300000, // 5 minutes default enableLogging = true, } = options; // Generate tool name const baseName = nameTransformer(agent.name); const toolName = prefix ? `${prefix}_${baseName}` : baseName; // Build description let description = agent.description; if (includeMetadataInDescription && agent.metadata) { const metaParts = []; if (agent.metadata.version) { metaParts.push(`v${agent.metadata.version}`); } if (agent.metadata.category) { metaParts.push(`category: ${agent.metadata.category}`); } if (agent.metadata.estimatedDuration) { metaParts.push(`~${agent.metadata.estimatedDuration}ms`); } if (metaParts.length > 0) { description += ` [${metaParts.join(", ")}]`; } } // Build annotations const annotations = { ...defaultAnnotations, complexity: defaultAnnotations.complexity ?? "complex", estimatedDuration: agent.metadata?.estimatedDuration ?? defaultAnnotations.estimatedDuration, costHint: agent.metadata?.costHint ?? defaultAnnotations.costHint, tags: [ ...(defaultAnnotations.tags ?? []), "agent", ...(agent.metadata?.tags ?? []), ], }; // Build input schema with agent context const inputSchema = agent.inputSchema ?? { type: "object", properties: {}, }; // Create execution wrapper const execute = async (params, context) => { const startTime = Date.now(); if (enableLogging) { logger.debug(`[AgentExposure] Executing agent '${agent.id}' as tool '${toolName}'`); } try { // Execute agent with context wrapper if enabled const existingConfig = context?.config && typeof context.config === "object" ? context.config : {}; const executionContext = wrapWithContext ? { ...context, config: { ...existingConfig, sourceType: "mcp-exposed-agent", agentId: agent.id, toolName, }, } : (context ?? {}); const result = await withTimeout(agent.execute(params, executionContext), executionTimeout); const duration = Date.now() - startTime; if (enableLogging) { logger.debug(`[AgentExposure] Agent '${agent.id}' completed in ${duration}ms`); } return { success: true, data: result, metadata: { agentId: agent.id, toolName, executionTime: duration, sourceType: "agent", }, }; } catch (error) { const duration = Date.now() - startTime; const agentError = error instanceof Error ? error : ErrorFactory.toolExecutionFailed(toolName, new Error(String(error))); if (enableLogging) { logger.error(`[AgentExposure] Agent '${agent.id}' failed after ${duration}ms: ${agentError.message}`); } return { success: false, error: agentError.message, metadata: { agentId: agent.id, toolName, executionTime: duration, sourceType: "agent", }, }; } }; const tool = { name: toolName, description, inputSchema, outputSchema: agent.outputSchema, annotations, execute, metadata: { sourceType: "agent", sourceId: agent.id, originalName: agent.name, ...agent.metadata, }, }; return { tool, sourceType: "agent", sourceId: agent.id, toolName, }; } /** * Expose a workflow as an MCP tool */ export function exposeWorkflowAsTool(workflow, options = {}) { const { prefix = "workflow", defaultAnnotations = {}, includeMetadataInDescription = true, nameTransformer = (name) => name.toLowerCase().replace(/\s+/g, "_"), wrapWithContext = true, executionTimeout = 600000, // 10 minutes default for workflows enableLogging = true, } = options; // Generate tool name const baseName = nameTransformer(workflow.name); const toolName = prefix ? `${prefix}_${baseName}` : baseName; // Build description let description = workflow.description; if (includeMetadataInDescription) { const metaParts = []; if (workflow.metadata?.version) { metaParts.push(`v${workflow.metadata.version}`); } if (workflow.steps?.length) { metaParts.push(`${workflow.steps.length} steps`); } if (workflow.metadata?.estimatedDuration) { metaParts.push(`~${workflow.metadata.estimatedDuration}ms`); } if (metaParts.length > 0) { description += ` [${metaParts.join(", ")}]`; } } // Build annotations const annotations = { ...defaultAnnotations, complexity: defaultAnnotations.complexity ?? "complex", estimatedDuration: workflow.metadata?.estimatedDuration ?? defaultAnnotations.estimatedDuration, idempotentHint: workflow.metadata?.idempotent ?? defaultAnnotations.idempotentHint, tags: [ ...(defaultAnnotations.tags ?? []), "workflow", ...(workflow.metadata?.tags ?? []), ], }; // Build input schema const inputSchema = workflow.inputSchema ?? { type: "object", properties: {}, }; // Create execution wrapper const execute = async (params, context) => { const startTime = Date.now(); if (enableLogging) { logger.debug(`[WorkflowExposure] Executing workflow '${workflow.id}' as tool '${toolName}'`); } try { // Execute workflow with context wrapper if enabled const existingConfig = context?.config && typeof context.config === "object" ? context.config : {}; const executionContext = wrapWithContext ? { ...context, config: { ...existingConfig, sourceType: "mcp-exposed-workflow", workflowId: workflow.id, toolName, }, } : (context ?? {}); const result = await withTimeout(workflow.execute(params, executionContext), executionTimeout); const duration = Date.now() - startTime; if (enableLogging) { logger.debug(`[WorkflowExposure] Workflow '${workflow.id}' completed in ${duration}ms`); } return { success: true, data: result, metadata: { workflowId: workflow.id, toolName, executionTime: duration, sourceType: "workflow", stepsCount: workflow.steps?.length, }, }; } catch (error) { const duration = Date.now() - startTime; const workflowError = error instanceof Error ? error : ErrorFactory.toolExecutionFailed(toolName, new Error(String(error))); if (enableLogging) { logger.error(`[WorkflowExposure] Workflow '${workflow.id}' failed after ${duration}ms: ${workflowError.message}`); } return { success: false, error: workflowError.message, metadata: { workflowId: workflow.id, toolName, executionTime: duration, sourceType: "workflow", }, }; } }; const tool = { name: toolName, description, inputSchema, outputSchema: workflow.outputSchema, annotations, execute, metadata: { sourceType: "workflow", sourceId: workflow.id, originalName: workflow.name, steps: workflow.steps, ...workflow.metadata, }, }; return { tool, sourceType: "workflow", sourceId: workflow.id, toolName, }; } /** * Batch expose agents as MCP tools */ export function exposeAgentsAsTools(agents, options = {}) { return agents.map((agent) => exposeAgentAsTool(agent, options)); } /** * Batch expose workflows as MCP tools */ export function exposeWorkflowsAsTools(workflows, options = {}) { return workflows.map((workflow) => exposeWorkflowAsTool(workflow, options)); } /** * Agent Exposure Manager * * Manages the lifecycle of exposed agents and workflows, * providing registration, lookup, and invocation capabilities. */ export class AgentExposureManager { exposedTools = new Map(); options; constructor(options = {}) { this.options = options; } /** * Expose an agent and register it */ exposeAgent(agent) { const result = exposeAgentAsTool(agent, this.options); this.exposedTools.set(result.toolName, result); return result.tool; } /** * Expose a workflow and register it */ exposeWorkflow(workflow) { const result = exposeWorkflowAsTool(workflow, this.options); this.exposedTools.set(result.toolName, result); return result.tool; } /** * Get all exposed tools */ getExposedTools() { return Array.from(this.exposedTools.values()).map((r) => r.tool); } /** * Get exposed tool by name */ getExposedTool(toolName) { return this.exposedTools.get(toolName)?.tool; } /** * Get exposure result by tool name */ getExposureResult(toolName) { return this.exposedTools.get(toolName); } /** * Get tools by source type */ getToolsBySourceType(sourceType) { return Array.from(this.exposedTools.values()) .filter((r) => r.sourceType === sourceType) .map((r) => r.tool); } /** * Remove exposed tool */ unexpose(toolName) { return this.exposedTools.delete(toolName); } /** * Clear all exposed tools */ clear() { this.exposedTools.clear(); } /** * Get statistics */ getStatistics() { const results = Array.from(this.exposedTools.values()); return { totalExposed: results.length, exposedAgents: results.filter((r) => r.sourceType === "agent").length, exposedWorkflows: results.filter((r) => r.sourceType === "workflow") .length, toolNames: results.map((r) => r.toolName), }; } } /** * Global agent exposure manager instance */ export const globalAgentExposureManager = new AgentExposureManager(); //# sourceMappingURL=agentExposure.js.map