wolfpack-mcp 1.0.84 → 1.0.86

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/client.js CHANGED
@@ -891,6 +891,22 @@ export class WolfpackClient {
891
891
  const { teamSlug, ...body } = data;
892
892
  return this.api.post(this.withTeamSlug(`/procedures/${procedureId}/start`, teamSlug), body);
893
893
  }
894
+ async listCases(options) {
895
+ const params = new URLSearchParams();
896
+ if (options?.teamSlug)
897
+ params.append('teamSlug', options.teamSlug);
898
+ if (options?.procedureId)
899
+ params.append('procedureId', options.procedureId);
900
+ if (options?.status)
901
+ params.append('status', options.status);
902
+ return this.fetchAllPages('/cases', params);
903
+ }
904
+ async upgradeCase(caseId, teamSlug, resumeNodeId) {
905
+ return this.api.post(this.withTeamSlug(`/cases/${caseId}/upgrade`, teamSlug), resumeNodeId ? { resumeNodeId } : {});
906
+ }
907
+ async upgradeProcedureCases(procedureId, teamSlug) {
908
+ return this.api.post(this.withTeamSlug(`/procedures/${procedureId}/upgrade-cases`, teamSlug), {});
909
+ }
894
910
  // ─── Agent Self-Introspection ──────────────────────────────────────────────
895
911
  async getSelf() {
896
912
  return this.api.get('/self');
package/dist/index.js CHANGED
@@ -13,6 +13,7 @@ import { AGENT_BUILDER_TOOLS, handleAgentBuilderTool } from './agentBuilderTools
13
13
  import { PROCEDURE_TOOLS, handleProcedureTool } from './procedureTools.js';
14
14
  import { AGENT_SELF_TOOLS, AGENT_MEMORY_TOOLS, handleAgentSelfTool } from './agentSelfTools.js';
15
15
  import { resolveRadarItemId } from './resolveRadarItemId.js';
16
+ import { SERVER_INSTRUCTIONS } from './serverInstructions.js';
16
17
  import { fetch as proxyFetch } from './proxyFetch.js';
17
18
  // Get current package version
18
19
  const require = createRequire(import.meta.url);
@@ -717,6 +718,7 @@ class WolfpackMCPServer {
717
718
  tools: { listChanged: true },
718
719
  logging: {},
719
720
  },
721
+ instructions: SERVER_INSTRUCTIONS,
720
722
  });
721
723
  this.setupHandlers();
722
724
  }
@@ -744,7 +746,7 @@ class WolfpackMCPServer {
744
746
  'Other document types REQUIRE a prefix and are never bare numbers: ' +
745
747
  '#i1 or #issue-1 for issues, #r1 or #roadmap-1 for roadmap/radar, ' +
746
748
  '#j1 or #journal-1 for journal, #c1 or #case-1 for cases, #p1 or #proc-1 for procedures. ' +
747
- 'Wiki pages are referenced by path (e.g. /docs/setup). There is no ambiguity: bare #N = work item.',
749
+ 'Wiki pages are referenced by path (e.g. /docs/setup). There is no ambiguity: bare #N = work item, and never a GitHub issue or pull request — read a number as a GitHub reference only when the user says GitHub, PR, or gh.',
748
750
  inputSchema: {
749
751
  type: 'object',
750
752
  properties: {},
@@ -861,7 +863,7 @@ class WolfpackMCPServer {
861
863
  },
862
864
  {
863
865
  name: 'get_work_item',
864
- description: 'Get a specific work item/task by reference number. This is the DEFAULT lookup when a user says "#1" or "work on #42" — bare #N always means a work item. ' +
866
+ description: 'Get a specific work item/task by reference number. This is the DEFAULT lookup when a user says "#1" or "work on #42" — bare #N always means a work item, never a GitHub issue: use this tool, not `gh issue view`. ' +
865
867
  'Returns full details including description (markdown notes). ' +
866
868
  'Call this before updating to see current content. ' +
867
869
  'WORKFLOW: When asked to work on an item, check its status and follow the required state transitions ' +
@@ -195,6 +195,79 @@ export const PROCEDURE_TOOLS = [
195
195
  required: ['procedure_id'],
196
196
  },
197
197
  },
198
+ {
199
+ name: 'list_cases',
200
+ description: 'List cases, newest first, optionally filtered to one procedure and/or one status ' +
201
+ '(running, paused, completed, failed, cancelled). Each case shows the procedure version it ' +
202
+ 'runs and `behind: true` when the procedure has since been edited — a running case that is ' +
203
+ 'behind can be moved on with upgrade_case.',
204
+ inputSchema: {
205
+ type: 'object',
206
+ properties: {
207
+ procedure_id: {
208
+ type: 'string',
209
+ description: 'Only cases of this procedure (UUID or refId number)',
210
+ },
211
+ status: {
212
+ type: 'string',
213
+ description: 'Only cases in this status, e.g. "running"',
214
+ },
215
+ project_slug: {
216
+ type: 'string',
217
+ description: 'Project slug (required when using a refId, or with access to several projects)',
218
+ },
219
+ },
220
+ },
221
+ },
222
+ {
223
+ name: 'upgrade_case',
224
+ description: 'Move a running (or paused) case onto the latest version of its procedure. The case resumes ' +
225
+ 'at the node it is waiting at, keeping its variables and earlier activity outputs; nothing ' +
226
+ 'before that node runs again. Refused — with the reason — when the case is not running, is ' +
227
+ 'already on the current version, or its current node id is not in the new version — then pass ' +
228
+ 'resume_node_id to choose the node of the new version it should continue from (see ' +
229
+ 'get_procedure for the node ids), or cancel the case and start the procedure again from the ' +
230
+ 'work item.',
231
+ inputSchema: {
232
+ type: 'object',
233
+ properties: {
234
+ case_id: {
235
+ type: 'string',
236
+ description: 'Case UUID or refId number',
237
+ },
238
+ resume_node_id: {
239
+ type: 'string',
240
+ description: 'Node id of the new version to resume at. Defaults to the node the case is waiting at; ' +
241
+ 'required when that node no longer exists in the new version.',
242
+ },
243
+ project_slug: {
244
+ type: 'string',
245
+ description: 'Project slug (required when using refId)',
246
+ },
247
+ },
248
+ required: ['case_id'],
249
+ },
250
+ },
251
+ {
252
+ name: 'upgrade_procedure_cases',
253
+ description: 'Upgrade every running case of a procedure that is still on an older version onto the ' +
254
+ 'current one (see upgrade_case). Cases are upgraded one at a time; the result lists the cases ' +
255
+ 'upgraded and, for each case skipped, why.',
256
+ inputSchema: {
257
+ type: 'object',
258
+ properties: {
259
+ procedure_id: {
260
+ type: 'string',
261
+ description: 'Procedure UUID or refId number',
262
+ },
263
+ project_slug: {
264
+ type: 'string',
265
+ description: 'Project slug (required when using refId)',
266
+ },
267
+ },
268
+ required: ['procedure_id'],
269
+ },
270
+ },
198
271
  ];
199
272
  export async function handleProcedureTool(name, args, client) {
200
273
  const text = (t) => JSON.stringify(t, null, 2);
@@ -315,6 +388,55 @@ export async function handleProcedureTool(name, args, client) {
315
388
  ],
316
389
  };
317
390
  }
391
+ case 'list_cases': {
392
+ const parsed = z
393
+ .object({
394
+ procedure_id: z.string().optional(),
395
+ status: z.string().optional(),
396
+ project_slug: z.string().optional(),
397
+ })
398
+ .parse(args);
399
+ const result = await client.listCases({
400
+ procedureId: parsed.procedure_id,
401
+ status: parsed.status,
402
+ teamSlug: parsed.project_slug,
403
+ });
404
+ return { content: [{ type: 'text', text: text(result) }] };
405
+ }
406
+ case 'upgrade_case': {
407
+ const parsed = z
408
+ .object({
409
+ case_id: z.string(),
410
+ resume_node_id: z.string().optional(),
411
+ project_slug: z.string().optional(),
412
+ })
413
+ .parse(args);
414
+ const result = await client.upgradeCase(parsed.case_id, parsed.project_slug, parsed.resume_node_id);
415
+ return {
416
+ content: [
417
+ {
418
+ type: 'text',
419
+ text: `Upgraded case #${result.caseRefId} from v${result.fromVersion} to v${result.toVersion}; ` +
420
+ `it resumes at "${result.resumeNodeLabel}" (${result.resumeNodeId})\n\n${text(result)}`,
421
+ },
422
+ ],
423
+ };
424
+ }
425
+ case 'upgrade_procedure_cases': {
426
+ const parsed = z
427
+ .object({ procedure_id: z.string(), project_slug: z.string().optional() })
428
+ .parse(args);
429
+ const result = await client.upgradeProcedureCases(parsed.procedure_id, parsed.project_slug);
430
+ return {
431
+ content: [
432
+ {
433
+ type: 'text',
434
+ text: `Upgraded ${result.upgraded.length} case(s), skipped ${result.skipped.length}\n\n` +
435
+ text(result),
436
+ },
437
+ ],
438
+ };
439
+ }
318
440
  default:
319
441
  return {
320
442
  content: [{ type: 'text', text: `Unknown procedure tool: ${name}` }],
@@ -0,0 +1,42 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
3
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
4
+ import { fileURLToPath } from 'url';
5
+ import { SERVER_INSTRUCTIONS } from './serverInstructions.js';
6
+ const entryPoint = fileURLToPath(new URL('./index.ts', import.meta.url));
7
+ // The constant existing is not the fix — a client only sees it if the server
8
+ // hands it back in the `initialize` result (#1373). index.ts starts the server
9
+ // at import, so this drives the real thing over stdio instead.
10
+ //
11
+ // The handshake completes before any tool call, so nothing here needs a live
12
+ // backend: the key only has to look like one for validateConfig to let the
13
+ // process start, and the API URL is pointed at a closed port so the server's
14
+ // background capabilities fetch fails immediately rather than calling out to
15
+ // whatever WOLFPACK_API_URL the developer's own environment is set to.
16
+ const SERVER_ENV = {
17
+ ...process.env,
18
+ WOLFPACK_API_KEY: 'wfp_sk_test',
19
+ WOLFPACK_API_URL: 'http://127.0.0.1:1',
20
+ WOLFPACK_PROJECT_SLUG: '',
21
+ WOLFPACK_TEAM_SLUG: '',
22
+ WOLFPACK_ORG_SLUG: '',
23
+ };
24
+ describe('the stdio server initialize', () => {
25
+ it('advertises the reference-convention instructions to the client', async () => {
26
+ const client = new Client({ name: 'test', version: '1.0.0' });
27
+ const transport = new StdioClientTransport({
28
+ // tsx directly, not via npx: the transport kills the process it spawned,
29
+ // and an npx wrapper would leave the server orphaned behind it.
30
+ command: process.execPath,
31
+ args: ['--import', 'tsx', entryPoint],
32
+ env: SERVER_ENV,
33
+ });
34
+ await client.connect(transport);
35
+ try {
36
+ expect(client.getInstructions()).toBe(SERVER_INSTRUCTIONS);
37
+ }
38
+ finally {
39
+ await client.close();
40
+ }
41
+ }, 60_000);
42
+ });
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Server instructions, returned in the MCP `initialize` result.
3
+ *
4
+ * Clients put this in the system prompt, which makes it the only text that
5
+ * reaches the model *before* it decides what to do. Tool descriptions do not:
6
+ * they are read once the model is already choosing between tools, and "work on
7
+ * #123" in a git repository sends it to `gh issue view` instead (#1373). The
8
+ * reference convention therefore lives here, and names the GitHub reading it is
9
+ * over-ruling rather than only the Wolfpack one.
10
+ *
11
+ * Mirrored in `packages/backend/src/mcp/serverInstructions.ts` for the remote
12
+ * server — the two servers have no shared package, and already keep their tool
13
+ * descriptions in step by hand.
14
+ */
15
+ export const SERVER_INSTRUCTIONS = `Wolfpack is this project's tracker. Its references override the usual GitHub reading of "#N".
16
+
17
+ - A bare "#123" — "work on #123", "look at #42" — is ALWAYS a Wolfpack work item. Call get_work_item. Do not run \`gh issue view\`, and do not search GitHub for it.
18
+ - Read a number as a GitHub issue or pull request only when the user says GitHub, PR, or gh — "the GitHub issue #123", "PR #123".
19
+ - Every other Wolfpack type needs its prefix and is never a bare number: #i123 issues, #r123 roadmap/initiatives, #j123 journal entries, #c123 cases, #p123 procedures. Wiki pages are addressed by path, e.g. /docs/setup.
20
+ - On its own, "issue" means a Wolfpack issue (#i123). A GitHub issue is only ever called a GitHub issue.`;
@@ -0,0 +1,31 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { SERVER_INSTRUCTIONS } from './serverInstructions.js';
3
+ describe('SERVER_INSTRUCTIONS', () => {
4
+ it('claims a bare #N for work items before any tool is chosen (#1373)', () => {
5
+ // The whole point of the instructions field: a client puts it in the system
6
+ // prompt, so it is read before the model decides between get_work_item and
7
+ // reaching for the shell.
8
+ expect(SERVER_INSTRUCTIONS).toContain('#123');
9
+ expect(SERVER_INSTRUCTIONS).toContain('work item');
10
+ expect(SERVER_INSTRUCTIONS).toContain('get_work_item');
11
+ });
12
+ it('names the GitHub reading it is over-ruling', () => {
13
+ // "bare #N is not a Wolfpack issue" was already on the tools and did not
14
+ // help: the rival reading is a GitHub one, so it has to be named.
15
+ expect(SERVER_INSTRUCTIONS).toContain('gh issue');
16
+ expect(SERVER_INSTRUCTIONS).toContain('GitHub');
17
+ });
18
+ it('says when a number IS a GitHub reference, so the rule can be applied', () => {
19
+ // A rule with no exception gets ignored the first time the user genuinely
20
+ // means a pull request.
21
+ expect(SERVER_INSTRUCTIONS).toMatch(/only when .*GitHub/i);
22
+ });
23
+ it('covers every prefixed reference type so nothing else is guessed at', () => {
24
+ for (const prefix of ['#i', '#r', '#j', '#c', '#p']) {
25
+ expect(SERVER_INSTRUCTIONS).toContain(`${prefix}123`);
26
+ }
27
+ });
28
+ it('stays short enough to sit in a system prompt', () => {
29
+ expect(SERVER_INSTRUCTIONS.length).toBeLessThan(1200);
30
+ });
31
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wolfpack-mcp",
3
- "version": "1.0.84",
3
+ "version": "1.0.86",
4
4
  "description": "MCP server for Wolfpack AI-enhanced software delivery tools",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",