wolfpack-mcp 1.0.90 → 1.0.92
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.
- package/dist/agentBuilderTools.js +81 -2
- package/dist/agentBuilderTools.test.js +65 -0
- package/dist/client.js +15 -0
- package/dist/index.js +142 -2
- package/package.json +1 -1
|
@@ -113,6 +113,10 @@ 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
|
+
},
|
|
116
120
|
...ORG_SLUG_PROP,
|
|
117
121
|
},
|
|
118
122
|
required: ['agent_id'],
|
|
@@ -532,6 +536,23 @@ export const AGENT_BUILDER_TOOLS = [
|
|
|
532
536
|
description: 'When true, tasks run once with access to all assigned projects. ' +
|
|
533
537
|
'When false, tasks run once per project.',
|
|
534
538
|
},
|
|
539
|
+
preflight_check: {
|
|
540
|
+
type: 'string',
|
|
541
|
+
enum: ['claimable-work', 'claimable-check'],
|
|
542
|
+
description: 'LLM-free condition evaluated when the schedule fires; a false result skips the fire with no session and no cost. ' +
|
|
543
|
+
'"claimable-work": an approved, unclaimed item is assigned to the agent or sits in one of its work pools. ' +
|
|
544
|
+
'"claimable-check": a pending, unclaimed review check sits on an item in one of its pools. Null to clear.',
|
|
545
|
+
},
|
|
546
|
+
check_kinds: {
|
|
547
|
+
type: 'array',
|
|
548
|
+
items: { type: 'string' },
|
|
549
|
+
description: 'With "claimable-check": only checks of these kinds wake the schedule (e.g. ["security-review"]), so reviewers ' +
|
|
550
|
+
'sharing a pool each answer their own kind. Empty for every kind.',
|
|
551
|
+
},
|
|
552
|
+
skip_while_active: {
|
|
553
|
+
type: 'boolean',
|
|
554
|
+
description: 'Skip a fire entirely while the agent still has a queued or running task, or leads an item awaiting review.',
|
|
555
|
+
},
|
|
535
556
|
task_ids: {
|
|
536
557
|
type: 'array',
|
|
537
558
|
items: { type: 'string' },
|
|
@@ -575,6 +596,23 @@ export const AGENT_BUILDER_TOOLS = [
|
|
|
575
596
|
type: 'boolean',
|
|
576
597
|
description: 'Run once across all projects (true) or once per project (false)',
|
|
577
598
|
},
|
|
599
|
+
preflight_check: {
|
|
600
|
+
type: 'string',
|
|
601
|
+
enum: ['claimable-work', 'claimable-check'],
|
|
602
|
+
description: 'LLM-free condition evaluated when the schedule fires; a false result skips the fire with no session and no cost. ' +
|
|
603
|
+
'"claimable-work": an approved, unclaimed item is assigned to the agent or sits in one of its work pools. ' +
|
|
604
|
+
'"claimable-check": a pending, unclaimed review check sits on an item in one of its pools. Null to clear.',
|
|
605
|
+
},
|
|
606
|
+
check_kinds: {
|
|
607
|
+
type: 'array',
|
|
608
|
+
items: { type: 'string' },
|
|
609
|
+
description: 'With "claimable-check": only checks of these kinds wake the schedule (e.g. ["security-review"]), so reviewers ' +
|
|
610
|
+
'sharing a pool each answer their own kind. Empty for every kind.',
|
|
611
|
+
},
|
|
612
|
+
skip_while_active: {
|
|
613
|
+
type: 'boolean',
|
|
614
|
+
description: 'Skip a fire entirely while the agent still has a queued or running task, or leads an item awaiting review.',
|
|
615
|
+
},
|
|
578
616
|
task_ids: {
|
|
579
617
|
type: 'array',
|
|
580
618
|
items: { type: 'string' },
|
|
@@ -798,6 +836,19 @@ const AgentTaskSchema = z.object({
|
|
|
798
836
|
org_slug: orgSlugField,
|
|
799
837
|
});
|
|
800
838
|
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
|
+
}
|
|
801
852
|
export async function handleAgentBuilderTool(name, args, client) {
|
|
802
853
|
const text = (t) => JSON.stringify(t, null, 2);
|
|
803
854
|
switch (name) {
|
|
@@ -855,6 +906,7 @@ export async function handleAgentBuilderTool(name, args, client) {
|
|
|
855
906
|
llm_model: z.string().nullable().optional(),
|
|
856
907
|
llm_family: z.string().nullable().optional(),
|
|
857
908
|
api_key_permissions: z.array(z.string()).optional(),
|
|
909
|
+
scheduling_enabled: z.boolean().optional(),
|
|
858
910
|
org_slug: orgSlugField,
|
|
859
911
|
})
|
|
860
912
|
.parse(args);
|
|
@@ -878,6 +930,8 @@ export async function handleAgentBuilderTool(name, args, client) {
|
|
|
878
930
|
fields.llmFamily = rest.llm_family;
|
|
879
931
|
if (rest.api_key_permissions !== undefined)
|
|
880
932
|
fields.apiKeyPermissions = rest.api_key_permissions;
|
|
933
|
+
if (rest.scheduling_enabled !== undefined)
|
|
934
|
+
fields.schedulingEnabled = rest.scheduling_enabled;
|
|
881
935
|
const agent = await client.updateAgent(agent_id, fields, resolveOrg(parsed));
|
|
882
936
|
return { content: [{ type: 'text', text: `Updated agent\n\n${text(agent)}` }] };
|
|
883
937
|
}
|
|
@@ -1166,6 +1220,9 @@ export async function handleAgentBuilderTool(name, args, client) {
|
|
|
1166
1220
|
timezone: z.string().optional(),
|
|
1167
1221
|
dst_aware: z.boolean().optional(),
|
|
1168
1222
|
multi_project: z.boolean().optional(),
|
|
1223
|
+
preflight_check: z.enum(['claimable-work', 'claimable-check']).nullable().optional(),
|
|
1224
|
+
check_kinds: z.array(z.string()).optional(),
|
|
1225
|
+
skip_while_active: z.boolean().optional(),
|
|
1169
1226
|
task_ids: z.array(z.string()).optional(),
|
|
1170
1227
|
org_slug: orgSlugField,
|
|
1171
1228
|
})
|
|
@@ -1190,12 +1247,22 @@ export async function handleAgentBuilderTool(name, args, client) {
|
|
|
1190
1247
|
body.dstAware = rest.dst_aware;
|
|
1191
1248
|
if (rest.multi_project !== undefined)
|
|
1192
1249
|
body.multiProject = rest.multi_project;
|
|
1250
|
+
if (rest.preflight_check !== undefined)
|
|
1251
|
+
body.preflightCheck = rest.preflight_check;
|
|
1252
|
+
if (rest.check_kinds !== undefined)
|
|
1253
|
+
body.checkKinds = rest.check_kinds;
|
|
1254
|
+
if (rest.skip_while_active !== undefined)
|
|
1255
|
+
body.skipWhileActive = rest.skip_while_active;
|
|
1193
1256
|
if (rest.task_ids !== undefined)
|
|
1194
1257
|
body.taskIds = rest.task_ids;
|
|
1195
1258
|
const schedule = await client.createAgentSchedule(agent_id, body, resolveOrg(parsed));
|
|
1259
|
+
const warning = await schedulingOffWarning(client, agent_id, schedule.enabled, resolveOrg(parsed));
|
|
1196
1260
|
return {
|
|
1197
1261
|
content: [
|
|
1198
|
-
{
|
|
1262
|
+
{
|
|
1263
|
+
type: 'text',
|
|
1264
|
+
text: `Created schedule "${schedule.name}"${warning}\n\n${text(schedule)}`,
|
|
1265
|
+
},
|
|
1199
1266
|
],
|
|
1200
1267
|
};
|
|
1201
1268
|
}
|
|
@@ -1213,6 +1280,9 @@ export async function handleAgentBuilderTool(name, args, client) {
|
|
|
1213
1280
|
timezone: z.string().optional(),
|
|
1214
1281
|
dst_aware: z.boolean().optional(),
|
|
1215
1282
|
multi_project: z.boolean().optional(),
|
|
1283
|
+
preflight_check: z.enum(['claimable-work', 'claimable-check']).nullable().optional(),
|
|
1284
|
+
check_kinds: z.array(z.string()).optional(),
|
|
1285
|
+
skip_while_active: z.boolean().optional(),
|
|
1216
1286
|
task_ids: z.array(z.string()).optional(),
|
|
1217
1287
|
org_slug: orgSlugField,
|
|
1218
1288
|
})
|
|
@@ -1237,10 +1307,19 @@ export async function handleAgentBuilderTool(name, args, client) {
|
|
|
1237
1307
|
body.dstAware = rest.dst_aware;
|
|
1238
1308
|
if (rest.multi_project !== undefined)
|
|
1239
1309
|
body.multiProject = rest.multi_project;
|
|
1310
|
+
if (rest.preflight_check !== undefined)
|
|
1311
|
+
body.preflightCheck = rest.preflight_check;
|
|
1312
|
+
if (rest.check_kinds !== undefined)
|
|
1313
|
+
body.checkKinds = rest.check_kinds;
|
|
1314
|
+
if (rest.skip_while_active !== undefined)
|
|
1315
|
+
body.skipWhileActive = rest.skip_while_active;
|
|
1240
1316
|
if (rest.task_ids !== undefined)
|
|
1241
1317
|
body.taskIds = rest.task_ids;
|
|
1242
1318
|
const schedule = await client.updateAgentSchedule(agent_id, schedule_id, body, resolveOrg(parsed));
|
|
1243
|
-
|
|
1319
|
+
const warning = await schedulingOffWarning(client, agent_id, schedule.enabled, resolveOrg(parsed));
|
|
1320
|
+
return {
|
|
1321
|
+
content: [{ type: 'text', text: `Updated schedule${warning}\n\n${text(schedule)}` }],
|
|
1322
|
+
};
|
|
1244
1323
|
}
|
|
1245
1324
|
// ─── Skills ─────────────────────────────────────────────────────────────
|
|
1246
1325
|
case 'create_skill': {
|
|
@@ -98,3 +98,68 @@ 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)', () => {
|
|
104
|
+
const SCHEDULE_FIELDS = ['preflight_check', 'check_kinds', 'skip_while_active'];
|
|
105
|
+
it.each(['create_agent_schedule', 'update_agent_schedule'])('%s advertises the pre-flight fields', (name) => {
|
|
106
|
+
const properties = toolNamed(name).inputSchema.properties;
|
|
107
|
+
expect(Object.keys(properties)).toEqual(expect.arrayContaining(SCHEDULE_FIELDS));
|
|
108
|
+
});
|
|
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) {
|
|
114
|
+
const schedule = { id: 'sched-1', name: 'Reviews', enabled: true };
|
|
115
|
+
const createAgentSchedule = vi.fn().mockResolvedValue(schedule);
|
|
116
|
+
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
|
+
return {
|
|
120
|
+
client: {
|
|
121
|
+
createAgentSchedule,
|
|
122
|
+
updateAgentSchedule,
|
|
123
|
+
updateAgent,
|
|
124
|
+
getAgent,
|
|
125
|
+
},
|
|
126
|
+
createAgentSchedule,
|
|
127
|
+
updateAgentSchedule,
|
|
128
|
+
updateAgent,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
it('sends the pre-flight, kinds and skip-while-active when creating a schedule', async () => {
|
|
132
|
+
const { client, createAgentSchedule } = scheduleClient(true);
|
|
133
|
+
await handleAgentBuilderTool('create_agent_schedule', {
|
|
134
|
+
agent_id: 'agent-1',
|
|
135
|
+
preflight_check: 'claimable-check',
|
|
136
|
+
check_kinds: ['security-review'],
|
|
137
|
+
skip_while_active: true,
|
|
138
|
+
}, client);
|
|
139
|
+
expect(createAgentSchedule.mock.calls[0][1]).toMatchObject({
|
|
140
|
+
preflightCheck: 'claimable-check',
|
|
141
|
+
checkKinds: ['security-review'],
|
|
142
|
+
skipWhileActive: true,
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
it('sends the same fields when updating a schedule', async () => {
|
|
146
|
+
const { client, updateAgentSchedule } = scheduleClient(true);
|
|
147
|
+
await handleAgentBuilderTool('update_agent_schedule', { agent_id: 'agent-1', schedule_id: 'sched-1', preflight_check: null, check_kinds: [] }, client);
|
|
148
|
+
expect(updateAgentSchedule.mock.calls[0][2]).toEqual({ preflightCheck: null, checkKinds: [] });
|
|
149
|
+
});
|
|
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');
|
|
164
|
+
});
|
|
165
|
+
});
|
package/dist/client.js
CHANGED
|
@@ -247,9 +247,14 @@ export class WolfpackClient {
|
|
|
247
247
|
params.set('workItemId', filter.workItemId);
|
|
248
248
|
if (filter.status)
|
|
249
249
|
params.set('status', filter.status);
|
|
250
|
+
if (filter.kind)
|
|
251
|
+
params.set('kind', filter.kind);
|
|
250
252
|
const query = params.toString();
|
|
251
253
|
return this.api.get(this.withTeamSlug(`/work-item-checks${query ? `?${query}` : ''}`, teamSlug));
|
|
252
254
|
}
|
|
255
|
+
async addWorkItemCheck(workItemId, body, teamSlug) {
|
|
256
|
+
return this.api.post(this.withTeamSlug(`/work-items/${workItemId}/checks`, teamSlug), body);
|
|
257
|
+
}
|
|
253
258
|
async claimWorkItemCheck(workItemId, checkId, teamSlug) {
|
|
254
259
|
return this.api.post(this.withTeamSlug(`/work-items/${workItemId}/checks/${checkId}/claim`, teamSlug), {});
|
|
255
260
|
}
|
|
@@ -502,6 +507,16 @@ export class WolfpackClient {
|
|
|
502
507
|
const query = params.toString();
|
|
503
508
|
return this.api.get(`/categories${query ? `?${query}` : ''}`);
|
|
504
509
|
}
|
|
510
|
+
// Work pool methods (#2083)
|
|
511
|
+
async listWorkPools(teamSlug) {
|
|
512
|
+
return this.api.get(this.withTeamSlug('/work-pools', teamSlug));
|
|
513
|
+
}
|
|
514
|
+
async addWorkPoolMember(workPool, userId, teamSlug) {
|
|
515
|
+
return this.api.post(this.withTeamSlug(`/work-pools/${encodeURIComponent(workPool)}/members`, teamSlug), { userId });
|
|
516
|
+
}
|
|
517
|
+
async removeWorkPoolMember(workPool, userId, teamSlug) {
|
|
518
|
+
await this.api.delete(this.withTeamSlug(`/work-pools/${encodeURIComponent(workPool)}/members/${encodeURIComponent(userId)}`, teamSlug));
|
|
519
|
+
}
|
|
505
520
|
// Tag methods
|
|
506
521
|
async listTags(teamSlug) {
|
|
507
522
|
const params = new URLSearchParams();
|
package/dist/index.js
CHANGED
|
@@ -215,11 +215,32 @@ const SubmitWorkItemFormSchema = z.object({
|
|
|
215
215
|
.optional()
|
|
216
216
|
.describe('Project slug (required for multi-project users, use list_projects to get slugs)'),
|
|
217
217
|
});
|
|
218
|
+
const WorkPoolMemberSchema = z.object({
|
|
219
|
+
work_pool: z.string().describe('Work pool id or name'),
|
|
220
|
+
user_id: z.string().describe('The user id to add or remove'),
|
|
221
|
+
project_slug: z
|
|
222
|
+
.string()
|
|
223
|
+
.optional()
|
|
224
|
+
.describe('Project slug (required for multi-project users, use list_projects to get slugs)'),
|
|
225
|
+
});
|
|
218
226
|
const ListWorkItemChecksSchema = z.object({
|
|
219
227
|
work_item_id: refIdString()
|
|
220
228
|
.optional()
|
|
221
229
|
.describe('The refId of a work item; omit for your reviewer inbox'),
|
|
222
230
|
status: z.enum(['pending', 'passed', 'failed']).optional().describe('Filter by check status'),
|
|
231
|
+
kind: z.string().optional().describe('Filter by check kind, e.g. code-review or security-review'),
|
|
232
|
+
project_slug: z
|
|
233
|
+
.string()
|
|
234
|
+
.optional()
|
|
235
|
+
.describe('Project slug (required for multi-project users, use list_projects to get slugs)'),
|
|
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'),
|
|
223
244
|
project_slug: z
|
|
224
245
|
.string()
|
|
225
246
|
.optional()
|
|
@@ -1087,7 +1108,8 @@ class WolfpackMCPServer {
|
|
|
1087
1108
|
'Checks on blocked items are left out — blocked work is not picked up, and claiming one is refused. ' +
|
|
1088
1109
|
'REVIEW WORKFLOW: claim a check with claim_work_item_check, get_work_item to read the work, review it, ' +
|
|
1089
1110
|
'then complete_work_item_check with "passed" or "failed" and a short summary. ' +
|
|
1090
|
-
'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.',
|
|
1091
1113
|
inputSchema: {
|
|
1092
1114
|
type: 'object',
|
|
1093
1115
|
properties: {
|
|
@@ -1100,11 +1122,40 @@ class WolfpackMCPServer {
|
|
|
1100
1122
|
enum: ['pending', 'passed', 'failed'],
|
|
1101
1123
|
description: 'Filter by check status',
|
|
1102
1124
|
},
|
|
1125
|
+
kind: {
|
|
1126
|
+
type: 'string',
|
|
1127
|
+
description: 'Filter by check kind (code-review, security-review, ...). A reviewer that answers one kind lists only that kind.',
|
|
1128
|
+
},
|
|
1129
|
+
project_slug: {
|
|
1130
|
+
type: 'string',
|
|
1131
|
+
description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
|
|
1132
|
+
},
|
|
1133
|
+
},
|
|
1134
|
+
},
|
|
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
|
+
},
|
|
1103
1153
|
project_slug: {
|
|
1104
1154
|
type: 'string',
|
|
1105
1155
|
description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
|
|
1106
1156
|
},
|
|
1107
1157
|
},
|
|
1158
|
+
required: ['work_item_id', 'kind'],
|
|
1108
1159
|
},
|
|
1109
1160
|
},
|
|
1110
1161
|
{
|
|
@@ -1837,6 +1888,52 @@ class WolfpackMCPServer {
|
|
|
1837
1888
|
},
|
|
1838
1889
|
},
|
|
1839
1890
|
},
|
|
1891
|
+
// Work pool tools (#2083)
|
|
1892
|
+
{
|
|
1893
|
+
name: 'list_work_pools',
|
|
1894
|
+
description: 'List the work pools in a project with their members. Reviewer and DevOps agents find their checks through pool membership, so this is how to see who is routed where.',
|
|
1895
|
+
inputSchema: {
|
|
1896
|
+
type: 'object',
|
|
1897
|
+
properties: {
|
|
1898
|
+
project_slug: {
|
|
1899
|
+
type: 'string',
|
|
1900
|
+
description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
|
|
1901
|
+
},
|
|
1902
|
+
},
|
|
1903
|
+
},
|
|
1904
|
+
},
|
|
1905
|
+
{
|
|
1906
|
+
name: 'add_work_pool_member',
|
|
1907
|
+
description: "Add a user or agent (by user id, e.g. an agent profile's userId) to a work pool, named by id or name. Requires mcp:agents:update.",
|
|
1908
|
+
inputSchema: {
|
|
1909
|
+
type: 'object',
|
|
1910
|
+
properties: {
|
|
1911
|
+
work_pool: { type: 'string', description: 'Work pool id or name' },
|
|
1912
|
+
user_id: { type: 'string', description: 'The user id to add' },
|
|
1913
|
+
project_slug: {
|
|
1914
|
+
type: 'string',
|
|
1915
|
+
description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
|
|
1916
|
+
},
|
|
1917
|
+
},
|
|
1918
|
+
required: ['work_pool', 'user_id'],
|
|
1919
|
+
},
|
|
1920
|
+
},
|
|
1921
|
+
{
|
|
1922
|
+
name: 'remove_work_pool_member',
|
|
1923
|
+
description: 'Remove a user or agent from a work pool. Requires mcp:agents:update.',
|
|
1924
|
+
inputSchema: {
|
|
1925
|
+
type: 'object',
|
|
1926
|
+
properties: {
|
|
1927
|
+
work_pool: { type: 'string', description: 'Work pool id or name' },
|
|
1928
|
+
user_id: { type: 'string', description: 'The user id to remove' },
|
|
1929
|
+
project_slug: {
|
|
1930
|
+
type: 'string',
|
|
1931
|
+
description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
|
|
1932
|
+
},
|
|
1933
|
+
},
|
|
1934
|
+
required: ['work_pool', 'user_id'],
|
|
1935
|
+
},
|
|
1936
|
+
},
|
|
1840
1937
|
// Tag tools
|
|
1841
1938
|
{
|
|
1842
1939
|
name: 'list_tags',
|
|
@@ -2439,7 +2536,7 @@ class WolfpackMCPServer {
|
|
|
2439
2536
|
case 'list_work_item_checks': {
|
|
2440
2537
|
const parsed = ListWorkItemChecksSchema.parse(args);
|
|
2441
2538
|
const teamSlug = parsed.project_slug || this.client.getProjectSlug() || undefined;
|
|
2442
|
-
const checks = await this.client.listWorkItemChecks({ workItemId: parsed.work_item_id, status: parsed.status }, teamSlug);
|
|
2539
|
+
const checks = await this.client.listWorkItemChecks({ workItemId: parsed.work_item_id, status: parsed.status, kind: parsed.kind }, teamSlug);
|
|
2443
2540
|
const text = checks.length === 0
|
|
2444
2541
|
? parsed.work_item_id
|
|
2445
2542
|
? 'No review checks on this work item'
|
|
@@ -2447,6 +2544,19 @@ class WolfpackMCPServer {
|
|
|
2447
2544
|
: JSON.stringify(checks.map(formatWorkItemCheck), null, 2);
|
|
2448
2545
|
return { content: [{ type: 'text', text }] };
|
|
2449
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
|
+
}
|
|
2450
2560
|
case 'claim_work_item_check': {
|
|
2451
2561
|
const parsed = ClaimWorkItemCheckSchema.parse(args);
|
|
2452
2562
|
const teamSlug = parsed.project_slug || this.client.getProjectSlug() || undefined;
|
|
@@ -2938,6 +3048,36 @@ class WolfpackMCPServer {
|
|
|
2938
3048
|
content: [{ type: 'text', text: JSON.stringify(categories, null, 2) }],
|
|
2939
3049
|
};
|
|
2940
3050
|
}
|
|
3051
|
+
// Work pool handlers (#2083)
|
|
3052
|
+
case 'list_work_pools': {
|
|
3053
|
+
const parsed = ListCategoriesSchema.parse(args);
|
|
3054
|
+
const pools = await this.client.listWorkPools(parsed.project_slug || this.client.getProjectSlug() || undefined);
|
|
3055
|
+
return { content: [{ type: 'text', text: JSON.stringify(pools, null, 2) }] };
|
|
3056
|
+
}
|
|
3057
|
+
case 'add_work_pool_member': {
|
|
3058
|
+
const parsed = WorkPoolMemberSchema.parse(args);
|
|
3059
|
+
await this.client.addWorkPoolMember(parsed.work_pool, parsed.user_id, parsed.project_slug || this.client.getProjectSlug() || undefined);
|
|
3060
|
+
return {
|
|
3061
|
+
content: [
|
|
3062
|
+
{
|
|
3063
|
+
type: 'text',
|
|
3064
|
+
text: `Added ${parsed.user_id} to work pool "${parsed.work_pool}"`,
|
|
3065
|
+
},
|
|
3066
|
+
],
|
|
3067
|
+
};
|
|
3068
|
+
}
|
|
3069
|
+
case 'remove_work_pool_member': {
|
|
3070
|
+
const parsed = WorkPoolMemberSchema.parse(args);
|
|
3071
|
+
await this.client.removeWorkPoolMember(parsed.work_pool, parsed.user_id, parsed.project_slug || this.client.getProjectSlug() || undefined);
|
|
3072
|
+
return {
|
|
3073
|
+
content: [
|
|
3074
|
+
{
|
|
3075
|
+
type: 'text',
|
|
3076
|
+
text: `Removed ${parsed.user_id} from work pool "${parsed.work_pool}"`,
|
|
3077
|
+
},
|
|
3078
|
+
],
|
|
3079
|
+
};
|
|
3080
|
+
}
|
|
2941
3081
|
// Tag handlers
|
|
2942
3082
|
case 'list_tags': {
|
|
2943
3083
|
const parsed = ListTagsSchema.parse(args);
|