wolfpack-mcp 1.0.103 → 1.0.104
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/brand.js +13 -0
- package/dist/config.js +3 -2
- package/dist/index.js +10 -6
- package/dist/serverInstructions.js +5 -4
- package/dist/toolCatalogue.js +28 -8
- package/dist/toolCatalogue.test.js +5 -2
- package/package.json +1 -1
package/dist/brand.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the product calls itself, and the domain it is served from (#2466).
|
|
3
|
+
*
|
|
4
|
+
* This package is published and installed on its own, away from the repository, so —
|
|
5
|
+
* unlike the Vite apps — it cannot read `brand.json`. It takes the values from the
|
|
6
|
+
* environment, with the fallbacks below pinned to `brand.json` by
|
|
7
|
+
* `scripts/check-product-brand.js`.
|
|
8
|
+
*
|
|
9
|
+
* `WOLFPACK_*` variables are this package's public configuration contract with the MCP
|
|
10
|
+
* clients that already set them, so they are deliberately not renamed here.
|
|
11
|
+
*/
|
|
12
|
+
export const productName = process.env.PRODUCT_NAME || 'Wolfpack';
|
|
13
|
+
export const productDomain = process.env.PRODUCT_DOMAIN || 'wolfpacks.work';
|
package/dist/config.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { productDomain, productName } from './brand.js';
|
|
1
2
|
export const config = {
|
|
2
|
-
apiUrl: process.env.WOLFPACK_API_URL ||
|
|
3
|
+
apiUrl: process.env.WOLFPACK_API_URL || `https://${productDomain}/api/mcp`,
|
|
3
4
|
apiKey: process.env.WOLFPACK_API_KEY,
|
|
4
5
|
projectSlug: process.env.WOLFPACK_PROJECT_SLUG || process.env.WOLFPACK_TEAM_SLUG,
|
|
5
6
|
orgSlug: process.env.WOLFPACK_ORG_SLUG,
|
|
@@ -8,7 +9,7 @@ export const config = {
|
|
|
8
9
|
export function validateConfig() {
|
|
9
10
|
if (!config.apiKey) {
|
|
10
11
|
console.error('Error: WOLFPACK_API_KEY environment variable is required');
|
|
11
|
-
console.error(
|
|
12
|
+
console.error(`Please set WOLFPACK_API_KEY to your API key from the ${productName} application`);
|
|
12
13
|
console.error('Example: WOLFPACK_API_KEY=wfp_sk_... (user) or wfp_ak_... (agent)');
|
|
13
14
|
process.exit(1);
|
|
14
15
|
}
|
package/dist/index.js
CHANGED
|
@@ -7,11 +7,12 @@ import { createRequire } from 'module';
|
|
|
7
7
|
import { readFile, stat } from 'fs/promises';
|
|
8
8
|
import { basename, extname, resolve } from 'path';
|
|
9
9
|
import { WolfpackClient } from './client.js';
|
|
10
|
+
import { productName } from './brand.js';
|
|
10
11
|
import { allTasksChecked, getWorkItemReminders } from './workItemReminders.js';
|
|
11
12
|
import { validateConfig, config } from './config.js';
|
|
12
13
|
import { AGENT_BUILDER_TOOLS, handleAgentBuilderTool } from './agentBuilderTools.js';
|
|
13
14
|
import { handleProcedureTool } from './procedureTools.js';
|
|
14
|
-
import { stdioTools, toolNamesFor } from './toolCatalogue.js';
|
|
15
|
+
import { stdioTools, toolNamesFor, withProductName } from './toolCatalogue.js';
|
|
15
16
|
import { AGENT_SELF_TOOLS, AGENT_MEMORY_TOOLS, handleAgentSelfTool } from './agentSelfTools.js';
|
|
16
17
|
import { AGENT_OBSERVE_TOOLS, handleAgentObserveTool } from './agentObserveTools.js';
|
|
17
18
|
import { BROWSER_TOOLS, handleBrowserTool } from './browserTools.js';
|
|
@@ -65,7 +66,7 @@ const ListWorkItemsSchema = z.object({
|
|
|
65
66
|
status: z.coerce
|
|
66
67
|
.string()
|
|
67
68
|
.optional()
|
|
68
|
-
.describe('Filter by status. Board columns: "new", "doing", "
|
|
69
|
+
.describe('Filter by status. Board columns: "new", "doing", "paused", "blocked", "review", "ready", "completed". Use "pending" or "backlog" for backlog items. Use "all" to include completed/closed. Default excludes completed/closed.'),
|
|
69
70
|
assigned_to_id: z.coerce
|
|
70
71
|
.string()
|
|
71
72
|
.optional()
|
|
@@ -121,6 +122,7 @@ const VALID_STATUSES = [
|
|
|
121
122
|
'pending',
|
|
122
123
|
'new',
|
|
123
124
|
'doing',
|
|
125
|
+
'paused',
|
|
124
126
|
'blocked',
|
|
125
127
|
'review',
|
|
126
128
|
'ready',
|
|
@@ -375,7 +377,7 @@ const CreateWorkItemSchema = z.object({
|
|
|
375
377
|
status: z
|
|
376
378
|
.enum(VALID_STATUSES)
|
|
377
379
|
.optional()
|
|
378
|
-
.describe('Initial status: "pending" (backlog), "new" (to do), "doing", "
|
|
380
|
+
.describe('Initial status: "pending" (backlog), "new" (to do), "doing", "paused", "blocked", "review", "ready", "completed". Defaults to "new".'),
|
|
379
381
|
priority: z.number().optional().describe('Priority level (0-4, higher is more important)'),
|
|
380
382
|
size: z
|
|
381
383
|
.enum(['S', 'M', 'L'])
|
|
@@ -831,14 +833,16 @@ class WolfpackMCPServer {
|
|
|
831
833
|
await this.fetchCapabilities();
|
|
832
834
|
}
|
|
833
835
|
return {
|
|
834
|
-
|
|
836
|
+
// The catalogue carries a token where the product's name belongs (#2466); it is
|
|
837
|
+
// filled in here, as the tools are advertised.
|
|
838
|
+
tools: withProductName([
|
|
835
839
|
...stdioTools(this.capabilities, this.permissions),
|
|
836
840
|
...(this.capabilities.includes('agent_self') ? AGENT_SELF_TOOLS : []),
|
|
837
841
|
...(this.capabilities.includes('agent_memory') ? AGENT_MEMORY_TOOLS : []),
|
|
838
842
|
...(this.capabilities.includes('agent_observer') ? AGENT_OBSERVE_TOOLS : []),
|
|
839
843
|
...(this.capabilities.includes('agent_builder') ? AGENT_BUILDER_TOOLS : []),
|
|
840
844
|
...(this.capabilities.includes('browser_control') ? BROWSER_TOOLS : []),
|
|
841
|
-
],
|
|
845
|
+
], productName),
|
|
842
846
|
};
|
|
843
847
|
});
|
|
844
848
|
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
@@ -2072,7 +2076,7 @@ class WolfpackMCPServer {
|
|
|
2072
2076
|
// Connect stdio transport FIRST so MCP clients don't deadlock waiting for initialize
|
|
2073
2077
|
const transport = new StdioServerTransport();
|
|
2074
2078
|
await this.server.connect(transport);
|
|
2075
|
-
console.error(
|
|
2079
|
+
console.error(`${productName} MCP Server v${CURRENT_VERSION} started`);
|
|
2076
2080
|
if (!this.capabilitiesLoaded) {
|
|
2077
2081
|
console.error('Starting with base tools only; retrying capabilities fetch in background');
|
|
2078
2082
|
void this.recoverCapabilities();
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { productName } from './brand.js';
|
|
1
2
|
/**
|
|
2
3
|
* Server instructions, returned in the MCP `initialize` result.
|
|
3
4
|
*
|
|
@@ -12,9 +13,9 @@
|
|
|
12
13
|
* server — the two servers have no shared package, and already keep their tool
|
|
13
14
|
* descriptions in step by hand.
|
|
14
15
|
*/
|
|
15
|
-
export const SERVER_INSTRUCTIONS =
|
|
16
|
+
export const SERVER_INSTRUCTIONS = `${productName} is this project's tracker. Its references override the usual GitHub reading of "#N".
|
|
16
17
|
|
|
17
|
-
- A bare "#123" — "work on #123", "look at #42" — is ALWAYS a
|
|
18
|
+
- A bare "#123" — "work on #123", "look at #42" — is ALWAYS a ${productName} work item. Call get_work_item. Do not run \`gh issue view\`, and do not search GitHub for it.
|
|
18
19
|
- 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
|
|
20
|
-
- On its own, "issue" means a
|
|
20
|
+
- Every other ${productName} 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.
|
|
21
|
+
- On its own, "issue" means a ${productName} issue (#i123). A GitHub issue is only ever called a GitHub issue.`;
|
package/dist/toolCatalogue.js
CHANGED
|
@@ -21,6 +21,22 @@
|
|
|
21
21
|
* `agentBuilderTools.ts`, `agentSelfTools.ts`, `browserTools.ts`), so they have
|
|
22
22
|
* no second declaration to drift from.
|
|
23
23
|
*/
|
|
24
|
+
/**
|
|
25
|
+
* The token a description carries where the product's name belongs (#2466).
|
|
26
|
+
*
|
|
27
|
+
* The catalogue is serialised into the backend's generated copy, so a description cannot
|
|
28
|
+
* interpolate the name — it would be baked in at generation time, and the copy would go
|
|
29
|
+
* stale on a rename with nothing to notice. Each transport fills the token in as it
|
|
30
|
+
* advertises its tools instead.
|
|
31
|
+
*/
|
|
32
|
+
export const PRODUCT_NAME_TOKEN = '__PRODUCT_NAME__';
|
|
33
|
+
/** The same tools, as a person reading their client's tool list should see them. */
|
|
34
|
+
export function withProductName(tools, productName) {
|
|
35
|
+
return tools.map((tool) => ({
|
|
36
|
+
...tool,
|
|
37
|
+
description: tool.description?.split(PRODUCT_NAME_TOKEN).join(productName),
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
24
40
|
// Cross-reference syntax for linking between content items in markdown fields
|
|
25
41
|
const CONTENT_LINKING_HELP = 'CROSS-REFERENCES: In any markdown content, you can link to other items using these patterns: ' +
|
|
26
42
|
'#123, #w123, or #work-123 for work items (DEFAULT — bare #N always means a work item), ' +
|
|
@@ -89,9 +105,9 @@ export const TOOL_CATALOGUE = [
|
|
|
89
105
|
// Checks each entity type itself and drops the types it may not read, so a
|
|
90
106
|
// key holding only `mcp:wiki:read` still searches pages.
|
|
91
107
|
permission: null,
|
|
92
|
-
description: 'Ranked full-text search across project content: wiki pages, work items, cases, journal entries, issues and the
|
|
108
|
+
description: 'Ranked full-text search across project content: wiki pages, work items, cases, journal entries, issues and the __PRODUCT_NAME__ user manual. ' +
|
|
93
109
|
'Title matches outrank body matches; results include a snippet, entity type, and refId/slug for follow-up calls ' +
|
|
94
|
-
'(get_work_item, get_issue, get_wiki_page, ...). Results with entityType "manual" are user manual sections — platform documentation on how
|
|
110
|
+
'(get_work_item, get_issue, get_wiki_page, ...). Results with entityType "manual" are user manual sections — platform documentation on how __PRODUCT_NAME__ features work. ' +
|
|
95
111
|
'Use this to find existing content before creating new items, or when the user asks to "find" or "look up" something without saying where it lives.',
|
|
96
112
|
inputSchema: {
|
|
97
113
|
type: 'object',
|
|
@@ -132,7 +148,7 @@ export const TOOL_CATALOGUE = [
|
|
|
132
148
|
'with a non-empty blockedBy is NOT claimable however approved it is — take the work it names ' +
|
|
133
149
|
'first; the response says so too. ' +
|
|
134
150
|
'TERMINOLOGY: "board" and "kanban" are synonymous - both refer to the Kanban board of work items. ' +
|
|
135
|
-
'The board has columns: "new" (to do), "doing" (in progress), "review" (pending review), "ready" (code done, awaiting deployment), "
|
|
151
|
+
'The board has columns: "new" (to do), "doing" (in progress), "paused" (set aside by a human), "blocked", "review" (pending review), "ready" (code done, awaiting deployment), "completed" (deployed). ' +
|
|
136
152
|
'The "backlog" or "pending" status represents items not yet on the board. ' +
|
|
137
153
|
'By default, completed/closed items are excluded - use status="all" to include them. ' +
|
|
138
154
|
'IMPORTANT: Work items are NOT the same as issues. Work items live on the Kanban board/backlog. ' +
|
|
@@ -147,7 +163,7 @@ export const TOOL_CATALOGUE = [
|
|
|
147
163
|
},
|
|
148
164
|
status: {
|
|
149
165
|
type: 'string',
|
|
150
|
-
description: 'Filter by status. Board columns: "new", "doing", "
|
|
166
|
+
description: 'Filter by status. Board columns: "new", "doing", "paused", "blocked", "review", "ready", "completed". ' +
|
|
151
167
|
'Use "pending" or "backlog" for backlog items not on board. ' +
|
|
152
168
|
'Use "all" to include completed/closed items. Default excludes completed/closed.',
|
|
153
169
|
},
|
|
@@ -208,8 +224,9 @@ export const TOOL_CATALOGUE = [
|
|
|
208
224
|
'WORKFLOW: When asked to work on an item, check its status and follow the required state transitions ' +
|
|
209
225
|
'(pending→pull first, new→doing, review→doing when you are picking the work back up, ' +
|
|
210
226
|
'ready/completed/closed→new→doing, then review when done). ' +
|
|
211
|
-
'AGENTS: a "blocked" item is not yours to restart — a human clears the blocker
|
|
212
|
-
'
|
|
227
|
+
'AGENTS: a "blocked" or "paused" item is not yours to restart — a human clears the blocker, and a ' +
|
|
228
|
+
'human decides when paused work resumes. Leave it where it is, comment if you can help, and take ' +
|
|
229
|
+
'the next claimable item instead. ' +
|
|
213
230
|
'PLANNING: Check if the description contains a plan (markdown checklist). If not, APPEND one using update_work_progress - preserve all original description text and add your plan below a "---" separator. ' +
|
|
214
231
|
'FORMS: Procedure-created work items may include formDefinition (field definitions with name, label, type, required, options) and formValues (current values). Use submit_work_item_form to fill in form values. ' +
|
|
215
232
|
'REVIEW CHECKS: items under review may carry review checks (code-review, security-review, ...); see list_work_item_checks and claim/complete_work_item_check. ' +
|
|
@@ -264,7 +281,8 @@ export const TOOL_CATALOGUE = [
|
|
|
264
281
|
'STATUS WORKFLOW: "pending" (backlog) → "new" (to do) → "doing" (in progress) → "review" (work done) → "ready" (awaiting deployment) → "completed" (deployed). ' +
|
|
265
282
|
'Use "blocked" when work cannot proceed. AGENTS: you cannot start un-started work here — ' +
|
|
266
283
|
'a status change on a "pending" or "new" item that is not assigned to you is refused. ' +
|
|
267
|
-
'Nor can you move an item OUT of "blocked": clearing a blocker
|
|
284
|
+
'Nor can you move an item OUT of "blocked" or "paused": clearing a blocker, and resuming work a ' +
|
|
285
|
+
'human set aside, are human decisions. ' +
|
|
268
286
|
'Take it with pull_work_item, which assigns it to you and puts it in "doing"; that is ' +
|
|
269
287
|
'what makes the board show who is working on what. ' +
|
|
270
288
|
'When moving to "review", add a completion comment via create_work_item_comment. When moving to "blocked", add a comment explaining the blocker. ' +
|
|
@@ -291,6 +309,7 @@ export const TOOL_CATALOGUE = [
|
|
|
291
309
|
'pending',
|
|
292
310
|
'new',
|
|
293
311
|
'doing',
|
|
312
|
+
'paused',
|
|
294
313
|
'blocked',
|
|
295
314
|
'review',
|
|
296
315
|
'ready',
|
|
@@ -803,6 +822,7 @@ export const TOOL_CATALOGUE = [
|
|
|
803
822
|
'pending',
|
|
804
823
|
'new',
|
|
805
824
|
'doing',
|
|
825
|
+
'paused',
|
|
806
826
|
'blocked',
|
|
807
827
|
'review',
|
|
808
828
|
'ready',
|
|
@@ -810,7 +830,7 @@ export const TOOL_CATALOGUE = [
|
|
|
810
830
|
'closed',
|
|
811
831
|
'archived',
|
|
812
832
|
],
|
|
813
|
-
description: 'Initial status: "pending" (backlog), "new" (to do), "doing", "
|
|
833
|
+
description: 'Initial status: "pending" (backlog), "new" (to do), "doing", "paused", "blocked", "review", "ready", "completed", "closed", "archived". Defaults to "new".',
|
|
814
834
|
},
|
|
815
835
|
priority: {
|
|
816
836
|
type: 'number',
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
2
|
import { readFileSync } from 'fs';
|
|
3
3
|
import { join } from 'path';
|
|
4
|
-
import
|
|
4
|
+
import * as catalogue from './toolCatalogue.js';
|
|
5
|
+
import { TOOL_CATALOGUE, remoteTools, stdioTools } from './toolCatalogue.js';
|
|
5
6
|
import { BROWSER_TOOLS } from './browserTools.js';
|
|
6
7
|
// @ts-expect-error — a plain .mjs build script, deliberately not part of this program
|
|
7
8
|
import { render, GENERATED_PATH } from '../../../scripts/generate-mcp-catalogue.mjs';
|
|
@@ -16,7 +17,9 @@ const repoFile = (path) => readFileSync(join(__dirname, '../../..', path), 'utf8
|
|
|
16
17
|
describe('the MCP tool catalogue', () => {
|
|
17
18
|
it("is what the backend's generated copy holds", () => {
|
|
18
19
|
// The one check that makes the other declaration a copy rather than a rival.
|
|
19
|
-
|
|
20
|
+
// The whole module, exactly as the generator imports it: passing named exports
|
|
21
|
+
// one by one is how the copy went stale when the generator grew a new one (#2466).
|
|
22
|
+
expect(readFileSync(GENERATED_PATH, 'utf8')).toBe(render(catalogue));
|
|
20
23
|
});
|
|
21
24
|
it('gives every tool a name no other tool has', () => {
|
|
22
25
|
const names = TOOL_CATALOGUE.map((t) => t.name);
|