fix: remote MCP plugins fail to start in native agent sessions (#162376)

* fix: remote MCP plugins fail to start in native agent sessions

* test: align remote MCP assertions with normalized transports
This commit is contained in:
Patrick Erichsen 2026-10-01 16:43:44 -07:00 • committed by GitHub
parent 6df9e06382
commit 4587ef903c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 44 additions and 4 deletions

View file

@ -141,6 +141,8 @@ These are recognized and shown in diagnostics, but OpenClaw does not run them:
## MCP for embedded OpenClaw
- Enabled bundles can contribute MCP server config.
- Stdio commands default their working directory to the plugin config directory.
Remote HTTP servers do not receive an implicit working directory.
- OpenClaw merges bundle MCP config into the effective embedded OpenClaw
settings as `mcpServers`.
- OpenClaw exposes supported bundle MCP tools during embedded OpenClaw agent

View file

@ -157,6 +157,35 @@ describe("loadEnabledBundleMcpConfig", () => {
});
});
it("keeps file-backed HTTP bundle servers free of implicit stdio fields", async () => {
await withBundleFixture(async ({ homeDir, workspaceDir }) => {
const pluginRoot = await writeClaudeBundleManifest({
homeDir,
pluginId: "remote-bundle",
manifest: { name: "remote-bundle" },
});
await writeBundleTextFiles(pluginRoot, {
".mcp.json": JSON.stringify({
mcpServers: {
http: { type: "http", url: "https://example.test/mcp" },
sse: { type: "sse", url: "https://example.test/sse" },
},
}),
});
const loaded = loadEnabledBundleMcpConfig({
workspaceDir,
cfg: createEnabledBundleConfig(["remote-bundle"]),
});
expectNoDiagnostics(loaded.diagnostics);
expect(loaded.config.mcpServers).toEqual({
http: { transport: "streamable-http", url: "https://example.test/mcp" },
sse: { transport: "sse", url: "https://example.test/sse" },
});
});
});
it("uses a provided manifest registry instead of rediscovering bundle plugins", async () => {
const homeDir = await tempHarness.createTempDir("openclaw-bundle-mcp-home-");
const workspaceDir = await tempHarness.createTempDir("openclaw-bundle-mcp-workspace-");
@ -217,6 +246,8 @@ describe("loadEnabledBundleMcpConfig", () => {
command: "node",
args: ["./mcp-server.js"],
},
remote: { transport: "streamable-http", url: "https://example.test/mcp" },
events: { transport: "sse", url: "https://example.test/sse" },
},
},
],
@ -230,6 +261,14 @@ describe("loadEnabledBundleMcpConfig", () => {
args: [path.join(pluginRoot, "mcp-server.js")],
cwd: pluginRoot,
});
expect(loaded.config.mcpServers.remote).toEqual({
transport: "streamable-http",
url: "https://example.test/mcp",
});
expect(loaded.config.mcpServers.events).toEqual({
transport: "sse",
url: "https://example.test/sse",
});
});
it("skips MCP servers declared by a disabled native plugin", async () => {

View file

@ -149,7 +149,6 @@ function absolutizeBundleMcpServer(params: {
baseDir: string;
server: BundleMcpServerConfig;
pluginDataDir?: string;
agentFormat?: boolean;
}): BundleMcpServerConfig {
const next: BundleMcpServerConfig = { ...params.server };
const expand = (value: string) =>
@ -168,10 +167,11 @@ function absolutizeBundleMcpServer(params: {
: normalizeExpandedAbsolutePath(expanded);
};
// Remote transports have no process cwd; native runners reject that stdio-only field.
if (
typeof next.command === "string" &&
typeof next.cwd !== "string" &&
typeof next.workingDirectory !== "string" &&
(!params.agentFormat || typeof next.command === "string")
typeof next.workingDirectory !== "string"
) {
next.cwd = params.baseDir;
}
@ -418,7 +418,6 @@ function loadBundleFileBackedMcpConfig(params: {
baseDir,
server,
pluginDataDir: agentLoaded?.pluginDataDir,
agentFormat: params.bundleFormat === "agent",
}),
]),
),