UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

154 lines (153 loc) 5.84 kB
import { i as formatErrorMessage } from "../errors-BXgSefBE.js"; import { n as VERSION } from "../version-Crcn9X9T.js"; import { a as routeLogsToStderr } from "../console-BOsG6sD1.js"; import { i as getRuntimeConfig } from "../io-Gi7-pyU-.js"; import "../config-C9RxTsn1.js"; import { t as coerceChatContentText } from "../chat-content-BbLAEXko.js"; import { n as pickSandboxToolPolicy } from "../sandbox-tool-policy-BRuLtPlA.js"; import { a as collectExplicitDenylist, g as resolveToolProfilePolicy, i as collectExplicitAllowlist, l as mergeAlsoAllowPolicy } from "../tool-policy-CpBMaMTY.js"; import { a as resolvePluginTools, r as ensureStandalonePluginToolRegistryLoaded } from "../tools-oBw0X8xm.js"; import { _ as wrapToolWithBeforeToolCallHook, p as rewrapToolWithBeforeToolCallHook, u as isToolWrappedWithBeforeToolCallHook } from "../agent-tools.before-tool-call-BkVrW03y.js"; import { pathToFileURL } from "node:url"; import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js"; import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; //#region src/mcp/plugin-tools-handlers.ts function resolveJsonSchemaForTool(tool) { const params = tool.parameters; if (params && typeof params === "object" && "type" in params) return params; return { type: "object", properties: {} }; } function createPluginToolsMcpHandlers(tools) { const wrappedTools = tools.map((tool) => { if (isToolWrappedWithBeforeToolCallHook(tool)) return rewrapToolWithBeforeToolCallHook(tool, void 0, { approvalMode: "report" }); return wrapToolWithBeforeToolCallHook(tool, void 0, { approvalMode: "report" }); }); const toolMap = /* @__PURE__ */ new Map(); for (const tool of wrappedTools) toolMap.set(tool.name, tool); return { listTools: async () => ({ tools: wrappedTools.map((tool) => ({ name: tool.name, description: tool.description ?? "", inputSchema: resolveJsonSchemaForTool(tool) })) }), callTool: async (params, signal) => { const tool = toolMap.get(params.name); if (!tool) return { content: [{ type: "text", text: `Unknown tool: ${params.name}` }], isError: true }; try { const result = await tool.execute(`mcp-${Date.now()}`, params.arguments ?? {}, signal); const rawContent = result && typeof result === "object" && "content" in result ? result.content : result; return { content: Array.isArray(rawContent) ? rawContent : [{ type: "text", text: coerceChatContentText(rawContent) }] }; } catch (err) { return { content: [{ type: "text", text: `Tool error: ${formatErrorMessage(err)}` }], isError: true }; } } }; } //#endregion //#region src/mcp/tools-stdio-server.ts function createToolsMcpServer(params) { const handlers = createPluginToolsMcpHandlers(params.tools); const server = new Server({ name: params.name, version: VERSION }, { capabilities: { tools: {} } }); server.setRequestHandler(ListToolsRequestSchema, handlers.listTools); server.setRequestHandler(CallToolRequestSchema, async (request, extra) => { return await handlers.callTool(request.params, extra.signal); }); return server; } async function connectToolsMcpServerToStdio(server) { routeLogsToStderr(); const transport = new StdioServerTransport(); let shuttingDown = false; const shutdown = () => { if (shuttingDown) return; shuttingDown = true; process.stdin.off("end", shutdown); process.stdin.off("close", shutdown); process.off("SIGINT", shutdown); process.off("SIGTERM", shutdown); server.close(); }; process.stdin.once("end", shutdown); process.stdin.once("close", shutdown); process.once("SIGINT", shutdown); process.once("SIGTERM", shutdown); await server.connect(transport); } //#endregion //#region src/mcp/plugin-tools-serve.ts /** * Standalone MCP server that exposes OpenClaw plugin-registered tools * (e.g. memory-lancedb's memory_recall, memory_store, memory_forget) * so ACP sessions running Claude Code can use them. * * Run via: node --import tsx src/mcp/plugin-tools-serve.ts * Or: bun src/mcp/plugin-tools-serve.ts */ function resolvePluginToolPolicy(config) { const profilePolicy = mergeAlsoAllowPolicy(resolveToolProfilePolicy(config.tools?.profile), config.tools?.alsoAllow); const globalPolicy = pickSandboxToolPolicy(config.tools); const toolAllowlist = collectExplicitAllowlist([profilePolicy, globalPolicy]); const toolDenylist = collectExplicitDenylist([profilePolicy, globalPolicy]); return { ...toolAllowlist.length > 0 ? { toolAllowlist } : {}, ...toolDenylist.length > 0 ? { toolDenylist } : {} }; } function resolveTools(config) { const pluginToolPolicy = resolvePluginToolPolicy(config); ensureStandalonePluginToolRegistryLoaded({ context: { config }, ...pluginToolPolicy }); return resolvePluginTools({ context: { config }, ...pluginToolPolicy, suppressNameConflicts: true }); } function createPluginToolsMcpServer(params = {}) { const cfg = params.config ?? getRuntimeConfig(); return createToolsMcpServer({ name: "openclaw-plugin-tools", tools: params.tools ?? resolveTools(cfg) }); } async function servePluginToolsMcp() { routeLogsToStderr(); const config = getRuntimeConfig(); const tools = resolveTools(config); const server = createPluginToolsMcpServer({ config, tools }); if (tools.length === 0) process.stderr.write("plugin-tools-serve: no plugin tools found\n"); await connectToolsMcpServerToStdio(server); } if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) servePluginToolsMcp().catch((err) => { process.stderr.write(`plugin-tools-serve: ${formatErrorMessage(err)}\n`); process.exit(1); }); //#endregion export { createPluginToolsMcpServer, servePluginToolsMcp };