pipebrain-mcp 0.53.0 → 0.54.0
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/main.js +292 -84
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -367,26 +367,26 @@ var AGENT_CONFIG_ENTRIES = [
|
|
|
367
367
|
{ target: "agents/blind-spot-pass.md", sha256: "564751a751f7896e89c67e0d20788aff873e599766a3c30360073ba74e4c6099" },
|
|
368
368
|
{ target: "agents/code-verifier.md", sha256: "c3f0a3b7397f0c903056971e71f2640a979a52e6efd18b624b3d29716d1e43e0" },
|
|
369
369
|
{ target: "agents/heavy-implementer.md", sha256: "7d3af78d4241aaa67c283db245dd4065af6e8f55efce54c2f8623c1f83879830" },
|
|
370
|
-
{ target: "agents/issue-header-writer.md", sha256: "
|
|
370
|
+
{ target: "agents/issue-header-writer.md", sha256: "185d8c656216bfd755f23391761f172d80e23b0d8a10d58acea0110df91d5c84" },
|
|
371
371
|
{ target: "agents/spec-reviewer.md", sha256: "a46dc637006d1f810081046c57edcd731406727810ccc5e0b1c7d7b9b98be5ef" },
|
|
372
372
|
{ target: "agents/surgical-editor.md", sha256: "c8b7fe2ec8d76c396c0c698f36b0523be430b31e4b73b902409479a52d42ea5c" },
|
|
373
373
|
{ target: "pipebrain/hooks/block-bash4-features.js", sha256: "383b38169a1795d9da34aa1c939349b6a1f76bba1359d0af7fd14c11ea5da25f" },
|
|
374
374
|
{ target: "pipebrain/hooks/block-browser-dialogs.js", sha256: "b4f16a249881e6bd3c8d7f270c955270047a0636ffa99b89ad409fefd44b2b2f" },
|
|
375
|
-
{ target: "pipebrain/hooks/block-credential-echo.js", sha256: "
|
|
375
|
+
{ target: "pipebrain/hooks/block-credential-echo.js", sha256: "dce33084ef919ec051a6d05a474d2f732779b03d7a96577312b3db3e45f1a760" },
|
|
376
376
|
{ target: "pipebrain/hooks/pre-commit-publication-fitness.js", sha256: "68a4f5f15dea22057b7217ad54d5cf8c34a8c4aaa5ce139ace79911acece8504" },
|
|
377
377
|
{ target: "pipebrain/hooks/pre-commit-secret-scan.js", sha256: "1bc634774184e66748b3152016e806ef974449e976920fd710732170fdb4ad6b" },
|
|
378
|
-
{ target: "pipebrain/hooks/stop-orphaned-servers.js", sha256: "
|
|
379
|
-
{ target: "pipebrain/team.md", sha256: "
|
|
378
|
+
{ target: "pipebrain/hooks/stop-orphaned-servers.js", sha256: "16ec7e690b3bd5eb7aceacefd462fc3ac4ae31ae0b5ee5280472fb9c34ffc09b" },
|
|
379
|
+
{ target: "pipebrain/team.md", sha256: "073a1f59681f09153e323b7cf0476c6860aa9a4584cdebf5ba754ad5251da13f" },
|
|
380
380
|
{ target: "rules/react-tailwind.md", sha256: "80bb51b2d8fd5782b7c742526544e7841ee38aa575faf7a8c7b4217ba5731989" },
|
|
381
381
|
{ target: "rules/shell.md", sha256: "499aac60b6419acb0bb362c0267b9399139dcceafb0eaf67702e40f3118b5c04" },
|
|
382
382
|
{ target: "rules/typescript.md", sha256: "c55eab0c243e38cdf903ab50b1e9d6d621516b893e602e1abff9a999a14e72d4" },
|
|
383
|
-
{ target: "skills/capture-correction/SKILL.md", sha256: "
|
|
384
|
-
{ target: "skills/pipebrain-header-pass/SKILL.md", sha256: "
|
|
385
|
-
{ target: "skills/pipebrain-secrets/SKILL.md", sha256: "
|
|
386
|
-
{ target: "skills/pipebrain-work-session/SKILL.md", sha256: "
|
|
387
|
-
{ target: "skills/pipebrain-writing/SKILL.md", sha256: "
|
|
388
|
-
{ target: "skills/pipebrain/SKILL.md", sha256: "
|
|
389
|
-
{ target: "skills/requirements-altitude/SKILL.md", sha256: "
|
|
383
|
+
{ target: "skills/capture-correction/SKILL.md", sha256: "43155819d172d2cd32474312df6244cc710f23cc76eeb8c0aa77664bd81dd9ca" },
|
|
384
|
+
{ target: "skills/pipebrain-header-pass/SKILL.md", sha256: "ef163f0530def7ba90b1014a47efb98f86e4ba222c6ab4d4c6bfb549dfed10f1" },
|
|
385
|
+
{ target: "skills/pipebrain-secrets/SKILL.md", sha256: "f74d3a97a94a026c612698fb3ba17da06891a217c7d44573075dd423189ae084" },
|
|
386
|
+
{ target: "skills/pipebrain-work-session/SKILL.md", sha256: "d6803c66123e3c2715376d41f63cedefc08b5ceb12fa0bae1d9ad8c8cd5d200d" },
|
|
387
|
+
{ target: "skills/pipebrain-writing/SKILL.md", sha256: "1496937104f6d7469fb8b1eb00b553502e8de0422366b2130a178f68bbcdbe44" },
|
|
388
|
+
{ target: "skills/pipebrain/SKILL.md", sha256: "71c287f905af2ed9207d4d2829bbb5b2aa51f5abb486210c78a6a65d4da8f5e2" },
|
|
389
|
+
{ target: "skills/requirements-altitude/SKILL.md", sha256: "ffe20eb73fbc73e1fd9bfaec5f9759736a4375dce5fedb015754b135fc12f225" }
|
|
390
390
|
];
|
|
391
391
|
|
|
392
392
|
// ../../libs/agent-shared/dist/agent-config.js
|
|
@@ -15444,6 +15444,21 @@ var projectSchema = external_exports.object({
|
|
|
15444
15444
|
environments: external_exports.array(projectEnvironmentSchema),
|
|
15445
15445
|
milestones: external_exports.array(projectMilestoneSchema),
|
|
15446
15446
|
deliverableTargets: external_exports.array(projectDeliverableTargetSchema),
|
|
15447
|
+
/**
|
|
15448
|
+
* The project's current release (PIPE-484), or `null` when none is active.
|
|
15449
|
+
*
|
|
15450
|
+
* DERIVED from the `milestones` array beside it — the row carrying `active: true` —
|
|
15451
|
+
* rather than queried again, so the two spellings of one answer cannot disagree. It
|
|
15452
|
+
* is carried at all because the summary read (see {@link projectSummarySchema}) has
|
|
15453
|
+
* no milestone array to look in, and "which release is current" is the one milestone
|
|
15454
|
+
* question a chooser asks; deriving it in every caller instead would be the same
|
|
15455
|
+
* `find` written in each of them.
|
|
15456
|
+
*
|
|
15457
|
+
* `.nullable()` and REQUIRED, like `defaultEnvironmentId`: the server derives it on
|
|
15458
|
+
* every row, so an absent key would mean a response predating the field while `null`
|
|
15459
|
+
* means this project has no active milestone.
|
|
15460
|
+
*/
|
|
15461
|
+
activeMilestone: projectMilestoneSchema.nullable(),
|
|
15447
15462
|
openIssueCount: external_exports.number().int().nonnegative(),
|
|
15448
15463
|
/**
|
|
15449
15464
|
* What an issue's placement falls back to when neither it nor any ancestor carries a
|
|
@@ -15467,6 +15482,16 @@ var projectSchema = external_exports.object({
|
|
|
15467
15482
|
*/
|
|
15468
15483
|
defaultIntegrationMode: integrationModeSchema
|
|
15469
15484
|
});
|
|
15485
|
+
var projectSummarySchema = external_exports.object({
|
|
15486
|
+
key: projectKeySchema,
|
|
15487
|
+
name: external_exports.string().min(1),
|
|
15488
|
+
openIssueCount: external_exports.number().int().nonnegative(),
|
|
15489
|
+
activeMilestone: projectMilestoneSchema.nullable(),
|
|
15490
|
+
defaultBranch: external_exports.string().nullable(),
|
|
15491
|
+
defaultWorktree: external_exports.string().nullable(),
|
|
15492
|
+
defaultBase: external_exports.string().nullable(),
|
|
15493
|
+
defaultIntegrationMode: integrationModeSchema
|
|
15494
|
+
});
|
|
15470
15495
|
var environmentSchema = external_exports.object({
|
|
15471
15496
|
id: uuidSchema,
|
|
15472
15497
|
orgId: uuidSchema,
|
|
@@ -15992,13 +16017,13 @@ var criterionInputSchema = external_exports.strictObject({
|
|
|
15992
16017
|
var refusedIssueHeaderFields = {
|
|
15993
16018
|
summary: external_exports.never({
|
|
15994
16019
|
error: "summary cannot be set here \u2014 create or update the issue first, then write its header with set_issue_header"
|
|
15995
|
-
}).optional().describe("Not writable here
|
|
16020
|
+
}).optional().describe("Not writable here; `set_issue_header` writes it."),
|
|
15996
16021
|
why: external_exports.never({
|
|
15997
16022
|
error: "why cannot be set here \u2014 create or update the issue first, then write its header with set_issue_header"
|
|
15998
|
-
}).optional().describe("Not writable here
|
|
16023
|
+
}).optional().describe("Not writable here; `set_issue_header` writes it."),
|
|
15999
16024
|
affectedSurfaces: external_exports.never({
|
|
16000
16025
|
error: "affected surfaces cannot be set here \u2014 create or update the issue first, then write its header with set_issue_header"
|
|
16001
|
-
}).optional().describe("Not writable here
|
|
16026
|
+
}).optional().describe("Not writable here; `set_issue_header` writes it.")
|
|
16002
16027
|
};
|
|
16003
16028
|
var createIssueInputSchema = external_exports.strictObject({
|
|
16004
16029
|
...refusedIssueHeaderFields,
|
|
@@ -16075,9 +16100,9 @@ var updateIssueInputSchema = external_exports.strictObject({
|
|
|
16075
16100
|
* asks and a schema that could not tell them apart would make an issue's own value
|
|
16076
16101
|
* impossible to remove.
|
|
16077
16102
|
*/
|
|
16078
|
-
branch: external_exports.string().min(1, { message: "branch must not be empty \u2014 pass null to clear it" }).nullable().optional().describe("The branch this issue\u2019s work happens on.
|
|
16079
|
-
worktree: external_exports.string().min(1, { message: "worktree must not be empty \u2014 pass null to clear it" }).nullable().optional().describe("A label for the working tree this issue\u2019s work happens in
|
|
16080
|
-
base: external_exports.string().min(1, { message: "base must not be empty \u2014 pass null to clear it" }).nullable().optional().describe("What this issue\u2019s work integrates into.
|
|
16103
|
+
branch: external_exports.string().min(1, { message: "branch must not be empty \u2014 pass null to clear it" }).nullable().optional().describe("The branch this issue\u2019s work happens on. Null clears it, so the issue inherits from the nearest ancestor carrying one, then from the project default."),
|
|
16104
|
+
worktree: external_exports.string().min(1, { message: "worktree must not be empty \u2014 pass null to clear it" }).nullable().optional().describe("A label for the working tree this issue\u2019s work happens in, never a path. Null clears it."),
|
|
16105
|
+
base: external_exports.string().min(1, { message: "base must not be empty \u2014 pass null to clear it" }).nullable().optional().describe("What this issue\u2019s work integrates into. Null clears it, so the issue inherits from its chain, then from the project default.")
|
|
16081
16106
|
});
|
|
16082
16107
|
var ISSUE_SUMMARY_MAX = 350;
|
|
16083
16108
|
function inPlainWords(schema, field) {
|
|
@@ -16103,16 +16128,35 @@ var setStatusInputSchema = external_exports.strictObject({
|
|
|
16103
16128
|
var WORK_CONTEXT_COMMENT_LIMIT = 20;
|
|
16104
16129
|
var ISSUE_BODY_CHAR_LIMIT = 1e5;
|
|
16105
16130
|
var ISSUE_COMMENT_LIMIT = 50;
|
|
16131
|
+
var crossCuttingModeSchema = external_exports.enum(["full", "keys"]);
|
|
16132
|
+
var ISSUE_LIST_LIMIT = 50;
|
|
16133
|
+
var issueListDetailSchema = external_exports.enum(["summary", "header"]);
|
|
16106
16134
|
var issueReadWindowSchema = external_exports.strictObject({
|
|
16107
16135
|
bodyOffset: nonNegativeIntSchema.optional(),
|
|
16108
16136
|
bodyLimit: positiveIntSchema.optional(),
|
|
16109
16137
|
commentOffset: nonNegativeIntSchema.optional(),
|
|
16110
|
-
commentLimit: positiveIntSchema.optional()
|
|
16138
|
+
commentLimit: positiveIntSchema.optional(),
|
|
16139
|
+
/**
|
|
16140
|
+
* How much of the cross-cutting requirements to carry (PIPE-482). It rides HERE
|
|
16141
|
+
* rather than on a schema of its own because it is the same kind of thing as the
|
|
16142
|
+
* four above — a caller saying how much of the record it needs — and both surfaces
|
|
16143
|
+
* that parse this object are the two that must accept it. Omitted means `full`,
|
|
16144
|
+
* resolved server-side, so a caller that says nothing reads what it always read.
|
|
16145
|
+
*
|
|
16146
|
+
* IT CARRIES A `.describe()`, unlike the four sibling window fields, for the reason the
|
|
16147
|
+
* refused header fields above carry theirs: this one is an ENUM whose two members read
|
|
16148
|
+
* as interchangeable from the schema alone, and the JSON Schema is what an agent has in
|
|
16149
|
+
* front of it when it decides which to pass. `bodyOffset` and its siblings name
|
|
16150
|
+
* themselves; `keys` does not say what it drops.
|
|
16151
|
+
*/
|
|
16152
|
+
crossCutting: crossCuttingModeSchema.describe("`full` (the default) carries each cross-cutting requirement body; `keys` omits them.").optional()
|
|
16111
16153
|
});
|
|
16112
16154
|
var getIssueInputSchema = external_exports.strictObject({
|
|
16113
16155
|
issue: issueRefSchema,
|
|
16114
16156
|
...issueReadWindowSchema.shape
|
|
16115
16157
|
});
|
|
16158
|
+
var WORK_CONTEXT_AVAILABLE_LIMIT = 25;
|
|
16159
|
+
var WORK_CONTEXT_AVAILABLE_LIMIT_MAX = 200;
|
|
16116
16160
|
var getWorkContextInputSchema = external_exports.strictObject({
|
|
16117
16161
|
issue: issueRefSchema.optional(),
|
|
16118
16162
|
project: projectRefSchema.optional(),
|
|
@@ -16126,7 +16170,34 @@ var getWorkContextInputSchema = external_exports.strictObject({
|
|
|
16126
16170
|
* exception and buy nothing, since naming a session cannot change what an item read
|
|
16127
16171
|
* returns.
|
|
16128
16172
|
*/
|
|
16129
|
-
session: sessionIdSchema.optional()
|
|
16173
|
+
session: sessionIdSchema.optional(),
|
|
16174
|
+
/**
|
|
16175
|
+
* How much of the cross-cutting requirements to carry (PIPE-482), accepted on BOTH
|
|
16176
|
+
* forms for the reason `session` is accepted on both: a session that holds them
|
|
16177
|
+
* passes this on every read it makes, and making one call the exception buys
|
|
16178
|
+
* nothing. Omitted means `full`.
|
|
16179
|
+
*/
|
|
16180
|
+
crossCutting: crossCuttingModeSchema.optional(),
|
|
16181
|
+
/**
|
|
16182
|
+
* How many rows of `available` the PROJECT form returns (PIPE-483), defaulting to
|
|
16183
|
+
* {@link WORK_CONTEXT_AVAILABLE_LIMIT} and bounded at {@link
|
|
16184
|
+
* WORK_CONTEXT_AVAILABLE_LIMIT_MAX}. The tool description has named this field as
|
|
16185
|
+
* the applied cap since PIPE-282 while the schema refused it as an unknown key, so a
|
|
16186
|
+
* caller that read the description and passed it was told the key does not exist.
|
|
16187
|
+
*
|
|
16188
|
+
* Accepted on both forms, like `session` above and for the same reason: naming a cap
|
|
16189
|
+
* cannot change what an ITEM read returns, and refusing it there would make one call
|
|
16190
|
+
* the exception.
|
|
16191
|
+
*
|
|
16192
|
+
* IT CARRIES A `.describe()`, like `crossCutting` on {@link issueReadWindowSchema} and
|
|
16193
|
+
* for the same reason: the JSON Schema is what an agent has in front of it when it
|
|
16194
|
+
* decides what to pass, and a bare integer says neither what arrives when it passes
|
|
16195
|
+
* nothing nor how much it may ask for. Both numbers are INTERPOLATED from the
|
|
16196
|
+
* constants they document, so a bound that moves cannot leave the advertised text
|
|
16197
|
+
* claiming the old one. The text also names `availableCount`, because a cap is only
|
|
16198
|
+
* usable beside the total that says whether it was reached.
|
|
16199
|
+
*/
|
|
16200
|
+
availableLimit: positiveIntSchema.max(WORK_CONTEXT_AVAILABLE_LIMIT_MAX).describe(`How many rows of \`available\` the project form returns: ${WORK_CONTEXT_AVAILABLE_LIMIT} when omitted, ${WORK_CONTEXT_AVAILABLE_LIMIT_MAX} at most. \`availableCount\` beside the list is the true total.`).optional()
|
|
16130
16201
|
}).refine((v) => v.issue === void 0 !== (v.project === void 0), {
|
|
16131
16202
|
message: "pass exactly one of `issue` or `project` \u2014 an issue names its own project, and passing neither names nothing to read"
|
|
16132
16203
|
});
|
|
@@ -16336,6 +16407,13 @@ var listIssuesFilterSchema = external_exports.strictObject({
|
|
|
16336
16407
|
* asks — the filter narrows to sessions that said who they were.
|
|
16337
16408
|
*/
|
|
16338
16409
|
session: sessionIdSchema.optional(),
|
|
16410
|
+
/**
|
|
16411
|
+
* How much of the header each row carries (PIPE-484) — see {@link
|
|
16412
|
+
* issueListDetailSchema}. Omitted means `summary` on every surface: the route
|
|
16413
|
+
* applies that default, so the agent tool and the raw read cannot disagree about
|
|
16414
|
+
* what a bare list returns.
|
|
16415
|
+
*/
|
|
16416
|
+
detail: issueListDetailSchema.optional(),
|
|
16339
16417
|
// Pagination — server default 200, max 500 (§9); default applied server-side.
|
|
16340
16418
|
limit: external_exports.number().int().min(1, { message: "limit must be at least 1" }).max(500, { message: "limit must be at most 500" }).optional(),
|
|
16341
16419
|
offset: external_exports.number().int().nonnegative().optional()
|
|
@@ -16827,12 +16905,30 @@ var ApiClient = class _ApiClient {
|
|
|
16827
16905
|
return this.requestList("GET", "/api/v1/issues", { query: filter });
|
|
16828
16906
|
}
|
|
16829
16907
|
/**
|
|
16830
|
-
*
|
|
16908
|
+
* The same read as {@link listIssues}, returning the whole envelope (PIPE-484):
|
|
16909
|
+
* `items` plus the true `total` the filter matched and the `next` offset, or null at
|
|
16910
|
+
* the end of the list.
|
|
16911
|
+
*
|
|
16912
|
+
* A SECOND method rather than a widened return on the first, because the two callers
|
|
16913
|
+
* want different things and neither should carry the other's shape. The web renders
|
|
16914
|
+
* rows and pages from `offset: 0`, so a total beside them is nothing it reads; the
|
|
16915
|
+
* agent surface is bounded by a page it did not choose, and there `total` and `next`
|
|
16916
|
+
* are the difference between a first page and a complete answer.
|
|
16917
|
+
*/
|
|
16918
|
+
listIssuesPage(filter = {}) {
|
|
16919
|
+
return this.request("GET", "/api/v1/issues", { query: filter });
|
|
16920
|
+
}
|
|
16921
|
+
/**
|
|
16922
|
+
* Reads the full detail, optionally windowed (PIPE-386): `window`'s paging fields ride
|
|
16831
16923
|
* as the route's query params of the same names, and a response that left anything
|
|
16832
16924
|
* out carries `omitted` saying so. No window means the WHOLE record — this client
|
|
16833
16925
|
* injects no default, so the split between the bounded agent surface and the
|
|
16834
16926
|
* unbounded raw read is decided by the caller that owns it (the MCP handler), not
|
|
16835
16927
|
* silently here for everyone.
|
|
16928
|
+
*
|
|
16929
|
+
* `window.crossCutting` rides the same way (PIPE-482): `keys` asks for the project's
|
|
16930
|
+
* cross-cutting requirements without their bodies, for a caller that already holds
|
|
16931
|
+
* them and needs only to check the approved revision it has against the current one.
|
|
16836
16932
|
*/
|
|
16837
16933
|
getIssue(ref, window) {
|
|
16838
16934
|
return this.request("GET", `/api/v1/issues/${enc(ref)}`, { query: window });
|
|
@@ -16845,6 +16941,10 @@ var ApiClient = class _ApiClient {
|
|
|
16845
16941
|
* `placement` omits keeps whatever the issue already recorded; the call never clears
|
|
16846
16942
|
* one. Refused 409 `placement_conflict` when another live claim in the project holds
|
|
16847
16943
|
* the branch or the worktree.
|
|
16944
|
+
*
|
|
16945
|
+
* Answers with an {@link IssueReceipt}, like the five state-changing writes below it
|
|
16946
|
+
* (PIPE-481): the display id and the fields the call changed, with the whole issue one
|
|
16947
|
+
* {@link ApiClient.getIssue} away.
|
|
16848
16948
|
*/
|
|
16849
16949
|
claimIssue(ref, placement) {
|
|
16850
16950
|
return this.request("POST", `/api/v1/issues/${enc(ref)}/claim`, {
|
|
@@ -16874,9 +16974,14 @@ var ApiClient = class _ApiClient {
|
|
|
16874
16974
|
*
|
|
16875
16975
|
* `issue` is dropped from the body because the ref rides the path, the way it
|
|
16876
16976
|
* does on every other whole-value write here.
|
|
16977
|
+
*
|
|
16978
|
+
* Answers with an {@link IssueHeaderReceipt} (PIPE-481) — the ordinary receipt plus
|
|
16979
|
+
* all three texts this call wrote, so the write can be verified from its own answer.
|
|
16877
16980
|
*/
|
|
16878
16981
|
setIssueHeader(ref, input) {
|
|
16879
|
-
return this.request("PUT", `/api/v1/issues/${enc(ref)}/header`, {
|
|
16982
|
+
return this.request("PUT", `/api/v1/issues/${enc(ref)}/header`, {
|
|
16983
|
+
body: input
|
|
16984
|
+
});
|
|
16880
16985
|
}
|
|
16881
16986
|
/** Records the issue as blocked with a required reason (PIPE-99). */
|
|
16882
16987
|
setBlocked(ref, reason) {
|
|
@@ -17216,6 +17321,17 @@ var ApiClient = class _ApiClient {
|
|
|
17216
17321
|
createProject(input) {
|
|
17217
17322
|
return this.request("POST", "/api/v1/projects", { body: input });
|
|
17218
17323
|
}
|
|
17324
|
+
/**
|
|
17325
|
+
* Every project this token can see (§7). `detail` chooses the row shape (PIPE-484):
|
|
17326
|
+
* `full` — the default the route applies — is the row with its environments,
|
|
17327
|
+
* milestones and deliverable targets, and `summary` is the chooser's row.
|
|
17328
|
+
*
|
|
17329
|
+
* The return type stays `Project[]`, the full row, because that is what every caller
|
|
17330
|
+
* in this repository that does not pass `detail` receives. A caller asking for
|
|
17331
|
+
* `summary` narrows it itself: `listProjects({ detail: 'summary' }) as ProjectSummary[]`
|
|
17332
|
+
* is the honest cast, and the overload that would express it in the type system would
|
|
17333
|
+
* have to be repeated on every wrapper this client is reached through.
|
|
17334
|
+
*/
|
|
17219
17335
|
listProjects(page) {
|
|
17220
17336
|
return this.requestList("GET", "/api/v1/projects", { query: page });
|
|
17221
17337
|
}
|
|
@@ -17316,8 +17432,15 @@ var ApiClient = class _ApiClient {
|
|
|
17316
17432
|
* answer.
|
|
17317
17433
|
*/
|
|
17318
17434
|
getWorkContext(input) {
|
|
17319
|
-
|
|
17320
|
-
|
|
17435
|
+
const crossCutting = input.crossCutting === void 0 ? {} : { crossCutting: input.crossCutting };
|
|
17436
|
+
return input.issue !== void 0 ? this.request("GET", `/api/v1/issues/${enc(input.issue)}/work-context`, {
|
|
17437
|
+
query: crossCutting
|
|
17438
|
+
}) : this.request("GET", `/api/v1/projects/${enc(input.project)}/work-context`, {
|
|
17439
|
+
query: {
|
|
17440
|
+
...crossCutting,
|
|
17441
|
+
...input.session === void 0 ? {} : { session: input.session },
|
|
17442
|
+
...input.availableLimit === void 0 ? {} : { availableLimit: input.availableLimit }
|
|
17443
|
+
}
|
|
17321
17444
|
});
|
|
17322
17445
|
}
|
|
17323
17446
|
// --- secrets ---------------------------------------------------------------
|
|
@@ -17568,6 +17691,8 @@ function safeJsonParse(text) {
|
|
|
17568
17691
|
// ../../libs/mcp-server/dist/server.js
|
|
17569
17692
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
17570
17693
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
17694
|
+
import { toJsonSchemaCompat } from "@modelcontextprotocol/sdk/server/zod-json-schema-compat.js";
|
|
17695
|
+
import { ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
17571
17696
|
|
|
17572
17697
|
// ../../libs/mcp-server/dist/instructions.js
|
|
17573
17698
|
var STARTUP_FETCH_TIMEOUT_MS = 5e3;
|
|
@@ -17666,7 +17791,7 @@ async function run(body, appended) {
|
|
|
17666
17791
|
}
|
|
17667
17792
|
}
|
|
17668
17793
|
async function resolveMilestoneId(client2, projectKey, ref) {
|
|
17669
|
-
const projects = await client2.listProjects();
|
|
17794
|
+
const projects = await client2.listProjects({ detail: "full" });
|
|
17670
17795
|
const project = projects.find((p) => p.key === projectKey);
|
|
17671
17796
|
if (project === void 0) {
|
|
17672
17797
|
throw new Error(`No project with key \`${projectKey}\` is visible to this token.`);
|
|
@@ -17679,7 +17804,7 @@ async function resolveMilestoneId(client2, projectKey, ref) {
|
|
|
17679
17804
|
return match.id;
|
|
17680
17805
|
}
|
|
17681
17806
|
async function resolveDeliverableTargetId(client2, projectKey, ref) {
|
|
17682
|
-
const projects = await client2.listProjects();
|
|
17807
|
+
const projects = await client2.listProjects({ detail: "full" });
|
|
17683
17808
|
const project = projects.find((p) => p.key === projectKey);
|
|
17684
17809
|
if (project === void 0) {
|
|
17685
17810
|
throw new Error(`No project with key \`${projectKey}\` is visible to this token.`);
|
|
@@ -17691,10 +17816,14 @@ async function resolveDeliverableTargetId(client2, projectKey, ref) {
|
|
|
17691
17816
|
}
|
|
17692
17817
|
return match.id;
|
|
17693
17818
|
}
|
|
17694
|
-
var ORG_ARGUMENT_DESCRIPTION = "
|
|
17819
|
+
var ORG_ARGUMENT_DESCRIPTION = "Org slug or uuid; omit only with a session default.";
|
|
17695
17820
|
var orgArgumentSchema = external_exports.string().min(1).describe(ORG_ARGUMENT_DESCRIPTION).optional();
|
|
17696
17821
|
function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__ */ new Map(), opts = {}) {
|
|
17822
|
+
return createMcpServerWithRegistry(baseClient, apiBaseUrl, orgInstructions2, opts).server;
|
|
17823
|
+
}
|
|
17824
|
+
function createMcpServerWithRegistry(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__ */ new Map(), opts = {}) {
|
|
17697
17825
|
const surface = opts.surface ?? "local";
|
|
17826
|
+
const registry2 = [];
|
|
17698
17827
|
const scope = new AsyncLocalStorage();
|
|
17699
17828
|
function client2() {
|
|
17700
17829
|
return scope.getStore()?.client ?? baseClient;
|
|
@@ -17726,6 +17855,12 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
17726
17855
|
const declared = config3.inputSchema;
|
|
17727
17856
|
const strict = declared === void 0 ? external_exports.strictObject({}) : "_zod" in declared || "_def" in declared ? declared : external_exports.strictObject(declared);
|
|
17728
17857
|
const inputSchema = strict.extend({ org: orgArgumentSchema });
|
|
17858
|
+
registry2.push({
|
|
17859
|
+
name,
|
|
17860
|
+
description: config3.description,
|
|
17861
|
+
inputSchema,
|
|
17862
|
+
...config3._meta === void 0 ? {} : { _meta: config3._meta }
|
|
17863
|
+
});
|
|
17729
17864
|
server2.registerTool(
|
|
17730
17865
|
name,
|
|
17731
17866
|
{ ...config3, inputSchema },
|
|
@@ -17752,7 +17887,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
17752
17887
|
);
|
|
17753
17888
|
}
|
|
17754
17889
|
registerStrict("list_orgs", {
|
|
17755
|
-
description: "List the organizations this credential may act in
|
|
17890
|
+
description: "List the organizations this credential may act in: name, slug, id, kind and your role. This is the one tool that needs no `org`: every other call names the organization it acts in, and omitting it is refused unless this session has a default. The answer is your memberships right now."
|
|
17756
17891
|
}, () => (
|
|
17757
17892
|
// A BARE ARRAY, unlike `list_waiting`'s `{ items }`, and for the reason that tool
|
|
17758
17893
|
// gives rather than against it: the envelope has already been unwrapped by
|
|
@@ -17771,17 +17906,31 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
17771
17906
|
// makes the tool's own description true.
|
|
17772
17907
|
run(() => client2().unscoped().listOrgs(), GUIDANCE_POINTER)
|
|
17773
17908
|
));
|
|
17774
|
-
registerStrict(
|
|
17775
|
-
|
|
17776
|
-
|
|
17777
|
-
|
|
17778
|
-
|
|
17779
|
-
limit
|
|
17780
|
-
|
|
17781
|
-
|
|
17782
|
-
|
|
17909
|
+
registerStrict(
|
|
17910
|
+
"list_projects",
|
|
17911
|
+
{
|
|
17912
|
+
description: "List every project: key, name, open-issue count, the active milestone and the placement defaults work falls back to. Summary rows carry no `id`, so chain off `key`; `detail: full` returns the whole project row \u2014 environments, milestones (an archived one stays valid on issues naming it), deliverable targets, and the widget and default fields. Pages with `limit` (default 200, max 500) and `offset`.",
|
|
17913
|
+
// Optional pagination (§9) — bounds mirror list_issues/list_requirements; the
|
|
17914
|
+
// server defaults limit to 200 (max 500) and passes limit/offset as query params.
|
|
17915
|
+
// `detail` is declared INLINE like the rest of this shape: there is no
|
|
17916
|
+
// list-projects schema in @pipeline/domain, so this is where the option lives.
|
|
17917
|
+
inputSchema: {
|
|
17918
|
+
limit: external_exports.number().int().min(1).max(500).optional(),
|
|
17919
|
+
offset: external_exports.number().int().nonnegative().optional(),
|
|
17920
|
+
detail: external_exports.enum(["summary", "full"]).optional()
|
|
17921
|
+
}
|
|
17922
|
+
},
|
|
17923
|
+
// THE AGENT-SURFACE DEFAULT (PIPE-484), injected here — the same deliberate
|
|
17924
|
+
// exception to this side's inject-no-default rule (PIPE-D26) that `get_issue`'s
|
|
17925
|
+
// caps are, and for the same reason: the two surfaces differ by design. A browser
|
|
17926
|
+
// reading this route is rendering a settings page and needs every milestone and
|
|
17927
|
+
// every deliverable target, so the route keeps `full`; an agent reading it is
|
|
17928
|
+
// choosing a project, and was being handed every milestone of every project to do
|
|
17929
|
+
// it. A caller that wants the arrays here says `detail: full` and gets them.
|
|
17930
|
+
(args) => run(() => client2().listProjects({ ...args, detail: args.detail ?? "summary" }), GUIDANCE_POINTER)
|
|
17931
|
+
);
|
|
17783
17932
|
registerStrict("create_project", {
|
|
17784
|
-
description: "Create a project. Nothing can be filed until one exists. `name` is the display label; `key` is 2\u20135 uppercase letters
|
|
17933
|
+
description: "Create a project. Nothing can be filed until one exists. `name` is the display label; `key` is 2\u20135 uppercase letters, unique per organization, and prefixes every display id.",
|
|
17785
17934
|
// Inline shape: @pipeline/domain has no standalone create-project schema — the
|
|
17786
17935
|
// same `{ name, key }` pair lives only inside createOrgInputSchema.project, so
|
|
17787
17936
|
// this mirrors it exactly (key = 2–5 A–Z via projectKeySchema) and matches
|
|
@@ -17792,7 +17941,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
17792
17941
|
}
|
|
17793
17942
|
}, (args) => run(() => client2().createProject(args)));
|
|
17794
17943
|
registerStrict("list_waiting", {
|
|
17795
|
-
description: "Everything not moving on its own across all projects in the organization named by `org`, longest wait first: a requirement revision awaiting approval, an unanswered question, and work at verify holding a criterion that has not passed. Each entry names its project, the ref to act on, and when the wait began. Read-only. A revision clears with `approve_requirement`, a question with `answer_question`, an `auto` criterion with `set_criterion_state
|
|
17944
|
+
description: "Everything not moving on its own across all projects in the organization named by `org`, longest wait first: a requirement revision awaiting approval, an unanswered question, and work at verify holding a criterion that has not passed. Each entry names its project, the ref to act on, and when the wait began. Read-only. A revision clears with `approve_requirement`, a question with `answer_question`, an `auto` criterion with `set_criterion_state`; one left to human judgment waits for a person."
|
|
17796
17945
|
}, () => (
|
|
17797
17946
|
// `{ items }`, mirroring the HTTP envelope rather than unwrapping to a bare array
|
|
17798
17947
|
// like the sibling list tools. The reason is the envelope itself: this tool is a
|
|
@@ -17815,7 +17964,36 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
17815
17964
|
// one description loaded into EVERY session by `anthropic/alwaysLoad`. The clauses
|
|
17816
17965
|
// that went were the ones arguing for the rule rather than stating it; what a caller
|
|
17817
17966
|
// needs to predict a refusal all stayed.
|
|
17818
|
-
|
|
17967
|
+
//
|
|
17968
|
+
// IT SITS AT THE 80-WORD CAP `plain-descriptions.test.ts` enforces, so a new clause
|
|
17969
|
+
// has to displace an old one. Four suites also demand named tokens in here:
|
|
17970
|
+
// `availableCount` inside a clause that states a cap, and `availableLimit`, from
|
|
17971
|
+
// `work-context-bounds.test.ts`; `done`, `killed` and an exclusion verb from the
|
|
17972
|
+
// e2e AC-9 spec.
|
|
17973
|
+
//
|
|
17974
|
+
// TWO CLAUSES THE SHAPE ITSELF PAID FOR. `item.issue` is no longer the whole issue
|
|
17975
|
+
// read: PIPE-480 moved the cross-cutting requirements onto `project` and the
|
|
17976
|
+
// conventions to the pack's top level, so a description that promised "its detail"
|
|
17977
|
+
// was sending a caller to read two lists off a key that no longer carries them —
|
|
17978
|
+
// `undefined.map`, not an empty list. And `available` arrives in SELECTION ORDER
|
|
17979
|
+
// (PIPE-483): a bounded page is only the right page if the ordering is the server's,
|
|
17980
|
+
// so a caller that re-sorts what it was handed re-sorts one page of the queue.
|
|
17981
|
+
//
|
|
17982
|
+
// THE COMMENT CAP IS A NUMBER AGAIN, AND THAT IS THE POINT. It read "capped at
|
|
17983
|
+
// `commentLimit`", which names a field of the RESPONSE as though it were an argument:
|
|
17984
|
+
// this schema is strict and accepts `issue`, `project`, `session`, `crossCutting` and
|
|
17985
|
+
// `availableLimit`, so a caller that tried to raise the cap was refused for an unknown
|
|
17986
|
+
// key. Interpolating {@link WORK_CONTEXT_COMMENT_LIMIT} says the size of the tail
|
|
17987
|
+
// without offering a knob that does not exist.
|
|
17988
|
+
//
|
|
17989
|
+
// IT AND THE CONVENTIONS CLAUSE WERE PAID FOR BY "active milestone first", which the
|
|
17990
|
+
// cap forced out. The conventions now sit "at the top level" rather than "beside it" —
|
|
17991
|
+
// "it" being the `project` block the clause had just named, so the old words said only
|
|
17992
|
+
// that the conventions were not quite where the sentence had been looking. What went
|
|
17993
|
+
// is the head of the selection order, which is the one clause here a caller cannot act
|
|
17994
|
+
// on differently: "in selection order" already tells it not to re-sort, and which
|
|
17995
|
+
// release leads the order changes nothing it does with the rows.
|
|
17996
|
+
description: "Call it first: it alone returns the doctrine and your organization\u2019s writing rules whole. Pass `issue` or `project`, never both. An issue returns its detail, linked requirements\u2019 text and the newest " + String(WORK_CONTEXT_COMMENT_LIMIT) + " comments (capped; `commentCount` the total); cross-cutting requirements ride on `project`, conventions at the top level. A project returns `available` in selection order, excluding done, killed, blocked, person-reserved, others\u2019 claims and, with `session`, your other sessions\u2019, capped at `availableLimit` beside the true `availableCount`. `crossCutting: keys` drops requirement bodies. Read-only.",
|
|
17819
17997
|
inputSchema: getWorkContextInputSchema,
|
|
17820
17998
|
// THE TWO ANNOTATIONS PIPE-D30 REQUIRES, and the reason each key is spelled out
|
|
17821
17999
|
// rather than built: an unknown `_meta` key is IGNORED — no error, no warning — so a
|
|
@@ -17915,7 +18093,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
17915
18093
|
}
|
|
17916
18094
|
}, ({ project, target, ...fields }) => run(async () => client2().updateDeliverableTarget(project, await resolveDeliverableTargetId(client2(), project, target), fields)));
|
|
17917
18095
|
registerStrict("delete_deliverable_target", {
|
|
17918
|
-
description: "Delete a deliverable target by slug or uuid. Only
|
|
18096
|
+
description: "Delete a deliverable target by slug or uuid. Only one with no deliverables can be deleted; one in use is rejected \u2014 deactivate it with `update_deliverable_target` instead.",
|
|
17919
18097
|
inputSchema: {
|
|
17920
18098
|
project: projectKeySchema,
|
|
17921
18099
|
target: external_exports.string().min(1)
|
|
@@ -17928,8 +18106,15 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
17928
18106
|
registerStrict(
|
|
17929
18107
|
"list_issues",
|
|
17930
18108
|
{
|
|
17931
|
-
description: "List issue summaries
|
|
17932
|
-
inputSchema: listIssuesFilterSchema.shape
|
|
18109
|
+
description: "List issue summaries: status, priority, milestone, claimer, criteria progress and the header summary. `detail: header` adds the reason and affected surfaces. " + String(ISSUE_LIST_LIMIT) + " `items` come back by default, beside the true `total` and the `next` offset. With no status filter the default is buildable work: " + OPEN_STATUSES.join(", ") + "; name `done` or `killed` for terminal work. An explicit filter is used verbatim. `session` drops work another session holds; `needsHeader: true` returns issues whose header is missing or stale.",
|
|
18110
|
+
inputSchema: listIssuesFilterSchema.shape,
|
|
18111
|
+
// The same result-threshold annotation `get_work_context` and `get_issue`
|
|
18112
|
+
// declare, spelled out the same way and for the same reason (an unknown `_meta`
|
|
18113
|
+
// key is silently ignored). A long list is where its absence BIT: 157 open issues
|
|
18114
|
+
// measured 161,128 bytes, past the client's own default output cap, so what
|
|
18115
|
+
// reached the session was a truncated list with nothing on it saying rows were
|
|
18116
|
+
// missing. No `alwaysLoad`: that stays the session-start call's alone.
|
|
18117
|
+
_meta: { "anthropic/maxResultSizeChars": WORK_CONTEXT_RESULT_CHARS }
|
|
17933
18118
|
},
|
|
17934
18119
|
// The default lives in the API now, not here (PIPE-D35, Listing issues
|
|
17935
18120
|
// without a status returns only non-terminal work, and the default lives in
|
|
@@ -17941,10 +18126,17 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
17941
18126
|
// and an explicit filter, terminal values and all, is forwarded verbatim as
|
|
17942
18127
|
// it always was. (The work page kept its terminal rows by naming every
|
|
17943
18128
|
// status on its reads, so nothing over there leant on the defaultless API.)
|
|
17944
|
-
|
|
18129
|
+
//
|
|
18130
|
+
// THE PAGE SIZE IS THE EXCEPTION (PIPE-484), injected here as an explicit `limit`
|
|
18131
|
+
// exactly as `get_issue`'s caps are and for the same reason: the raw HTTP route
|
|
18132
|
+
// keeps its 200 because the web reads there and passes its own limit, while every
|
|
18133
|
+
// response on THIS surface has to land whole. The envelope read — `items`, `total`,
|
|
18134
|
+
// `next` — is what makes a bounded page honest: an agent handed 50 of 157 rows can
|
|
18135
|
+
// see that it was handed 50 of 157.
|
|
18136
|
+
(args) => run(() => client2().listIssuesPage({ ...args, limit: args.limit ?? ISSUE_LIST_LIMIT }), GUIDANCE_POINTER)
|
|
17945
18137
|
);
|
|
17946
18138
|
registerStrict("get_issue", {
|
|
17947
|
-
description: "Full issue detail by key or uuid: header, body, criteria, requirement links, cross-cutting requirements, approved conventions, children and comments. `terminal` is true
|
|
18139
|
+
description: "Full issue detail by key or uuid: header, body, criteria, requirement links, cross-cutting requirements, approved conventions, children and comments. `terminal` is true for done or killed work; `projectArchived` for a retired project, which this read still answers under while lists and writes do not. `headerStale` is true when no header was written or the body moved. `crossCutting: keys` returns the cross-cutting requirements without bodies. Long records page with `bodyOffset` and `commentOffset`; `omitted` and `next` say what was left out.",
|
|
17948
18140
|
inputSchema: getIssueInputSchema.shape,
|
|
17949
18141
|
// The same result-threshold annotation `get_work_context` declares, and the same
|
|
17950
18142
|
// key spelled out for the same reason (an unknown `_meta` key is silently
|
|
@@ -17954,7 +18146,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
17954
18146
|
// reintroduced by a default this server cannot see. No `alwaysLoad`: that stays
|
|
17955
18147
|
// the session-start call's alone.
|
|
17956
18148
|
_meta: { "anthropic/maxResultSizeChars": WORK_CONTEXT_RESULT_CHARS }
|
|
17957
|
-
}, ({ issue: issue2, bodyOffset, bodyLimit, commentOffset, commentLimit }) => run(() => (
|
|
18149
|
+
}, ({ issue: issue2, bodyOffset, bodyLimit, commentOffset, commentLimit, crossCutting }) => run(() => (
|
|
17958
18150
|
// THE AGENT-SURFACE DEFAULTS (PIPE-386), injected here as explicit wire
|
|
17959
18151
|
// params — the ONE deliberate exception to this side's inject-no-default
|
|
17960
18152
|
// rule (PIPE-D26), because the two surfaces genuinely differ by design: the
|
|
@@ -17964,34 +18156,42 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
17964
18156
|
// comment tail needs no offset — an omitted `commentOffset` beside a present
|
|
17965
18157
|
// `commentLimit` IS the newest tail, resolved server-side, so no extra round
|
|
17966
18158
|
// trip asks how many comments exist (PIPE-R15 rule 4).
|
|
18159
|
+
//
|
|
18160
|
+
// `crossCutting` (PIPE-482) is FORWARDED AND NOT DEFAULTED — the exception
|
|
18161
|
+
// above stays exactly as wide as it was. It exists because a body can grow
|
|
18162
|
+
// past the response ceiling and has to be bounded by somebody; the number of
|
|
18163
|
+
// documents a project flags cross-cutting cannot, so there is nothing here to
|
|
18164
|
+
// protect a caller from, and a default injected on this side would make the
|
|
18165
|
+
// agent surface answer a different question from the route it mirrors.
|
|
17967
18166
|
client2().getIssue(issue2, {
|
|
17968
18167
|
bodyOffset,
|
|
17969
18168
|
bodyLimit: bodyLimit ?? ISSUE_BODY_CHAR_LIMIT,
|
|
17970
18169
|
commentOffset,
|
|
17971
|
-
commentLimit: commentLimit ?? ISSUE_COMMENT_LIMIT
|
|
18170
|
+
commentLimit: commentLimit ?? ISSUE_COMMENT_LIMIT,
|
|
18171
|
+
crossCutting
|
|
17972
18172
|
})
|
|
17973
18173
|
), GUIDANCE_POINTER));
|
|
17974
18174
|
registerStrict("create_issue", {
|
|
17975
|
-
description: "Create an epic, task, bug or feedback. Status defaults to triage; anything but triage or killed needs a milestone, else refused `milestone_required`. An archived milestone is refused `milestone_archived_write`. An open issue under a done or killed parent is refused `parent_terminal`. Epics, tasks and bugs carry criteria; feedback is refused `criteria_not_supported`. A `manual` criterion needs `howTo`, else refused `how_to_required`. A bug may name the criterion it was filed against. See
|
|
18175
|
+
description: "Create an epic, task, bug or feedback. Status defaults to triage; anything but triage or killed needs a milestone, else refused `milestone_required`. An archived milestone is refused `milestone_archived_write`. An open issue under a done or killed parent is refused `parent_terminal`. Epics, tasks and bugs carry criteria; feedback is refused `criteria_not_supported`. A `manual` criterion needs `howTo`, else refused `how_to_required`. A bug may name the criterion it was filed against. See \u201CSearch before you create\u201D in the doctrine `get_work_context` returns.",
|
|
17976
18176
|
inputSchema: createIssueInputSchema.shape
|
|
17977
18177
|
}, (args) => run(() => client2().createIssue(args)));
|
|
17978
18178
|
registerStrict("promote_feedback", {
|
|
17979
|
-
description: "Retype a feedback report as
|
|
18179
|
+
description: "Retype a feedback report as work that can be built: an epic or a task. Epics, tasks and bugs carry criteria; feedback itself is refused `criteria_not_supported`. Scheduling the new work into an archived milestone is refused `milestone_archived_write`. Closing the source feedback is refused `status_guard` when anything beneath it, at any depth, is not done or killed. A `manual` criterion needs `howTo`, else refused `how_to_required`. See \u201CThe bar for filing a bug\u201D in the doctrine `get_work_context` returns.",
|
|
17980
18180
|
inputSchema: {
|
|
17981
18181
|
issue: external_exports.string().min(1),
|
|
17982
18182
|
...promoteFeedbackInputSchema.shape
|
|
17983
18183
|
}
|
|
17984
18184
|
}, ({ issue: issue2, ...fields }) => run(() => client2().promoteFeedback(issue2, fields)));
|
|
17985
18185
|
registerStrict("open_review", {
|
|
17986
|
-
description: "Open an epic\u2019s completion review, read by whoever neither built nor evaluated it: one ready task carrying the checklist. Pass exactly one of `epic`, or `project` plus `milestone`. A first call is refused `review_target_not_epic`, `review_target_terminal` (finished epic), `milestone_required` (no milestone), `milestone_not_found` (unknown token). One review per scope; a second is refused `review_already_open`. Epic-form also holds that milestone scope; milestone-form blocks no epic. File findings under the epic, not the review. See
|
|
18186
|
+
description: "Open an epic\u2019s completion review, read by whoever neither built nor evaluated it: one ready task carrying the checklist. Pass exactly one of `epic`, or `project` plus `milestone`. A first call is refused `review_target_not_epic`, `review_target_terminal` (finished epic), `milestone_required` (no milestone), `milestone_not_found` (unknown token). One review per scope; a second is refused `review_already_open`. Epic-form also holds that milestone scope; milestone-form blocks no epic. File findings under the epic, not the review. See \u201CHow to write here\u201D in the doctrine `get_work_context` returns.",
|
|
17987
18187
|
inputSchema: openReviewInputSchema
|
|
17988
18188
|
}, (args) => run(() => client2().openReview(args)));
|
|
17989
18189
|
registerStrict("update_issue", {
|
|
17990
|
-
description: "Change an issue\u2019s fields, parent and links. Header fields are refused; `set_criteria` sets criteria, which epics, tasks and bugs carry. An archived milestone is refused `milestone_archived_write`, unless the issue or its parent carries it. A cycle-closing re-parent is refused `parent_cycle`. A finished parent refuses an open issue, `parent_terminal`; a finished one holding open work, `parent_terminal_subtree`. Attaching open work starts a parent that is not blocked, unscheduled or under finished work. See
|
|
18190
|
+
description: "Change an issue\u2019s fields, parent and links. Header fields are refused; `set_criteria` sets criteria, which epics, tasks and bugs carry. An archived milestone is refused `milestone_archived_write`, unless the issue or its parent carries it. A cycle-closing re-parent is refused `parent_cycle`. A finished parent refuses an open issue, `parent_terminal`; a finished one holding open work, `parent_terminal_subtree`. Attaching open work starts a parent that is not blocked, unscheduled or under finished work. See \u201CMost choices are yours\u201D in the doctrine `get_work_context` returns.",
|
|
17991
18191
|
inputSchema: updateIssueInputSchema.shape
|
|
17992
18192
|
}, ({ issue: issue2, ...fields }) => run(() => client2().updateIssue(issue2, fields)));
|
|
17993
18193
|
registerStrict("set_issue_header", {
|
|
17994
|
-
description: "Write an issue\u2019s header \u2014 `summary` (the change in one sentence), `why` (the user, business or cost reason, not the technical one) and `affectedSurfaces` (what this touches and who feels it). All three are required and replace the header whole. Write them in plain words: no identifiers, no file paths, no jargon. A summary shaped like code is refused; `summary` caps at 350 characters. Create and update refuse these fields. See
|
|
18194
|
+
description: "Write an issue\u2019s header \u2014 `summary` (the change in one sentence), `why` (the user, business or cost reason, not the technical one) and `affectedSurfaces` (what this touches and who feels it). All three are required and replace the header whole. Write them in plain words: no identifiers, no file paths, no jargon. A summary shaped like code is refused; `summary` caps at 350 characters. Create and update refuse these fields. See \u201CHow to write here\u201D in the doctrine `get_work_context` returns.",
|
|
17995
18195
|
inputSchema: setIssueHeaderInputSchema.shape
|
|
17996
18196
|
}, ({ issue: issue2, ...fields }) => run(() => client2().setIssueHeader(issue2, fields)));
|
|
17997
18197
|
registerStrict("set_status", {
|
|
@@ -17999,7 +18199,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
17999
18199
|
inputSchema: setStatusInputSchema.shape
|
|
18000
18200
|
}, (args) => run(() => client2().setStatus(args.issue, args.status)));
|
|
18001
18201
|
registerStrict("claim_issue", {
|
|
18002
|
-
description: "Claim an issue for this session and reserve where the work happens. It succeeds only if unclaimed
|
|
18202
|
+
description: "Claim an issue for this session and reserve where the work happens. It succeeds only if unclaimed, and the loser of a race is told who holds it. `branch` and `worktree` are refused `placement_conflict` when another live claim in the project holds either, naming that issue. A field you omit keeps what the issue already recorded. Claiming is separate from status, and is refused `reserved_for_person` when an operator has kept the work for a person.",
|
|
18003
18203
|
inputSchema: claimInputSchema.shape
|
|
18004
18204
|
}, ({ issue: issue2, ...placement }) => run(() => client2().claimIssue(issue2, placement)));
|
|
18005
18205
|
registerStrict("release_issue", {
|
|
@@ -18007,15 +18207,15 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18007
18207
|
inputSchema: { issue: issueRefSchema }
|
|
18008
18208
|
}, (args) => run(() => client2().releaseIssue(args.issue)));
|
|
18009
18209
|
registerStrict("add_comment", {
|
|
18010
|
-
description: "Add a markdown comment to an issue, attributed to the operator and token label. A comment explains a blockage; `set_blocked` and `link_blocker` are what record one. See
|
|
18210
|
+
description: "Add a markdown comment to an issue, attributed to the operator and token label. A comment explains a blockage; `set_blocked` and `link_blocker` are what record one. See \u201CHow to write here\u201D in the doctrine `get_work_context` returns.",
|
|
18011
18211
|
inputSchema: commentInputSchema.shape
|
|
18012
18212
|
}, (args) => run(() => client2().addComment(args.issue, args.body)));
|
|
18013
18213
|
registerStrict("set_blocked", {
|
|
18014
|
-
description: "Mark an issue blocked with a required `reason` \u2014 state on the issue, not a comment, and only `clear_blocked` ends it. Setting it again replaces the reason and keeps the old one as history. The reason is
|
|
18214
|
+
description: "Mark an issue blocked with a required `reason` \u2014 state on the issue, not a comment, and only `clear_blocked` ends it. Setting it again replaces the reason and keeps the old one as history. The reason is what the work waits on; what you tried goes in a comment. See \u201CHow to write here\u201D in the doctrine `get_work_context` returns.",
|
|
18015
18215
|
inputSchema: setBlockedInputSchema.shape
|
|
18016
18216
|
}, (args) => run(() => client2().setBlocked(args.issue, args.reason)));
|
|
18017
18217
|
registerStrict("clear_blocked", {
|
|
18018
|
-
description: "Clear an issue\u2019s blocked mark. Clearing one that is not blocked is a no-op, not an error. The mark stays as history, and `blocked`
|
|
18218
|
+
description: "Clear an issue\u2019s blocked mark. Clearing one that is not blocked is a no-op, not an error. The mark stays as history, and the receipt carries `blocked` as null.",
|
|
18019
18219
|
inputSchema: { issue: issueRefSchema }
|
|
18020
18220
|
}, (args) => run(() => client2().clearBlocked(args.issue)));
|
|
18021
18221
|
registerStrict("link_blocker", {
|
|
@@ -18023,15 +18223,15 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18023
18223
|
inputSchema: linkBlockerInputSchema.shape
|
|
18024
18224
|
}, (args) => run(() => client2().addBlocker(args.issue, args.blocker)));
|
|
18025
18225
|
registerStrict("unlink_blocker", {
|
|
18026
|
-
description: "Remove the blocked-by edge between `issue` and `blocker`; one call clears
|
|
18226
|
+
description: "Remove the blocked-by edge between `issue` and `blocker`; one call clears both sides. Unlinking a pair that is not linked is a no-op, not an error.",
|
|
18027
18227
|
inputSchema: linkBlockerInputSchema.shape
|
|
18028
18228
|
}, (args) => run(() => client2().removeBlocker(args.issue, args.blocker)));
|
|
18029
18229
|
registerStrict("set_criteria", {
|
|
18030
|
-
description: "Define or replace
|
|
18230
|
+
description: "Define or replace an issue\u2019s acceptance criteria. A criterion is one sentence: a concrete situation and what must be true afterward. Epics, tasks and bugs all carry criteria; feedback is refused `criteria_not_supported` \u2014 promote it first. Matched rows keep their AC number; changing text or mode resets state. `verification` defaults to `manual`; a `manual` criterion needs `howTo`, else refused `how_to_required`; `howTo` on an `auto` criterion is refused by the schema. See \u201CChoosing auto or manual\u201D in the doctrine `get_work_context` returns.",
|
|
18031
18231
|
inputSchema: setCriteriaInputSchema.shape
|
|
18032
18232
|
}, (args) => run(() => client2().setCriteria(args.issue, args.criteria)));
|
|
18033
18233
|
registerStrict("set_criterion_state", {
|
|
18034
|
-
description: "Set a criterion\u2019s state \u2014 pending, passed or failed \u2014 on an epic, a task or a bug alike. Address it by `criterionId` alone, or by issue plus `criterionNumber`; exactly one form. Feedback never carries criteria, so neither address resolves to one. A token is refused on a manual criterion, which only a person in the browser may set, and a token marking passed or failed must supply evidence. See
|
|
18234
|
+
description: "Set a criterion\u2019s state \u2014 pending, passed or failed \u2014 on an epic, a task or a bug alike. Address it by `criterionId` alone, or by issue plus `criterionNumber`; exactly one form. Feedback never carries criteria, so neither address resolves to one. A token is refused on a manual criterion, which only a person in the browser may set, and a token marking passed or failed must supply evidence. See \u201CChoosing auto or manual\u201D in the doctrine `get_work_context` returns.",
|
|
18035
18235
|
inputSchema: {
|
|
18036
18236
|
issue: issueRefSchema.optional(),
|
|
18037
18237
|
criterionId: uuidSchema.optional(),
|
|
@@ -18054,19 +18254,19 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18054
18254
|
});
|
|
18055
18255
|
}));
|
|
18056
18256
|
registerStrict("ask_questions", {
|
|
18057
|
-
description: "Ask the operator questions on an issue. Send them in one call
|
|
18257
|
+
description: "Ask the operator questions on an issue. Send them in one call. Each question gets a stable `Q-{n}` number, never reused; `options` are the choices you propose. Answers come back on every later read; read them before asking again. Answer one yourself with `answer_question` when your work settles it; withdraw one with `withdraw_question` if it should never have been asked. See \u201CFile decisions as questions\u201D in the doctrine `get_work_context` returns.",
|
|
18058
18258
|
inputSchema: askQuestionsInputSchema.shape
|
|
18059
18259
|
}, (args) => run(() => client2().askQuestions(args.issue, args.questions)));
|
|
18060
18260
|
registerStrict("withdraw_question", {
|
|
18061
|
-
description: "Withdraw a question by its `Q-{n}` number, in the organization named by `org`. Only the asker may withdraw, and the asker is the person your credential belongs to
|
|
18261
|
+
description: "Withdraw a question by its `Q-{n}` number, in the organization named by `org`. Only the asker may withdraw, and the asker is the person your credential belongs to. Use it for a question that should never have been asked; one your own work settled deserves `answer_question`, which keeps the answer. The question is kept for the audit trail, its number retires, and later reads no longer carry it.",
|
|
18062
18262
|
inputSchema: withdrawQuestionInputSchema.shape
|
|
18063
18263
|
}, (args) => run(() => client2().withdrawQuestion(args.issue, args.number)));
|
|
18064
18264
|
registerStrict("answer_question", {
|
|
18065
|
-
description: "Answer a question by its `Q-{n}` number
|
|
18265
|
+
description: "Answer a question by its `Q-{n}` number. Pass exactly one of `option` (a 1-based index into the choices offered) or `text`; both, or neither, is refused before anything is sent. Answering again revises, and every answer records who answered and which credential. An out-of-range index is refused `option_out_of_range`, naming how many choices there are; a withdrawn or unknown number, `question_not_found`.",
|
|
18066
18266
|
inputSchema: answerQuestionInputSchema
|
|
18067
18267
|
}, (args) => run(() => client2().answerQuestion(args.issue, args.number, { option: args.option, text: args.text })));
|
|
18068
18268
|
registerStrict("list_requirements", {
|
|
18069
|
-
description: "List requirements filtered by project and text: lifecycle (draft/approved/amended), the latest and approved revision numbers, the cross-cutting flag and the linked-epic count.",
|
|
18269
|
+
description: "List requirements filtered by project and text: lifecycle (draft/approved/amended), the latest and approved revision numbers, the cross-cutting flag and the linked-epic count. Up to 200 by default; page with `limit`/`offset`.",
|
|
18070
18270
|
inputSchema: listRequirementsFilterSchema.shape
|
|
18071
18271
|
}, (args) => run(() => client2().listRequirements(args), GUIDANCE_POINTER));
|
|
18072
18272
|
registerStrict("get_requirement", {
|
|
@@ -18074,11 +18274,11 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18074
18274
|
inputSchema: { requirement: requirementRefSchema, revision: positiveIntSchema.optional() }
|
|
18075
18275
|
}, (args) => run(() => client2().getRequirement(args.requirement, args.revision), GUIDANCE_POINTER));
|
|
18076
18276
|
registerStrict("create_requirement", {
|
|
18077
|
-
description: "Create a project-scoped requirement at revision 1 from `title` and `body`. Optional `summary` is a short outline, versioned with them; `area` is a free-text product-area label. The cross-cutting flag makes it bind every epic in the project on its own; never link one explicitly. See
|
|
18277
|
+
description: "Create a project-scoped requirement at revision 1 from `title` and `body`. Optional `summary` is a short outline, versioned with them; `area` is a free-text product-area label. The cross-cutting flag makes it bind every epic in the project on its own; never link one explicitly. See \u201CSearch before you create\u201D in the doctrine `get_work_context` returns.",
|
|
18078
18278
|
inputSchema: createRequirementInputSchema.shape
|
|
18079
18279
|
}, (args) => run(() => client2().createRequirement(args)));
|
|
18080
18280
|
registerStrict("update_requirement", {
|
|
18081
|
-
description: "Update a requirement. `title`, `body` and `summary` append a new revision \u2014 nothing is edited in place \u2014 and `note` records why. Area, cross-cutting and decision links are metadata, with no revision. Turning cross-cutting on is rejected while explicit links exist; unlink first. An empty edit is refused (400). See
|
|
18281
|
+
description: "Update a requirement. `title`, `body` and `summary` append a new revision \u2014 nothing is edited in place \u2014 and `note` records why. Area, cross-cutting and decision links are metadata, with no revision. Turning cross-cutting on is rejected while explicit links exist; unlink first. An empty edit is refused (400). See \u201CLink by key, never paste\u201D in the doctrine `get_work_context` returns.",
|
|
18082
18282
|
// Inline shape (updateRequirementInputSchema is a refine, no .shape) — add
|
|
18083
18283
|
// summary/area here explicitly (§210) and decisionLinks (PIPE-84 §6); the
|
|
18084
18284
|
// empty-edit refine is enforced by the API, not this shape.
|
|
@@ -18098,7 +18298,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18098
18298
|
inputSchema: approveRequirementInputSchema.shape
|
|
18099
18299
|
}, (args) => run(() => client2().approveRequirement(args.requirement, args.revision)));
|
|
18100
18300
|
registerStrict("list_conventions", {
|
|
18101
|
-
description: "List this organization\u2019s conventions \u2014 standing guidance that binds every project here
|
|
18301
|
+
description: "List this organization\u2019s conventions \u2014 standing guidance that binds every project here: display id (`CONV-{n}`), title, the standing text, lifecycle and revision numbers. Retired ones are included and marked. `text` matches title and body. Only approved, unretired conventions are delivered to agents.",
|
|
18102
18302
|
inputSchema: listConventionsFilterSchema.shape
|
|
18103
18303
|
}, (args) => run(() => client2().listConventions(args), GUIDANCE_POINTER));
|
|
18104
18304
|
registerStrict("get_convention", {
|
|
@@ -18110,7 +18310,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18110
18310
|
inputSchema: createConventionInputSchema.shape
|
|
18111
18311
|
}, (args) => run(() => client2().createConvention(args)));
|
|
18112
18312
|
registerStrict("update_convention", {
|
|
18113
|
-
description: "Amend a convention. `title` and `body` append a new revision \u2014 nothing is edited in place \u2014 and `note` records why. Supplying neither is an empty edit (400). Amended text naming one project is refused as on create. The amendment binds nobody until a person approves it. See
|
|
18313
|
+
description: "Amend a convention. `title` and `body` append a new revision \u2014 nothing is edited in place \u2014 and `note` records why. Supplying neither is an empty edit (400). Amended text naming one project is refused as on create. The amendment binds nobody until a person approves it. See \u201CConventions need a person\u2019s approval\u201D in the doctrine `get_work_context` returns.",
|
|
18114
18314
|
// Inline shape (updateConventionInputSchema is a refine, so it has no `.shape`);
|
|
18115
18315
|
// the empty-edit rule is enforced by the API, not by this shape.
|
|
18116
18316
|
inputSchema: {
|
|
@@ -18125,7 +18325,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18125
18325
|
inputSchema: listPromptsFilterSchema.shape
|
|
18126
18326
|
}, (args) => run(() => client2().listPrompts(args), GUIDANCE_POINTER));
|
|
18127
18327
|
registerStrict("get_prompt", {
|
|
18128
|
-
description: "Get one prompt by display id (`PROMPT-{n}`) or uuid: its current text, which is
|
|
18328
|
+
description: "Get one prompt by display id (`PROMPT-{n}`) or uuid: its current text, which is the latest revision, plus every earlier one with who saved it, when and why.",
|
|
18129
18329
|
inputSchema: { prompt: promptRefSchema }
|
|
18130
18330
|
}, (args) => run(() => client2().getPrompt(args.prompt), GUIDANCE_POINTER));
|
|
18131
18331
|
registerStrict("create_prompt", {
|
|
@@ -18133,7 +18333,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18133
18333
|
inputSchema: createPromptInputSchema.shape
|
|
18134
18334
|
}, (args) => run(() => client2().createPrompt(args)));
|
|
18135
18335
|
registerStrict("update_prompt", {
|
|
18136
|
-
description: "Amend a prompt. `title` and `body` append a new revision \u2014 nothing is edited in place \u2014 and `note` records why. `project` moves the attachment and `project: null` detaches it; neither writes a revision. Naming none of the three is an empty edit (400). See
|
|
18336
|
+
description: "Amend a prompt. `title` and `body` append a new revision \u2014 nothing is edited in place \u2014 and `note` records why. `project` moves the attachment and `project: null` detaches it; neither writes a revision. Naming none of the three is an empty edit (400). See \u201CHow to write here\u201D in the doctrine `get_work_context` returns.",
|
|
18137
18337
|
// Inline shape (updatePromptInputSchema is a refine, so it has no `.shape`),
|
|
18138
18338
|
// mirroring that schema's fields — it is the wording this copy follows, `.min(1)`
|
|
18139
18339
|
// bounds included, so a caller learns a blank body is refused from the advertised
|
|
@@ -18148,7 +18348,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18148
18348
|
}
|
|
18149
18349
|
}, ({ prompt, ...fields }) => run(() => client2().updatePrompt(prompt, fields)));
|
|
18150
18350
|
registerStrict("create_decision", {
|
|
18151
|
-
description: "Record one decision as a project-scoped record (`KEY-D{n}`): `title` names it, `body` says what was decided and why. It starts `proposed` and is frozen once accepted; changing it then means a new record plus `supersede_decision`. See
|
|
18351
|
+
description: "Record one decision as a project-scoped record (`KEY-D{n}`): `title` names it, `body` says what was decided and why. It starts `proposed` and is frozen once accepted; changing it then means a new record plus `supersede_decision`. See \u201CA decision record is written once\u201D in the doctrine `get_work_context` returns.",
|
|
18152
18352
|
inputSchema: createDecisionInputSchema.shape
|
|
18153
18353
|
}, (args) => run(() => client2().createDecision(args)));
|
|
18154
18354
|
registerStrict("get_decision", {
|
|
@@ -18156,11 +18356,11 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18156
18356
|
inputSchema: getDecisionInputSchema.shape
|
|
18157
18357
|
}, (args) => run(() => client2().getDecision(args.decision), GUIDANCE_POINTER));
|
|
18158
18358
|
registerStrict("list_decisions", {
|
|
18159
|
-
description: "List decision records filtered by `project` and `text`: each one\u2019s state, its replacement when superseded, and how many requirements and work items cite it. `text` matches title and body.",
|
|
18359
|
+
description: "List decision records filtered by `project` and `text`: each one\u2019s state, its replacement when superseded, and how many requirements and work items cite it. `text` matches title and body. Up to 200 by default; page with `limit`/`offset`.",
|
|
18160
18360
|
inputSchema: listDecisionsFilterSchema.shape
|
|
18161
18361
|
}, (args) => run(() => client2().listDecisions(args), GUIDANCE_POINTER));
|
|
18162
18362
|
registerStrict("update_decision", {
|
|
18163
|
-
description: "Correct a decision record while it is still `proposed`: `title`, `body`, or both. An edit naming nothing to change is refused
|
|
18363
|
+
description: "Correct a decision record while it is still `proposed`: `title`, `body`, or both. An edit naming nothing to change is refused before anything is sent. Once a record is `accepted` or `superseded` its text is frozen and the correction is refused `decision_frozen`, which says to write the change as a new record and supersede this one. Use `accept_decision` to accept; asking for `superseded` here is refused, because superseding must name the replacement.",
|
|
18164
18364
|
inputSchema: updateDecisionInputSchema
|
|
18165
18365
|
}, (args) => run(() => client2().updateDecision(args.decision, {
|
|
18166
18366
|
title: args.title,
|
|
@@ -18168,7 +18368,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18168
18368
|
status: args.status
|
|
18169
18369
|
})));
|
|
18170
18370
|
registerStrict("accept_decision", {
|
|
18171
|
-
description: "Accept a decision record that is still `proposed`: it becomes `accepted`, stamped with who accepted it and when, and its text is frozen from then on \u2014 changing it later means a new record plus `supersede_decision`.
|
|
18371
|
+
description: "Accept a decision record that is still `proposed`: it becomes `accepted`, stamped with who accepted it and when, and its text is frozen from then on \u2014 changing it later means a new record plus `supersede_decision`. A record that is already `accepted` or `superseded` is refused `decision_transition_not_allowed`, naming what it can still do.",
|
|
18172
18372
|
inputSchema: { decision: decisionRefSchema }
|
|
18173
18373
|
}, (args) => run(() => client2().updateDecision(args.decision, { status: "accepted" })));
|
|
18174
18374
|
registerStrict("supersede_decision", {
|
|
@@ -18184,19 +18384,19 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18184
18384
|
inputSchema: { content: contentRefSchema, revision: positiveIntSchema.optional() }
|
|
18185
18385
|
}, (args) => run(() => client2().getContent(args.content, args.revision), GUIDANCE_POINTER));
|
|
18186
18386
|
registerStrict("create_content", {
|
|
18187
|
-
description: "Create a project-scoped content draft at revision 1 from `title` and `body`. Optional `summary` is versioned with them; `area` is a free-text label; `author` is the byline credited on the draft, not the operator calling; `metadata` is stored passively. See
|
|
18387
|
+
description: "Create a project-scoped content draft at revision 1 from `title` and `body`. Optional `summary` is versioned with them; `area` is a free-text label; `author` is the byline credited on the draft, not the operator calling; `metadata` is stored passively. See \u201CHow to write here\u201D in the doctrine `get_work_context` returns.",
|
|
18188
18388
|
inputSchema: createContentInputSchema.shape
|
|
18189
18389
|
}, (args) => run(() => client2().createContent(args)));
|
|
18190
18390
|
registerStrict("update_content", {
|
|
18191
|
-
description: "Update a content draft. `title`, `body` or `summary` appends a revision and `note` says why; `area`, `author` and `metadata` are metadata only. An empty edit is refused (400). See
|
|
18391
|
+
description: "Update a content draft. `title`, `body` or `summary` appends a revision and `note` says why; `area`, `author` and `metadata` are metadata only. An empty edit is refused (400). See \u201CHow to write here\u201D in the doctrine `get_work_context` returns.",
|
|
18192
18392
|
inputSchema: updateContentInputSchema.shape
|
|
18193
18393
|
}, ({ content, ...fields }) => run(() => client2().updateContent(content, fields)));
|
|
18194
18394
|
registerStrict("create_deliverable", {
|
|
18195
|
-
description: "Add a per-platform deliverable to a content draft at revision 1, addressed `DRAFT-KEY/platform`; optional `body` seeds it. `platform` must be one of the project\u2019s active deliverable targets (see `list_projects`); any other slug is rejected. One deliverable per platform per draft; another wording is a revision (`update_deliverable`). See
|
|
18395
|
+
description: "Add a per-platform deliverable to a content draft at revision 1, addressed `DRAFT-KEY/platform`; optional `body` seeds it. `platform` must be one of the project\u2019s active deliverable targets (see `list_projects` with `detail: full`); any other slug is rejected. One deliverable per platform per draft; another wording is a revision (`update_deliverable`). See \u201CHow to write here\u201D in the doctrine `get_work_context` returns.",
|
|
18196
18396
|
inputSchema: createDeliverableInputSchema.shape
|
|
18197
18397
|
}, ({ content, ...fields }) => run(() => client2().createDeliverable(content, fields)));
|
|
18198
18398
|
registerStrict("update_deliverable", {
|
|
18199
|
-
description: "Append a revision of the post text to a deliverable, addressed `DRAFT-KEY/platform`; `note` says why. The platform itself is immutable. See
|
|
18399
|
+
description: "Append a revision of the post text to a deliverable, addressed `DRAFT-KEY/platform`; `note` says why. The platform itself is immutable. See \u201CHow to write here\u201D in the doctrine `get_work_context` returns.",
|
|
18200
18400
|
inputSchema: updateDeliverableInputSchema.shape
|
|
18201
18401
|
}, ({ deliverable, ...fields }) => run(() => client2().updateDeliverable(deliverable, fields)));
|
|
18202
18402
|
registerStrict("record_post", {
|
|
@@ -18206,7 +18406,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18206
18406
|
registerStrict("promote_deliverable", {
|
|
18207
18407
|
// The paste exception is stated because without it an agent holding the doctrine
|
|
18208
18408
|
// would refuse this tool's whole purpose.
|
|
18209
|
-
description: "Create a NEW issue from a deliverable (`DRAFT-KEY/platform`) with a protected link
|
|
18409
|
+
description: "Create a NEW issue from a deliverable (`DRAFT-KEY/platform`) with a protected link, which cannot be removed, pinning its current revision. Needs `title` and `type`; `status` defaults to triage, `body` seeds the issue. A `status` past triage or killed needs a `milestone`, else refused `milestone_required`. An archived milestone is refused `milestone_archived_write`. Seeding `body` with the post text is the exception to linking rather than pasting. See \u201CLink by key, never paste\u201D in the doctrine `get_work_context` returns.",
|
|
18210
18410
|
inputSchema: {
|
|
18211
18411
|
deliverable: external_exports.string().min(1),
|
|
18212
18412
|
title: external_exports.string().min(1),
|
|
@@ -18243,7 +18443,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18243
18443
|
inputSchema: uploadAssetInputSchema.shape
|
|
18244
18444
|
}, (args) => run(() => uploadAssetFromPath(client2(), apiBaseUrl, args)));
|
|
18245
18445
|
registerStrict("download_asset", {
|
|
18246
|
-
description: "Write an ACTIVE asset\u2019s bytes to a local file at `path`, created or overwritten
|
|
18446
|
+
description: "Write an ACTIVE asset\u2019s bytes to a local file at `path`, created or overwritten. Addressed by asset uuid. The bytes are checked against the recorded sha256; the result is metadata and the path.",
|
|
18247
18447
|
inputSchema: downloadAssetInputSchema.shape
|
|
18248
18448
|
}, (args) => run(() => downloadAssetToPath(client2(), args)));
|
|
18249
18449
|
}
|
|
@@ -18256,7 +18456,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18256
18456
|
inputSchema: unlinkAssetInputSchema.shape
|
|
18257
18457
|
}, (args) => run(() => client2().unlinkAsset(args.linkId)));
|
|
18258
18458
|
registerStrict("set_secret", {
|
|
18259
|
-
description: "Create a secret or add a new version under a project and environment. Do not send a sensitive value here \u2014 it lands in the chat transcript before the server can encrypt it; use `ppb secrets set` from stdin or `--from-file`. `name` is UPPER_SNAKE_CASE, `environment` is a slug (dev/prod). See
|
|
18459
|
+
description: "Create a secret or add a new version under a project and environment. Do not send a sensitive value here \u2014 it lands in the chat transcript before the server can encrypt it; use `ppb secrets set` from stdin or `--from-file`. `name` is UPPER_SNAKE_CASE, `environment` is a slug (dev/prod). See \u201CA secret value never travels in a tool call\u201D in the doctrine `get_work_context` returns.",
|
|
18260
18460
|
inputSchema: {
|
|
18261
18461
|
project: projectKeySchema,
|
|
18262
18462
|
environment: external_exports.string().min(1),
|
|
@@ -18290,7 +18490,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18290
18490
|
}
|
|
18291
18491
|
}, ({ project, environment, name, value }) => run(() => client2().rotateSecret(project, name, { env: environment, value })));
|
|
18292
18492
|
registerStrict("update_secret_description", {
|
|
18293
|
-
description: "Change a secret\u2019s description \u2014 metadata only: no new version, and nothing here returns a value. See
|
|
18493
|
+
description: "Change a secret\u2019s description \u2014 metadata only: no new version, and nothing here returns a value. See \u201CHow to write here\u201D in the doctrine `get_work_context` returns.",
|
|
18294
18494
|
inputSchema: {
|
|
18295
18495
|
project: projectKeySchema,
|
|
18296
18496
|
environment: external_exports.string().min(1),
|
|
@@ -18313,14 +18513,22 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18313
18513
|
description: "List this organization\u2019s agent instructions, one per scope \u2014 the texts composed into the guidance `get_work_context` returns. What is saved here may be newer than what your session composed at startup."
|
|
18314
18514
|
}, () => run(() => client2().listOrgInstructions(), GUIDANCE_POINTER));
|
|
18315
18515
|
registerStrict("set_org_instruction", {
|
|
18316
|
-
description: "Set one scope\u2019s organization-level agent instruction,
|
|
18516
|
+
description: "Set one scope\u2019s organization-level agent instruction, replacing that scope\u2019s body whole; over 4000 characters is refused, not truncated. Scopes are `general`, `requirements`, `content` and `work.<type>`. It writes the organization this call acts in, so passing `org` rewrites that organization\u2019s standing instructions to every agent working there. It needs the ADMIN role there. New sessions pick it up; running ones keep what they started with. See \u201CSetting an instruction replaces the whole body\u201D in the doctrine `get_work_context` returns.",
|
|
18317
18517
|
inputSchema: setOrgInstructionInputSchema.shape
|
|
18318
18518
|
}, (args) => run(() => client2().setOrgInstruction(args.scope, args.body)));
|
|
18319
18519
|
registerStrict("delete_org_instruction", {
|
|
18320
|
-
description: "Clear one scope\u2019s organization-level agent instruction, in the organization named by `org` \u2014 so this removes that organization\u2019s standing instruction to every agent working there. Clearing a scope that is already unset is an error naming it. It needs the ADMIN role in that organization
|
|
18520
|
+
description: "Clear one scope\u2019s organization-level agent instruction, in the organization named by `org` \u2014 so this removes that organization\u2019s standing instruction to every agent working there. Clearing a scope that is already unset is an error naming it. It needs the ADMIN role in that organization. New sessions pick up the change; running ones keep what they started with.",
|
|
18321
18521
|
inputSchema: deleteOrgInstructionInputSchema.shape
|
|
18322
18522
|
}, (args) => run(() => client2().deleteOrgInstruction(args.scope)));
|
|
18323
|
-
|
|
18523
|
+
const advertised = registry2.map(({ name, description, inputSchema, _meta }) => {
|
|
18524
|
+
const { $schema: _draft07, ...jsonSchema } = toJsonSchemaCompat(inputSchema, {
|
|
18525
|
+
strictUnions: true,
|
|
18526
|
+
pipeStrategy: "input"
|
|
18527
|
+
});
|
|
18528
|
+
return _meta === void 0 ? { name, description, inputSchema: jsonSchema } : { name, description, inputSchema: jsonSchema, _meta };
|
|
18529
|
+
});
|
|
18530
|
+
server2.server.setRequestHandler(ListToolsRequestSchema, () => ({ tools: advertised }));
|
|
18531
|
+
return { server: server2, registry: registry2 };
|
|
18324
18532
|
}
|
|
18325
18533
|
|
|
18326
18534
|
// src/main.ts
|
|
@@ -18386,7 +18594,7 @@ function loadMcpConfig() {
|
|
|
18386
18594
|
}
|
|
18387
18595
|
|
|
18388
18596
|
// src/version.ts
|
|
18389
|
-
var VERSION = "0.
|
|
18597
|
+
var VERSION = "0.54.0";
|
|
18390
18598
|
|
|
18391
18599
|
// src/main.ts
|
|
18392
18600
|
async function scopedClient(base, org) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pipebrain-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.54.0",
|
|
4
4
|
"description": "Pipebrain MCP server for Claude Code and Claude Desktop — issues, requirements, decision records, conventions, prompts, content, assets, metadata-only secrets, and more of the platform's modules over the Pipebrain API.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|