Plugins: warn on unsupported bundle MCP transports
This commit is contained in:
parent
cb45d7d737
commit
320c718c7e
@ -28,6 +28,11 @@ export type EnabledBundleMcpConfigResult = {
|
||||
config: BundleMcpConfig;
|
||||
diagnostics: BundleMcpDiagnostic[];
|
||||
};
|
||||
export type BundleMcpRuntimeSupport = {
|
||||
hasSupportedStdioServer: boolean;
|
||||
unsupportedServerNames: string[];
|
||||
diagnostics: string[];
|
||||
};
|
||||
|
||||
const MANIFEST_PATH_BY_FORMAT: Record<PluginBundleFormat, string> = {
|
||||
claude: CLAUDE_BUNDLE_MANIFEST_RELATIVE_PATH,
|
||||
@ -292,6 +297,28 @@ function loadBundleMcpConfig(params: {
|
||||
return { config: merged, diagnostics: [] };
|
||||
}
|
||||
|
||||
export function inspectBundleMcpRuntimeSupport(params: {
|
||||
pluginId: string;
|
||||
rootDir: string;
|
||||
bundleFormat: PluginBundleFormat;
|
||||
}): BundleMcpRuntimeSupport {
|
||||
const loaded = loadBundleMcpConfig(params);
|
||||
const unsupportedServerNames: string[] = [];
|
||||
let hasSupportedStdioServer = false;
|
||||
for (const [serverName, server] of Object.entries(loaded.config.mcpServers)) {
|
||||
if (typeof server.command === "string" && server.command.trim().length > 0) {
|
||||
hasSupportedStdioServer = true;
|
||||
continue;
|
||||
}
|
||||
unsupportedServerNames.push(serverName);
|
||||
}
|
||||
return {
|
||||
hasSupportedStdioServer,
|
||||
unsupportedServerNames,
|
||||
diagnostics: loaded.diagnostics,
|
||||
};
|
||||
}
|
||||
|
||||
export function loadEnabledBundleMcpConfig(params: {
|
||||
workspaceDir: string;
|
||||
cfg?: OpenClawConfig;
|
||||
|
||||
@ -471,6 +471,65 @@ describe("bundle plugins", () => {
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("warns when bundle MCP only declares unsupported non-stdio transports", () => {
|
||||
useNoBundledPlugins();
|
||||
const workspaceDir = makeTempDir();
|
||||
const stateDir = makeTempDir();
|
||||
const bundleRoot = path.join(workspaceDir, ".openclaw", "extensions", "claude-mcp-url");
|
||||
fs.mkdirSync(path.join(bundleRoot, ".claude-plugin"), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(bundleRoot, ".claude-plugin", "plugin.json"),
|
||||
JSON.stringify({
|
||||
name: "Claude MCP URL",
|
||||
}),
|
||||
"utf-8",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(bundleRoot, ".mcp.json"),
|
||||
JSON.stringify({
|
||||
mcpServers: {
|
||||
remoteProbe: {
|
||||
url: "http://127.0.0.1:8787/mcp",
|
||||
},
|
||||
},
|
||||
}),
|
||||
"utf-8",
|
||||
);
|
||||
|
||||
const registry = withEnv(
|
||||
{
|
||||
OPENCLAW_HOME: stateDir,
|
||||
OPENCLAW_STATE_DIR: stateDir,
|
||||
},
|
||||
() =>
|
||||
loadOpenClawPlugins({
|
||||
workspaceDir,
|
||||
config: {
|
||||
plugins: {
|
||||
entries: {
|
||||
"claude-mcp-url": {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
cache: false,
|
||||
}),
|
||||
);
|
||||
|
||||
const plugin = registry.plugins.find((entry) => entry.id === "claude-mcp-url");
|
||||
expect(plugin?.status).toBe("loaded");
|
||||
expect(plugin?.bundleCapabilities).toEqual(expect.arrayContaining(["mcpServers"]));
|
||||
expect(
|
||||
registry.diagnostics.some(
|
||||
(diag) =>
|
||||
diag.pluginId === "claude-mcp-url" &&
|
||||
diag.message.includes("stdio only today") &&
|
||||
diag.message.includes("remoteProbe"),
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("treats Cursor command roots as supported bundle skill surfaces", () => {
|
||||
useNoBundledPlugins();
|
||||
const workspaceDir = makeTempDir();
|
||||
|
||||
@ -11,6 +11,7 @@ import { openBoundaryFileSync } from "../infra/boundary-file-read.js";
|
||||
import { resolveOpenClawPackageRootSync } from "../infra/openclaw-root.js";
|
||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
import { resolveUserPath } from "../utils.js";
|
||||
import { inspectBundleMcpRuntimeSupport } from "./bundle-mcp.js";
|
||||
import { clearPluginCommands } from "./commands.js";
|
||||
import {
|
||||
applyTestPluginDefaults,
|
||||
@ -1115,6 +1116,36 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
|
||||
message: `bundle capability detected but not wired into OpenClaw yet: ${capability}`,
|
||||
});
|
||||
}
|
||||
if (
|
||||
enableState.enabled &&
|
||||
record.rootDir &&
|
||||
record.bundleFormat &&
|
||||
(record.bundleCapabilities ?? []).includes("mcpServers")
|
||||
) {
|
||||
const runtimeSupport = inspectBundleMcpRuntimeSupport({
|
||||
pluginId: record.id,
|
||||
rootDir: record.rootDir,
|
||||
bundleFormat: record.bundleFormat,
|
||||
});
|
||||
for (const message of runtimeSupport.diagnostics) {
|
||||
registry.diagnostics.push({
|
||||
level: "warn",
|
||||
pluginId: record.id,
|
||||
source: record.source,
|
||||
message,
|
||||
});
|
||||
}
|
||||
if (runtimeSupport.unsupportedServerNames.length > 0) {
|
||||
registry.diagnostics.push({
|
||||
level: "warn",
|
||||
pluginId: record.id,
|
||||
source: record.source,
|
||||
message:
|
||||
"bundle MCP servers use unsupported transports or incomplete configs " +
|
||||
`(stdio only today): ${runtimeSupport.unsupportedServerNames.join(", ")}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
registry.plugins.push(record);
|
||||
seenIds.set(pluginId, candidate.origin);
|
||||
continue;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user