wolfpack-mcp 1.0.89 → 1.0.91
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 +89 -3
- package/dist/agentBuilderTools.test.js +89 -0
- package/dist/client.js +15 -2
- package/dist/index.js +90 -1
- 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'],
|
|
@@ -428,6 +432,12 @@ export const AGENT_BUILDER_TOOLS = [
|
|
|
428
432
|
properties: {
|
|
429
433
|
agent_id: { type: 'string', description: 'Agent profile ID' },
|
|
430
434
|
task_id: { type: 'string', description: 'Task ID to run' },
|
|
435
|
+
prompt: {
|
|
436
|
+
type: 'string',
|
|
437
|
+
description: "Extra instructions for this run only, appended to the task's saved prompt. " +
|
|
438
|
+
'The task still runs as written — this is added after it, and the task is not changed. ' +
|
|
439
|
+
'Omit it to run the task exactly as saved.',
|
|
440
|
+
},
|
|
431
441
|
project_slug: { type: 'string', description: 'Project to scope the session to (optional)' },
|
|
432
442
|
git_ref: {
|
|
433
443
|
type: 'string',
|
|
@@ -526,6 +536,23 @@ export const AGENT_BUILDER_TOOLS = [
|
|
|
526
536
|
description: 'When true, tasks run once with access to all assigned projects. ' +
|
|
527
537
|
'When false, tasks run once per project.',
|
|
528
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
|
+
},
|
|
529
556
|
task_ids: {
|
|
530
557
|
type: 'array',
|
|
531
558
|
items: { type: 'string' },
|
|
@@ -569,6 +596,23 @@ export const AGENT_BUILDER_TOOLS = [
|
|
|
569
596
|
type: 'boolean',
|
|
570
597
|
description: 'Run once across all projects (true) or once per project (false)',
|
|
571
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
|
+
},
|
|
572
616
|
task_ids: {
|
|
573
617
|
type: 'array',
|
|
574
618
|
items: { type: 'string' },
|
|
@@ -792,6 +836,19 @@ const AgentTaskSchema = z.object({
|
|
|
792
836
|
org_slug: orgSlugField,
|
|
793
837
|
});
|
|
794
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
|
+
}
|
|
795
852
|
export async function handleAgentBuilderTool(name, args, client) {
|
|
796
853
|
const text = (t) => JSON.stringify(t, null, 2);
|
|
797
854
|
switch (name) {
|
|
@@ -849,6 +906,7 @@ export async function handleAgentBuilderTool(name, args, client) {
|
|
|
849
906
|
llm_model: z.string().nullable().optional(),
|
|
850
907
|
llm_family: z.string().nullable().optional(),
|
|
851
908
|
api_key_permissions: z.array(z.string()).optional(),
|
|
909
|
+
scheduling_enabled: z.boolean().optional(),
|
|
852
910
|
org_slug: orgSlugField,
|
|
853
911
|
})
|
|
854
912
|
.parse(args);
|
|
@@ -872,6 +930,8 @@ export async function handleAgentBuilderTool(name, args, client) {
|
|
|
872
930
|
fields.llmFamily = rest.llm_family;
|
|
873
931
|
if (rest.api_key_permissions !== undefined)
|
|
874
932
|
fields.apiKeyPermissions = rest.api_key_permissions;
|
|
933
|
+
if (rest.scheduling_enabled !== undefined)
|
|
934
|
+
fields.schedulingEnabled = rest.scheduling_enabled;
|
|
875
935
|
const agent = await client.updateAgent(agent_id, fields, resolveOrg(parsed));
|
|
876
936
|
return { content: [{ type: 'text', text: `Updated agent\n\n${text(agent)}` }] };
|
|
877
937
|
}
|
|
@@ -1110,13 +1170,14 @@ export async function handleAgentBuilderTool(name, args, client) {
|
|
|
1110
1170
|
.object({
|
|
1111
1171
|
agent_id: z.string(),
|
|
1112
1172
|
task_id: z.string(),
|
|
1173
|
+
prompt: z.string().optional(),
|
|
1113
1174
|
project_slug: z.string().optional(),
|
|
1114
1175
|
git_ref: z.string().optional(),
|
|
1115
1176
|
pr_number: z.number().int().positive().optional(),
|
|
1116
1177
|
org_slug: orgSlugField,
|
|
1117
1178
|
})
|
|
1118
1179
|
.parse(args);
|
|
1119
|
-
const result = await client.runAgentTask(parsed.agent_id, parsed.task_id, parsed.project_slug, resolveOrg(parsed), parsed.git_ref, parsed.pr_number);
|
|
1180
|
+
const result = await client.runAgentTask(parsed.agent_id, parsed.task_id, parsed.project_slug, resolveOrg(parsed), parsed.git_ref, parsed.pr_number, parsed.prompt);
|
|
1120
1181
|
return {
|
|
1121
1182
|
content: [
|
|
1122
1183
|
{
|
|
@@ -1159,6 +1220,9 @@ export async function handleAgentBuilderTool(name, args, client) {
|
|
|
1159
1220
|
timezone: z.string().optional(),
|
|
1160
1221
|
dst_aware: z.boolean().optional(),
|
|
1161
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(),
|
|
1162
1226
|
task_ids: z.array(z.string()).optional(),
|
|
1163
1227
|
org_slug: orgSlugField,
|
|
1164
1228
|
})
|
|
@@ -1183,12 +1247,22 @@ export async function handleAgentBuilderTool(name, args, client) {
|
|
|
1183
1247
|
body.dstAware = rest.dst_aware;
|
|
1184
1248
|
if (rest.multi_project !== undefined)
|
|
1185
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;
|
|
1186
1256
|
if (rest.task_ids !== undefined)
|
|
1187
1257
|
body.taskIds = rest.task_ids;
|
|
1188
1258
|
const schedule = await client.createAgentSchedule(agent_id, body, resolveOrg(parsed));
|
|
1259
|
+
const warning = await schedulingOffWarning(client, agent_id, schedule.enabled, resolveOrg(parsed));
|
|
1189
1260
|
return {
|
|
1190
1261
|
content: [
|
|
1191
|
-
{
|
|
1262
|
+
{
|
|
1263
|
+
type: 'text',
|
|
1264
|
+
text: `Created schedule "${schedule.name}"${warning}\n\n${text(schedule)}`,
|
|
1265
|
+
},
|
|
1192
1266
|
],
|
|
1193
1267
|
};
|
|
1194
1268
|
}
|
|
@@ -1206,6 +1280,9 @@ export async function handleAgentBuilderTool(name, args, client) {
|
|
|
1206
1280
|
timezone: z.string().optional(),
|
|
1207
1281
|
dst_aware: z.boolean().optional(),
|
|
1208
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(),
|
|
1209
1286
|
task_ids: z.array(z.string()).optional(),
|
|
1210
1287
|
org_slug: orgSlugField,
|
|
1211
1288
|
})
|
|
@@ -1230,10 +1307,19 @@ export async function handleAgentBuilderTool(name, args, client) {
|
|
|
1230
1307
|
body.dstAware = rest.dst_aware;
|
|
1231
1308
|
if (rest.multi_project !== undefined)
|
|
1232
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;
|
|
1233
1316
|
if (rest.task_ids !== undefined)
|
|
1234
1317
|
body.taskIds = rest.task_ids;
|
|
1235
1318
|
const schedule = await client.updateAgentSchedule(agent_id, schedule_id, body, resolveOrg(parsed));
|
|
1236
|
-
|
|
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
|
+
};
|
|
1237
1323
|
}
|
|
1238
1324
|
// ─── Skills ─────────────────────────────────────────────────────────────
|
|
1239
1325
|
case 'create_skill': {
|
|
@@ -74,3 +74,92 @@ describe('per-task model pin over MCP (#2034)', () => {
|
|
|
74
74
|
expect(updateAgentTask.mock.calls[0][2]).toEqual({ prompt: 'Analyse the transcript twice.' });
|
|
75
75
|
});
|
|
76
76
|
});
|
|
77
|
+
// #2048 — run_agent_task can add instructions for one run without editing the
|
|
78
|
+
// task. The prompt is the 7th argument of client.runAgentTask, so these assert
|
|
79
|
+
// on the position as well as the value.
|
|
80
|
+
describe('extra instructions on a task run over MCP (#2048)', () => {
|
|
81
|
+
const runClient = () => {
|
|
82
|
+
const runAgentTask = vi.fn().mockResolvedValue({ entryId: 'e1', position: 0, executing: true });
|
|
83
|
+
return { client: { runAgentTask }, runAgentTask };
|
|
84
|
+
};
|
|
85
|
+
it('advertises the prompt argument as an addition, not a replacement', () => {
|
|
86
|
+
const properties = toolNamed('run_agent_task').inputSchema.properties;
|
|
87
|
+
expect(properties.prompt.description).toContain("appended to the task's saved prompt");
|
|
88
|
+
});
|
|
89
|
+
it('sends the extra instructions with the run', async () => {
|
|
90
|
+
const { client, runAgentTask } = runClient();
|
|
91
|
+
await handleAgentBuilderTool('run_agent_task', { agent_id: 'agent-1', task_id: 'task-1', prompt: 'Only cover last week.' }, client);
|
|
92
|
+
expect(runAgentTask.mock.calls[0][1]).toBe('task-1');
|
|
93
|
+
expect(runAgentTask.mock.calls[0][6]).toBe('Only cover last week.');
|
|
94
|
+
});
|
|
95
|
+
it('sends none when the caller only wants the task as saved', async () => {
|
|
96
|
+
const { client, runAgentTask } = runClient();
|
|
97
|
+
await handleAgentBuilderTool('run_agent_task', { agent_id: 'agent-1', task_id: 'task-1' }, client);
|
|
98
|
+
expect(runAgentTask.mock.calls[0][6]).toBeUndefined();
|
|
99
|
+
});
|
|
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,6 +247,8 @@ 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
|
}
|
|
@@ -502,6 +504,16 @@ export class WolfpackClient {
|
|
|
502
504
|
const query = params.toString();
|
|
503
505
|
return this.api.get(`/categories${query ? `?${query}` : ''}`);
|
|
504
506
|
}
|
|
507
|
+
// Work pool methods (#2083)
|
|
508
|
+
async listWorkPools(teamSlug) {
|
|
509
|
+
return this.api.get(this.withTeamSlug('/work-pools', teamSlug));
|
|
510
|
+
}
|
|
511
|
+
async addWorkPoolMember(workPool, userId, teamSlug) {
|
|
512
|
+
return this.api.post(this.withTeamSlug(`/work-pools/${encodeURIComponent(workPool)}/members`, teamSlug), { userId });
|
|
513
|
+
}
|
|
514
|
+
async removeWorkPoolMember(workPool, userId, teamSlug) {
|
|
515
|
+
await this.api.delete(this.withTeamSlug(`/work-pools/${encodeURIComponent(workPool)}/members/${encodeURIComponent(userId)}`, teamSlug));
|
|
516
|
+
}
|
|
505
517
|
// Tag methods
|
|
506
518
|
async listTags(teamSlug) {
|
|
507
519
|
const params = new URLSearchParams();
|
|
@@ -748,8 +760,9 @@ export class WolfpackClient {
|
|
|
748
760
|
async updateAgentTask(agentId, taskId, body, orgSlug) {
|
|
749
761
|
return this.api.patch(this.withOrgSlug(`/agents/${agentId}/tasks/${taskId}`, orgSlug), body);
|
|
750
762
|
}
|
|
751
|
-
|
|
752
|
-
|
|
763
|
+
/** `prompt` is appended to the task's saved prompt for this run only (#2048). */
|
|
764
|
+
async runAgentTask(agentId, taskId, teamSlug, orgSlug, gitRef, prNumber, prompt) {
|
|
765
|
+
return this.api.post(this.withOrgSlug(`/agents/${agentId}/tasks/${taskId}/run`, orgSlug), { teamSlug, gitRef, prNumber, prompt });
|
|
753
766
|
}
|
|
754
767
|
// ─── Agent Builder: Queue ──────────────────────────────────────────────────
|
|
755
768
|
async listAgentQueue(agentId, status, orgSlug) {
|
package/dist/index.js
CHANGED
|
@@ -215,11 +215,20 @@ 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'),
|
|
223
232
|
project_slug: z
|
|
224
233
|
.string()
|
|
225
234
|
.optional()
|
|
@@ -1100,6 +1109,10 @@ class WolfpackMCPServer {
|
|
|
1100
1109
|
enum: ['pending', 'passed', 'failed'],
|
|
1101
1110
|
description: 'Filter by check status',
|
|
1102
1111
|
},
|
|
1112
|
+
kind: {
|
|
1113
|
+
type: 'string',
|
|
1114
|
+
description: 'Filter by check kind (code-review, security-review, ...). A reviewer that answers one kind lists only that kind.',
|
|
1115
|
+
},
|
|
1103
1116
|
project_slug: {
|
|
1104
1117
|
type: 'string',
|
|
1105
1118
|
description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
|
|
@@ -1837,6 +1850,52 @@ class WolfpackMCPServer {
|
|
|
1837
1850
|
},
|
|
1838
1851
|
},
|
|
1839
1852
|
},
|
|
1853
|
+
// Work pool tools (#2083)
|
|
1854
|
+
{
|
|
1855
|
+
name: 'list_work_pools',
|
|
1856
|
+
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.',
|
|
1857
|
+
inputSchema: {
|
|
1858
|
+
type: 'object',
|
|
1859
|
+
properties: {
|
|
1860
|
+
project_slug: {
|
|
1861
|
+
type: 'string',
|
|
1862
|
+
description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
|
|
1863
|
+
},
|
|
1864
|
+
},
|
|
1865
|
+
},
|
|
1866
|
+
},
|
|
1867
|
+
{
|
|
1868
|
+
name: 'add_work_pool_member',
|
|
1869
|
+
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.",
|
|
1870
|
+
inputSchema: {
|
|
1871
|
+
type: 'object',
|
|
1872
|
+
properties: {
|
|
1873
|
+
work_pool: { type: 'string', description: 'Work pool id or name' },
|
|
1874
|
+
user_id: { type: 'string', description: 'The user id to add' },
|
|
1875
|
+
project_slug: {
|
|
1876
|
+
type: 'string',
|
|
1877
|
+
description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
|
|
1878
|
+
},
|
|
1879
|
+
},
|
|
1880
|
+
required: ['work_pool', 'user_id'],
|
|
1881
|
+
},
|
|
1882
|
+
},
|
|
1883
|
+
{
|
|
1884
|
+
name: 'remove_work_pool_member',
|
|
1885
|
+
description: 'Remove a user or agent from a work pool. Requires mcp:agents:update.',
|
|
1886
|
+
inputSchema: {
|
|
1887
|
+
type: 'object',
|
|
1888
|
+
properties: {
|
|
1889
|
+
work_pool: { type: 'string', description: 'Work pool id or name' },
|
|
1890
|
+
user_id: { type: 'string', description: 'The user id to remove' },
|
|
1891
|
+
project_slug: {
|
|
1892
|
+
type: 'string',
|
|
1893
|
+
description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
|
|
1894
|
+
},
|
|
1895
|
+
},
|
|
1896
|
+
required: ['work_pool', 'user_id'],
|
|
1897
|
+
},
|
|
1898
|
+
},
|
|
1840
1899
|
// Tag tools
|
|
1841
1900
|
{
|
|
1842
1901
|
name: 'list_tags',
|
|
@@ -2439,7 +2498,7 @@ class WolfpackMCPServer {
|
|
|
2439
2498
|
case 'list_work_item_checks': {
|
|
2440
2499
|
const parsed = ListWorkItemChecksSchema.parse(args);
|
|
2441
2500
|
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);
|
|
2501
|
+
const checks = await this.client.listWorkItemChecks({ workItemId: parsed.work_item_id, status: parsed.status, kind: parsed.kind }, teamSlug);
|
|
2443
2502
|
const text = checks.length === 0
|
|
2444
2503
|
? parsed.work_item_id
|
|
2445
2504
|
? 'No review checks on this work item'
|
|
@@ -2938,6 +2997,36 @@ class WolfpackMCPServer {
|
|
|
2938
2997
|
content: [{ type: 'text', text: JSON.stringify(categories, null, 2) }],
|
|
2939
2998
|
};
|
|
2940
2999
|
}
|
|
3000
|
+
// Work pool handlers (#2083)
|
|
3001
|
+
case 'list_work_pools': {
|
|
3002
|
+
const parsed = ListCategoriesSchema.parse(args);
|
|
3003
|
+
const pools = await this.client.listWorkPools(parsed.project_slug || this.client.getProjectSlug() || undefined);
|
|
3004
|
+
return { content: [{ type: 'text', text: JSON.stringify(pools, null, 2) }] };
|
|
3005
|
+
}
|
|
3006
|
+
case 'add_work_pool_member': {
|
|
3007
|
+
const parsed = WorkPoolMemberSchema.parse(args);
|
|
3008
|
+
await this.client.addWorkPoolMember(parsed.work_pool, parsed.user_id, parsed.project_slug || this.client.getProjectSlug() || undefined);
|
|
3009
|
+
return {
|
|
3010
|
+
content: [
|
|
3011
|
+
{
|
|
3012
|
+
type: 'text',
|
|
3013
|
+
text: `Added ${parsed.user_id} to work pool "${parsed.work_pool}"`,
|
|
3014
|
+
},
|
|
3015
|
+
],
|
|
3016
|
+
};
|
|
3017
|
+
}
|
|
3018
|
+
case 'remove_work_pool_member': {
|
|
3019
|
+
const parsed = WorkPoolMemberSchema.parse(args);
|
|
3020
|
+
await this.client.removeWorkPoolMember(parsed.work_pool, parsed.user_id, parsed.project_slug || this.client.getProjectSlug() || undefined);
|
|
3021
|
+
return {
|
|
3022
|
+
content: [
|
|
3023
|
+
{
|
|
3024
|
+
type: 'text',
|
|
3025
|
+
text: `Removed ${parsed.user_id} from work pool "${parsed.work_pool}"`,
|
|
3026
|
+
},
|
|
3027
|
+
],
|
|
3028
|
+
};
|
|
3029
|
+
}
|
|
2941
3030
|
// Tag handlers
|
|
2942
3031
|
case 'list_tags': {
|
|
2943
3032
|
const parsed = ListTagsSchema.parse(args);
|