wolfpack-mcp 1.0.94 → 1.0.95
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/README.md +4 -4
- package/dist/agentBuilderTools.js +57 -10
- package/dist/agentBuilderTools.test.js +48 -0
- package/dist/client.js +8 -4
- package/dist/index.js +17 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -357,11 +357,11 @@ Get a specific radar item.
|
|
|
357
357
|
|
|
358
358
|
### Tags
|
|
359
359
|
|
|
360
|
-
Tags label work items, issues, wiki pages and journal entries, and are filed under nestable groups. Project admins define them; a key with `mcp:tags:manage` lets an admin's agent keep them in step with an external system (for example, one tag per CRM customer account keyed on `external_id`). There is no delete tool — removing a tag is done in the browser.
|
|
360
|
+
Tags label work items, issues, wiki pages and journal entries, and are filed under nestable groups. Project admins define them; a key with `mcp:tags:manage` lets an admin's agent keep them in step with an external system (for example, one tag per CRM customer account keyed on `external_id`). There is no delete tool — archive instead (`update_tag` with `archived: true`); removing a tag outright is done in the browser.
|
|
361
361
|
|
|
362
362
|
#### `list_tags`
|
|
363
363
|
|
|
364
|
-
List a project's tags with id, name, colour, `externalId
|
|
364
|
+
List a project's tags with id, name, colour, `externalId`, the group each is filed under and whether it is `archived`. Any key that can read a tagged module may list them.
|
|
365
365
|
|
|
366
366
|
- `project_slug` (optional): Project slug
|
|
367
367
|
|
|
@@ -375,10 +375,10 @@ List a project's tags with id, name, colour, `externalId` and the group each is
|
|
|
375
375
|
|
|
376
376
|
#### `update_tag`
|
|
377
377
|
|
|
378
|
-
Only the fields given change; pass `null` for `colour`, `group` or `external_id` to clear it.
|
|
378
|
+
Only the fields given change; pass `null` for `colour`, `group` or `external_id` to clear it. `archived: true` is the soft delete: the tag stays on the items that carry it and keeps its page, but no picker offers it and it cannot be applied; `archived: false` restores it. A sync uses this for accounts the source system has dropped.
|
|
379
379
|
|
|
380
380
|
- `tag` (required): Tag id or current name
|
|
381
|
-
- `name`, `colour`, `group`, `external_id` (optional)
|
|
381
|
+
- `name`, `colour`, `group`, `external_id`, `archived` (optional)
|
|
382
382
|
- `project_slug` (optional): Project slug
|
|
383
383
|
|
|
384
384
|
#### `list_tag_groups`
|
|
@@ -40,12 +40,25 @@ export const AGENT_BUILDER_TOOLS = [
|
|
|
40
40
|
// ─── Group 1: Agent CRUD ─────────────────────────────────────────────────
|
|
41
41
|
{
|
|
42
42
|
name: 'list_agents',
|
|
43
|
-
description: 'List
|
|
44
|
-
|
|
43
|
+
description: 'List agents. Returns name, status, template, and assigned projects. ' +
|
|
44
|
+
'scope "org" (the default) lists the organisation\'s agents; scope "personal" lists your own ' +
|
|
45
|
+
'My Agents from My Space, which belong to no organisation. Call it twice to see both.',
|
|
46
|
+
inputSchema: {
|
|
47
|
+
type: 'object',
|
|
48
|
+
properties: {
|
|
49
|
+
scope: {
|
|
50
|
+
type: 'string',
|
|
51
|
+
enum: ['org', 'personal'],
|
|
52
|
+
description: "Which agents to list: the organisation's (default) or your own personal ones.",
|
|
53
|
+
},
|
|
54
|
+
...ORG_SLUG_PROP,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
45
57
|
},
|
|
46
58
|
{
|
|
47
59
|
name: 'get_agent',
|
|
48
|
-
description: 'Get full details for an agent including config, instructions, LLM model, and linked skills.'
|
|
60
|
+
description: 'Get full details for an agent including config, instructions, LLM model, and linked skills. ' +
|
|
61
|
+
'Takes an organisation agent or one of your own personal agents.',
|
|
49
62
|
inputSchema: {
|
|
50
63
|
type: 'object',
|
|
51
64
|
properties: {
|
|
@@ -57,7 +70,10 @@ export const AGENT_BUILDER_TOOLS = [
|
|
|
57
70
|
},
|
|
58
71
|
{
|
|
59
72
|
name: 'create_agent',
|
|
60
|
-
description: 'Create a new agent from a container image. Use list_container_images to see available images.'
|
|
73
|
+
description: 'Create a new agent from a container image. Use list_container_images to see available images. ' +
|
|
74
|
+
'With personal=true, creates one of your own My Agents instead of an organisation agent: ' +
|
|
75
|
+
'it needs a handle, and only an image opened to personal use (list_container_images with ' +
|
|
76
|
+
'personal=true) qualifies.',
|
|
61
77
|
inputSchema: {
|
|
62
78
|
type: 'object',
|
|
63
79
|
properties: {
|
|
@@ -67,6 +83,14 @@ export const AGENT_BUILDER_TOOLS = [
|
|
|
67
83
|
},
|
|
68
84
|
name: { type: 'string', description: 'Display name for the agent' },
|
|
69
85
|
config: { type: 'object', description: 'Optional agent-specific configuration' },
|
|
86
|
+
personal: {
|
|
87
|
+
type: 'boolean',
|
|
88
|
+
description: 'Create one of your own personal agents (My Space > My Agents) rather than an organisation agent.',
|
|
89
|
+
},
|
|
90
|
+
handle: {
|
|
91
|
+
type: 'string',
|
|
92
|
+
description: 'Handle for a personal agent — it becomes "@<your username>/<handle>". Required with personal=true, ignored otherwise.',
|
|
93
|
+
},
|
|
70
94
|
...ORG_SLUG_PROP,
|
|
71
95
|
},
|
|
72
96
|
required: ['container_image_id', 'name'],
|
|
@@ -798,8 +822,19 @@ export const AGENT_BUILDER_TOOLS = [
|
|
|
798
822
|
// ─── Group 7: Discovery ───────────────────────────────────────────────────
|
|
799
823
|
{
|
|
800
824
|
name: 'list_container_images',
|
|
801
|
-
description: 'List agent-capable container images assigned to the organisation. Use image IDs when calling create_agent.'
|
|
802
|
-
|
|
825
|
+
description: 'List agent-capable container images assigned to the organisation. Use image IDs when calling create_agent. ' +
|
|
826
|
+
'With personal=true, lists the images a personal agent may run instead — the bring-your-own-token ' +
|
|
827
|
+
'images opened to personal use, which are not an organisation catalogue.',
|
|
828
|
+
inputSchema: {
|
|
829
|
+
type: 'object',
|
|
830
|
+
properties: {
|
|
831
|
+
personal: {
|
|
832
|
+
type: 'boolean',
|
|
833
|
+
description: 'List the images available to personal agents rather than the organisation catalogue.',
|
|
834
|
+
},
|
|
835
|
+
...ORG_SLUG_PROP,
|
|
836
|
+
},
|
|
837
|
+
},
|
|
803
838
|
},
|
|
804
839
|
{
|
|
805
840
|
name: 'list_llm_models',
|
|
@@ -837,8 +872,10 @@ export async function handleAgentBuilderTool(name, args, client) {
|
|
|
837
872
|
}
|
|
838
873
|
// ─── Agent CRUD ────────────────────────────────────────────────────────
|
|
839
874
|
case 'list_agents': {
|
|
840
|
-
const parsed = z
|
|
841
|
-
|
|
875
|
+
const parsed = z
|
|
876
|
+
.object({ scope: z.enum(['org', 'personal']).optional(), org_slug: orgSlugField })
|
|
877
|
+
.parse(args);
|
|
878
|
+
const agents = await client.listAgents(resolveOrg(parsed), parsed.scope);
|
|
842
879
|
return { content: [{ type: 'text', text: text(agents) }] };
|
|
843
880
|
}
|
|
844
881
|
case 'get_agent': {
|
|
@@ -854,13 +891,21 @@ export async function handleAgentBuilderTool(name, args, client) {
|
|
|
854
891
|
container_image_id: z.string(),
|
|
855
892
|
name: z.string(),
|
|
856
893
|
config: z.record(z.unknown()).optional(),
|
|
894
|
+
personal: z.boolean().optional(),
|
|
895
|
+
handle: z.string().optional(),
|
|
857
896
|
org_slug: orgSlugField,
|
|
897
|
+
})
|
|
898
|
+
.refine((v) => !v.personal || !!v.handle, {
|
|
899
|
+
message: 'handle is required with personal=true',
|
|
900
|
+
path: ['handle'],
|
|
858
901
|
})
|
|
859
902
|
.parse(args);
|
|
860
903
|
const agent = await client.createAgent({
|
|
861
904
|
containerImageId: parsed.container_image_id,
|
|
862
905
|
name: parsed.name,
|
|
863
906
|
config: parsed.config,
|
|
907
|
+
personal: parsed.personal,
|
|
908
|
+
handleName: parsed.handle,
|
|
864
909
|
}, resolveOrg(parsed));
|
|
865
910
|
return {
|
|
866
911
|
content: [
|
|
@@ -1427,8 +1472,10 @@ export async function handleAgentBuilderTool(name, args, client) {
|
|
|
1427
1472
|
}
|
|
1428
1473
|
// ─── Discovery ────────────────────────────────────────────────────────────
|
|
1429
1474
|
case 'list_container_images': {
|
|
1430
|
-
const parsed = z
|
|
1431
|
-
|
|
1475
|
+
const parsed = z
|
|
1476
|
+
.object({ personal: z.boolean().optional(), org_slug: orgSlugField })
|
|
1477
|
+
.parse(args);
|
|
1478
|
+
const images = await client.listContainerImages(resolveOrg(parsed), parsed.personal);
|
|
1432
1479
|
return { content: [{ type: 'text', text: text(images) }] };
|
|
1433
1480
|
}
|
|
1434
1481
|
case 'list_llm_models': {
|
|
@@ -144,3 +144,51 @@ describe('schedule pre-flight over MCP (#2083)', () => {
|
|
|
144
144
|
expect(Object.keys(properties)).not.toContain('scheduling_enabled');
|
|
145
145
|
});
|
|
146
146
|
});
|
|
147
|
+
// #2273 — My Agents reached over MCP. There is no second tool set: a personal
|
|
148
|
+
// agent is addressed by the same tools and the same agent_id as an org one, so
|
|
149
|
+
// what these cover is the three tools that had to learn the difference.
|
|
150
|
+
describe('personal agents over MCP (#2273)', () => {
|
|
151
|
+
function fake(method, result) {
|
|
152
|
+
const fn = vi.fn().mockResolvedValue(result);
|
|
153
|
+
return { client: { [method]: fn }, fn };
|
|
154
|
+
}
|
|
155
|
+
it('list_agents offers both scopes and defaults to neither being sent', async () => {
|
|
156
|
+
const scope = toolNamed('list_agents').inputSchema.properties.scope;
|
|
157
|
+
expect(scope.enum).toEqual(['org', 'personal']);
|
|
158
|
+
const { client, fn } = fake('listAgents', []);
|
|
159
|
+
await handleAgentBuilderTool('list_agents', {}, client);
|
|
160
|
+
expect(fn.mock.calls[0][1]).toBeUndefined();
|
|
161
|
+
});
|
|
162
|
+
it('list_agents passes the personal scope through', async () => {
|
|
163
|
+
const { client, fn } = fake('listAgents', [{ id: 'agent-1' }]);
|
|
164
|
+
await handleAgentBuilderTool('list_agents', { scope: 'personal' }, client);
|
|
165
|
+
expect(fn.mock.calls[0][1]).toBe('personal');
|
|
166
|
+
});
|
|
167
|
+
it('create_agent sends the personal flag and the handle', async () => {
|
|
168
|
+
const { client, fn } = fake('createAgent', { userId: 'agent_1' });
|
|
169
|
+
await handleAgentBuilderTool('create_agent', {
|
|
170
|
+
container_image_id: 'image-1',
|
|
171
|
+
name: 'Raiven',
|
|
172
|
+
personal: true,
|
|
173
|
+
handle: 'raiven',
|
|
174
|
+
}, client);
|
|
175
|
+
expect(fn.mock.calls[0][0]).toMatchObject({ personal: true, handleName: 'raiven' });
|
|
176
|
+
});
|
|
177
|
+
// A personal agent's handle is required, so say so here rather than spending a
|
|
178
|
+
// round trip to learn it from the backend.
|
|
179
|
+
it('create_agent refuses personal without a handle', async () => {
|
|
180
|
+
const { client, fn } = fake('createAgent', { userId: 'agent_1' });
|
|
181
|
+
await expect(handleAgentBuilderTool('create_agent', { container_image_id: 'image-1', name: 'Raiven', personal: true }, client)).rejects.toThrow('handle is required with personal=true');
|
|
182
|
+
expect(fn).not.toHaveBeenCalled();
|
|
183
|
+
});
|
|
184
|
+
it('create_agent stays organisational when the flag is left off', async () => {
|
|
185
|
+
const { client, fn } = fake('createAgent', { userId: 'agent_1' });
|
|
186
|
+
await handleAgentBuilderTool('create_agent', { container_image_id: 'image-1', name: 'Scout' }, client);
|
|
187
|
+
expect(fn.mock.calls[0][0].personal).toBeUndefined();
|
|
188
|
+
});
|
|
189
|
+
it('list_container_images asks for the personal catalogue', async () => {
|
|
190
|
+
const { client, fn } = fake('listContainerImages', []);
|
|
191
|
+
await handleAgentBuilderTool('list_container_images', { personal: true }, client);
|
|
192
|
+
expect(fn.mock.calls[0][1]).toBe(true);
|
|
193
|
+
});
|
|
194
|
+
});
|
package/dist/client.js
CHANGED
|
@@ -673,8 +673,9 @@ export class WolfpackClient {
|
|
|
673
673
|
return response.organisations;
|
|
674
674
|
}
|
|
675
675
|
// ─── Agent Builder: Agent CRUD ─────────────────────────────────────────────
|
|
676
|
-
async listAgents(orgSlug) {
|
|
677
|
-
|
|
676
|
+
async listAgents(orgSlug, scope) {
|
|
677
|
+
const path = scope === 'personal' ? '/agents?scope=personal' : '/agents';
|
|
678
|
+
return this.api.get(this.withOrgSlug(path, orgSlug));
|
|
678
679
|
}
|
|
679
680
|
async getAgent(agentId, orgSlug) {
|
|
680
681
|
try {
|
|
@@ -866,8 +867,11 @@ export class WolfpackClient {
|
|
|
866
867
|
}
|
|
867
868
|
}
|
|
868
869
|
// ─── Agent Builder: Discovery ──────────────────────────────────────────────
|
|
869
|
-
async listContainerImages(orgSlug) {
|
|
870
|
-
|
|
870
|
+
async listContainerImages(orgSlug, personal) {
|
|
871
|
+
const path = personal
|
|
872
|
+
? '/agent-builder/container-images?personal=true'
|
|
873
|
+
: '/agent-builder/container-images';
|
|
874
|
+
return this.api.get(this.withOrgSlug(path, orgSlug));
|
|
871
875
|
}
|
|
872
876
|
async listLlmModels(orgSlug) {
|
|
873
877
|
return this.api.get(this.withOrgSlug('/agent-builder/llm-models', orgSlug));
|
package/dist/index.js
CHANGED
|
@@ -627,6 +627,10 @@ const UpdateTagSchema = z.object({
|
|
|
627
627
|
.optional()
|
|
628
628
|
.describe('Tag group name or id, or null to move the tag out of its group'),
|
|
629
629
|
external_id: z.string().nullable().optional().describe('External id, or null to clear'),
|
|
630
|
+
archived: z
|
|
631
|
+
.boolean()
|
|
632
|
+
.optional()
|
|
633
|
+
.describe('true archives the tag (soft delete), false restores it'),
|
|
630
634
|
});
|
|
631
635
|
const CreateTagGroupSchema = z.object({
|
|
632
636
|
project_slug: z
|
|
@@ -863,6 +867,9 @@ class WolfpackMCPServer {
|
|
|
863
867
|
'assigned_to_id you pass; the response says so when this applies. ' +
|
|
864
868
|
'AGENTS: when more than one item is claimable, the response also states the order to take ' +
|
|
865
869
|
'them in (bug fixes first, then higher priority, then oldest) and which to pull next. ' +
|
|
870
|
+
'AGENTS: blockedBy on each item names the prerequisites of it that have not finished. An item ' +
|
|
871
|
+
'with a non-empty blockedBy is NOT claimable however approved it is — take the work it names ' +
|
|
872
|
+
'first; the response says so too. ' +
|
|
866
873
|
'TERMINOLOGY: "board" and "kanban" are synonymous - both refer to the Kanban board of work items. ' +
|
|
867
874
|
'The board has columns: "new" (to do), "doing" (in progress), "review" (pending review), "ready" (code done, awaiting deployment), "blocked", "completed" (deployed). ' +
|
|
868
875
|
'The "backlog" or "pending" status represents items not yet on the board. ' +
|
|
@@ -1986,8 +1993,8 @@ class WolfpackMCPServer {
|
|
|
1986
1993
|
// Tag tools
|
|
1987
1994
|
{
|
|
1988
1995
|
name: 'list_tags',
|
|
1989
|
-
description: 'List all tags
|
|
1990
|
-
'Items are tagged by name via update_work_item / update_issue. ' +
|
|
1996
|
+
description: 'List all tags of a project: id, name, colour, external id, the group each is filed under, and whether it is archived. ' +
|
|
1997
|
+
'Items are tagged by name via update_work_item / update_issue; archived tags cannot be applied. ' +
|
|
1991
1998
|
'A sync from an external system (e.g. a CRM) keys on externalId.',
|
|
1992
1999
|
inputSchema: {
|
|
1993
2000
|
type: 'object',
|
|
@@ -2026,8 +2033,9 @@ class WolfpackMCPServer {
|
|
|
2026
2033
|
},
|
|
2027
2034
|
{
|
|
2028
2035
|
name: 'update_tag',
|
|
2029
|
-
description: 'Rename, recolour, regroup
|
|
2030
|
-
'Only the fields given change; pass null for colour, group or external_id to clear it.'
|
|
2036
|
+
description: 'Rename, recolour, regroup, re-key or archive a tag. Project admins only (mcp:tags:manage). ' +
|
|
2037
|
+
'Only the fields given change; pass null for colour, group or external_id to clear it. ' +
|
|
2038
|
+
'archived: true is the soft delete — the tag stays on the items that carry it but no picker offers it; archived: false restores it.',
|
|
2031
2039
|
inputSchema: {
|
|
2032
2040
|
type: 'object',
|
|
2033
2041
|
properties: {
|
|
@@ -2046,6 +2054,10 @@ class WolfpackMCPServer {
|
|
|
2046
2054
|
type: ['string', 'null'],
|
|
2047
2055
|
description: 'External id, or null to clear',
|
|
2048
2056
|
},
|
|
2057
|
+
archived: {
|
|
2058
|
+
type: 'boolean',
|
|
2059
|
+
description: 'true archives the tag (soft delete), false restores it',
|
|
2060
|
+
},
|
|
2049
2061
|
},
|
|
2050
2062
|
required: ['tag'],
|
|
2051
2063
|
},
|
|
@@ -3256,6 +3268,7 @@ class WolfpackMCPServer {
|
|
|
3256
3268
|
colour: parsed.colour,
|
|
3257
3269
|
group: parsed.group,
|
|
3258
3270
|
externalId: parsed.external_id,
|
|
3271
|
+
archived: parsed.archived,
|
|
3259
3272
|
}, parsed.project_slug || this.client.getProjectSlug() || undefined);
|
|
3260
3273
|
return {
|
|
3261
3274
|
content: [{ type: 'text', text: JSON.stringify(tag, null, 2) }],
|