wolfpack-mcp 1.0.91 → 1.0.93

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.
@@ -113,10 +113,6 @@ export const AGENT_BUILDER_TOOLS = [
113
113
  items: { type: 'string' },
114
114
  description: 'Replace the full set of MCP permissions granted to the agent\'s session API key (e.g. ["mcp:work_items:read", "mcp:comments:create"]). Grant only what the agent\'s tasks need. An empty array falls back to a read-only default.',
115
115
  },
116
- scheduling_enabled: {
117
- type: 'boolean',
118
- description: "The agent-level scheduling switch. No schedule fires while it is off, whatever the schedule's own enabled flag says.",
119
- },
120
116
  ...ORG_SLUG_PROP,
121
117
  },
122
118
  required: ['agent_id'],
@@ -354,10 +350,6 @@ export const AGENT_BUILDER_TOOLS = [
354
350
  agent_id: { type: 'string', description: 'Agent profile ID' },
355
351
  name: { type: 'string', description: 'Task name' },
356
352
  prompt: { type: 'string', description: 'What the agent should do when this task runs' },
357
- scheduled_enabled: {
358
- type: 'boolean',
359
- description: 'Whether this task is included in scheduled runs (default false)',
360
- },
361
353
  sort_order: {
362
354
  type: 'number',
363
355
  description: 'Order within scheduled batch (lower = first)',
@@ -388,7 +380,7 @@ export const AGENT_BUILDER_TOOLS = [
388
380
  },
389
381
  {
390
382
  name: 'update_agent_task',
391
- description: "Update a task's prompt, schedule settings, model pin, or disabled tools/MCP servers.",
383
+ description: "Update a task's prompt, sort order, model pin, or disabled tools/MCP servers.",
392
384
  inputSchema: {
393
385
  type: 'object',
394
386
  properties: {
@@ -396,7 +388,6 @@ export const AGENT_BUILDER_TOOLS = [
396
388
  task_id: { type: 'string', description: 'Task ID' },
397
389
  name: { type: 'string', description: 'Updated task name' },
398
390
  prompt: { type: 'string', description: 'Updated prompt' },
399
- scheduled_enabled: { type: 'boolean', description: 'Include in scheduled runs' },
400
391
  sort_order: { type: 'number', description: 'Order within scheduled batch' },
401
392
  is_active: { type: 'boolean', description: 'Whether this task is active' },
402
393
  ...TASK_MODEL_PROPS,
@@ -836,19 +827,6 @@ const AgentTaskSchema = z.object({
836
827
  org_slug: orgSlugField,
837
828
  });
838
829
  const SkillIdSchema = z.object({ skill_id: z.string(), org_slug: orgSlugField });
839
- /**
840
- * A schedule enabled on an agent whose own scheduling switch is off never
841
- * fires (#2083) — say so in the result rather than letting the caller find
842
- * out from a schedule that stays silent.
843
- */
844
- async function schedulingOffWarning(client, agentId, scheduleEnabled, orgSlug) {
845
- if (!scheduleEnabled)
846
- return '';
847
- const agent = await client.getAgent(agentId, orgSlug);
848
- return !agent || agent.schedulingEnabled
849
- ? ''
850
- : "\n\nWARNING: this agent's scheduling is switched off, so the schedule will not fire until update_agent sets scheduling_enabled: true.";
851
- }
852
830
  export async function handleAgentBuilderTool(name, args, client) {
853
831
  const text = (t) => JSON.stringify(t, null, 2);
854
832
  switch (name) {
@@ -906,7 +884,6 @@ export async function handleAgentBuilderTool(name, args, client) {
906
884
  llm_model: z.string().nullable().optional(),
907
885
  llm_family: z.string().nullable().optional(),
908
886
  api_key_permissions: z.array(z.string()).optional(),
909
- scheduling_enabled: z.boolean().optional(),
910
887
  org_slug: orgSlugField,
911
888
  })
912
889
  .parse(args);
@@ -930,8 +907,6 @@ export async function handleAgentBuilderTool(name, args, client) {
930
907
  fields.llmFamily = rest.llm_family;
931
908
  if (rest.api_key_permissions !== undefined)
932
909
  fields.apiKeyPermissions = rest.api_key_permissions;
933
- if (rest.scheduling_enabled !== undefined)
934
- fields.schedulingEnabled = rest.scheduling_enabled;
935
910
  const agent = await client.updateAgent(agent_id, fields, resolveOrg(parsed));
936
911
  return { content: [{ type: 'text', text: `Updated agent\n\n${text(agent)}` }] };
937
912
  }
@@ -1095,7 +1070,6 @@ export async function handleAgentBuilderTool(name, args, client) {
1095
1070
  agent_id: z.string(),
1096
1071
  name: z.string(),
1097
1072
  prompt: z.string(),
1098
- scheduled_enabled: z.boolean().optional(),
1099
1073
  sort_order: z.number().optional(),
1100
1074
  llm_provider: z.string().nullable().optional(),
1101
1075
  llm_model: z.string().nullable().optional(),
@@ -1109,7 +1083,6 @@ export async function handleAgentBuilderTool(name, args, client) {
1109
1083
  const task = await client.createAgentTask(parsed.agent_id, {
1110
1084
  name: parsed.name,
1111
1085
  prompt: parsed.prompt,
1112
- scheduledEnabled: parsed.scheduled_enabled,
1113
1086
  sortOrder: parsed.sort_order,
1114
1087
  llmProvider: parsed.llm_provider,
1115
1088
  llmModel: parsed.llm_model,
@@ -1127,7 +1100,6 @@ export async function handleAgentBuilderTool(name, args, client) {
1127
1100
  task_id: z.string(),
1128
1101
  name: z.string().optional(),
1129
1102
  prompt: z.string().optional(),
1130
- scheduled_enabled: z.boolean().optional(),
1131
1103
  sort_order: z.number().optional(),
1132
1104
  is_active: z.boolean().optional(),
1133
1105
  llm_provider: z.string().nullable().optional(),
@@ -1144,8 +1116,6 @@ export async function handleAgentBuilderTool(name, args, client) {
1144
1116
  fields.name = parsed.name;
1145
1117
  if (parsed.prompt !== undefined)
1146
1118
  fields.prompt = parsed.prompt;
1147
- if (parsed.scheduled_enabled !== undefined)
1148
- fields.scheduledEnabled = parsed.scheduled_enabled;
1149
1119
  if (parsed.sort_order !== undefined)
1150
1120
  fields.sortOrder = parsed.sort_order;
1151
1121
  if (parsed.is_active !== undefined)
@@ -1256,12 +1226,11 @@ export async function handleAgentBuilderTool(name, args, client) {
1256
1226
  if (rest.task_ids !== undefined)
1257
1227
  body.taskIds = rest.task_ids;
1258
1228
  const schedule = await client.createAgentSchedule(agent_id, body, resolveOrg(parsed));
1259
- const warning = await schedulingOffWarning(client, agent_id, schedule.enabled, resolveOrg(parsed));
1260
1229
  return {
1261
1230
  content: [
1262
1231
  {
1263
1232
  type: 'text',
1264
- text: `Created schedule "${schedule.name}"${warning}\n\n${text(schedule)}`,
1233
+ text: `Created schedule "${schedule.name}"\n\n${text(schedule)}`,
1265
1234
  },
1266
1235
  ],
1267
1236
  };
@@ -1316,9 +1285,8 @@ export async function handleAgentBuilderTool(name, args, client) {
1316
1285
  if (rest.task_ids !== undefined)
1317
1286
  body.taskIds = rest.task_ids;
1318
1287
  const schedule = await client.updateAgentSchedule(agent_id, schedule_id, body, resolveOrg(parsed));
1319
- const warning = await schedulingOffWarning(client, agent_id, schedule.enabled, resolveOrg(parsed));
1320
1288
  return {
1321
- content: [{ type: 'text', text: `Updated schedule${warning}\n\n${text(schedule)}` }],
1289
+ content: [{ type: 'text', text: `Updated schedule\n\n${text(schedule)}` }],
1322
1290
  };
1323
1291
  }
1324
1292
  // ─── Skills ─────────────────────────────────────────────────────────────
@@ -98,38 +98,28 @@ describe('extra instructions on a task run over MCP (#2048)', () => {
98
98
  expect(runAgentTask.mock.calls[0][6]).toBeUndefined();
99
99
  });
100
100
  });
101
- // #2083: everything the schedule editor can set is settable over MCP, and a
102
- // schedule enabled on an agent whose scheduling switch is off says so.
103
- describe('schedule pre-flight and scheduling switch over MCP (#2083)', () => {
101
+ // #2083: everything the schedule editor can set is settable over MCP.
102
+ describe('schedule pre-flight over MCP (#2083)', () => {
104
103
  const SCHEDULE_FIELDS = ['preflight_check', 'check_kinds', 'skip_while_active'];
105
104
  it.each(['create_agent_schedule', 'update_agent_schedule'])('%s advertises the pre-flight fields', (name) => {
106
105
  const properties = toolNamed(name).inputSchema.properties;
107
106
  expect(Object.keys(properties)).toEqual(expect.arrayContaining(SCHEDULE_FIELDS));
108
107
  });
109
- it('update_agent advertises the scheduling switch', () => {
110
- const properties = toolNamed('update_agent').inputSchema.properties;
111
- expect(Object.keys(properties)).toContain('scheduling_enabled');
112
- });
113
- function scheduleClient(schedulingEnabled) {
108
+ function scheduleClient() {
114
109
  const schedule = { id: 'sched-1', name: 'Reviews', enabled: true };
115
110
  const createAgentSchedule = vi.fn().mockResolvedValue(schedule);
116
111
  const updateAgentSchedule = vi.fn().mockResolvedValue(schedule);
117
- const updateAgent = vi.fn().mockResolvedValue({ id: 'agent-1', schedulingEnabled });
118
- const getAgent = vi.fn().mockResolvedValue({ id: 'agent-1', schedulingEnabled });
119
112
  return {
120
113
  client: {
121
114
  createAgentSchedule,
122
115
  updateAgentSchedule,
123
- updateAgent,
124
- getAgent,
125
116
  },
126
117
  createAgentSchedule,
127
118
  updateAgentSchedule,
128
- updateAgent,
129
119
  };
130
120
  }
131
121
  it('sends the pre-flight, kinds and skip-while-active when creating a schedule', async () => {
132
- const { client, createAgentSchedule } = scheduleClient(true);
122
+ const { client, createAgentSchedule } = scheduleClient();
133
123
  await handleAgentBuilderTool('create_agent_schedule', {
134
124
  agent_id: 'agent-1',
135
125
  preflight_check: 'claimable-check',
@@ -143,23 +133,14 @@ describe('schedule pre-flight and scheduling switch over MCP (#2083)', () => {
143
133
  });
144
134
  });
145
135
  it('sends the same fields when updating a schedule', async () => {
146
- const { client, updateAgentSchedule } = scheduleClient(true);
136
+ const { client, updateAgentSchedule } = scheduleClient();
147
137
  await handleAgentBuilderTool('update_agent_schedule', { agent_id: 'agent-1', schedule_id: 'sched-1', preflight_check: null, check_kinds: [] }, client);
148
138
  expect(updateAgentSchedule.mock.calls[0][2]).toEqual({ preflightCheck: null, checkKinds: [] });
149
139
  });
150
- it('sends the scheduling switch when updating an agent', async () => {
151
- const { client, updateAgent } = scheduleClient(true);
152
- await handleAgentBuilderTool('update_agent', { agent_id: 'agent-1', scheduling_enabled: true }, client);
153
- expect(updateAgent.mock.calls[0][1]).toEqual({ schedulingEnabled: true });
154
- });
155
- it('warns when an enabled schedule lands on an agent whose scheduling is off', async () => {
156
- const { client } = scheduleClient(false);
157
- const result = await handleAgentBuilderTool('create_agent_schedule', { agent_id: 'agent-1', enabled: true }, client);
158
- expect(result.content[0].text).toContain('scheduling is switched off');
159
- });
160
- it('stays quiet when the agent is scheduling', async () => {
161
- const { client } = scheduleClient(true);
162
- const result = await handleAgentBuilderTool('create_agent_schedule', { agent_id: 'agent-1', enabled: true }, client);
163
- expect(result.content[0].text).not.toContain('WARNING');
140
+ // #2067 retired the profile-level scheduling switch: a schedule's own
141
+ // enabled flag is the only switch, so update_agent must not offer another.
142
+ it('update_agent no longer advertises a scheduling switch', () => {
143
+ const properties = toolNamed('update_agent').inputSchema.properties;
144
+ expect(Object.keys(properties)).not.toContain('scheduling_enabled');
164
145
  });
165
146
  });
package/dist/client.js CHANGED
@@ -252,6 +252,9 @@ export class WolfpackClient {
252
252
  const query = params.toString();
253
253
  return this.api.get(this.withTeamSlug(`/work-item-checks${query ? `?${query}` : ''}`, teamSlug));
254
254
  }
255
+ async addWorkItemCheck(workItemId, body, teamSlug) {
256
+ return this.api.post(this.withTeamSlug(`/work-items/${workItemId}/checks`, teamSlug), body);
257
+ }
255
258
  async claimWorkItemCheck(workItemId, checkId, teamSlug) {
256
259
  return this.api.post(this.withTeamSlug(`/work-items/${workItemId}/checks/${checkId}/claim`, teamSlug), {});
257
260
  }
package/dist/index.js CHANGED
@@ -234,6 +234,18 @@ const ListWorkItemChecksSchema = z.object({
234
234
  .optional()
235
235
  .describe('Project slug (required for multi-project users, use list_projects to get slugs)'),
236
236
  });
237
+ const AddWorkItemCheckSchema = z.object({
238
+ work_item_id: refIdString().describe('The refId of the work item'),
239
+ kind: z.string().describe('The check kind to add, e.g. security-review'),
240
+ comment: z
241
+ .string()
242
+ .optional()
243
+ .describe('Why the check is being requested — posted as a comment on the item'),
244
+ project_slug: z
245
+ .string()
246
+ .optional()
247
+ .describe('Project slug (required for multi-project users, use list_projects to get slugs)'),
248
+ });
237
249
  const ClaimWorkItemCheckSchema = z.object({
238
250
  work_item_id: refIdString().describe('The refId of the work item'),
239
251
  check_id: z.string().describe('The check ID (from list_work_item_checks)'),
@@ -1096,7 +1108,8 @@ class WolfpackMCPServer {
1096
1108
  'Checks on blocked items are left out — blocked work is not picked up, and claiming one is refused. ' +
1097
1109
  'REVIEW WORKFLOW: claim a check with claim_work_item_check, get_work_item to read the work, review it, ' +
1098
1110
  'then complete_work_item_check with "passed" or "failed" and a short summary. ' +
1099
- 'A failed check sends the item back to its developer with your summary as feedback; do not change the item status yourself.',
1111
+ 'A failed check sends the item back to its developer with your summary as feedback; do not change the item status yourself. ' +
1112
+ 'To request a further review of another kind on an item (e.g. security-review), use add_work_item_check.',
1100
1113
  inputSchema: {
1101
1114
  type: 'object',
1102
1115
  properties: {
@@ -1120,6 +1133,31 @@ class WolfpackMCPServer {
1120
1133
  },
1121
1134
  },
1122
1135
  },
1136
+ {
1137
+ name: 'add_work_item_check',
1138
+ description: 'Add a pending review check of a kind (e.g. security-review) to a work item you do not lead, so the reviewer that answers that kind picks it up from its inbox. ' +
1139
+ "Use it when a change touches a sensitive path and needs a second, specialised review. A case waiting on the item's checks keeps waiting until the new check is resolved. " +
1140
+ 'Refused for a kind already pending on the item, for person-only kinds, and on your own work.',
1141
+ inputSchema: {
1142
+ type: 'object',
1143
+ properties: {
1144
+ work_item_id: { type: 'string', description: 'The refId of the work item' },
1145
+ kind: {
1146
+ type: 'string',
1147
+ description: 'The check kind to add, e.g. security-review',
1148
+ },
1149
+ comment: {
1150
+ type: 'string',
1151
+ description: 'Why the check is being requested — posted as a comment on the item',
1152
+ },
1153
+ project_slug: {
1154
+ type: 'string',
1155
+ description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
1156
+ },
1157
+ },
1158
+ required: ['work_item_id', 'kind'],
1159
+ },
1160
+ },
1123
1161
  {
1124
1162
  name: 'claim_work_item_check',
1125
1163
  description: 'Claim a pending, unclaimed review check on a work item so other reviewers know it is being handled. ' +
@@ -2506,6 +2544,19 @@ class WolfpackMCPServer {
2506
2544
  : JSON.stringify(checks.map(formatWorkItemCheck), null, 2);
2507
2545
  return { content: [{ type: 'text', text }] };
2508
2546
  }
2547
+ case 'add_work_item_check': {
2548
+ const parsed = AddWorkItemCheckSchema.parse(args);
2549
+ const teamSlug = parsed.project_slug || this.client.getProjectSlug() || undefined;
2550
+ const check = await this.client.addWorkItemCheck(parsed.work_item_id, { kind: parsed.kind, comment: parsed.comment }, teamSlug);
2551
+ return {
2552
+ content: [
2553
+ {
2554
+ type: 'text',
2555
+ text: `Added ${check.kind} check to #${check.workItemRefId} "${check.workItemTitle}"\n\n${JSON.stringify(formatWorkItemCheck(check), null, 2)}`,
2556
+ },
2557
+ ],
2558
+ };
2559
+ }
2509
2560
  case 'claim_work_item_check': {
2510
2561
  const parsed = ClaimWorkItemCheckSchema.parse(args);
2511
2562
  const teamSlug = parsed.project_slug || this.client.getProjectSlug() || undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wolfpack-mcp",
3
- "version": "1.0.91",
3
+ "version": "1.0.93",
4
4
  "description": "MCP server for Wolfpack AI-enhanced software delivery tools",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",