




























1+import { isRecord } from "@openclaw/normalization-core/record-coerce";
2+import { uniqueStrings } from "@openclaw/normalization-core/string-normalization";
3+import { normalizeConfiguredMcpServers } from "../config/mcp-config-normalize.js";
4+import type { OpenClawConfig } from "../config/types.openclaw.js";
5+import { normalizePluginsConfig } from "../plugins/config-state.js";
6+import {
7+isManifestPluginAvailableForControlPlane,
8+loadManifestMetadataSnapshot,
9+} from "../plugins/manifest-contract-eligibility.js";
10+import type { PluginManifestRecord } from "../plugins/manifest-registry.js";
11+import { hasManifestToolAvailability } from "../plugins/manifest-tool-availability.js";
12+import { sanitizeServerName, TOOL_NAME_SEPARATOR } from "./agent-bundle-mcp-names.js";
13+import { compileGlobPatterns, matchesAnyGlobPattern } from "./glob-pattern.js";
14+import type { DeclaredToolAllowlistContext } from "./tool-policy.js";
15+import { normalizeToolName } from "./tool-policy.js";
16+17+type ToolDenylist = ReturnType<typeof compileGlobPatterns>;
18+19+function normalizeToolDenylist(list?: string[]): ToolDenylist {
20+return compileGlobPatterns({ raw: list, normalize: normalizeToolName });
21+}
22+23+function denylistBlocksName(name: string, denylist: ToolDenylist): boolean {
24+const normalized = normalizeToolName(name);
25+return normalized ? matchesAnyGlobPattern(normalized, denylist) : false;
26+}
27+28+function denylistBlocksMcpServerNamespace(params: {
29+safeServerName: string;
30+denylist: ToolDenylist;
31+}): boolean {
32+const serverPrefix = normalizeToolName(params.safeServerName + TOOL_NAME_SEPARATOR);
33+if (!serverPrefix) {
34+return false;
35+}
36+return matchesAnyGlobPattern(serverPrefix, params.denylist);
37+}
38+39+function denylistBlocksMcpServer(params: {
40+safeServerName: string;
41+denylist: ToolDenylist;
42+}): boolean {
43+return (
44+denylistBlocksName("bundle-mcp", params.denylist) ||
45+matchesAnyGlobPattern("group:plugins", params.denylist) ||
46+denylistBlocksMcpServerNamespace({
47+safeServerName: params.safeServerName,
48+denylist: params.denylist,
49+})
50+);
51+}
52+53+function denylistBlocksPlugin(params: { pluginId: string; denylist: ToolDenylist }): boolean {
54+return (
55+denylistBlocksName(params.pluginId, params.denylist) ||
56+matchesAnyGlobPattern("group:plugins", params.denylist)
57+);
58+}
59+60+function denylistBlocksPluginTool(params: {
61+pluginId: string;
62+toolName: string;
63+denylist: ToolDenylist;
64+}): boolean {
65+return (
66+denylistBlocksPlugin({ pluginId: params.pluginId, denylist: params.denylist }) ||
67+denylistBlocksName(params.toolName, params.denylist)
68+);
69+}
70+71+function collectConfiguredMcpServerNames(params: {
72+config?: OpenClawConfig;
73+toolDenylist?: string[];
74+}): string[] {
75+const servers = normalizeConfiguredMcpServers(params.config?.mcp?.servers);
76+const denylist = normalizeToolDenylist(params.toolDenylist);
77+const usedServerNames = new Set<string>();
78+const names: string[] = [];
79+for (const [name, value] of Object.entries(servers)) {
80+if (!isRecord(value) || value.enabled === false || !name.trim()) {
81+continue;
82+}
83+const safeServerName = sanitizeServerName(name, usedServerNames);
84+if (
85+denylistBlocksMcpServer({
86+ safeServerName,
87+ denylist,
88+})
89+) {
90+continue;
91+}
92+names.push(safeServerName);
93+}
94+return names;
95+}
96+97+function collectAvailableManifestToolNames(params: {
98+plugin: PluginManifestRecord;
99+config?: OpenClawConfig;
100+env: NodeJS.ProcessEnv;
101+denylist: ToolDenylist;
102+}): string[] {
103+return (params.plugin.contracts?.tools ?? [])
104+.filter(
105+(toolName) =>
106+!denylistBlocksPluginTool({
107+pluginId: params.plugin.id,
108+ toolName,
109+denylist: params.denylist,
110+}),
111+)
112+.filter((toolName) =>
113+hasManifestToolAvailability({
114+plugin: params.plugin,
115+toolNames: [toolName],
116+config: params.config,
117+env: params.env,
118+}),
119+)
120+.map(normalizeToolName)
121+.filter(Boolean);
122+}
123+124+function collectDeclaredPluginContext(params: {
125+config?: OpenClawConfig;
126+workspaceDir?: string;
127+toolDenylist?: string[];
128+env?: NodeJS.ProcessEnv;
129+}): Pick<DeclaredToolAllowlistContext, "pluginIds" | "pluginToolNames"> {
130+if (params.config?.plugins?.enabled === false) {
131+return {};
132+}
133+const env = params.env ?? process.env;
134+const snapshot = loadManifestMetadataSnapshot({
135+config: params.config,
136+ ...(params.workspaceDir ? { workspaceDir: params.workspaceDir } : {}),
137+ env,
138+});
139+const normalizedPlugins = normalizePluginsConfig(params.config?.plugins);
140+const denylist = normalizeToolDenylist(params.toolDenylist);
141+const pluginIds = new Set<string>();
142+const pluginToolNames = new Set<string>();
143+for (const plugin of snapshot.manifestRegistry.plugins) {
144+if (
145+!isManifestPluginAvailableForControlPlane({
146+ snapshot,
147+ plugin,
148+config: params.config,
149+}) ||
150+normalizedPlugins.entries[plugin.id]?.enabled === false ||
151+normalizedPlugins.deny.includes(plugin.id) ||
152+denylistBlocksPlugin({ pluginId: plugin.id, denylist })
153+) {
154+continue;
155+}
156+const availableToolNames = collectAvailableManifestToolNames({
157+ plugin,
158+config: params.config,
159+ env,
160+ denylist,
161+});
162+if (availableToolNames.length === 0) {
163+continue;
164+}
165+pluginIds.add(plugin.id);
166+for (const toolName of availableToolNames) {
167+pluginToolNames.add(toolName);
168+}
169+}
170+return { pluginIds, pluginToolNames };
171+}
172+173+export function buildDeclaredToolAllowlistContext(params: {
174+config?: OpenClawConfig;
175+workspaceDir?: string;
176+toolDenylist?: string[];
177+env?: NodeJS.ProcessEnv;
178+}): DeclaredToolAllowlistContext | undefined {
179+const mcpServerNames = uniqueStrings(
180+collectConfiguredMcpServerNames({
181+config: params.config,
182+toolDenylist: params.toolDenylist,
183+}),
184+);
185+const pluginContext = collectDeclaredPluginContext(params);
186+const pluginIds = uniqueStrings(pluginContext.pluginIds ?? []);
187+const pluginToolNames = uniqueStrings(pluginContext.pluginToolNames ?? []);
188+if (mcpServerNames.length === 0 && pluginIds.length === 0 && pluginToolNames.length === 0) {
189+return undefined;
190+}
191+return {
192+ ...(pluginIds.length > 0 ? { pluginIds } : {}),
193+ ...(pluginToolNames.length > 0 ? { pluginToolNames } : {}),
194+ ...(mcpServerNames.length > 0 ? { mcpServerNames } : {}),
195+};
196+}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。