pipebrain-mcp 0.16.4 → 0.17.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 +141 -15
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -14988,6 +14988,7 @@ var orgRoleSchema = external_exports.enum(["OWNER", "ADMIN", "CONTRIBUTOR"]);
|
|
|
14988
14988
|
var orgKindSchema = external_exports.enum(["personal", "shared"]);
|
|
14989
14989
|
var inviteRoleSchema = external_exports.enum(["ADMIN", "CONTRIBUTOR"]);
|
|
14990
14990
|
var requirementLifecycleSchema = external_exports.enum(["draft", "approved", "amended"]);
|
|
14991
|
+
var decisionStatusSchema = external_exports.enum(["proposed", "accepted", "superseded"]);
|
|
14991
14992
|
var platformSchema = external_exports.enum(["bsky", "x", "reddit", "hn", "blog"]);
|
|
14992
14993
|
var assetStatusSchema = external_exports.enum(["pending", "active", "rejected"]);
|
|
14993
14994
|
var assetLinkTargetSchema = external_exports.enum([
|
|
@@ -15040,7 +15041,8 @@ var widgetOriginSchema = external_exports.string().trim().transform((raw) => raw
|
|
|
15040
15041
|
});
|
|
15041
15042
|
var emailSchema = external_exports.email({ message: "enter a valid email address" });
|
|
15042
15043
|
var passwordSchema = external_exports.string().min(8, { message: "password must be at least 8 characters" }).max(128, { message: "password must be at most 128 characters" });
|
|
15043
|
-
var
|
|
15044
|
+
var ACCEPTED_TOKEN_PREFIXES = ["pbt_", "plt_"];
|
|
15045
|
+
var tokenPrefixSchema = external_exports.string().regex(new RegExp(`^(?:${ACCEPTED_TOKEN_PREFIXES.join("|")})[0-9A-Za-z]{4}$`), {
|
|
15044
15046
|
message: "token prefix must be `pbt_` (or the legacy `plt_`) followed by 4 base62 characters"
|
|
15045
15047
|
});
|
|
15046
15048
|
var SECRET_NAME_RE = /^[A-Z_][A-Z0-9_]*$/;
|
|
@@ -15060,6 +15062,9 @@ var issueRefSchema = external_exports.string().min(1, { message: "issue referenc
|
|
|
15060
15062
|
var requirementRefSchema = external_exports.string().min(1, {
|
|
15061
15063
|
message: "requirement reference must not be empty \u2014 use a display id (`DWB-R4`) or uuid"
|
|
15062
15064
|
});
|
|
15065
|
+
var decisionRefSchema = external_exports.string().min(1, {
|
|
15066
|
+
message: "decision reference must not be empty \u2014 use a display id (`DWB-D7`) or uuid"
|
|
15067
|
+
});
|
|
15063
15068
|
var contentRefSchema = external_exports.string().min(1, {
|
|
15064
15069
|
message: "content reference must not be empty \u2014 use a display id (`DWB-C3`) or uuid"
|
|
15065
15070
|
});
|
|
@@ -15603,6 +15608,10 @@ var deliverableLinkChangesSchema = external_exports.strictObject({
|
|
|
15603
15608
|
add: external_exports.array(deliverableRefSchema).optional(),
|
|
15604
15609
|
remove: external_exports.array(deliverableRefSchema).optional()
|
|
15605
15610
|
});
|
|
15611
|
+
var decisionLinkChangesSchema = external_exports.strictObject({
|
|
15612
|
+
add: external_exports.array(decisionRefSchema).optional(),
|
|
15613
|
+
remove: external_exports.array(decisionRefSchema).optional()
|
|
15614
|
+
});
|
|
15606
15615
|
var updateIssueInputSchema = external_exports.strictObject({
|
|
15607
15616
|
issue: issueRefSchema,
|
|
15608
15617
|
title: external_exports.string().min(1, { message: "issue title must not be empty" }).optional(),
|
|
@@ -15613,7 +15622,8 @@ var updateIssueInputSchema = external_exports.strictObject({
|
|
|
15613
15622
|
environment: external_exports.string().min(1, { message: "environment must not be empty" }).optional(),
|
|
15614
15623
|
parent: issueRefSchema.nullable().optional(),
|
|
15615
15624
|
requirementLinks: requirementLinkChangesSchema.optional(),
|
|
15616
|
-
deliverableLinks: deliverableLinkChangesSchema.optional()
|
|
15625
|
+
deliverableLinks: deliverableLinkChangesSchema.optional(),
|
|
15626
|
+
decisionLinks: decisionLinkChangesSchema.optional()
|
|
15617
15627
|
});
|
|
15618
15628
|
var setStatusInputSchema = external_exports.strictObject({
|
|
15619
15629
|
issue: issueRefSchema,
|
|
@@ -15690,14 +15700,41 @@ var updateRequirementInputSchema = external_exports.strictObject({
|
|
|
15690
15700
|
body: markdownSchema.optional(),
|
|
15691
15701
|
area: external_exports.string().optional(),
|
|
15692
15702
|
note: external_exports.string().min(1, { message: "note must not be empty \u2014 omit it if there is nothing to record" }).optional(),
|
|
15693
|
-
crossCutting: external_exports.boolean().optional()
|
|
15694
|
-
|
|
15695
|
-
|
|
15703
|
+
crossCutting: external_exports.boolean().optional(),
|
|
15704
|
+
decisionLinks: decisionLinkChangesSchema.optional()
|
|
15705
|
+
}).refine((v) => v.title !== void 0 || v.body !== void 0 || v.summary !== void 0 || v.area !== void 0 || v.crossCutting !== void 0 || v.decisionLinks !== void 0, {
|
|
15706
|
+
message: "empty edit: supply at least one of `title`, `body`, `summary`, `area`, `crossCutting`, or `decisionLinks`"
|
|
15696
15707
|
});
|
|
15697
15708
|
var approveRequirementInputSchema = external_exports.strictObject({
|
|
15698
15709
|
requirement: requirementRefSchema,
|
|
15699
15710
|
revision: positiveIntSchema.optional()
|
|
15700
15711
|
});
|
|
15712
|
+
var createDecisionInputSchema = external_exports.strictObject({
|
|
15713
|
+
project: projectRefSchema,
|
|
15714
|
+
title: external_exports.string().min(1, { message: "decision title must not be empty" }),
|
|
15715
|
+
body: markdownSchema.min(1, { message: "decision body must not be empty" })
|
|
15716
|
+
});
|
|
15717
|
+
var getDecisionInputSchema = external_exports.strictObject({
|
|
15718
|
+
decision: decisionRefSchema
|
|
15719
|
+
});
|
|
15720
|
+
var updateDecisionInputSchema = external_exports.strictObject({
|
|
15721
|
+
decision: decisionRefSchema,
|
|
15722
|
+
title: external_exports.string().min(1, { message: "decision title must not be empty" }).optional(),
|
|
15723
|
+
body: markdownSchema.min(1, { message: "decision body must not be empty" }).optional(),
|
|
15724
|
+
status: decisionStatusSchema.optional()
|
|
15725
|
+
}).refine((v) => v.title !== void 0 || v.body !== void 0 || v.status !== void 0, {
|
|
15726
|
+
message: "empty edit: supply at least one of `title`, `body`, or `status`"
|
|
15727
|
+
});
|
|
15728
|
+
var supersedeDecisionInputSchema = external_exports.strictObject({
|
|
15729
|
+
decision: decisionRefSchema,
|
|
15730
|
+
replacedBy: decisionRefSchema
|
|
15731
|
+
});
|
|
15732
|
+
var listDecisionsFilterSchema = external_exports.strictObject({
|
|
15733
|
+
project: projectRefSchema.optional(),
|
|
15734
|
+
text: external_exports.string().optional(),
|
|
15735
|
+
limit: external_exports.number().int().min(1, { message: "limit must be at least 1" }).max(500, { message: "limit must be at most 500" }).optional(),
|
|
15736
|
+
offset: external_exports.number().int().nonnegative().optional()
|
|
15737
|
+
});
|
|
15701
15738
|
var claimedByFilterSchema = external_exports.union([
|
|
15702
15739
|
external_exports.literal("me"),
|
|
15703
15740
|
external_exports.literal("unclaimed"),
|
|
@@ -15965,6 +16002,15 @@ function scopesForTool(tool) {
|
|
|
15965
16002
|
});
|
|
15966
16003
|
}
|
|
15967
16004
|
|
|
16005
|
+
// ../../libs/agent-shared/dist/token-shape.js
|
|
16006
|
+
function isAcceptedTokenShape(value) {
|
|
16007
|
+
return ACCEPTED_TOKEN_PREFIXES.some((prefix) => value.startsWith(prefix));
|
|
16008
|
+
}
|
|
16009
|
+
var UNEXPANDED_PLACEHOLDER = /^\$(\{[^}]*\}|[A-Za-z_][A-Za-z0-9_]*)$/;
|
|
16010
|
+
function isUnexpandedPlaceholder(value) {
|
|
16011
|
+
return UNEXPANDED_PLACEHOLDER.test(value);
|
|
16012
|
+
}
|
|
16013
|
+
|
|
15968
16014
|
// ../../libs/api-client/dist/errors.js
|
|
15969
16015
|
var ApiError = class extends Error {
|
|
15970
16016
|
status;
|
|
@@ -16248,6 +16294,42 @@ var ApiClient = class _ApiClient {
|
|
|
16248
16294
|
body: revision === void 0 ? {} : { revision }
|
|
16249
16295
|
});
|
|
16250
16296
|
}
|
|
16297
|
+
// --- decision records (PIPE-84) --------------------------------------------
|
|
16298
|
+
//
|
|
16299
|
+
// Five routes, no DELETE: PIPE-73 made four record kinds deletable and this epic
|
|
16300
|
+
// puts itself out of that scope explicitly, so a decision record is superseded
|
|
16301
|
+
// rather than removed — which leaves the original readable and says what replaced
|
|
16302
|
+
// it. There is deliberately no `deleteDecision` method here, and there is no route
|
|
16303
|
+
// for one to call.
|
|
16304
|
+
createDecision(input) {
|
|
16305
|
+
return this.request("POST", "/api/v1/decisions", { body: input });
|
|
16306
|
+
}
|
|
16307
|
+
listDecisions(filter = {}) {
|
|
16308
|
+
return this.requestList("GET", "/api/v1/decisions", { query: filter });
|
|
16309
|
+
}
|
|
16310
|
+
getDecision(ref) {
|
|
16311
|
+
return this.request("GET", `/api/v1/decisions/${enc(ref)}`);
|
|
16312
|
+
}
|
|
16313
|
+
/**
|
|
16314
|
+
* The proposed-stage title/body edit AND the move to `accepted`, on the one route
|
|
16315
|
+
* (contract §5). Both refusals are the server's: an edit once the record is
|
|
16316
|
+
* `accepted`/`superseded`, and any transition that is not the next step in
|
|
16317
|
+
* `proposed → accepted → superseded`, each surface as a typed 422.
|
|
16318
|
+
*/
|
|
16319
|
+
updateDecision(ref, fields) {
|
|
16320
|
+
return this.request("PATCH", `/api/v1/decisions/${enc(ref)}`, { body: fields });
|
|
16321
|
+
}
|
|
16322
|
+
/**
|
|
16323
|
+
* Supersedes `ref` with the record at `replacedBy`, in one call, because a
|
|
16324
|
+
* superseded record with nowhere to point is a dead end for whoever opens it. This
|
|
16325
|
+
* is the ONLY way into `superseded` — {@link updateDecision} refuses that target
|
|
16326
|
+
* rather than write a record that names no replacement.
|
|
16327
|
+
*/
|
|
16328
|
+
supersedeDecision(ref, replacedBy) {
|
|
16329
|
+
return this.request("POST", `/api/v1/decisions/${enc(ref)}/supersede`, {
|
|
16330
|
+
body: { replacedBy }
|
|
16331
|
+
});
|
|
16332
|
+
}
|
|
16251
16333
|
// --- content ---------------------------------------------------------------
|
|
16252
16334
|
createContent(input) {
|
|
16253
16335
|
return this.request("POST", "/api/v1/content", { body: input });
|
|
@@ -16454,6 +16536,22 @@ var ApiClient = class _ApiClient {
|
|
|
16454
16536
|
query: { q }
|
|
16455
16537
|
});
|
|
16456
16538
|
}
|
|
16539
|
+
// --- the waiting queue -----------------------------------------------------
|
|
16540
|
+
/**
|
|
16541
|
+
* Every gate awaiting a person across ALL projects of the caller's organisation
|
|
16542
|
+
* (PIPE-87): revisions awaiting approval, questions awaiting an answer, and epics
|
|
16543
|
+
* in `verify` with a criterion not yet passed, longest wait first.
|
|
16544
|
+
*
|
|
16545
|
+
* Takes NO project and NO org: a queue you point at a project is the per-project
|
|
16546
|
+
* surface this replaces, and org scope is the client's own (`X-Org-Id` for a
|
|
16547
|
+
* session, the token's org for a bearer caller). `mine` narrows to the gates whose
|
|
16548
|
+
* work the viewer claimed — plus the requirement gates, which have no claimant at
|
|
16549
|
+
* all and would otherwise be waited on by nobody; omitting it is the wide view.
|
|
16550
|
+
*/
|
|
16551
|
+
listWaiting(filter) {
|
|
16552
|
+
const query = filter?.mine === true ? { mine: "1" } : void 0;
|
|
16553
|
+
return this.requestList("GET", "/api/v1/waiting", { query });
|
|
16554
|
+
}
|
|
16457
16555
|
// --- secrets ---------------------------------------------------------------
|
|
16458
16556
|
// Metadata only: no method here returns a secret VALUE (the reveal plane is a
|
|
16459
16557
|
// separate surface). Every response type is a `*View` with no `value` field.
|
|
@@ -16834,6 +16932,19 @@ function createMcpServer(client2, apiBaseUrl, orgInstructions2 = /* @__PURE__ */
|
|
|
16834
16932
|
key: projectKeySchema
|
|
16835
16933
|
}
|
|
16836
16934
|
}, (args) => run(() => client2.createProject(args)));
|
|
16935
|
+
registerTool("list_waiting", {
|
|
16936
|
+
description: "Every gate awaiting a PERSON across ALL projects in the token\u2019s org, longest wait first: a requirement revision awaiting approval, a question awaiting an answer, and an epic in `verify` holding at least one criterion not yet passed. Each entry names its project, the ref the action is taken on (`KEY-R{n}` for a revision, the ISSUE\u2019s `KEY-{n}` for a question or an epic), and `waitingSince` \u2014 the instant the wait began, not a duration. Nothing else is a gate: work in `triage` never appears, and neither does an epic in verify whose criteria have ALL passed. READ-ONLY, and there is no tool on this surface that clears any of the three \u2014 approving a revision, answering a question and marking a manual criterion are all decisions a person takes in the web app. Use it to see what your work is blocked on before asking, rather than to act on it."
|
|
16937
|
+
// NO arguments — not even an org. Org scope arrives from the token, exactly as
|
|
16938
|
+
// it does for `list_projects`, and an org parameter would open a cross-org read
|
|
16939
|
+
// path that has no precedent anywhere on this surface. The viewer narrowing the
|
|
16940
|
+
// web queue offers is filter state on that view, not something an agent picks.
|
|
16941
|
+
}, () => (
|
|
16942
|
+
// `{ items }`, mirroring the HTTP envelope rather than unwrapping to a bare
|
|
16943
|
+
// array like the sibling list tools: AC-7 asks that an agent read the SAME set
|
|
16944
|
+
// the queue shows, and the two surfaces answering in the same shape is what
|
|
16945
|
+
// makes that comparable at a glance.
|
|
16946
|
+
run(async () => ({ items: await client2.listWaiting() }))
|
|
16947
|
+
));
|
|
16837
16948
|
registerTool("create_milestone", {
|
|
16838
16949
|
description: "Create a milestone on a project. `token` is the short ref issues point at via their `milestone` field (e.g. `M1`) and is unique per project (a duplicate is rejected); `label` is its display name (e.g. `Feature Verification`). Optional `position` orders it (0-based, defaults to last); optional `active` marks it the current milestone \u2014 AT MOST ONE milestone per project is active, so `active: true` transactionally DEMOTES the previously active one rather than failing. Returns the created milestone (\xA77).",
|
|
16839
16950
|
inputSchema: {
|
|
@@ -16935,7 +17046,7 @@ function createMcpServer(client2, apiBaseUrl, orgInstructions2 = /* @__PURE__ */
|
|
|
16935
17046
|
}
|
|
16936
17047
|
}, ({ issue: issue2, ...fields }) => run(() => client2.promoteFeedback(issue2, fields)));
|
|
16937
17048
|
registerTool("update_issue", {
|
|
16938
|
-
description: 'Update any subset of an issue\u2019s title, body, type, priority, milestone, environment, parent (self/cycle rejected), requirement_links (add/remove; adding a link to an unapproved requirement is rejected),
|
|
17049
|
+
description: 'Update any subset of an issue\u2019s title, body, type, priority, milestone, environment, parent (self/cycle rejected), requirement_links (add/remove; adding a link to an unapproved requirement is rejected), deliverable_links (add/remove content-deliverable links, add pins at the current revision), or decision_links (add/remove decision records this work sits under \u2014 `KEY-D{n}`, from `list_decisions`). Changing type away from epic is rejected while the epic has criteria (\xA77). Cite the decision record a piece of work implements rather than restating what was decided in its body: the record is where the reasoning stays correct, and a body repeating it goes stale the moment the decision is superseded. Link another record by key plus title on first mention; never paste its text into a body \u2014 the source changes, the copy does not, and a stale copy gets built. A decision you need from the operator is filed as a question via `ask_questions`, never written as an "Open questions" heading inside a body \u2014 a heading prompts nobody and records no answer, so a later session cannot tell whether the decision was made. Cross-cutting requirements bind every epic automatically and arrive with `get_issue` \u2014 never restate them in a body and never add them to `requirement_links`; an explicit link pins the linked document\u2019s approved revision.',
|
|
16939
17050
|
inputSchema: updateIssueInputSchema.shape
|
|
16940
17051
|
}, ({ issue: issue2, ...fields }) => run(() => client2.updateIssue(issue2, fields)));
|
|
16941
17052
|
registerTool("set_status", {
|
|
@@ -17021,10 +17132,10 @@ function createMcpServer(client2, apiBaseUrl, orgInstructions2 = /* @__PURE__ */
|
|
|
17021
17132
|
inputSchema: createRequirementInputSchema.shape
|
|
17022
17133
|
}, (args) => run(() => client2.createRequirement(args)));
|
|
17023
17134
|
registerTool("update_requirement", {
|
|
17024
|
-
description: "Update a requirement: title, body, and/or summary APPENDS a new revision (nothing is edited in place; summary versions like title/body); optional note records why. Optional area (product-area label) and
|
|
17135
|
+
description: "Update a requirement: title, body, and/or summary APPENDS a new revision (nothing is edited in place; summary versions like title/body); optional note records why. Optional area (product-area label), cross_cutting, and decision_links (add/remove decision records this rule sits under \u2014 `KEY-D{n}`, from `list_decisions`) are metadata changes (no revision) \u2014 toggling cross_cutting ON is rejected while explicit links exist; unlink first. Supplying none of title/body/summary/area/cross_cutting/decision_links is an empty edit (400) (\xA77). Cite the decision record a rule rests on rather than restating what was decided in its body: the record is where the reasoning stays correct, and a body repeating it goes stale the moment the decision is superseded. Link another record by key plus title on first mention; never paste its text into a body \u2014 the source changes, the copy does not, and a stale copy gets built.",
|
|
17025
17136
|
// Inline shape (updateRequirementInputSchema is a refine, no .shape) — add
|
|
17026
|
-
// summary/area here explicitly (§210)
|
|
17027
|
-
// the API, not this shape.
|
|
17137
|
+
// summary/area here explicitly (§210) and decisionLinks (PIPE-84 §6); the
|
|
17138
|
+
// empty-edit refine is enforced by the API, not this shape.
|
|
17028
17139
|
inputSchema: {
|
|
17029
17140
|
requirement: requirementRefSchema,
|
|
17030
17141
|
title: external_exports.string().min(1).optional(),
|
|
@@ -17032,13 +17143,30 @@ function createMcpServer(client2, apiBaseUrl, orgInstructions2 = /* @__PURE__ */
|
|
|
17032
17143
|
body: markdownSchema.optional(),
|
|
17033
17144
|
area: external_exports.string().optional(),
|
|
17034
17145
|
note: external_exports.string().min(1).optional(),
|
|
17035
|
-
crossCutting: external_exports.boolean().optional()
|
|
17146
|
+
crossCutting: external_exports.boolean().optional(),
|
|
17147
|
+
decisionLinks: decisionLinkChangesSchema.optional()
|
|
17036
17148
|
}
|
|
17037
17149
|
}, ({ requirement, ...fields }) => run(() => client2.updateRequirement(requirement, fields)));
|
|
17038
17150
|
registerTool("approve_requirement", {
|
|
17039
17151
|
description: "Approve (pin) a requirement\u2019s latest revision, or a specified `revision`, recording it in approvals history and advancing the approved revision that downstream links and reads build against (\xA75, \xA77).",
|
|
17040
17152
|
inputSchema: approveRequirementInputSchema.shape
|
|
17041
17153
|
}, (args) => run(() => client2.approveRequirement(args.requirement, args.revision)));
|
|
17154
|
+
registerTool("create_decision", {
|
|
17155
|
+
description: "Record ONE decision as a project-scoped decision record (`KEY-D{n}`): `title` names the decision and `body` says what was decided and why. A new record starts `proposed`; moving it to `accepted` IS the acceptance \u2014 there is no separate approval step. Returns the created record. A decision record is written ONCE. Title and body can be corrected only while it is `proposed`; from `accepted` onward the record is frozen, and the way to change a decision is to write a NEW record and `supersede_decision` the old one \u2014 which leaves the original readable and says where the current decision went. Nothing on any surface deletes one. Search before you create: `list_decisions` takes a `text` filter matching TITLE AND BODY \u2014 run it on the distinctive nouns first. Sessions do not see each other, so re-arguing a settled question leaves two answers on the ledger with nothing saying which one holds. Link another record by key plus title on first mention; never paste its text into a body \u2014 the source changes, the copy does not, and a stale copy gets built.",
|
|
17156
|
+
inputSchema: createDecisionInputSchema.shape
|
|
17157
|
+
}, (args) => run(() => client2.createDecision(args)));
|
|
17158
|
+
registerTool("get_decision", {
|
|
17159
|
+
description: "Get a decision record by display id (`KEY-D{n}`) or uuid: title, body, lifecycle state (`proposed`/`accepted`/`superseded`), who accepted it and when, the record that replaced it, where the CURRENT decision lives when this one has been superseded, and everything that references it \u2014 every requirement and every work item, gathered from both directions.",
|
|
17160
|
+
inputSchema: getDecisionInputSchema.shape
|
|
17161
|
+
}, (args) => run(() => client2.getDecision(args.decision)));
|
|
17162
|
+
registerTool("list_decisions", {
|
|
17163
|
+
description: "List decision records filtered by `project` and `text`: display id, title, lifecycle state, the replacement when superseded, and how many requirements and work items cite each one. `text` matches over TITLE AND BODY, so search on what a decision was about rather than on what someone chose to call it. Read the decisions before re-opening a settled question: an `accepted` record is the project\u2019s answer, and arguing it again inside an issue or requirement body produces a second answer with nothing saying which one holds. If the answer genuinely has changed, record a NEW decision and `supersede_decision` the old one rather than editing anything.",
|
|
17164
|
+
inputSchema: listDecisionsFilterSchema.shape
|
|
17165
|
+
}, (args) => run(() => client2.listDecisions(args)));
|
|
17166
|
+
registerTool("supersede_decision", {
|
|
17167
|
+
description: "Replace an ACCEPTED decision record with another one: `decision` moves to `superseded` and names `replacedBy` as where the current decision now lives, so whoever opens the old record is sent to the standing one. This is the only way a decision changes \u2014 the original is never edited and never removed, and the whole chain stays readable. Refused when the record is still `proposed` (nothing has been decided yet, so correct it instead), when a record is named as its own replacement, when the replacement belongs to another project, or when the replacement\u2019s own chain already leads back to this record. Returns the superseded record.",
|
|
17168
|
+
inputSchema: supersedeDecisionInputSchema.shape
|
|
17169
|
+
}, (args) => run(() => client2.supersedeDecision(args.decision, args.replacedBy)));
|
|
17042
17170
|
registerTool("list_content", {
|
|
17043
17171
|
description: "List content drafts filtered by project, text, area, and author: display id (KEY-C{n}), title, summary (latest revision), area, author, latest revision number, deliverable count, and linked-issue count. Content is a project-scoped sibling of requirements \u2014 raw material that gets promoted into work, not an issue type.",
|
|
17044
17172
|
inputSchema: listContentFilterSchema.shape
|
|
@@ -17188,17 +17316,15 @@ ${issues.map((issue2) => ` - ${issue2}`).join("\n")}`);
|
|
|
17188
17316
|
|
|
17189
17317
|
// src/config.ts
|
|
17190
17318
|
var import_dotenv2 = __toESM(require_main(), 1);
|
|
17191
|
-
var UNEXPANDED_PLACEHOLDER = /^\$(\{[^}]*\}|[A-Za-z_][A-Za-z0-9_]*)$/;
|
|
17192
|
-
var TOKEN_PREFIXES = ["pbt_", "plt_"];
|
|
17193
17319
|
var apiTokenSchema2 = external_exports.string().min(1).superRefine((value, ctx) => {
|
|
17194
|
-
if (
|
|
17320
|
+
if (isUnexpandedPlaceholder(value)) {
|
|
17195
17321
|
ctx.addIssue({
|
|
17196
17322
|
code: "custom",
|
|
17197
17323
|
message: `is the literal string \`${value}\` \u2014 an unexpanded shell placeholder, not a token. The variable was unset in the environment that launched this server, so nothing substituted it. Export a pbt_ token before launching, or point the launcher at a wrapper that reads your token file.`
|
|
17198
17324
|
});
|
|
17199
17325
|
return;
|
|
17200
17326
|
}
|
|
17201
|
-
if (!
|
|
17327
|
+
if (!isAcceptedTokenShape(value)) {
|
|
17202
17328
|
ctx.addIssue({
|
|
17203
17329
|
code: "custom",
|
|
17204
17330
|
message: "is not a Pipebrain API token \u2014 expected `pbt_\u2026` (legacy `plt_\u2026` accepted) (value withheld). Mint one under Settings \u2192 Organization \u2192 API tokens."
|
|
@@ -17235,7 +17361,7 @@ function loadMcpConfig() {
|
|
|
17235
17361
|
}
|
|
17236
17362
|
|
|
17237
17363
|
// src/version.ts
|
|
17238
|
-
var VERSION = "0.
|
|
17364
|
+
var VERSION = "0.17.0";
|
|
17239
17365
|
|
|
17240
17366
|
// src/main.ts
|
|
17241
17367
|
var config2 = loadMcpConfig();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pipebrain-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Pipebrain MCP server for Claude Code and Claude Desktop — issues, requirements, content, assets, and metadata-only secrets over the Pipebrain API.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|