wolfpack-mcp 1.0.108 → 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
+ });
package/dist/index.js CHANGED
@@ -383,7 +383,10 @@ const CreateWorkItemSchema = z.object({
383
383
  .enum(['S', 'M', 'L'])
384
384
  .optional()
385
385
  .describe('Size estimate: "S" (small), "M" (medium), "L" (large)'),
386
- leading_user_id: z.string().optional().describe('User ID to assign as leading user'),
386
+ leading_user_id: z
387
+ .string()
388
+ .optional()
389
+ .describe('User ID to assign as leading user. AGENTS: you may put your own id here — filing work you already know you will do is the one assignment that is yours to make. Leaving it out is just as normal: the Delivery Lead assigns it instead. Either way the item waits in the backlog for a human to approve before you can pull it.'),
387
390
  category_id: z.string().optional().describe('Category ID to organize the work item'),
388
391
  radar_item_id: z
389
392
  .string()
@@ -328,7 +328,7 @@ const SHARED_TOOLS = [
328
328
  },
329
329
  leading_user_id: {
330
330
  type: ['string', 'null'],
331
- description: "User ID to assign as leading user, or null to unassign. Agents may not assign work to themselves — that is the Delivery Lead's decision.",
331
+ description: "User ID to assign as leading user, or null to unassign. Agents may assign themselves only to a work item they created; who does anyone else's work is the Delivery Lead's decision.",
332
332
  },
333
333
  radar_item_id: {
334
334
  type: ['string', 'null'],
@@ -806,7 +806,7 @@ const SHARED_TOOLS = [
806
806
  name: 'create_work_item',
807
807
  permission: 'mcp:work_items:create',
808
808
  description: 'Create a new work item in your current project (auto-selected for single-project users, or use list_projects first for multi-project). Requires mcp:work_items:create permission. ' +
809
- 'Agents: work you file lands unassigned in the backlog, whatever status you ask for, and waits there for the Delivery Lead to approve and assign it. ' +
809
+ 'Agents: work you file lands in the backlog whatever status you ask for, and waits there for a human to approve it. Leaving it unassigned is the norm — the Delivery Lead assigns it. Put your own id in leading_user_id when you already know you will do it; that says who, not when, so it still waits for approval before you can pull it. ' +
810
810
  'ORDERED WORK: when you file a set of items that must happen in order, give each one depends_on ' +
811
811
  'naming the reference numbers it follows, rather than writing "Follows #2322" into the description — ' +
812
812
  'that way the board knows the order and the pickup gate enforces it. ' +
@@ -850,7 +850,7 @@ const SHARED_TOOLS = [
850
850
  },
851
851
  leading_user_id: {
852
852
  type: 'string',
853
- description: "User ID to assign as leading user. Agents may not assign work to themselves — that is the Delivery Lead's decision.",
853
+ description: 'User ID to assign as leading user. AGENTS: you may put your own id here — filing work you already know you will do is the one assignment that is yours to make. Leaving it out is just as normal: the Delivery Lead assigns it instead. Either way the item waits in the backlog for a human to approve before you can pull it.',
854
854
  },
855
855
  category_id: {
856
856
  type: 'string',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wolfpack-mcp",
3
- "version": "1.0.108",
3
+ "version": "1.0.110",
4
4
  "description": "MCP server for Wolfpack AI-enhanced software delivery tools",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",