UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

164 lines (163 loc) • 6.94 kB
import { c as isRecord } from "./record-coerce-TIsquoVR.mjs"; import { n as isAutomationsToolName } from "./automations-tool-name-DBMZPbPL.mjs"; import { b as runBeforeToolCallHook } from "./agent-tools.before-tool-call-DSzRS11c.mjs"; import { l as copyInternalToolResultState } from "./internal-hooks-Cr0v44EY.mjs"; import { c as resolveToolResultFailureKind, i as protectNetworkToolExecutionError, s as resolveToolExecutionErrorKind, t as formatToolExecutionErrorMessage } from "./tool-result-error-DGhCl3TG.mjs"; import { t as runAgentHarnessAfterToolCallHook } from "./hook-helpers-BSadQEM-.mjs"; import { i as extractToolErrorMessage } from "./embedded-agent-tool-results-C0yTOgai.mjs"; import { n as readMcpLoopbackToolName } from "./mcp-http.schema-DgFmKp3d.mjs"; import { a as jsonRpcResult, i as jsonRpcError, n as MCP_LOOPBACK_SERVER_VERSION, r as MCP_LOOPBACK_SUPPORTED_PROTOCOL_VERSIONS, t as MCP_LOOPBACK_SERVER_NAME } from "./mcp-http.protocol-DlFNaqVL.mjs"; import crypto from "node:crypto"; import { ContentBlockSchema } from "@modelcontextprotocol/sdk/types.js"; //#region src/gateway/mcp-http.handlers.ts function stringifyMcpContent(value) { return typeof value === "string" ? value : JSON.stringify(value) ?? String(value); } const MCP_LOOPBACK_CONTENT_TYPES = /* @__PURE__ */ new Set([ "text", "image", "resource" ]); function normalizeToolCallContent(result) { const content = result?.content; if (Array.isArray(content)) return content.map((block) => { const parsed = ContentBlockSchema.safeParse(block); if (parsed.success && MCP_LOOPBACK_CONTENT_TYPES.has(parsed.data.type)) return parsed.data; return { type: "text", text: stringifyMcpContent(block) }; }); return [{ type: "text", text: stringifyMcpContent(result) }]; } /** Handles one MCP loopback JSON-RPC message and returns a response or notification null. */ async function handleMcpJsonRpc(params) { const { id, method, params: methodParams } = params.message; const toolError = (text) => jsonRpcResult(id, { content: [{ type: "text", text }], isError: true }); switch (method) { case "initialize": { const clientVersion = methodParams?.protocolVersion ?? ""; const negotiated = MCP_LOOPBACK_SUPPORTED_PROTOCOL_VERSIONS.find((version) => version === clientVersion) ?? MCP_LOOPBACK_SUPPORTED_PROTOCOL_VERSIONS[0]; return jsonRpcResult(id, { protocolVersion: negotiated, capabilities: { tools: {} }, serverInfo: { name: MCP_LOOPBACK_SERVER_NAME, version: MCP_LOOPBACK_SERVER_VERSION } }); } case "notifications/initialized": case "notifications/cancelled": return null; case "tools/list": return jsonRpcResult(id, { tools: params.toolSchema }); case "tools/call": { const requestedToolName = typeof methodParams?.name === "string" ? methodParams.name.trim() : ""; const toolName = !params.toolSchema.some((tool) => tool.name === requestedToolName) && isAutomationsToolName(requestedToolName) ? params.toolSchema.find((tool) => isAutomationsToolName(tool.name))?.name ?? requestedToolName : requestedToolName; const rawToolArgs = methodParams?.arguments; if (rawToolArgs !== void 0 && !isRecord(rawToolArgs)) return jsonRpcError(id, -32602, "Invalid params: tools/call arguments must be an object"); const toolArgs = rawToolArgs ?? {}; const tool = toolName && params.toolSchema.some((entry) => entry.name === toolName) ? params.tools.find((candidate) => readMcpLoopbackToolName(candidate) === toolName) : void 0; if (!tool) return toolError(`Tool not available: ${toolName || "unknown"}`); const toolCallId = `mcp-${crypto.randomUUID()}`; const startedAt = Date.now(); let executedToolArgs = toolArgs; const reportToolCallResult = (outcome, result, error) => { runAgentHarnessAfterToolCallHook({ toolName, toolCallId, runId: params.hookContext?.runId, agentId: params.hookContext?.agentId, sessionId: params.hookContext?.sessionId, sessionKey: params.hookContext?.sessionKey, channelId: params.hookContext?.channelId, startArgs: executedToolArgs, result, error, startedAt }).catch(() => {}); try { params.onToolCallResult?.({ toolName, args: executedToolArgs, ...outcome }); } catch {} }; try { const preparedToolArgs = tool.prepareBeforeToolCallParams ? await tool.prepareBeforeToolCallParams(toolArgs, { toolCallId, hookContext: params.hookContext, signal: params.signal }) : toolArgs; executedToolArgs = preparedToolArgs; const hookResult = await runBeforeToolCallHook({ toolName, params: preparedToolArgs, toolCallId, ctx: params.hookContext, signal: params.signal }); if (hookResult.blocked) { const disposition = hookResult.kind === "failure" ? hookResult.disposition : "blocked"; reportToolCallResult(disposition === "blocked" ? { outcome: disposition, deniedReason: hookResult.deniedReason ?? "plugin-before-tool-call" } : { outcome: disposition }, hookResult.reason, hookResult.reason); return toolError(hookResult.reason); } const finalizedToolArgs = tool.finalizeBeforeToolCallParams?.(hookResult.params, preparedToolArgs) ?? hookResult.params; executedToolArgs = finalizedToolArgs; try { params.onToolCallPrepared?.({ toolName, args: executedToolArgs }); } catch {} if (params.authorizeToolCall && !params.authorizeToolCall()) { reportToolCallResult({ outcome: "blocked", deniedReason: "client-grant-revoked" }, void 0, "Tool call authorization expired"); return toolError("Tool call authorization expired"); } let result; try { result = await tool.execute(toolCallId, finalizedToolArgs, params.signal); } catch (error) { throw tool.resultContentSource === "network" ? protectNetworkToolExecutionError(error, "tool execution failed", params.signal) : error; } const failureKind = resolveToolResultFailureKind(result); reportToolCallResult(failureKind === "blocked" ? { outcome: "blocked", deniedReason: "tool_result_blocked" } : { outcome: failureKind ?? "completed", result }, result, failureKind ? extractToolErrorMessage(result) ?? "Tool execution failed" : void 0); return copyInternalToolResultState(result, jsonRpcResult(id, { content: normalizeToolCallContent(result), isError: failureKind !== void 0 })); } catch (error) { const message = formatToolExecutionErrorMessage(error, "tool execution failed"); reportToolCallResult({ outcome: params.signal?.aborted ? "unknown" : resolveToolExecutionErrorKind(error), result: error }, error, message); return toolError(message || "tool execution failed"); } } default: return jsonRpcError(id, -32601, `Method not found: ${method}`); } } //#endregion export { handleMcpJsonRpc };