mirror of https://github.com/openclaw/openclaw.git
Plugins: surface MCP servers and bundle capabilities in inspect reports
This commit is contained in:
parent
b9b891b614
commit
b48413e252
|
|
@ -660,6 +660,8 @@ export function registerPluginsCli(program: Command) {
|
|||
.map((entry) => (entry.severity === "warn" ? `warn:${entry.code}` : entry.code))
|
||||
.join(", ")
|
||||
: "none",
|
||||
Bundle:
|
||||
inspect.bundleCapabilities.length > 0 ? inspect.bundleCapabilities.join(", ") : "-",
|
||||
Hooks: formatHookSummary({
|
||||
usesLegacyBeforeAgentStart: inspect.usesLegacyBeforeAgentStart,
|
||||
typedHookCount: inspect.typedHooks.length,
|
||||
|
|
@ -676,6 +678,7 @@ export function registerPluginsCli(program: Command) {
|
|||
{ key: "Shape", header: "Shape", minWidth: 18 },
|
||||
{ key: "Capabilities", header: "Capabilities", minWidth: 28, flex: true },
|
||||
{ key: "Compatibility", header: "Compatibility", minWidth: 24, flex: true },
|
||||
{ key: "Bundle", header: "Bundle", minWidth: 14, flex: true },
|
||||
{ key: "Hooks", header: "Hooks", minWidth: 20, flex: true },
|
||||
],
|
||||
rows,
|
||||
|
|
@ -738,9 +741,9 @@ export function registerPluginsCli(program: Command) {
|
|||
lines.push(
|
||||
`${theme.muted("Legacy before_agent_start:")} ${inspect.usesLegacyBeforeAgentStart ? "yes" : "no"}`,
|
||||
);
|
||||
if ((inspect.plugin.bundleCapabilities?.length ?? 0) > 0) {
|
||||
if (inspect.bundleCapabilities.length > 0) {
|
||||
lines.push(
|
||||
`${theme.muted("Bundle capabilities:")} ${inspect.plugin.bundleCapabilities?.join(", ")}`,
|
||||
`${theme.muted("Bundle capabilities:")} ${inspect.bundleCapabilities.join(", ")}`,
|
||||
);
|
||||
}
|
||||
lines.push(
|
||||
|
|
@ -785,6 +788,14 @@ export function registerPluginsCli(program: Command) {
|
|||
lines.push(...formatInspectSection("CLI commands", inspect.cliCommands));
|
||||
lines.push(...formatInspectSection("Services", inspect.services));
|
||||
lines.push(...formatInspectSection("Gateway methods", inspect.gatewayMethods));
|
||||
lines.push(
|
||||
...formatInspectSection(
|
||||
"MCP servers",
|
||||
inspect.mcpServers.map((entry) =>
|
||||
entry.hasStdioTransport ? entry.name : `${entry.name} (unsupported transport)`,
|
||||
),
|
||||
),
|
||||
);
|
||||
if (inspect.httpRouteCount > 0) {
|
||||
lines.push(...formatInspectSection("HTTP routes", [String(inspect.httpRouteCount)]));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ export type EnabledBundleMcpConfigResult = {
|
|||
};
|
||||
export type BundleMcpRuntimeSupport = {
|
||||
hasSupportedStdioServer: boolean;
|
||||
supportedServerNames: string[];
|
||||
unsupportedServerNames: string[];
|
||||
diagnostics: string[];
|
||||
};
|
||||
|
|
@ -279,17 +280,20 @@ export function inspectBundleMcpRuntimeSupport(params: {
|
|||
bundleFormat: PluginBundleFormat;
|
||||
}): BundleMcpRuntimeSupport {
|
||||
const loaded = loadBundleMcpConfig(params);
|
||||
const supportedServerNames: string[] = [];
|
||||
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;
|
||||
supportedServerNames.push(serverName);
|
||||
continue;
|
||||
}
|
||||
unsupportedServerNames.push(serverName);
|
||||
}
|
||||
return {
|
||||
hasSupportedStdioServer,
|
||||
supportedServerNames,
|
||||
unsupportedServerNames,
|
||||
diagnostics: loaded.diagnostics,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -493,6 +493,117 @@ describe("buildPluginStatusReport", () => {
|
|||
expect(buildPluginCompatibilityWarnings()).toEqual([]);
|
||||
});
|
||||
|
||||
it("populates bundleCapabilities from plugin record", () => {
|
||||
loadOpenClawPluginsMock.mockReturnValue({
|
||||
plugins: [
|
||||
{
|
||||
id: "claude-bundle",
|
||||
name: "Claude Bundle",
|
||||
description: "A bundle plugin with skills and commands",
|
||||
source: "/tmp/claude-bundle/.claude-plugin/plugin.json",
|
||||
origin: "workspace",
|
||||
enabled: true,
|
||||
status: "loaded",
|
||||
format: "bundle",
|
||||
bundleFormat: "claude",
|
||||
bundleCapabilities: ["skills", "commands", "agents", "settings"],
|
||||
rootDir: "/tmp/claude-bundle",
|
||||
toolNames: [],
|
||||
hookNames: [],
|
||||
channelIds: [],
|
||||
providerIds: [],
|
||||
speechProviderIds: [],
|
||||
mediaUnderstandingProviderIds: [],
|
||||
imageGenerationProviderIds: [],
|
||||
webSearchProviderIds: [],
|
||||
gatewayMethods: [],
|
||||
cliCommands: [],
|
||||
services: [],
|
||||
commands: [],
|
||||
httpRoutes: 0,
|
||||
hookCount: 0,
|
||||
configSchema: false,
|
||||
},
|
||||
],
|
||||
diagnostics: [],
|
||||
channels: [],
|
||||
channelSetups: [],
|
||||
providers: [],
|
||||
speechProviders: [],
|
||||
mediaUnderstandingProviders: [],
|
||||
imageGenerationProviders: [],
|
||||
webSearchProviders: [],
|
||||
tools: [],
|
||||
hooks: [],
|
||||
typedHooks: [],
|
||||
httpRoutes: [],
|
||||
gatewayHandlers: {},
|
||||
cliRegistrars: [],
|
||||
services: [],
|
||||
commands: [],
|
||||
});
|
||||
|
||||
const inspect = buildPluginInspectReport({ id: "claude-bundle" });
|
||||
|
||||
expect(inspect).not.toBeNull();
|
||||
expect(inspect?.bundleCapabilities).toEqual(["skills", "commands", "agents", "settings"]);
|
||||
expect(inspect?.mcpServers).toEqual([]);
|
||||
expect(inspect?.shape).toBe("non-capability");
|
||||
});
|
||||
|
||||
it("returns empty bundleCapabilities and mcpServers for non-bundle plugins", () => {
|
||||
loadOpenClawPluginsMock.mockReturnValue({
|
||||
plugins: [
|
||||
{
|
||||
id: "plain-plugin",
|
||||
name: "Plain Plugin",
|
||||
description: "A regular plugin",
|
||||
source: "/tmp/plain-plugin/index.ts",
|
||||
origin: "workspace",
|
||||
enabled: true,
|
||||
status: "loaded",
|
||||
toolNames: [],
|
||||
hookNames: [],
|
||||
channelIds: [],
|
||||
providerIds: ["plain"],
|
||||
speechProviderIds: [],
|
||||
mediaUnderstandingProviderIds: [],
|
||||
imageGenerationProviderIds: [],
|
||||
webSearchProviderIds: [],
|
||||
gatewayMethods: [],
|
||||
cliCommands: [],
|
||||
services: [],
|
||||
commands: [],
|
||||
httpRoutes: 0,
|
||||
hookCount: 0,
|
||||
configSchema: false,
|
||||
},
|
||||
],
|
||||
diagnostics: [],
|
||||
channels: [],
|
||||
channelSetups: [],
|
||||
providers: [],
|
||||
speechProviders: [],
|
||||
mediaUnderstandingProviders: [],
|
||||
imageGenerationProviders: [],
|
||||
webSearchProviders: [],
|
||||
tools: [],
|
||||
hooks: [],
|
||||
typedHooks: [],
|
||||
httpRoutes: [],
|
||||
gatewayHandlers: {},
|
||||
cliRegistrars: [],
|
||||
services: [],
|
||||
commands: [],
|
||||
});
|
||||
|
||||
const inspect = buildPluginInspectReport({ id: "plain-plugin" });
|
||||
|
||||
expect(inspect).not.toBeNull();
|
||||
expect(inspect?.bundleCapabilities).toEqual([]);
|
||||
expect(inspect?.mcpServers).toEqual([]);
|
||||
});
|
||||
|
||||
it("formats and summarizes compatibility notices", () => {
|
||||
const notice = {
|
||||
pluginId: "legacy-plugin",
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent
|
|||
import { resolveDefaultAgentWorkspaceDir } from "../agents/workspace.js";
|
||||
import { loadConfig } from "../config/config.js";
|
||||
import { createSubsystemLogger } from "../logging/subsystem.js";
|
||||
import { inspectBundleMcpRuntimeSupport } from "./bundle-mcp.js";
|
||||
import { normalizePluginsConfig } from "./config-state.js";
|
||||
import { loadOpenClawPlugins } from "./loader.js";
|
||||
import { createPluginLoaderLogger } from "./logger.js";
|
||||
|
|
@ -64,7 +65,12 @@ export type PluginInspectReport = {
|
|||
cliCommands: string[];
|
||||
services: string[];
|
||||
gatewayMethods: string[];
|
||||
mcpServers: Array<{
|
||||
name: string;
|
||||
hasStdioTransport: boolean;
|
||||
}>;
|
||||
httpRouteCount: number;
|
||||
bundleCapabilities: string[];
|
||||
diagnostics: PluginDiagnostic[];
|
||||
policy: {
|
||||
allowPromptInjection?: boolean;
|
||||
|
|
@ -226,6 +232,26 @@ export function buildPluginInspectReport(params: {
|
|||
httpRouteCount: plugin.httpRoutes,
|
||||
});
|
||||
|
||||
// Populate MCP server info for bundle-format plugins with a known rootDir.
|
||||
let mcpServers: PluginInspectReport["mcpServers"] = [];
|
||||
if (plugin.format === "bundle" && plugin.bundleFormat && plugin.rootDir) {
|
||||
const mcpSupport = inspectBundleMcpRuntimeSupport({
|
||||
pluginId: plugin.id,
|
||||
rootDir: plugin.rootDir,
|
||||
bundleFormat: plugin.bundleFormat,
|
||||
});
|
||||
mcpServers = [
|
||||
...mcpSupport.supportedServerNames.map((name) => ({
|
||||
name,
|
||||
hasStdioTransport: true,
|
||||
})),
|
||||
...mcpSupport.unsupportedServerNames.map((name) => ({
|
||||
name,
|
||||
hasStdioTransport: false,
|
||||
})),
|
||||
];
|
||||
}
|
||||
|
||||
const usesLegacyBeforeAgentStart = typedHooks.some(
|
||||
(entry) => entry.name === "before_agent_start",
|
||||
);
|
||||
|
|
@ -248,7 +274,9 @@ export function buildPluginInspectReport(params: {
|
|||
cliCommands: [...plugin.cliCommands],
|
||||
services: [...plugin.services],
|
||||
gatewayMethods: [...plugin.gatewayMethods],
|
||||
mcpServers,
|
||||
httpRouteCount: plugin.httpRoutes,
|
||||
bundleCapabilities: plugin.bundleCapabilities ?? [],
|
||||
diagnostics,
|
||||
policy: {
|
||||
allowPromptInjection: policyEntry?.hooks?.allowPromptInjection,
|
||||
|
|
|
|||
Loading…
Reference in New Issue