UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

112 lines (111 loc) 5.32 kB
import { n as resolveOpenClawPackageRootSync } from "./openclaw-root-CfYY-fyD.js"; import { createRequire } from "node:module"; import fs from "node:fs"; import path from "node:path"; //#region src/mcp/openclaw-tools-serve-config.ts /** * Shared contract between the openclaw-tools MCP stdio entry and the callers * that inject it into CLI harness runs. Keep this module free of MCP SDK and * tool-runtime imports so CLI-runner prepare paths can build server configs * without loading the server. */ const OPENCLAW_TOOLS_MCP_TOOLS_ENV = "OPENCLAW_TOOLS_MCP_TOOLS"; const OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_SURFACE_ENV = "OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_SURFACE"; const OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_APPROVAL_ARMED_ENV = "OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_APPROVAL_ARMED"; const OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_PROPOSAL_ENV = "OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_PROPOSAL"; const APPROVAL_ARMED_OPERATOR_ONLY_VALUE = "operator-only"; const OPENCLAW_TOOLS_MCP_TOOL_IDS = ["cron", "openclaw"]; function isOpenClawToolsMcpToolId(value) { return OPENCLAW_TOOLS_MCP_TOOL_IDS.includes(value); } /** Parse the served tool selection; the default stays cron for acpx bridges. */ function resolveOpenClawToolsMcpToolSelection(env = process.env) { const raw = env[OPENCLAW_TOOLS_MCP_TOOLS_ENV]?.trim(); if (!raw) return ["cron"]; const entries = raw.split(",").map((entry) => entry.trim()).filter(Boolean); const selection = entries.filter(isOpenClawToolsMcpToolId); if (selection.length === 0 || selection.length !== entries.length) throw new Error(`${OPENCLAW_TOOLS_MCP_TOOLS_ENV} must be a comma list of: ${OPENCLAW_TOOLS_MCP_TOOL_IDS.join(", ")}`); return selection; } /** Parse the OpenClaw surface for served openclaw tools; defaults to cli. */ function resolveOpenClawToolsMcpSystemAgentSurface(env = process.env) { const raw = env[OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_SURFACE_ENV]?.trim(); if (!raw || raw === "cli") return "cli"; if (raw === "gateway") return "gateway"; throw new Error(`${OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_SURFACE_ENV} must be "cli" or "gateway"`); } /** * Reconstruct per-turn approval state for the served openclaw tool. The * stdio server runs out of process, so the host passes the armed bit and the * pending proposal hash through env; the host mirrors transitions back from * tool events (see mirrorSystemAgentToolStateFromEvents in agent-turn.ts). */ function resolveOpenClawToolsMcpSystemAgentApproval(env = process.env) { const pendingProposal = env[OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_PROPOSAL_ENV]?.trim(); const armedValue = env[OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_APPROVAL_ARMED_ENV]?.trim(); return { approvalArmed: armedValue === "1", proposalRef: pendingProposal ? { current: pendingProposal } : {}, ...armedValue === APPROVAL_ARMED_OPERATOR_ONLY_VALUE ? { operatorApprovalOnly: true } : {} }; } function resolveTsxImportSpecifier() { try { return createRequire(import.meta.url).resolve("tsx"); } catch { return "tsx"; } } function resolveOpenClawToolsServeCommand() { const packageRoot = resolveOpenClawPackageRootSync({ argv1: process.argv[1], moduleUrl: import.meta.url, cwd: process.cwd() }); if (!packageRoot) throw new Error("openclaw-tools MCP: could not resolve the OpenClaw package root"); const distEntry = path.join(packageRoot, "dist", "mcp", "openclaw-tools-serve.js"); if (fs.existsSync(distEntry)) return { command: process.execPath, args: [distEntry] }; const sourceEntry = path.join(packageRoot, "src", "mcp", "openclaw-tools-serve.ts"); if (!fs.existsSync(sourceEntry)) throw new Error(`openclaw-tools MCP: no serve entry under ${packageRoot}`); if (process.versions.bun) return { command: process.execPath, args: [sourceEntry] }; return { command: process.execPath, args: [ "--import", resolveTsxImportSpecifier(), sourceEntry ] }; } /** * OpenClaw CLI-harness runs get exactly one MCP server: this stdio entry * serving the ring-zero openclaw tool. The server keeps the "openclaw" name * so backend tool pre-approvals (e.g. Claude's --allowedTools mcp__openclaw__*) * apply without per-backend argument surgery. */ function buildSystemAgentToolsMcpServerConfig(options) { const entry = resolveOpenClawToolsServeCommand(); const pendingProposal = options.proposalRef?.current; return { mcpServers: { openclaw: { command: entry.command, args: options.agentId ? [ ...entry.args, "--openclaw-agent-id", options.agentId ] : entry.args, env: { [OPENCLAW_TOOLS_MCP_TOOLS_ENV]: "openclaw", [OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_SURFACE_ENV]: options.surface, ...options.operatorApprovalOnly === true ? { [OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_APPROVAL_ARMED_ENV]: APPROVAL_ARMED_OPERATOR_ONLY_VALUE } : options.approvalArmed === true ? { [OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_APPROVAL_ARMED_ENV]: "1" } : {}, ...pendingProposal ? { [OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_PROPOSAL_ENV]: pendingProposal } : {} } } } }; } //#endregion export { buildSystemAgentToolsMcpServerConfig as a, resolveOpenClawToolsMcpToolSelection as c, OPENCLAW_TOOLS_MCP_TOOLS_ENV as i, OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_PROPOSAL_ENV as n, resolveOpenClawToolsMcpSystemAgentApproval as o, OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_SURFACE_ENV as r, resolveOpenClawToolsMcpSystemAgentSurface as s, OPENCLAW_TOOLS_MCP_SYSTEM_AGENT_APPROVAL_ARMED_ENV as t };