openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
143 lines (142 loc) • 5.93 kB
JavaScript
import { t as formatErrorMessage } from "./errors-BSdPMFao.mjs";
import { i as logWarn } from "./logger-CYgsNcie.mjs";
import { a as updateMcpAppModelContext } from "./mcp-app-model-context-X2kgdER2.mjs";
import { r as GatewayErrorDetailCodes, t as ErrorCodes } from "./gateway-error-details-DIefe7Zy.mjs";
import { f as errorShape } from "./error-codes-DI20vDEB.mjs";
import { n as authorizeOperatorScopesForMethod } from "./method-scopes-C-MKLkWf.mjs";
import { n as resolveRequestedSessionAgentId } from "./session-request-agent-D8Xj-iBG.mjs";
import { s as buildMcpAppSandboxPath } from "./mcp-ui-resource-DlOChoWt.mjs";
import { a as resolveMcpAppActiveView, i as requireMcpAppInteraction, n as executeMcpAppOperation, s as withMcpAppActiveView, t as McpAppViewExpiredError } from "./mcp-app-operations-RarH15_D.mjs";
import { t as createMcpAppStandaloneTicket } from "./mcp-app-standalone-By79ZH7a.mjs";
//#region src/gateway/server-methods/mcp-app.ts
function requireString(params, key) {
const value = params[key];
if (typeof value !== "string" || !value.trim()) throw new Error(`${key} is required`);
return value.trim();
}
function optionalCursor(params) {
const cursor = params.cursor;
return typeof cursor === "string" && cursor.trim() ? { cursor: cursor.trim() } : void 0;
}
var McpAppRequestError = class extends Error {
constructor(shape) {
super(shape.message);
this.shape = shape;
}
};
function resolveMcpAppSessionOwner(params, cfg) {
const sessionKey = requireString(params, "sessionKey");
const explicitAgentId = typeof params.agentId === "string" && params.agentId.trim() ? params.agentId.trim() : void 0;
const owner = resolveRequestedSessionAgentId(cfg, sessionKey, explicitAgentId);
if (!owner.ok) throw new McpAppRequestError(owner.error);
return owner.agentId;
}
function operationHandler(buildOperation) {
return async ({ respond, params, context }) => {
await handle(respond, async () => {
const operation = buildOperation(params);
const cfg = context.getRuntimeConfig();
const active = await resolveMcpAppActiveView({
sessionKey: requireString(params, "sessionKey"),
agentId: resolveMcpAppSessionOwner(params, cfg),
viewId: requireString(params, "viewId")
});
return await executeMcpAppOperation(active, operation);
});
};
}
async function handle(respond, operation) {
try {
respond(true, await operation());
} catch (error) {
respond(false, void 0, error instanceof McpAppRequestError ? error.shape : errorShape(ErrorCodes.UNAVAILABLE, formatErrorMessage(error), error instanceof McpAppViewExpiredError ? { details: { code: GatewayErrorDetailCodes.MCP_APP_VIEW_EXPIRED } } : void 0));
}
}
const mcpAppHandlers = {
"mcp.app.view": async ({ respond, params, context, client }) => {
await handle(respond, async () => {
const active = await resolveMcpAppActiveView({
sessionKey: requireString(params, "sessionKey"),
agentId: resolveMcpAppSessionOwner(params, context.getRuntimeConfig()),
viewId: requireString(params, "viewId"),
cfg: context.getRuntimeConfig()
});
return await withMcpAppActiveView(active, "read", async () => {
const { view } = active;
let interactive = false;
try {
await requireMcpAppInteraction(view);
interactive = true;
} catch {}
const updateModelContextSupported = interactive && active.runtime.mcpAppModelContextRevoked !== true;
const sandboxPort = context.getMcpAppSandboxPort?.() ?? await context.ensureSandboxHostPort?.();
if (sandboxPort === void 0) throw new Error("MCP App sandbox listener is unavailable; restart the Gateway");
const configuredOrigin = context.getRuntimeConfig().mcp?.apps?.sandboxOrigin;
let standalone = void 0;
try {
standalone = createMcpAppStandaloneTicket({
sessionKey: requireString(params, "sessionKey"),
view,
toolOperationsAuthorized: authorizeOperatorScopesForMethod("mcp.app.callTool", client?.connect?.scopes ?? []).allowed
});
} catch (error) {
logWarn(`mcp-app: standalone ticket unavailable: ${formatErrorMessage(error)}`);
}
return {
sandboxUrl: buildMcpAppSandboxPath(view.csp),
sandboxPort,
...configuredOrigin ? { sandboxOrigin: new URL(configuredOrigin).origin } : {},
html: view.html,
...view.csp ? { csp: view.csp } : {},
toolInput: view.toolInput,
toolResult: view.toolResult,
...standalone ? {
standaloneUrl: standalone.url,
standaloneExpiresAtMs: standalone.expiresAtMs
} : {},
messageSupported: interactive,
updateModelContextSupported
};
});
});
},
"mcp.app.updateModelContext": async ({ respond, params, context }) => {
await handle(respond, async () => {
const active = await resolveMcpAppActiveView({
sessionKey: requireString(params, "sessionKey"),
agentId: resolveMcpAppSessionOwner(params, context.getRuntimeConfig()),
viewId: requireString(params, "viewId")
});
return await withMcpAppActiveView(active, "read", async () => {
await requireMcpAppInteraction(active.view);
updateMcpAppModelContext(active.runtime, active.view, params);
return {};
});
});
},
"mcp.app.callTool": operationHandler((params) => ({
method: "tools/call",
params: {
name: requireString(params, "toolName"),
arguments: params.arguments ?? {}
}
})),
"mcp.app.listTools": operationHandler((params) => ({
method: "tools/list",
params: optionalCursor(params) ?? {}
})),
"mcp.app.listResources": operationHandler((params) => ({
method: "resources/list",
params: optionalCursor(params) ?? {}
})),
"mcp.app.listResourceTemplates": operationHandler((params) => ({
method: "resources/templates/list",
params: optionalCursor(params) ?? {}
})),
"mcp.app.readResource": operationHandler((params) => ({
method: "resources/read",
params: { uri: requireString(params, "uri") }
}))
};
//#endregion
export { mcpAppHandlers };