openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
231 lines (230 loc) • 9.65 kB
JavaScript
import { a as getAdvertisedScopedMcpCatalog, d as retireSessionMcpRuntime, n as acquireSessionMcpRuntime, t as acquireRequesterScopedMcpRuntime, u as rememberAdvertisedScopedMcpCatalog } from "./agent-bundle-mcp-manager-api-DCHQm4w1.js";
import { n as mergeMcpConnectCatalog } from "./agent-bundle-mcp-requester-connect-Blx6kVwa.js";
import { i as resolveProjectedMcpCodexToolApprovalMode, r as requiresMcpCodexToolApproval, t as formatMcpCodexApprovalRemedy } from "./mcp-codex-tool-approval-UMdM31mU.js";
import { a as setPluginToolMeta, r as getPluginToolMeta } from "./tool-metadata-B5aqo73s.js";
import { t as resolveConversationCapabilityProfile } from "./conversation-capability-profile-B6PkMXFD.js";
import { r as materializeBundleMcpToolsForRun, t as buildBundleMcpToolsFromCatalog } from "./agent-bundle-mcp-materialize-CIHEU_uK.js";
import { t as applyFinalEffectiveToolPolicy } from "./effective-tool-policy-Bco8DCTH.js";
import { t as applyEmbeddedAttemptToolsAllow } from "./attempt-tool-construction-plan-CRC43c_k.js";
//#region src/agents/agent-bundle-mcp-harness.ts
function formatConfiguredMcpDiagnosticNotice(messages, runLabel) {
const bounded = [...new Set(messages)].map((message) => message.replaceAll(/\s+/g, " ").trim().slice(0, 180)).filter(Boolean).slice(0, 4);
if (bounded.length === 0) return;
return `Configured MCP is incomplete for ${runLabel}: ${bounded.join("; ")}. Do not claim MCP-backed work succeeded; report this blocker to the operator.`;
}
function applyConfiguredMcpApproval(tools, options) {
return tools.flatMap((tool) => {
const mcp = getPluginToolMeta(tool)?.mcp;
if (mcp?.operation !== "tool") return [tool];
const mode = resolveProjectedMcpCodexToolApprovalMode(mcp.serverName, {}, options.projectedMcpServers?.[mcp.serverName], mcp.toolName) ?? mcp.codexApproval?.mode;
if (!requiresMcpCodexToolApproval({
...mcp.codexApproval,
mode,
fullPermission: options.fullPermission
})) return [tool];
const approvalMode = mode === "prompt" ? "prompt" : "auto";
const requestApproval = options.requestApproval;
if (!requestApproval) {
options.onOmitted?.(`${mcp.serverName}/${mcp.toolName}: requires interactive Codex approval (${approvalMode}); ${formatMcpCodexApprovalRemedy(mcp.serverName)}`);
return [];
}
const meta = getPluginToolMeta(tool);
const execute = tool.execute;
const guarded = {
...tool,
execute: async (toolCallId, params, signal, onUpdate) => {
let active = true;
try {
await requestApproval({
signal,
safeToolName: tool.name,
toolCallId,
serverName: mcp.serverName,
toolName: mcp.toolName,
mode: approvalMode,
isActive: () => active
});
return await execute(toolCallId, params, signal, onUpdate);
} finally {
active = false;
}
}
};
setPluginToolMeta(guarded, meta);
return [guarded];
});
}
function notConnectedToolResult(serverName, toolName) {
const message = `Requester has not connected MCP server "${serverName}" (tool "${toolName}") for this turn.`;
return {
content: [{
type: "text",
text: message
}],
details: {
status: "error",
error: message,
mcpServer: serverName,
mcpTool: toolName
}
};
}
function applyHarnessToolPolicy(tools, params) {
if (tools.length === 0) return tools;
const allowed = applyEmbeddedAttemptToolsAllow(tools, params.toolsAllow, { toolMeta: (tool) => getPluginToolMeta(tool) });
const profile = params.conversationCapabilityProfile ?? (params.policyContext ? resolveConversationCapabilityProfile({
...params.policyContext,
runtimeToolAllowlist: params.toolsAllow
}) : void 0);
if (!profile) return allowed;
return applyFinalEffectiveToolPolicy({
bundledTools: allowed,
config: params.policyContext?.config ?? params.cfg,
conversationCapabilityProfile: profile,
warn: params.warn ?? (() => void 0)
});
}
function buildCatalogTools(catalog, params, requesterConnect) {
return buildBundleMcpToolsFromCatalog({
catalog,
reservedToolNames: params.reservedToolNames ? Array.from(params.reservedToolNames) : void 0,
createExecute: (tool) => {
return requesterConnect?.createExecute(tool.serverName) ?? (async () => notConnectedToolResult(tool.serverName, tool.toolName));
}
});
}
/**
* Materialize static configured MCP for a Codex harness turn.
* No requester identity is accepted here, so requester resolvers stay unreachable.
*/
async function materializeStaticMcpToolsForHarnessRunCore(params) {
const acquisition = await acquireSessionMcpRuntime({
sessionId: params.sessionId,
sessionKey: params.sessionKey,
workspaceDir: params.workspaceDir,
agentDir: params.agentDir,
cfg: params.cfg,
manifestRegistry: params.manifestRegistry,
toolOverrides: params.toolOverrides
});
const retireSnapshotRuntime = params.retireSessionRuntimeAfterDispose ? async () => {
await retireSessionMcpRuntime({
sessionId: params.sessionId,
reason: "scheduled-authority-snapshot-complete"
});
} : void 0;
let liveRuntime;
try {
liveRuntime = await materializeBundleMcpToolsForRun({
...acquisition,
agentId: params.agentId,
reservedToolNames: params.reservedToolNames,
...retireSnapshotRuntime ? { disposeRuntime: retireSnapshotRuntime } : {}
});
} catch (error) {
await retireSnapshotRuntime?.();
throw error;
}
try {
const policyWarnings = [];
const policyParams = {
...params,
warn: (message) => {
policyWarnings.push(message);
params.warn?.(message);
}
};
const fullPermission = params.autoApproveCodexAppServerApprovals === true;
const policyTools = applyHarnessToolPolicy(liveRuntime.tools, policyParams);
const projectedApproval = params.projectedMcpServers ? { projectedMcpServers: params.projectedMcpServers } : {};
const allowed = applyConfiguredMcpApproval(policyTools, {
fullPermission,
...projectedApproval,
...params.requestInteractiveCodexApproval ? { requestApproval: params.requestInteractiveCodexApproval } : {},
onOmitted: (message) => policyWarnings.push(message)
});
liveRuntime.restrictAppTools?.(applyConfiguredMcpApproval(applyHarnessToolPolicy(liveRuntime.appTools ?? liveRuntime.tools, policyParams), {
fullPermission,
...projectedApproval,
...params.requestInteractiveCodexApproval ? {} : { onOmitted: (message) => policyWarnings.push(message) }
}));
const diagnosticNotice = formatConfiguredMcpDiagnosticNotice([...(liveRuntime.diagnostics ?? []).map((diagnostic) => `${diagnostic.serverName}: ${diagnostic.message}`), ...policyWarnings], params.requestInteractiveCodexApproval ? "this run" : "this scheduled run");
let disposed = false;
return {
tools: allowed,
...diagnosticNotice ? { diagnosticNotice } : {},
dispose: async () => {
if (disposed) return;
disposed = true;
await liveRuntime.dispose();
}
};
} catch (error) {
await liveRuntime.dispose();
throw error;
}
}
/**
* Materialize requester-scoped MCP tools for a harness run (e.g. Codex dynamic tools).
* Updates the session advertised-catalog cache when a requester resolves a catalog.
* Before any requester resolves in the session, returns undefined (nothing to advertise).
*/
async function materializeRequesterScopedMcpToolsForHarnessRunCore(params) {
const scopedRuntimeHandle = await acquireRequesterScopedMcpRuntime({
sessionId: params.sessionId,
sessionKey: params.sessionKey,
workspaceDir: params.workspaceDir,
agentDir: params.agentDir,
cfg: params.cfg,
manifestRegistry: params.manifestRegistry,
toolOverrides: params.toolOverrides,
requesterSenderId: params.requesterSenderId,
agentAccountId: params.agentAccountId,
messageChannel: params.messageChannel
});
const scopedRuntime = scopedRuntimeHandle?.runtime;
let liveRuntime;
let liveCatalog;
try {
if (scopedRuntime) {
liveRuntime = await materializeBundleMcpToolsForRun({
runtime: scopedRuntime,
releaseLease: scopedRuntimeHandle?.releaseLease,
agentId: params.agentId,
reservedToolNames: params.reservedToolNames
});
liveCatalog = scopedRuntime.peekCatalog() ?? await scopedRuntime.getCatalog();
if (liveCatalog.tools.length > 0 && scopedRuntimeHandle) rememberAdvertisedScopedMcpCatalog(scopedRuntimeHandle, liveCatalog);
}
const advertisedCatalog = getAdvertisedScopedMcpCatalog(params.sessionId) ?? (liveCatalog ? mergeMcpConnectCatalog(liveCatalog, scopedRuntime?.requesterConnect) : void 0);
if (!advertisedCatalog || advertisedCatalog.tools.length === 0) {
await liveRuntime?.dispose();
return;
}
const reservedToolNames = params.reservedToolNames ? Array.from(params.reservedToolNames) : void 0;
const advertisedTools = buildCatalogTools(advertisedCatalog, {
...params,
reservedToolNames
}, scopedRuntime?.requesterConnect);
const liveByName = new Map((liveRuntime?.tools ?? []).map((tool) => [tool.name, tool]));
const filteredTools = applyHarnessToolPolicy(advertisedTools.map((tool) => liveByName.get(tool.name) ?? tool), params);
const filteredAdvertised = applyHarnessToolPolicy(advertisedTools, params);
const allowedNames = new Set(filteredAdvertised.map((tool) => tool.name));
const executableTools = filteredTools.filter((tool) => allowedNames.has(tool.name));
let disposed = false;
return {
tools: executableTools,
advertisedTools: filteredAdvertised,
dispose: async () => {
if (disposed) return;
disposed = true;
await liveRuntime?.dispose();
}
};
} catch (error) {
await liveRuntime?.dispose();
throw error;
}
}
//#endregion
export { materializeRequesterScopedMcpToolsForHarnessRunCore, materializeStaticMcpToolsForHarnessRunCore };