wolfpack-mcp 1.0.47 → 1.0.48

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.
@@ -0,0 +1,754 @@
1
+ /**
2
+ * Agent builder MCP tool definitions and handlers.
3
+ * Registered only when the API key has the `agent_builder` capability.
4
+ */
5
+ import { z } from 'zod';
6
+ export const AGENT_BUILDER_TOOLS = [
7
+ // ─── Group 1: Agent CRUD ─────────────────────────────────────────────────
8
+ {
9
+ name: 'list_agents',
10
+ description: 'List all agents in the organisation. Returns name, status, template, and assigned projects.',
11
+ inputSchema: { type: 'object', properties: {} },
12
+ },
13
+ {
14
+ name: 'get_agent',
15
+ description: 'Get full details for an agent including config, instructions, LLM model, and linked skills.',
16
+ inputSchema: {
17
+ type: 'object',
18
+ properties: {
19
+ agent_id: { type: 'string', description: 'Agent profile ID' },
20
+ },
21
+ required: ['agent_id'],
22
+ },
23
+ },
24
+ {
25
+ name: 'create_agent',
26
+ description: 'Create a new agent from a template. Use list_agent_templates to see available templates.',
27
+ inputSchema: {
28
+ type: 'object',
29
+ properties: {
30
+ template_id: { type: 'string', description: 'Template ID to create the agent from' },
31
+ name: { type: 'string', description: 'Display name for the agent' },
32
+ config: { type: 'object', description: 'Optional agent-specific configuration' },
33
+ },
34
+ required: ['template_id', 'name'],
35
+ },
36
+ },
37
+ {
38
+ name: 'update_agent',
39
+ description: "Update an agent's config, instructions, or LLM model. Omit fields to leave them unchanged.",
40
+ inputSchema: {
41
+ type: 'object',
42
+ properties: {
43
+ agent_id: { type: 'string', description: 'Agent profile ID' },
44
+ name: { type: 'string', description: 'Updated display name' },
45
+ instructions: {
46
+ type: 'string',
47
+ description: 'Agent-specific instructions (supplements template instructions). Set to null to clear.',
48
+ },
49
+ llm_provider: {
50
+ type: 'string',
51
+ description: 'LLM provider ID (e.g. "anthropic"). Set to null for default.',
52
+ },
53
+ llm_model: {
54
+ type: 'string',
55
+ description: 'LLM model ID (e.g. "claude-sonnet-4-6"). Set to null for family auto-select.',
56
+ },
57
+ llm_family: {
58
+ type: 'string',
59
+ description: 'LLM model family (e.g. "sonnet"). Agent auto-upgrades to latest in family.',
60
+ },
61
+ scheduling_enabled: { type: 'boolean', description: 'Whether scheduled tasks are enabled' },
62
+ },
63
+ required: ['agent_id'],
64
+ },
65
+ },
66
+ // ─── Group 2: Project Assignment ─────────────────────────────────────────
67
+ {
68
+ name: 'list_agent_projects',
69
+ description: 'List projects assigned to an agent.',
70
+ inputSchema: {
71
+ type: 'object',
72
+ properties: {
73
+ agent_id: { type: 'string', description: 'Agent profile ID' },
74
+ },
75
+ required: ['agent_id'],
76
+ },
77
+ },
78
+ {
79
+ name: 'assign_agent_to_project',
80
+ description: 'Assign an agent to a project by slug.',
81
+ inputSchema: {
82
+ type: 'object',
83
+ properties: {
84
+ agent_id: { type: 'string', description: 'Agent profile ID' },
85
+ project_slug: { type: 'string', description: 'Project slug to assign the agent to' },
86
+ },
87
+ required: ['agent_id', 'project_slug'],
88
+ },
89
+ },
90
+ {
91
+ name: 'remove_agent_from_project',
92
+ description: 'Remove an agent from a project.',
93
+ inputSchema: {
94
+ type: 'object',
95
+ properties: {
96
+ agent_id: { type: 'string', description: 'Agent profile ID' },
97
+ project_slug: { type: 'string', description: 'Project slug to remove the agent from' },
98
+ },
99
+ required: ['agent_id', 'project_slug'],
100
+ },
101
+ },
102
+ // ─── Group 3: Sessions ────────────────────────────────────────────────────
103
+ {
104
+ name: 'list_agent_sessions',
105
+ description: 'List sessions for an agent. Filter by status: running, stopped, failed.',
106
+ inputSchema: {
107
+ type: 'object',
108
+ properties: {
109
+ agent_id: { type: 'string', description: 'Agent profile ID' },
110
+ status: {
111
+ type: 'string',
112
+ enum: ['pending', 'starting', 'running', 'stopping', 'stopped', 'failed'],
113
+ description: 'Filter by session status',
114
+ },
115
+ limit: { type: 'number', description: 'Maximum sessions to return' },
116
+ offset: { type: 'number', description: 'Sessions to skip for pagination' },
117
+ },
118
+ required: ['agent_id'],
119
+ },
120
+ },
121
+ {
122
+ name: 'get_agent_session',
123
+ description: 'Get session metadata and recent events (last 20). Does NOT include full conversation — use get_agent_session_conversation for that.',
124
+ inputSchema: {
125
+ type: 'object',
126
+ properties: {
127
+ agent_id: { type: 'string', description: 'Agent profile ID' },
128
+ session_id: { type: 'string', description: 'Session ID' },
129
+ },
130
+ required: ['agent_id', 'session_id'],
131
+ },
132
+ },
133
+ {
134
+ name: 'get_agent_session_conversation',
135
+ description: 'Get the full conversation history for a session (paginated). Can be large for long-running sessions.',
136
+ inputSchema: {
137
+ type: 'object',
138
+ properties: {
139
+ agent_id: { type: 'string', description: 'Agent profile ID' },
140
+ session_id: { type: 'string', description: 'Session ID' },
141
+ limit: { type: 'number', description: 'Turns to return' },
142
+ offset: { type: 'number', description: 'Turns to skip' },
143
+ },
144
+ required: ['agent_id', 'session_id'],
145
+ },
146
+ },
147
+ {
148
+ name: 'run_agent',
149
+ description: 'Queue an ad-hoc session with an inline prompt. This is async — it returns a queue entry ID immediately. ' +
150
+ 'Use list_agent_queue or list_agent_sessions to track progress.',
151
+ inputSchema: {
152
+ type: 'object',
153
+ properties: {
154
+ agent_id: { type: 'string', description: 'Agent profile ID' },
155
+ prompt: { type: 'string', description: 'What the agent should do' },
156
+ project_slug: { type: 'string', description: 'Project to scope the session to (optional)' },
157
+ },
158
+ required: ['agent_id', 'prompt'],
159
+ },
160
+ },
161
+ {
162
+ name: 'stop_agent_session',
163
+ description: 'Stop a running agent session.',
164
+ inputSchema: {
165
+ type: 'object',
166
+ properties: {
167
+ agent_id: { type: 'string', description: 'Agent profile ID' },
168
+ session_id: { type: 'string', description: 'Session ID to stop' },
169
+ },
170
+ required: ['agent_id', 'session_id'],
171
+ },
172
+ },
173
+ {
174
+ name: 'resume_agent_session',
175
+ description: 'Resume a completed session with a follow-up prompt. The agent continues with full prior context.',
176
+ inputSchema: {
177
+ type: 'object',
178
+ properties: {
179
+ agent_id: { type: 'string', description: 'Agent profile ID' },
180
+ session_id: { type: 'string', description: 'Completed session ID to resume' },
181
+ prompt: { type: 'string', description: 'Follow-up prompt for the agent' },
182
+ },
183
+ required: ['agent_id', 'session_id', 'prompt'],
184
+ },
185
+ },
186
+ // ─── Group 4: Tasks & Queue ───────────────────────────────────────────────
187
+ {
188
+ name: 'list_agent_tasks',
189
+ description: 'List defined tasks for an agent.',
190
+ inputSchema: {
191
+ type: 'object',
192
+ properties: {
193
+ agent_id: { type: 'string', description: 'Agent profile ID' },
194
+ },
195
+ required: ['agent_id'],
196
+ },
197
+ },
198
+ {
199
+ name: 'get_agent_task',
200
+ description: 'Get details of a specific agent task.',
201
+ inputSchema: {
202
+ type: 'object',
203
+ properties: {
204
+ agent_id: { type: 'string', description: 'Agent profile ID' },
205
+ task_id: { type: 'string', description: 'Task ID' },
206
+ },
207
+ required: ['agent_id', 'task_id'],
208
+ },
209
+ },
210
+ {
211
+ name: 'create_agent_task',
212
+ description: 'Create a new task for an agent.',
213
+ inputSchema: {
214
+ type: 'object',
215
+ properties: {
216
+ agent_id: { type: 'string', description: 'Agent profile ID' },
217
+ name: { type: 'string', description: 'Task name' },
218
+ prompt: { type: 'string', description: 'What the agent should do when this task runs' },
219
+ scheduled_enabled: {
220
+ type: 'boolean',
221
+ description: 'Whether this task is included in scheduled runs (default false)',
222
+ },
223
+ sort_order: {
224
+ type: 'number',
225
+ description: 'Order within scheduled batch (lower = first)',
226
+ },
227
+ },
228
+ required: ['agent_id', 'name', 'prompt'],
229
+ },
230
+ },
231
+ {
232
+ name: 'update_agent_task',
233
+ description: "Update a task's prompt or schedule settings.",
234
+ inputSchema: {
235
+ type: 'object',
236
+ properties: {
237
+ agent_id: { type: 'string', description: 'Agent profile ID' },
238
+ task_id: { type: 'string', description: 'Task ID' },
239
+ name: { type: 'string', description: 'Updated task name' },
240
+ prompt: { type: 'string', description: 'Updated prompt' },
241
+ scheduled_enabled: { type: 'boolean', description: 'Include in scheduled runs' },
242
+ sort_order: { type: 'number', description: 'Order within scheduled batch' },
243
+ is_active: { type: 'boolean', description: 'Whether this task is active' },
244
+ },
245
+ required: ['agent_id', 'task_id'],
246
+ },
247
+ },
248
+ {
249
+ name: 'run_agent_task',
250
+ description: 'Queue a saved task for immediate execution. Async — returns a queue entry ID. ' +
251
+ 'Use list_agent_queue to track progress.',
252
+ inputSchema: {
253
+ type: 'object',
254
+ properties: {
255
+ agent_id: { type: 'string', description: 'Agent profile ID' },
256
+ task_id: { type: 'string', description: 'Task ID to run' },
257
+ project_slug: { type: 'string', description: 'Project to scope the session to (optional)' },
258
+ },
259
+ required: ['agent_id', 'task_id'],
260
+ },
261
+ },
262
+ {
263
+ name: 'list_agent_queue',
264
+ description: 'List queue entries for an agent. Use this to track progress after run_agent or run_agent_task.',
265
+ inputSchema: {
266
+ type: 'object',
267
+ properties: {
268
+ agent_id: { type: 'string', description: 'Agent profile ID' },
269
+ status: {
270
+ type: 'string',
271
+ enum: ['queued', 'running', 'completed', 'failed', 'cancelled'],
272
+ description: 'Filter by queue entry status',
273
+ },
274
+ },
275
+ required: ['agent_id'],
276
+ },
277
+ },
278
+ {
279
+ name: 'cancel_queue_entry',
280
+ description: 'Cancel a queued (not yet running) queue entry.',
281
+ inputSchema: {
282
+ type: 'object',
283
+ properties: {
284
+ agent_id: { type: 'string', description: 'Agent profile ID' },
285
+ entry_id: { type: 'string', description: 'Queue entry ID to cancel' },
286
+ },
287
+ required: ['agent_id', 'entry_id'],
288
+ },
289
+ },
290
+ // ─── Group 5: Skills (authoring) ─────────────────────────────────────────
291
+ {
292
+ name: 'create_skill',
293
+ description: 'Create a new org-level skill. Skills are reusable capability packs agents can load on demand.',
294
+ inputSchema: {
295
+ type: 'object',
296
+ properties: {
297
+ name: {
298
+ type: 'string',
299
+ description: 'Skill name (lowercase letters, numbers, hyphens only, e.g. "deploy-app")',
300
+ },
301
+ description: {
302
+ type: 'string',
303
+ description: 'What the skill does and when to use it (max 1024 chars)',
304
+ },
305
+ content: {
306
+ type: 'string',
307
+ description: 'Instruction body in markdown',
308
+ },
309
+ },
310
+ required: ['name', 'description', 'content'],
311
+ },
312
+ },
313
+ {
314
+ name: 'update_skill',
315
+ description: "Update a skill's instructions or description.",
316
+ inputSchema: {
317
+ type: 'object',
318
+ properties: {
319
+ skill_id: { type: 'string', description: 'Skill ID (UUID)' },
320
+ name: { type: 'string', description: 'Updated skill name' },
321
+ description: { type: 'string', description: 'Updated description' },
322
+ content: { type: 'string', description: 'Updated instruction body' },
323
+ },
324
+ required: ['skill_id'],
325
+ },
326
+ },
327
+ {
328
+ name: 'create_skill_resource',
329
+ description: 'Add a text resource to a skill (script, reference, or asset).',
330
+ inputSchema: {
331
+ type: 'object',
332
+ properties: {
333
+ skill_id: { type: 'string', description: 'Skill ID (UUID)' },
334
+ type: {
335
+ type: 'string',
336
+ enum: ['script', 'reference', 'asset'],
337
+ description: 'Resource type',
338
+ },
339
+ name: { type: 'string', description: 'Resource filename' },
340
+ content: { type: 'string', description: 'Resource text content' },
341
+ mime_type: { type: 'string', description: 'MIME type (optional)' },
342
+ },
343
+ required: ['skill_id', 'type', 'name', 'content'],
344
+ },
345
+ },
346
+ {
347
+ name: 'update_skill_resource',
348
+ description: "Update a skill resource's content.",
349
+ inputSchema: {
350
+ type: 'object',
351
+ properties: {
352
+ skill_id: { type: 'string', description: 'Skill ID (UUID)' },
353
+ resource_id: { type: 'string', description: 'Resource ID (UUID)' },
354
+ name: { type: 'string', description: 'Updated filename' },
355
+ content: { type: 'string', description: 'Updated text content' },
356
+ mime_type: { type: 'string', description: 'Updated MIME type' },
357
+ },
358
+ required: ['skill_id', 'resource_id'],
359
+ },
360
+ },
361
+ {
362
+ name: 'set_agent_skills',
363
+ description: 'Replace the full set of skills assigned to an agent. ' +
364
+ 'Use list_skills to find skill IDs, then pass all desired skill IDs here.',
365
+ inputSchema: {
366
+ type: 'object',
367
+ properties: {
368
+ agent_id: { type: 'string', description: 'Agent profile ID' },
369
+ skill_ids: {
370
+ type: 'array',
371
+ items: { type: 'string' },
372
+ description: 'Array of skill IDs to assign (replaces all current assignments)',
373
+ },
374
+ },
375
+ required: ['agent_id', 'skill_ids'],
376
+ },
377
+ },
378
+ // ─── Group 6: Secrets ─────────────────────────────────────────────────────
379
+ {
380
+ name: 'list_agent_secrets',
381
+ description: 'List secret names for an agent. Values are never returned.',
382
+ inputSchema: {
383
+ type: 'object',
384
+ properties: {
385
+ agent_id: { type: 'string', description: 'Agent profile ID' },
386
+ },
387
+ required: ['agent_id'],
388
+ },
389
+ },
390
+ {
391
+ name: 'set_agent_secret',
392
+ description: 'Create or update a secret for an agent. ' +
393
+ 'Name must be uppercase letters, digits, and underscores (e.g. MY_API_KEY).',
394
+ inputSchema: {
395
+ type: 'object',
396
+ properties: {
397
+ agent_id: { type: 'string', description: 'Agent profile ID' },
398
+ name: { type: 'string', description: 'Secret name (e.g. MY_API_KEY)' },
399
+ value: { type: 'string', description: 'Secret value (encrypted at rest)' },
400
+ },
401
+ required: ['agent_id', 'name', 'value'],
402
+ },
403
+ },
404
+ // ─── Group 7: Discovery ───────────────────────────────────────────────────
405
+ {
406
+ name: 'list_agent_templates',
407
+ description: 'List agent templates available to the organisation. Use template IDs when calling create_agent.',
408
+ inputSchema: { type: 'object', properties: {} },
409
+ },
410
+ {
411
+ name: 'list_llm_models',
412
+ description: 'List LLM providers and models available to the organisation. ' +
413
+ 'Use provider and model IDs when calling create_agent or update_agent.',
414
+ inputSchema: { type: 'object', properties: {} },
415
+ },
416
+ ];
417
+ // Zod schemas for argument parsing
418
+ const AgentIdSchema = z.object({ agent_id: z.string() });
419
+ const AgentSessionSchema = z.object({ agent_id: z.string(), session_id: z.string() });
420
+ const AgentTaskSchema = z.object({ agent_id: z.string(), task_id: z.string() });
421
+ const SkillIdSchema = z.object({ skill_id: z.string() });
422
+ export async function handleAgentBuilderTool(name, args, client) {
423
+ const text = (t) => JSON.stringify(t, null, 2);
424
+ switch (name) {
425
+ // ─── Agent CRUD ────────────────────────────────────────────────────────
426
+ case 'list_agents': {
427
+ const agents = await client.listAgents();
428
+ return { content: [{ type: 'text', text: text(agents) }] };
429
+ }
430
+ case 'get_agent': {
431
+ const { agent_id } = AgentIdSchema.parse(args);
432
+ const agent = await client.getAgent(agent_id);
433
+ if (!agent)
434
+ return { content: [{ type: 'text', text: 'Agent not found' }] };
435
+ return { content: [{ type: 'text', text: text(agent) }] };
436
+ }
437
+ case 'create_agent': {
438
+ const parsed = z
439
+ .object({
440
+ template_id: z.string(),
441
+ name: z.string(),
442
+ config: z.record(z.unknown()).optional(),
443
+ })
444
+ .parse(args);
445
+ const agent = await client.createAgent({
446
+ templateId: parsed.template_id,
447
+ name: parsed.name,
448
+ config: parsed.config,
449
+ });
450
+ return {
451
+ content: [
452
+ {
453
+ type: 'text',
454
+ text: `Created agent "${agent.user?.comment || agent.userId}"\n\n${text(agent)}`,
455
+ },
456
+ ],
457
+ };
458
+ }
459
+ case 'update_agent': {
460
+ const parsed = z
461
+ .object({
462
+ agent_id: z.string(),
463
+ name: z.string().optional(),
464
+ instructions: z.string().nullable().optional(),
465
+ llm_provider: z.string().nullable().optional(),
466
+ llm_model: z.string().nullable().optional(),
467
+ llm_family: z.string().nullable().optional(),
468
+ scheduling_enabled: z.boolean().optional(),
469
+ })
470
+ .parse(args);
471
+ const { agent_id, ...rest } = parsed;
472
+ const fields = {};
473
+ if (rest.name !== undefined)
474
+ fields.name = rest.name;
475
+ if (rest.instructions !== undefined)
476
+ fields.instructions = rest.instructions;
477
+ if (rest.llm_provider !== undefined)
478
+ fields.llmProvider = rest.llm_provider;
479
+ if (rest.llm_model !== undefined)
480
+ fields.llmModel = rest.llm_model;
481
+ if (rest.llm_family !== undefined)
482
+ fields.llmFamily = rest.llm_family;
483
+ if (rest.scheduling_enabled !== undefined)
484
+ fields.schedulingEnabled = rest.scheduling_enabled;
485
+ const agent = await client.updateAgent(agent_id, fields);
486
+ return { content: [{ type: 'text', text: `Updated agent\n\n${text(agent)}` }] };
487
+ }
488
+ // ─── Project Assignment ─────────────────────────────────────────────────
489
+ case 'list_agent_projects': {
490
+ const { agent_id } = AgentIdSchema.parse(args);
491
+ const projects = await client.listAgentProjects(agent_id);
492
+ return { content: [{ type: 'text', text: text(projects) }] };
493
+ }
494
+ case 'assign_agent_to_project': {
495
+ const parsed = z.object({ agent_id: z.string(), project_slug: z.string() }).parse(args);
496
+ await client.assignAgentToProject(parsed.agent_id, parsed.project_slug);
497
+ return {
498
+ content: [{ type: 'text', text: `Assigned agent to project "${parsed.project_slug}"` }],
499
+ };
500
+ }
501
+ case 'remove_agent_from_project': {
502
+ const parsed = z.object({ agent_id: z.string(), project_slug: z.string() }).parse(args);
503
+ await client.removeAgentFromProject(parsed.agent_id, parsed.project_slug);
504
+ return {
505
+ content: [{ type: 'text', text: `Removed agent from project "${parsed.project_slug}"` }],
506
+ };
507
+ }
508
+ // ─── Sessions ───────────────────────────────────────────────────────────
509
+ case 'list_agent_sessions': {
510
+ const parsed = z
511
+ .object({
512
+ agent_id: z.string(),
513
+ status: z.string().optional(),
514
+ limit: z.number().optional(),
515
+ offset: z.number().optional(),
516
+ })
517
+ .parse(args);
518
+ const sessions = await client.listAgentSessions(parsed.agent_id, {
519
+ status: parsed.status,
520
+ limit: parsed.limit,
521
+ offset: parsed.offset,
522
+ });
523
+ return { content: [{ type: 'text', text: text(sessions) }] };
524
+ }
525
+ case 'get_agent_session': {
526
+ const { agent_id, session_id } = AgentSessionSchema.parse(args);
527
+ const session = await client.getAgentSession(agent_id, session_id);
528
+ if (!session)
529
+ return { content: [{ type: 'text', text: 'Session not found' }] };
530
+ return { content: [{ type: 'text', text: text(session) }] };
531
+ }
532
+ case 'get_agent_session_conversation': {
533
+ const parsed = z
534
+ .object({
535
+ agent_id: z.string(),
536
+ session_id: z.string(),
537
+ limit: z.number().optional(),
538
+ offset: z.number().optional(),
539
+ })
540
+ .parse(args);
541
+ const conv = await client.getAgentSessionConversation(parsed.agent_id, parsed.session_id, {
542
+ limit: parsed.limit,
543
+ offset: parsed.offset,
544
+ });
545
+ return { content: [{ type: 'text', text: text(conv) }] };
546
+ }
547
+ case 'run_agent': {
548
+ const parsed = z
549
+ .object({ agent_id: z.string(), prompt: z.string(), project_slug: z.string().optional() })
550
+ .parse(args);
551
+ const result = await client.runAgent(parsed.agent_id, parsed.prompt, parsed.project_slug);
552
+ return {
553
+ content: [
554
+ {
555
+ type: 'text',
556
+ text: `Queued session (entry ID: ${result.entryId}, position: ${result.position}, executing: ${result.executing})\n\nUse list_agent_queue or list_agent_sessions to track progress.`,
557
+ },
558
+ ],
559
+ };
560
+ }
561
+ case 'stop_agent_session': {
562
+ const { agent_id, session_id } = AgentSessionSchema.parse(args);
563
+ const session = await client.stopAgentSession(agent_id, session_id);
564
+ return {
565
+ content: [
566
+ { type: 'text', text: `Stopped session (status: ${session.status})\n\n${text(session)}` },
567
+ ],
568
+ };
569
+ }
570
+ case 'resume_agent_session': {
571
+ const parsed = z
572
+ .object({ agent_id: z.string(), session_id: z.string(), prompt: z.string() })
573
+ .parse(args);
574
+ const session = await client.resumeAgentSession(parsed.agent_id, parsed.session_id, parsed.prompt);
575
+ return { content: [{ type: 'text', text: `Resumed session\n\n${text(session)}` }] };
576
+ }
577
+ // ─── Tasks ──────────────────────────────────────────────────────────────
578
+ case 'list_agent_tasks': {
579
+ const { agent_id } = AgentIdSchema.parse(args);
580
+ const tasks = await client.listAgentTasks(agent_id);
581
+ return { content: [{ type: 'text', text: text(tasks) }] };
582
+ }
583
+ case 'get_agent_task': {
584
+ const { agent_id, task_id } = AgentTaskSchema.parse(args);
585
+ const task = await client.getAgentTask(agent_id, task_id);
586
+ if (!task)
587
+ return { content: [{ type: 'text', text: 'Task not found' }] };
588
+ return { content: [{ type: 'text', text: text(task) }] };
589
+ }
590
+ case 'create_agent_task': {
591
+ const parsed = z
592
+ .object({
593
+ agent_id: z.string(),
594
+ name: z.string(),
595
+ prompt: z.string(),
596
+ scheduled_enabled: z.boolean().optional(),
597
+ sort_order: z.number().optional(),
598
+ })
599
+ .parse(args);
600
+ const task = await client.createAgentTask(parsed.agent_id, {
601
+ name: parsed.name,
602
+ prompt: parsed.prompt,
603
+ scheduledEnabled: parsed.scheduled_enabled,
604
+ sortOrder: parsed.sort_order,
605
+ });
606
+ return { content: [{ type: 'text', text: `Created task "${task.name}"\n\n${text(task)}` }] };
607
+ }
608
+ case 'update_agent_task': {
609
+ const parsed = z
610
+ .object({
611
+ agent_id: z.string(),
612
+ task_id: z.string(),
613
+ name: z.string().optional(),
614
+ prompt: z.string().optional(),
615
+ scheduled_enabled: z.boolean().optional(),
616
+ sort_order: z.number().optional(),
617
+ is_active: z.boolean().optional(),
618
+ })
619
+ .parse(args);
620
+ const fields = {};
621
+ if (parsed.name !== undefined)
622
+ fields.name = parsed.name;
623
+ if (parsed.prompt !== undefined)
624
+ fields.prompt = parsed.prompt;
625
+ if (parsed.scheduled_enabled !== undefined)
626
+ fields.scheduledEnabled = parsed.scheduled_enabled;
627
+ if (parsed.sort_order !== undefined)
628
+ fields.sortOrder = parsed.sort_order;
629
+ if (parsed.is_active !== undefined)
630
+ fields.isActive = parsed.is_active;
631
+ const task = await client.updateAgentTask(parsed.agent_id, parsed.task_id, fields);
632
+ return { content: [{ type: 'text', text: `Updated task\n\n${text(task)}` }] };
633
+ }
634
+ case 'run_agent_task': {
635
+ const parsed = z
636
+ .object({ agent_id: z.string(), task_id: z.string(), project_slug: z.string().optional() })
637
+ .parse(args);
638
+ const result = await client.runAgentTask(parsed.agent_id, parsed.task_id, parsed.project_slug);
639
+ return {
640
+ content: [
641
+ {
642
+ type: 'text',
643
+ text: `Queued task (entry ID: ${result.entryId}, position: ${result.position}, executing: ${result.executing})\n\nUse list_agent_queue to track progress.`,
644
+ },
645
+ ],
646
+ };
647
+ }
648
+ case 'list_agent_queue': {
649
+ const parsed = z.object({ agent_id: z.string(), status: z.string().optional() }).parse(args);
650
+ const entries = await client.listAgentQueue(parsed.agent_id, parsed.status);
651
+ return { content: [{ type: 'text', text: text(entries) }] };
652
+ }
653
+ case 'cancel_queue_entry': {
654
+ const parsed = z.object({ agent_id: z.string(), entry_id: z.string() }).parse(args);
655
+ await client.cancelQueueEntry(parsed.agent_id, parsed.entry_id);
656
+ return { content: [{ type: 'text', text: `Cancelled queue entry ${parsed.entry_id}` }] };
657
+ }
658
+ // ─── Skills ─────────────────────────────────────────────────────────────
659
+ case 'create_skill': {
660
+ const parsed = z
661
+ .object({ name: z.string(), description: z.string(), content: z.string() })
662
+ .parse(args);
663
+ const skill = await client.createSkill(parsed);
664
+ return {
665
+ content: [{ type: 'text', text: `Created skill "${parsed.name}"\n\n${text(skill)}` }],
666
+ };
667
+ }
668
+ case 'update_skill': {
669
+ const parsed = z
670
+ .object({
671
+ skill_id: z.string(),
672
+ name: z.string().optional(),
673
+ description: z.string().optional(),
674
+ content: z.string().optional(),
675
+ })
676
+ .parse(args);
677
+ const { skill_id, ...fields } = parsed;
678
+ const skill = await client.updateSkill(skill_id, fields);
679
+ return { content: [{ type: 'text', text: `Updated skill\n\n${text(skill)}` }] };
680
+ }
681
+ case 'create_skill_resource': {
682
+ const parsed = z
683
+ .object({
684
+ skill_id: z.string(),
685
+ type: z.enum(['script', 'reference', 'asset']),
686
+ name: z.string(),
687
+ content: z.string(),
688
+ mime_type: z.string().optional(),
689
+ })
690
+ .parse(args);
691
+ const { skill_id, mime_type, ...rest } = parsed;
692
+ const resource = await client.createSkillResource(skill_id, { ...rest, mimeType: mime_type });
693
+ return {
694
+ content: [{ type: 'text', text: `Created resource "${parsed.name}"\n\n${text(resource)}` }],
695
+ };
696
+ }
697
+ case 'update_skill_resource': {
698
+ const parsed = z
699
+ .object({
700
+ skill_id: z.string(),
701
+ resource_id: z.string(),
702
+ name: z.string().optional(),
703
+ content: z.string().optional(),
704
+ mime_type: z.string().optional(),
705
+ })
706
+ .parse(args);
707
+ const { skill_id, resource_id, mime_type, ...rest } = parsed;
708
+ const resource = await client.updateSkillResource(skill_id, resource_id, {
709
+ ...rest,
710
+ mimeType: mime_type,
711
+ });
712
+ return { content: [{ type: 'text', text: `Updated resource\n\n${text(resource)}` }] };
713
+ }
714
+ case 'set_agent_skills': {
715
+ const parsed = z.object({ agent_id: z.string(), skill_ids: z.array(z.string()) }).parse(args);
716
+ await client.setAgentSkills(parsed.agent_id, parsed.skill_ids);
717
+ return {
718
+ content: [
719
+ {
720
+ type: 'text',
721
+ text: `Set ${parsed.skill_ids.length} skill(s) on agent ${parsed.agent_id}`,
722
+ },
723
+ ],
724
+ };
725
+ }
726
+ // ─── Secrets ─────────────────────────────────────────────────────────────
727
+ case 'list_agent_secrets': {
728
+ const { agent_id } = AgentIdSchema.parse(args);
729
+ const secrets = await client.listAgentSecrets(agent_id);
730
+ return { content: [{ type: 'text', text: text(secrets) }] };
731
+ }
732
+ case 'set_agent_secret': {
733
+ const parsed = z
734
+ .object({ agent_id: z.string(), name: z.string(), value: z.string() })
735
+ .parse(args);
736
+ const secret = await client.setAgentSecret(parsed.agent_id, parsed.name, parsed.value);
737
+ return { content: [{ type: 'text', text: `Set secret "${secret.name}"` }] };
738
+ }
739
+ // ─── Discovery ────────────────────────────────────────────────────────────
740
+ case 'list_agent_templates': {
741
+ const templates = await client.listAgentTemplates();
742
+ return { content: [{ type: 'text', text: text(templates) }] };
743
+ }
744
+ case 'list_llm_models': {
745
+ const models = await client.listLlmModels();
746
+ return { content: [{ type: 'text', text: text(models) }] };
747
+ }
748
+ default:
749
+ return {
750
+ content: [{ type: 'text', text: `Unknown agent builder tool: ${name}` }],
751
+ isError: true,
752
+ };
753
+ }
754
+ }
package/dist/client.js CHANGED
@@ -568,6 +568,147 @@ export class WolfpackClient {
568
568
  parentCommentId,
569
569
  });
570
570
  }
571
+ // ─── Capabilities ─────────────────────────────────────────────────────────
572
+ async getCapabilities() {
573
+ return this.api.get('/capabilities');
574
+ }
575
+ // ─── Agent Builder: Agent CRUD ─────────────────────────────────────────────
576
+ async listAgents() {
577
+ return this.api.get('/agents');
578
+ }
579
+ async getAgent(agentId) {
580
+ try {
581
+ return await this.api.get(`/agents/${agentId}`);
582
+ }
583
+ catch (error) {
584
+ if (error && typeof error === 'object' && 'status' in error && error.status === 404)
585
+ return null;
586
+ throw error;
587
+ }
588
+ }
589
+ async createAgent(body) {
590
+ return this.api.post('/agents', body);
591
+ }
592
+ async updateAgent(agentId, body) {
593
+ return this.api.patch(`/agents/${agentId}`, body);
594
+ }
595
+ // ─── Agent Builder: Project Assignment ────────────────────────────────────
596
+ async listAgentProjects(agentId) {
597
+ return this.api.get(`/agents/${agentId}/projects`);
598
+ }
599
+ async assignAgentToProject(agentId, teamSlug) {
600
+ return this.api.post(`/agents/${agentId}/projects/${encodeURIComponent(teamSlug)}`, {});
601
+ }
602
+ async removeAgentFromProject(agentId, teamSlug) {
603
+ return this.api
604
+ .delete(`/agents/${agentId}/projects/${encodeURIComponent(teamSlug)}`)
605
+ .then(() => ({ success: true }));
606
+ }
607
+ // ─── Agent Builder: Sessions ───────────────────────────────────────────────
608
+ async listAgentSessions(agentId, options) {
609
+ const params = new URLSearchParams();
610
+ if (options?.status)
611
+ params.append('status', options.status);
612
+ if (options?.limit !== undefined)
613
+ params.append('limit', options.limit.toString());
614
+ if (options?.offset !== undefined)
615
+ params.append('offset', options.offset.toString());
616
+ const q = params.toString();
617
+ return this.api.get(`/agents/${agentId}/sessions${q ? `?${q}` : ''}`);
618
+ }
619
+ async getAgentSession(agentId, sessionId) {
620
+ try {
621
+ return await this.api.get(`/agents/${agentId}/sessions/${sessionId}`);
622
+ }
623
+ catch (error) {
624
+ if (error && typeof error === 'object' && 'status' in error && error.status === 404)
625
+ return null;
626
+ throw error;
627
+ }
628
+ }
629
+ async getAgentSessionConversation(agentId, sessionId, options) {
630
+ const params = new URLSearchParams();
631
+ if (options?.limit !== undefined)
632
+ params.append('limit', options.limit.toString());
633
+ if (options?.offset !== undefined)
634
+ params.append('offset', options.offset.toString());
635
+ const q = params.toString();
636
+ return this.api.get(`/agents/${agentId}/sessions/${sessionId}/conversation${q ? `?${q}` : ''}`);
637
+ }
638
+ async runAgent(agentId, prompt, teamSlug) {
639
+ return this.api.post(`/agents/${agentId}/run`, { prompt, teamSlug });
640
+ }
641
+ async stopAgentSession(agentId, sessionId) {
642
+ return this.api.post(`/agents/${agentId}/sessions/${sessionId}/stop`, {});
643
+ }
644
+ async resumeAgentSession(agentId, sessionId, prompt) {
645
+ return this.api.post(`/agents/${agentId}/sessions/${sessionId}/resume`, {
646
+ prompt,
647
+ });
648
+ }
649
+ // ─── Agent Builder: Tasks ──────────────────────────────────────────────────
650
+ async listAgentTasks(agentId) {
651
+ return this.api.get(`/agents/${agentId}/tasks`);
652
+ }
653
+ async getAgentTask(agentId, taskId) {
654
+ try {
655
+ return await this.api.get(`/agents/${agentId}/tasks/${taskId}`);
656
+ }
657
+ catch (error) {
658
+ if (error && typeof error === 'object' && 'status' in error && error.status === 404)
659
+ return null;
660
+ throw error;
661
+ }
662
+ }
663
+ async createAgentTask(agentId, body) {
664
+ return this.api.post(`/agents/${agentId}/tasks`, body);
665
+ }
666
+ async updateAgentTask(agentId, taskId, body) {
667
+ return this.api.patch(`/agents/${agentId}/tasks/${taskId}`, body);
668
+ }
669
+ async runAgentTask(agentId, taskId, teamSlug) {
670
+ return this.api.post(`/agents/${agentId}/tasks/${taskId}/run`, { teamSlug });
671
+ }
672
+ // ─── Agent Builder: Queue ──────────────────────────────────────────────────
673
+ async listAgentQueue(agentId, status) {
674
+ const q = status ? `?status=${encodeURIComponent(status)}` : '';
675
+ return this.api.get(`/agents/${agentId}/queue${q}`);
676
+ }
677
+ async cancelQueueEntry(agentId, entryId) {
678
+ return this.api.delete(`/agents/${agentId}/queue/${entryId}`).then(() => ({ success: true }));
679
+ }
680
+ // ─── Agent Builder: Secrets ────────────────────────────────────────────────
681
+ async listAgentSecrets(agentId) {
682
+ return this.api.get(`/agents/${agentId}/secrets`);
683
+ }
684
+ async setAgentSecret(agentId, name, value) {
685
+ return this.api.post(`/agents/${agentId}/secrets`, { name, value });
686
+ }
687
+ // ─── Agent Builder: Skills (write) ────────────────────────────────────────
688
+ async createSkill(body) {
689
+ return this.api.post('/skills', body);
690
+ }
691
+ async updateSkill(skillId, body) {
692
+ return this.api.patch(`/skills/${skillId}`, body);
693
+ }
694
+ async createSkillResource(skillId, body) {
695
+ return this.api.post(`/skills/${skillId}/resources`, body);
696
+ }
697
+ async updateSkillResource(skillId, resourceId, body) {
698
+ return this.api.patch(`/skills/${skillId}/resources/${resourceId}`, body);
699
+ }
700
+ async setAgentSkills(agentId, skillIds) {
701
+ return this.api.post(`/skills/agent-assignments/${agentId}`, {
702
+ skillIds,
703
+ });
704
+ }
705
+ // ─── Agent Builder: Discovery ──────────────────────────────────────────────
706
+ async listAgentTemplates() {
707
+ return this.api.get('/agent-builder/templates');
708
+ }
709
+ async listLlmModels() {
710
+ return this.api.get('/agent-builder/llm-models');
711
+ }
571
712
  close() {
572
713
  // No cleanup needed for API client
573
714
  }
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ import { readFile, stat } from 'fs/promises';
8
8
  import { basename, extname, resolve } from 'path';
9
9
  import { WolfpackClient } from './client.js';
10
10
  import { validateConfig, config } from './config.js';
11
+ import { AGENT_BUILDER_TOOLS, handleAgentBuilderTool } from './agentBuilderTools.js';
11
12
  // Get current package version
12
13
  const require = createRequire(import.meta.url);
13
14
  const packageJson = require('../package.json');
@@ -115,6 +116,10 @@ const VALID_STATUSES = [
115
116
  const UpdateWorkItemSchema = z.object({
116
117
  work_item_id: z.string().describe('The ID of the work item'),
117
118
  title: z.string().optional().describe('New title for the work item'),
119
+ description: z
120
+ .string()
121
+ .optional()
122
+ .describe('Updated description (supports Markdown and @mentions)'),
118
123
  status: z.enum(VALID_STATUSES).optional().describe('New status'),
119
124
  leading_user_id: z
120
125
  .string()
@@ -137,6 +142,27 @@ const UpdateWorkItemSchema = z.object({
137
142
  .nullable()
138
143
  .optional()
139
144
  .describe('Size estimate: "S" (small), "M" (medium), "L" (large), or null to remove'),
145
+ assisting_user1_id: z
146
+ .string()
147
+ .nullable()
148
+ .optional()
149
+ .describe('User ID for first assisting user, or null to remove'),
150
+ assisting_user2_id: z
151
+ .string()
152
+ .nullable()
153
+ .optional()
154
+ .describe('User ID for second assisting user, or null to remove'),
155
+ assisting_user3_id: z
156
+ .string()
157
+ .nullable()
158
+ .optional()
159
+ .describe('User ID for third assisting user, or null to remove'),
160
+ tag_ids: z.array(z.string()).optional().describe('Tag IDs to apply (replaces existing tags)'),
161
+ closing_comment: z
162
+ .string()
163
+ .nullable()
164
+ .optional()
165
+ .describe('Comment explaining closure/completion'),
140
166
  project_slug: z
141
167
  .string()
142
168
  .optional()
@@ -590,6 +616,7 @@ function hasPlan(description) {
590
616
  class WolfpackMCPServer {
591
617
  server;
592
618
  client;
619
+ capabilities = [];
593
620
  constructor() {
594
621
  this.client = new WolfpackClient();
595
622
  this.server = new Server({
@@ -737,8 +764,8 @@ class WolfpackMCPServer {
737
764
  {
738
765
  name: 'update_work_item',
739
766
  description: 'Update one or more fields on a work item. Only provide the fields you want to change. ' +
740
- 'Supports: title, status, leading_user_id (assignee), radar_item_id (initiative), ' +
741
- 'category_id, priority (0-4), and size (S/M/L). ' +
767
+ 'Supports: title, description, status, leading_user_id (assignee), assisting_user1/2/3_id, ' +
768
+ 'radar_item_id (initiative), category_id, priority (0-4), size (S/M/L), tag_ids, and closing_comment. ' +
742
769
  'STATUS WORKFLOW: "pending" (backlog) → "new" (to do) → "doing" (in progress) → "review" (work done) → "ready" (awaiting deployment) → "completed" (deployed). ' +
743
770
  'Use "blocked" when work cannot proceed. For "pending" items use pull_work_item first. ' +
744
771
  'When moving to "review", add a completion comment. When moving to "blocked", add a comment explaining the blocker.',
@@ -753,6 +780,10 @@ class WolfpackMCPServer {
753
780
  type: 'string',
754
781
  description: 'New title for the work item',
755
782
  },
783
+ description: {
784
+ type: 'string',
785
+ description: 'Updated description (supports Markdown and @mentions)',
786
+ },
756
787
  status: {
757
788
  type: 'string',
758
789
  enum: [
@@ -789,6 +820,27 @@ class WolfpackMCPServer {
789
820
  enum: ['S', 'M', 'L', null],
790
821
  description: 'Size estimate: "S" (small), "M" (medium), "L" (large), or null to remove',
791
822
  },
823
+ assisting_user1_id: {
824
+ type: ['string', 'null'],
825
+ description: 'User ID for first assisting user, or null to remove',
826
+ },
827
+ assisting_user2_id: {
828
+ type: ['string', 'null'],
829
+ description: 'User ID for second assisting user, or null to remove',
830
+ },
831
+ assisting_user3_id: {
832
+ type: ['string', 'null'],
833
+ description: 'User ID for third assisting user, or null to remove',
834
+ },
835
+ tag_ids: {
836
+ type: 'array',
837
+ items: { type: 'string' },
838
+ description: 'Tag IDs to apply (replaces existing tags)',
839
+ },
840
+ closing_comment: {
841
+ type: ['string', 'null'],
842
+ description: 'Comment explaining closure/completion',
843
+ },
792
844
  project_slug: {
793
845
  type: 'string',
794
846
  description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
@@ -1811,6 +1863,7 @@ class WolfpackMCPServer {
1811
1863
  required: ['discussion_id', 'content'],
1812
1864
  },
1813
1865
  },
1866
+ ...(this.capabilities.includes('agent_builder') ? AGENT_BUILDER_TOOLS : []),
1814
1867
  ],
1815
1868
  }));
1816
1869
  this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
@@ -1908,6 +1961,8 @@ class WolfpackMCPServer {
1908
1961
  const fields = {};
1909
1962
  if (parsed.title !== undefined)
1910
1963
  fields.title = parsed.title;
1964
+ if (parsed.description !== undefined)
1965
+ fields.description = parsed.description;
1911
1966
  if (parsed.status !== undefined)
1912
1967
  fields.status = parsed.status;
1913
1968
  if (parsed.leading_user_id !== undefined)
@@ -1920,6 +1975,16 @@ class WolfpackMCPServer {
1920
1975
  fields.priority = parsed.priority;
1921
1976
  if (parsed.size !== undefined)
1922
1977
  fields.size = parsed.size;
1978
+ if (parsed.assisting_user1_id !== undefined)
1979
+ fields.assistingUser1Id = parsed.assisting_user1_id;
1980
+ if (parsed.assisting_user2_id !== undefined)
1981
+ fields.assistingUser2Id = parsed.assisting_user2_id;
1982
+ if (parsed.assisting_user3_id !== undefined)
1983
+ fields.assistingUser3Id = parsed.assisting_user3_id;
1984
+ if (parsed.tag_ids !== undefined)
1985
+ fields.tagIds = parsed.tag_ids;
1986
+ if (parsed.closing_comment !== undefined)
1987
+ fields.closingComment = parsed.closing_comment;
1923
1988
  const workItem = await this.client.updateWorkItem(parsed.work_item_id, fields, teamSlug);
1924
1989
  if (workItem) {
1925
1990
  // Build a summary of what changed
@@ -1928,6 +1993,8 @@ class WolfpackMCPServer {
1928
1993
  changes.push(`status → ${parsed.status}`);
1929
1994
  if (parsed.title !== undefined)
1930
1995
  changes.push(`title → "${parsed.title}"`);
1996
+ if (parsed.description !== undefined)
1997
+ changes.push('description updated');
1931
1998
  if (parsed.leading_user_id !== undefined)
1932
1999
  changes.push(parsed.leading_user_id
1933
2000
  ? `assigned to ${workItem.leadingUser?.name || parsed.leading_user_id}`
@@ -1950,6 +2017,22 @@ class WolfpackMCPServer {
1950
2017
  }
1951
2018
  if (parsed.size !== undefined)
1952
2019
  changes.push(parsed.size ? `size → ${parsed.size}` : 'size removed');
2020
+ if (parsed.assisting_user1_id !== undefined)
2021
+ changes.push(parsed.assisting_user1_id
2022
+ ? `assisting user 1 updated`
2023
+ : 'assisting user 1 removed');
2024
+ if (parsed.assisting_user2_id !== undefined)
2025
+ changes.push(parsed.assisting_user2_id
2026
+ ? `assisting user 2 updated`
2027
+ : 'assisting user 2 removed');
2028
+ if (parsed.assisting_user3_id !== undefined)
2029
+ changes.push(parsed.assisting_user3_id
2030
+ ? `assisting user 3 updated`
2031
+ : 'assisting user 3 removed');
2032
+ if (parsed.tag_ids !== undefined)
2033
+ changes.push('tags updated');
2034
+ if (parsed.closing_comment !== undefined)
2035
+ changes.push('closing comment set');
1953
2036
  const summary = changes.length > 0 ? changes.join(', ') : 'no changes';
1954
2037
  return {
1955
2038
  content: [
@@ -2618,8 +2701,12 @@ class WolfpackMCPServer {
2618
2701
  content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
2619
2702
  };
2620
2703
  }
2621
- default:
2704
+ default: {
2705
+ if (this.capabilities.includes('agent_builder')) {
2706
+ return handleAgentBuilderTool(name, args, this.client);
2707
+ }
2622
2708
  throw new Error(`Unknown tool: ${name}`);
2709
+ }
2623
2710
  }
2624
2711
  }
2625
2712
  catch (error) {
@@ -2657,41 +2744,55 @@ class WolfpackMCPServer {
2657
2744
  return false;
2658
2745
  }
2659
2746
  async start() {
2747
+ // Fetch capabilities to determine which tools to register
2748
+ try {
2749
+ const caps = await this.client.getCapabilities();
2750
+ this.capabilities = caps.capabilities;
2751
+ if (this.capabilities.length > 0) {
2752
+ console.error(`Capabilities: ${this.capabilities.join(', ')}`);
2753
+ }
2754
+ }
2755
+ catch (error) {
2756
+ console.error(`Warning: Could not fetch capabilities: ${error instanceof Error ? error.message : String(error)}`);
2757
+ // Proceed with project tools only (backward compat)
2758
+ }
2660
2759
  // Connect stdio transport FIRST so MCP clients don't deadlock waiting for initialize
2661
2760
  const transport = new StdioServerTransport();
2662
2761
  await this.server.connect(transport);
2663
2762
  console.error(`Wolfpack MCP Server v${CURRENT_VERSION} started`);
2664
- // Resolve project after transport is connected (non-blocking for MCP handshake)
2665
- if (config.projectSlug) {
2666
- // Explicit project slug configured - use it
2667
- try {
2668
- await this.client.resolveProjectSlug(config.projectSlug);
2669
- console.error(`Resolved project: "${config.projectSlug}"`);
2670
- }
2671
- catch (error) {
2672
- console.error(`Failed to resolve project slug: ${error instanceof Error ? error.message : String(error)}`);
2673
- // Don't exit - project can be specified per tool call
2674
- }
2675
- }
2676
- else {
2677
- // No project slug configured - try to auto-detect
2678
- try {
2679
- const teams = await this.client.listProjects();
2680
- if (teams.length === 1) {
2681
- // Auto-select the only project
2682
- this.client.setProjectSlug(teams[0].slug);
2683
- console.error(`Auto-selected project: ${teams[0].name} (${teams[0].slug})`);
2684
- }
2685
- else if (teams.length === 0) {
2686
- console.error('Warning: No projects found for this API key');
2763
+ // Resolve project for project-capable keys
2764
+ if (this.capabilities.includes('project') || this.capabilities.length === 0) {
2765
+ if (config.projectSlug) {
2766
+ // Explicit project slug configured - use it
2767
+ try {
2768
+ await this.client.resolveProjectSlug(config.projectSlug);
2769
+ console.error(`Resolved project: "${config.projectSlug}"`);
2687
2770
  }
2688
- else {
2689
- console.error(`Multiple projects available (${teams.length}). Use list_projects tool to see them, then specify project_slug in tool calls.`);
2771
+ catch (error) {
2772
+ console.error(`Failed to resolve project slug: ${error instanceof Error ? error.message : String(error)}`);
2773
+ // Don't exit - project can be specified per tool call
2690
2774
  }
2691
2775
  }
2692
- catch (error) {
2693
- console.error(`Warning: Could not fetch projects: ${error instanceof Error ? error.message : String(error)}`);
2694
- // Don't exit - let the user use list_projects tool manually
2776
+ else {
2777
+ // No project slug configured - try to auto-detect
2778
+ try {
2779
+ const teams = await this.client.listProjects();
2780
+ if (teams.length === 1) {
2781
+ // Auto-select the only project
2782
+ this.client.setProjectSlug(teams[0].slug);
2783
+ console.error(`Auto-selected project: ${teams[0].name} (${teams[0].slug})`);
2784
+ }
2785
+ else if (teams.length === 0) {
2786
+ console.error('Warning: No projects found for this API key');
2787
+ }
2788
+ else {
2789
+ console.error(`Multiple projects available (${teams.length}). Use list_projects tool to see them, then specify project_slug in tool calls.`);
2790
+ }
2791
+ }
2792
+ catch (error) {
2793
+ console.error(`Warning: Could not fetch projects: ${error instanceof Error ? error.message : String(error)}`);
2794
+ // Don't exit - let the user use list_projects tool manually
2795
+ }
2695
2796
  }
2696
2797
  }
2697
2798
  // Check for updates (non-blocking)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wolfpack-mcp",
3
- "version": "1.0.47",
3
+ "version": "1.0.48",
4
4
  "description": "MCP server for Wolfpack AI-enhanced software delivery tools",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",