wolfpack-mcp 1.0.109 → 1.0.110
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -162,6 +162,21 @@ export const AGENT_BUILDER_TOOLS = [
|
|
|
162
162
|
type: 'object',
|
|
163
163
|
description: 'The agent\'s own Ghost runtime settings, merged into every session\'s generated config. Two keys are honoured: "options" (e.g. {"max_turn_requests": 150} — the per-turn model request cap, default 200) and "agents" (per-agent overrides, e.g. {"coder": {"disabled_tools": ["agent"]}} to switch the sub-agent tool off). Other keys are ignored. Replaces the stored config as a whole; set to null to clear. Ghost runtime only — Claude Code agents ignore it.',
|
|
164
164
|
},
|
|
165
|
+
disabled_tools: {
|
|
166
|
+
type: 'array',
|
|
167
|
+
items: { type: 'string' },
|
|
168
|
+
description: 'Tools the agent never gets, whatever starts the session (a task, a chat, a work-item pickup). Ghost names ("agent", "fetch"); Claude Code agents use Claude Code\'s own ("Task", "WebFetch") and "mcp__<server>__<tool>" for an MCP tool. Replaces the stored list; an empty array clears it. A task\'s own disabled_tools are added to these — a task narrows further, never widens.',
|
|
169
|
+
},
|
|
170
|
+
disabled_mcp_servers: {
|
|
171
|
+
type: 'array',
|
|
172
|
+
items: { type: 'string' },
|
|
173
|
+
description: 'MCP servers the agent never gets, by name (e.g. ["wolfpack"]). The server is left out of the session config altogether rather than started and denied per tool. Replaces the stored list; an empty array clears it.',
|
|
174
|
+
},
|
|
175
|
+
disabled_skills: {
|
|
176
|
+
type: 'array',
|
|
177
|
+
items: { type: 'string' },
|
|
178
|
+
description: 'Skills the agent never loads, by name (e.g. ["respond-to-notification"]). Replaces the stored list; an empty array clears it.',
|
|
179
|
+
},
|
|
165
180
|
...ORG_SLUG_PROP,
|
|
166
181
|
},
|
|
167
182
|
required: ['agent_id'],
|
|
@@ -1060,6 +1075,9 @@ export async function handleAgentBuilderTool(name, args, client) {
|
|
|
1060
1075
|
llm_family: z.string().nullable().optional(),
|
|
1061
1076
|
api_key_permissions: z.array(z.string()).optional(),
|
|
1062
1077
|
config: z.record(z.unknown()).nullable().optional(),
|
|
1078
|
+
disabled_tools: z.array(z.string()).optional(),
|
|
1079
|
+
disabled_mcp_servers: z.array(z.string()).optional(),
|
|
1080
|
+
disabled_skills: z.array(z.string()).optional(),
|
|
1063
1081
|
org_slug: orgSlugField,
|
|
1064
1082
|
})
|
|
1065
1083
|
.parse(args);
|
|
@@ -1085,6 +1103,12 @@ export async function handleAgentBuilderTool(name, args, client) {
|
|
|
1085
1103
|
fields.apiKeyPermissions = rest.api_key_permissions;
|
|
1086
1104
|
if (rest.config !== undefined)
|
|
1087
1105
|
fields.config = rest.config;
|
|
1106
|
+
if (rest.disabled_tools !== undefined)
|
|
1107
|
+
fields.disabledTools = rest.disabled_tools;
|
|
1108
|
+
if (rest.disabled_mcp_servers !== undefined)
|
|
1109
|
+
fields.disabledMcpServers = rest.disabled_mcp_servers;
|
|
1110
|
+
if (rest.disabled_skills !== undefined)
|
|
1111
|
+
fields.disabledSkills = rest.disabled_skills;
|
|
1088
1112
|
const agent = await client.updateAgent(agent_id, fields, resolveOrg(parsed));
|
|
1089
1113
|
return { content: [{ type: 'text', text: `Updated agent\n\n${text(agent)}` }] };
|
|
1090
1114
|
}
|
|
@@ -192,3 +192,40 @@ describe('personal agents over MCP (#2273)', () => {
|
|
|
192
192
|
expect(fn.mock.calls[0][1]).toBe(true);
|
|
193
193
|
});
|
|
194
194
|
});
|
|
195
|
+
// #2525 — the three opt-out lists a profile carries, reachable over MCP so an
|
|
196
|
+
// agent can be narrowed without hand-writing its config JSON.
|
|
197
|
+
describe('profile-level disabled tools, MCP servers and skills (#2525)', () => {
|
|
198
|
+
const DISABLED_FIELDS = ['disabled_tools', 'disabled_mcp_servers', 'disabled_skills'];
|
|
199
|
+
function fakeUpdate() {
|
|
200
|
+
const updateAgent = vi.fn().mockResolvedValue({ id: 'agent-1' });
|
|
201
|
+
return { client: { updateAgent }, updateAgent };
|
|
202
|
+
}
|
|
203
|
+
it('update_agent advertises all three lists', () => {
|
|
204
|
+
const properties = toolNamed('update_agent').inputSchema.properties;
|
|
205
|
+
expect(Object.keys(properties)).toEqual(expect.arrayContaining(DISABLED_FIELDS));
|
|
206
|
+
});
|
|
207
|
+
it('sends the three lists under their stored names', async () => {
|
|
208
|
+
const { client, updateAgent } = fakeUpdate();
|
|
209
|
+
await handleAgentBuilderTool('update_agent', {
|
|
210
|
+
agent_id: 'agent-1',
|
|
211
|
+
disabled_tools: ['agent', 'mcp__wolfpack__create_issue'],
|
|
212
|
+
disabled_mcp_servers: ['wolfpack'],
|
|
213
|
+
disabled_skills: ['respond-to-notification'],
|
|
214
|
+
}, client);
|
|
215
|
+
expect(updateAgent.mock.calls[0][1]).toEqual({
|
|
216
|
+
disabledTools: ['agent', 'mcp__wolfpack__create_issue'],
|
|
217
|
+
disabledMcpServers: ['wolfpack'],
|
|
218
|
+
disabledSkills: ['respond-to-notification'],
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
it('an empty array is sent, so a list can be cleared', async () => {
|
|
222
|
+
const { client, updateAgent } = fakeUpdate();
|
|
223
|
+
await handleAgentBuilderTool('update_agent', { agent_id: 'agent-1', disabled_tools: [] }, client);
|
|
224
|
+
expect(updateAgent.mock.calls[0][1]).toEqual({ disabledTools: [] });
|
|
225
|
+
});
|
|
226
|
+
it('a field nobody named is left out, so it stays as stored', async () => {
|
|
227
|
+
const { client, updateAgent } = fakeUpdate();
|
|
228
|
+
await handleAgentBuilderTool('update_agent', { agent_id: 'agent-1', name: 'Scout' }, client);
|
|
229
|
+
expect(updateAgent.mock.calls[0][1]).toEqual({ name: 'Scout' });
|
|
230
|
+
});
|
|
231
|
+
});
|