wolfpack-mcp 1.0.88 → 1.0.89

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.
@@ -10,6 +10,26 @@ const ORG_SLUG_PROP = {
10
10
  description: 'Organisation slug. Required when you belong to multiple organisations. Use list_organisations to find slugs.',
11
11
  },
12
12
  };
13
+ /**
14
+ * The per-task model pin, shared by create_agent_task and update_agent_task so
15
+ * both describe it the same way. Null on update clears the pin and the task
16
+ * falls back to the agent's own model; omitting a field leaves it unchanged.
17
+ */
18
+ const TASK_MODEL_PROPS = {
19
+ llm_provider: {
20
+ type: 'string',
21
+ description: 'Pin this task to an LLM provider ID (e.g. "anthropic") instead of the agent\'s own model. ' +
22
+ 'Use list_llm_models for IDs. Set to null to unpin.',
23
+ },
24
+ llm_model: {
25
+ type: 'string',
26
+ description: 'Pin this task to a model ID (e.g. "claude-sonnet-4-6"). Set to null for family auto-select.',
27
+ },
28
+ llm_family: {
29
+ type: 'string',
30
+ description: 'Pin this task to a model family (e.g. "sonnet"); the task auto-upgrades to the latest in the family.',
31
+ },
32
+ };
13
33
  export const AGENT_BUILDER_TOOLS = [
14
34
  // ─── Group 0: Organisations ─────────────────────────────────────────────
15
35
  {
@@ -338,6 +358,7 @@ export const AGENT_BUILDER_TOOLS = [
338
358
  type: 'number',
339
359
  description: 'Order within scheduled batch (lower = first)',
340
360
  },
361
+ ...TASK_MODEL_PROPS,
341
362
  disabled_tools: {
342
363
  type: 'array',
343
364
  items: { type: 'string' },
@@ -363,7 +384,7 @@ export const AGENT_BUILDER_TOOLS = [
363
384
  },
364
385
  {
365
386
  name: 'update_agent_task',
366
- description: "Update a task's prompt, schedule settings, or disabled tools/MCP servers.",
387
+ description: "Update a task's prompt, schedule settings, model pin, or disabled tools/MCP servers.",
367
388
  inputSchema: {
368
389
  type: 'object',
369
390
  properties: {
@@ -374,6 +395,7 @@ export const AGENT_BUILDER_TOOLS = [
374
395
  scheduled_enabled: { type: 'boolean', description: 'Include in scheduled runs' },
375
396
  sort_order: { type: 'number', description: 'Order within scheduled batch' },
376
397
  is_active: { type: 'boolean', description: 'Whether this task is active' },
398
+ ...TASK_MODEL_PROPS,
377
399
  disabled_tools: {
378
400
  type: 'array',
379
401
  items: { type: 'string' },
@@ -747,7 +769,8 @@ export const AGENT_BUILDER_TOOLS = [
747
769
  {
748
770
  name: 'list_llm_models',
749
771
  description: 'List LLM providers and models available to the organisation. ' +
750
- 'Use provider and model IDs when calling create_agent or update_agent.',
772
+ 'Use provider, model and family IDs when calling create_agent, update_agent, ' +
773
+ 'create_agent_task or update_agent_task.',
751
774
  inputSchema: { type: 'object', properties: { ...ORG_SLUG_PROP } },
752
775
  },
753
776
  ];
@@ -1014,6 +1037,9 @@ export async function handleAgentBuilderTool(name, args, client) {
1014
1037
  prompt: z.string(),
1015
1038
  scheduled_enabled: z.boolean().optional(),
1016
1039
  sort_order: z.number().optional(),
1040
+ llm_provider: z.string().nullable().optional(),
1041
+ llm_model: z.string().nullable().optional(),
1042
+ llm_family: z.string().nullable().optional(),
1017
1043
  disabled_tools: z.array(z.string()).optional(),
1018
1044
  disabled_mcp_servers: z.array(z.string()).optional(),
1019
1045
  clone_repository: z.boolean().optional(),
@@ -1025,6 +1051,9 @@ export async function handleAgentBuilderTool(name, args, client) {
1025
1051
  prompt: parsed.prompt,
1026
1052
  scheduledEnabled: parsed.scheduled_enabled,
1027
1053
  sortOrder: parsed.sort_order,
1054
+ llmProvider: parsed.llm_provider,
1055
+ llmModel: parsed.llm_model,
1056
+ llmFamily: parsed.llm_family,
1028
1057
  disabledTools: parsed.disabled_tools,
1029
1058
  disabledMcpServers: parsed.disabled_mcp_servers,
1030
1059
  cloneRepository: parsed.clone_repository,
@@ -1041,6 +1070,9 @@ export async function handleAgentBuilderTool(name, args, client) {
1041
1070
  scheduled_enabled: z.boolean().optional(),
1042
1071
  sort_order: z.number().optional(),
1043
1072
  is_active: z.boolean().optional(),
1073
+ llm_provider: z.string().nullable().optional(),
1074
+ llm_model: z.string().nullable().optional(),
1075
+ llm_family: z.string().nullable().optional(),
1044
1076
  disabled_tools: z.array(z.string()).nullable().optional(),
1045
1077
  disabled_mcp_servers: z.array(z.string()).nullable().optional(),
1046
1078
  clone_repository: z.boolean().optional(),
@@ -1058,6 +1090,12 @@ export async function handleAgentBuilderTool(name, args, client) {
1058
1090
  fields.sortOrder = parsed.sort_order;
1059
1091
  if (parsed.is_active !== undefined)
1060
1092
  fields.isActive = parsed.is_active;
1093
+ if (parsed.llm_provider !== undefined)
1094
+ fields.llmProvider = parsed.llm_provider;
1095
+ if (parsed.llm_model !== undefined)
1096
+ fields.llmModel = parsed.llm_model;
1097
+ if (parsed.llm_family !== undefined)
1098
+ fields.llmFamily = parsed.llm_family;
1061
1099
  if (parsed.disabled_tools !== undefined)
1062
1100
  fields.disabledTools = parsed.disabled_tools;
1063
1101
  if (parsed.disabled_mcp_servers !== undefined)
@@ -0,0 +1,76 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { AGENT_BUILDER_TOOLS, handleAgentBuilderTool } from './agentBuilderTools.js';
3
+ const toolNamed = (name) => {
4
+ const tool = AGENT_BUILDER_TOOLS.find((t) => t.name === name);
5
+ if (!tool)
6
+ throw new Error(`No tool named ${name}`);
7
+ return tool;
8
+ };
9
+ const MODEL_FIELDS = ['llm_provider', 'llm_model', 'llm_family'];
10
+ /** A client that records the one call each test makes and returns a bare task. */
11
+ function fakeClient() {
12
+ const createAgentTask = vi.fn().mockResolvedValue({ id: 'task-1', name: 'Analyse' });
13
+ const updateAgentTask = vi.fn().mockResolvedValue({ id: 'task-1', name: 'Analyse' });
14
+ return {
15
+ client: { createAgentTask, updateAgentTask },
16
+ createAgentTask,
17
+ updateAgentTask,
18
+ };
19
+ }
20
+ describe('per-task model pin over MCP (#2034)', () => {
21
+ it.each(['create_agent_task', 'update_agent_task'])('%s advertises the model fields', (name) => {
22
+ const properties = toolNamed(name).inputSchema.properties;
23
+ expect(Object.keys(properties)).toEqual(expect.arrayContaining(MODEL_FIELDS));
24
+ });
25
+ it('sends the model pin when creating a task', async () => {
26
+ const { client, createAgentTask } = fakeClient();
27
+ await handleAgentBuilderTool('create_agent_task', {
28
+ agent_id: 'agent-1',
29
+ name: 'Analyse',
30
+ prompt: 'Analyse the transcript.',
31
+ llm_provider: 'anthropic',
32
+ llm_model: 'claude-sonnet-4-6',
33
+ llm_family: 'sonnet',
34
+ }, client);
35
+ expect(createAgentTask.mock.calls[0][0]).toBe('agent-1');
36
+ expect(createAgentTask.mock.calls[0][1]).toMatchObject({
37
+ llmProvider: 'anthropic',
38
+ llmModel: 'claude-sonnet-4-6',
39
+ llmFamily: 'sonnet',
40
+ });
41
+ });
42
+ it('sends the model pin when updating a task', async () => {
43
+ const { client, updateAgentTask } = fakeClient();
44
+ await handleAgentBuilderTool('update_agent_task', {
45
+ agent_id: 'agent-1',
46
+ task_id: 'task-1',
47
+ llm_provider: 'anthropic',
48
+ llm_model: 'claude-opus-4-1',
49
+ }, client);
50
+ expect(updateAgentTask.mock.calls[0].slice(0, 3)).toEqual([
51
+ 'agent-1',
52
+ 'task-1',
53
+ { llmProvider: 'anthropic', llmModel: 'claude-opus-4-1' },
54
+ ]);
55
+ });
56
+ it('unpins a task when the model fields are sent as null', async () => {
57
+ const { client, updateAgentTask } = fakeClient();
58
+ await handleAgentBuilderTool('update_agent_task', {
59
+ agent_id: 'agent-1',
60
+ task_id: 'task-1',
61
+ llm_provider: null,
62
+ llm_model: null,
63
+ llm_family: null,
64
+ }, client);
65
+ expect(updateAgentTask.mock.calls[0][2]).toEqual({
66
+ llmProvider: null,
67
+ llmModel: null,
68
+ llmFamily: null,
69
+ });
70
+ });
71
+ it('leaves an existing pin alone when updating something else', async () => {
72
+ const { client, updateAgentTask } = fakeClient();
73
+ await handleAgentBuilderTool('update_agent_task', { agent_id: 'agent-1', task_id: 'task-1', prompt: 'Analyse the transcript twice.' }, client);
74
+ expect(updateAgentTask.mock.calls[0][2]).toEqual({ prompt: 'Analyse the transcript twice.' });
75
+ });
76
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wolfpack-mcp",
3
- "version": "1.0.88",
3
+ "version": "1.0.89",
4
4
  "description": "MCP server for Wolfpack AI-enhanced software delivery tools",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",