feat(FN-3717): add OpenClaw MCP bridge (+5 more)

Commits merged:
- docs(FN-3717): update openclaw runtime and settings docs
- test(FN-3717): fix openclaw engine test typing
- test(FN-3717): strengthen openclaw bridge coverage
- feat(FN-3717): complete Step 2 — wire engine and bundle delivery
- fix(FN-3717): address openclaw bridge review feedback
- feat(FN-3717): complete Step 1 — add OpenClaw MCP bridge

Files changed:
.changeset/fn-3717-openclaw-tool-bridge.md         |  5 ++
 docs/settings-reference.md                         | 28 +++++---
 packages/cli/src/__tests__/bundle-output.test.ts   |  9 +++
 packages/cli/tsup.config.ts                        |  7 ++
 .../src/__tests__/openclaw-runtime-e2e.test.ts     | 28 ++++++++
 .../__tests__/openclaw-runtime-integration.test.ts | 10 +++
 plugins/fusion-plugin-openclaw-runtime/README.md   | 13 ++++
 .../src/__tests__/cli-spawn.test.ts                | 82 +++++++++++++++++++---
 .../src/__tests__/mcp-config.test.ts               | 45 ++++++++++++
 .../src/__tests__/runtime-adapter.test.ts          | 46 ++++++++++++
 .../fusion-plugin-openclaw-runtime/src/index.ts    |  5 ++
 .../src/mcp-config.ts                              | 60 ++++++++++++++++
 .../src/mcp-schema-server.cjs                      | 59 ++++++++++++++++
 .../src/pi-module.ts                               | 52 ++++++++++++--
 .../src/runtime-adapter.ts                         | 26 +++++++
 .../fusion-plugin-openclaw-runtime/src/types.ts    |  4 ++
 16 files changed, 457 insertions(+), 22 deletions(-)

Fusion-Task-Id: FN-3717
This commit is contained in:
Fusion
2026-05-07 17:56:43 -07:00
committed by gsxdsm
parent c32999b293
commit 836ca6807a
16 changed files with 457 additions and 22 deletions

View File

@@ -10,6 +10,7 @@
import { spawn } from "node:child_process";
import { randomUUID } from "node:crypto";
import { readFile } from "node:fs/promises";
import type {
CliConfig,
GatewayCallbacks,
@@ -101,15 +102,21 @@ export function resolveCliConfig(settings?: Record<string, unknown>): CliConfig
*/
export function buildOpenClawArgs(
config: CliConfig,
sessionId: string,
session: Pick<GatewaySession, "sessionId" | "mcpProfile">,
message: string,
): string[] {
const args: string[] = ["--no-color", "agent"];
const args: string[] = ["--no-color"];
if (session.mcpProfile) {
args.push("--profile", session.mcpProfile);
}
args.push("agent");
if (!config.useGateway) args.push("--local");
args.push("--json");
args.push("--session-id", sessionId);
args.push("--session-id", session.sessionId);
args.push("--message", message);
args.push("--agent", config.agentId);
@@ -152,6 +159,8 @@ export function createCliSession(opts: {
systemPrompt: string;
agentId?: string;
callbacks?: GatewayCallbacks;
mcpProfile?: string;
mcpConfigPath?: string;
}): GatewaySession {
return {
sessionId: randomUUID(),
@@ -161,9 +170,44 @@ export function createCliSession(opts: {
lastModelDescription: `openclaw/${opts.agentId ?? DEFAULT_AGENT_ID}`,
lastUsage: undefined,
callbacks: opts.callbacks,
mcpProfile: opts.mcpProfile,
mcpConfigPath: opts.mcpConfigPath,
};
}
// ---------------------------------------------------------------------------
// configureOpenClawMcpServer — configure profile-scoped MCP server via CLI
// ---------------------------------------------------------------------------
export async function configureOpenClawMcpServer(opts: {
binaryPath: string;
profile: string;
serverName: string;
serverConfigPath: string;
}): Promise<void> {
const serverValue = await readFile(opts.serverConfigPath, "utf-8");
await new Promise<void>((resolve, reject) => {
const child = spawn(opts.binaryPath, ["--no-color", "--profile", opts.profile, "mcp", "set", opts.serverName, serverValue], {
stdio: ["ignore", "pipe", "pipe"],
});
let stderr = "";
child.stderr?.on("data", (chunk: Buffer) => {
stderr += chunk.toString("utf-8");
});
child.on("error", (err) => reject(new Error(`openclaw: failed to configure MCP server — ${err.message}`)));
child.on("close", (code) => {
if (code === 0) {
resolve();
return;
}
reject(new Error(`openclaw: mcp set failed (${String(code)}): ${extractStderrError(stderr)}`));
});
});
}
// ---------------------------------------------------------------------------
// promptCli — spawns openclaw, parses the JSON, fires callbacks
// ---------------------------------------------------------------------------
@@ -175,7 +219,7 @@ export async function promptCli(
callbacks?: GatewayCallbacks,
signal?: AbortSignal,
): Promise<void> {
const args = buildOpenClawArgs(config, session.sessionId, message);
const args = buildOpenClawArgs(config, session, message);
const cb: GatewayCallbacks = { ...session.callbacks, ...callbacks };
cb.onToolStart?.("openclaw.agent", { sessionId: session.sessionId });