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.
Files changed (2) hide show
  1. package/dist/main.js +141 -15
  2. 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 tokenPrefixSchema = external_exports.string().regex(/^(plt|pbt)_[0-9A-Za-z]{4}$/, {
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
- }).refine((v) => v.title !== void 0 || v.body !== void 0 || v.summary !== void 0 || v.area !== void 0 || v.crossCutting !== void 0, {
15695
- message: "empty edit: supply at least one of `title`, `body`, `summary`, `area`, or `crossCutting`"
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), or deliverable_links (add/remove content-deliverable links, add pins at the current revision). Changing type away from epic is rejected while the epic has criteria (\xA77). 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.',
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 cross_cutting 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 is an empty edit (400) (\xA77). 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.",
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); the empty-edit refine is enforced by
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 (UNEXPANDED_PLACEHOLDER.test(value)) {
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 (!TOKEN_PREFIXES.some((prefix) => value.startsWith(prefix))) {
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.16.4";
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.16.4",
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",