Skip to content

Commit 1d5c77c

Browse files
committed
fix(gateway): include active plugin tools in catalog
1 parent bd6035d commit 1d5c77c

1 file changed

Lines changed: 41 additions & 1 deletion

File tree

src/gateway/server-methods/tools-catalog.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,17 +107,19 @@ function buildPluginGroups(params: {
107107
suppressNameConflicts: true,
108108
allowGatewaySubagentBinding: true,
109109
});
110+
const activeRegistry = getActivePluginRegistry();
110111
const groups = new Map<string, ToolCatalogGroup>();
111112
// Key metadata by plugin ownership and tool name so we only project metadata that
112113
// was registered BY the tool's owning plugin. Without this scoping, plugin-X
113114
// could override the catalog label/description/risk/tags for another plugin's
114115
// tool by registering metadata with the same toolName.
115116
const pluginToolMetadata = new Map(
116-
(getActivePluginRegistry()?.toolMetadata ?? []).map((entry) => [
117+
(activeRegistry?.toolMetadata ?? []).map((entry) => [
117118
buildPluginToolMetadataKey(entry.pluginId, entry.metadata.toolName),
118119
entry.metadata,
119120
]),
120121
);
122+
const seenToolIds = new Set<string>();
121123
for (const tool of pluginTools) {
122124
const meta = getPluginToolMeta(tool);
123125
const pluginId = meta?.pluginId ?? "plugin";
@@ -153,8 +155,46 @@ function buildPluginGroups(params: {
153155
tags: ownedMetadata?.tags,
154156
defaultProfiles: [],
155157
});
158+
seenToolIds.add(tool.name);
156159
groups.set(groupId, existing);
157160
}
161+
for (const entry of activeRegistry?.tools ?? []) {
162+
const names = entry.names.length > 0 ? entry.names : (entry.declaredNames ?? []);
163+
for (const name of names) {
164+
if (seenToolIds.has(name) || params.existingToolNames.has(name)) {
165+
continue;
166+
}
167+
const groupId = `plugin:${entry.pluginId}`;
168+
const existing =
169+
groups.get(groupId) ??
170+
({
171+
id: groupId,
172+
label: entry.pluginName ?? entry.pluginId,
173+
source: "plugin",
174+
pluginId: entry.pluginId,
175+
tools: [],
176+
} as ToolCatalogGroup);
177+
const ownedMetadata = pluginToolMetadata.get(
178+
buildPluginToolMetadataKey(entry.pluginId, name),
179+
);
180+
existing.tools.push({
181+
id: name,
182+
label: normalizeOptionalString(ownedMetadata?.displayName) ?? name,
183+
description:
184+
summarizeToolDescriptionText({
185+
rawDescription: ownedMetadata?.description,
186+
}) || `Plugin tool from ${entry.pluginName ?? entry.pluginId}`,
187+
source: "plugin",
188+
pluginId: entry.pluginId,
189+
optional: entry.optional,
190+
risk: ownedMetadata?.risk,
191+
tags: ownedMetadata?.tags,
192+
defaultProfiles: [],
193+
});
194+
seenToolIds.add(name);
195+
groups.set(groupId, existing);
196+
}
197+
}
158198
return [...groups.values()]
159199
.map((group) =>
160200
Object.assign({}, group, { tools: group.tools.toSorted((a, b) => a.id.localeCompare(b.id)) }),

0 commit comments

Comments
 (0)