pipebrain-mcp 0.32.0 → 0.34.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/README.md +1 -1
- package/dist/main.js +129 -39
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ at each launch.
|
|
|
32
32
|
|
|
33
33
|
| Variable | Meaning |
|
|
34
34
|
| --- | --- |
|
|
35
|
-
| `PIPEBRAIN_TOKEN` | Your `pbt_…` API token (Settings → Organization → API tokens).
|
|
35
|
+
| `PIPEBRAIN_TOKEN` | Your `pbt_…` API token (Settings → Organization → API tokens). |
|
|
36
36
|
| `PIPEBRAIN_API_URL` | API base URL, e.g. `https://api.pipebrain.dev`. Required — there is no default, so a session can always tell which environment it is bound to. |
|
|
37
37
|
|
|
38
38
|
Secret VALUES never cross this server: the secrets tools return metadata only, and there
|
package/dist/main.js
CHANGED
|
@@ -356,9 +356,52 @@ var require_main = __commonJS({
|
|
|
356
356
|
}
|
|
357
357
|
});
|
|
358
358
|
|
|
359
|
+
// ../../libs/agent-shared/dist/agent-config.js
|
|
360
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
361
|
+
import { homedir } from "node:os";
|
|
362
|
+
import { join } from "node:path";
|
|
363
|
+
|
|
364
|
+
// ../../libs/agent-shared/dist/agent-config-manifest.js
|
|
365
|
+
var AGENT_CONFIG_DIGEST = "898da8356dac55bd14531dcb5440aa3bd7bbcaa72e873d0fee4a102e3d718ee1";
|
|
366
|
+
|
|
367
|
+
// ../../libs/agent-shared/dist/agent-config.js
|
|
368
|
+
function agentConfigDir(env = process.env) {
|
|
369
|
+
const configured = env.CLAUDE_CONFIG_DIR;
|
|
370
|
+
return configured !== void 0 && configured !== "" ? configured : join(homedir(), ".claude");
|
|
371
|
+
}
|
|
372
|
+
var STAMP_REL = "pipebrain/installed.json";
|
|
373
|
+
function readInstalledStamp(dir) {
|
|
374
|
+
const file2 = join(dir, STAMP_REL);
|
|
375
|
+
if (!existsSync(file2))
|
|
376
|
+
return null;
|
|
377
|
+
try {
|
|
378
|
+
const parsed = JSON.parse(readFileSync(file2, "utf8"));
|
|
379
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
380
|
+
return null;
|
|
381
|
+
const stamp = parsed;
|
|
382
|
+
return typeof stamp.configDigest === "string" && stamp.configDigest !== "" ? {
|
|
383
|
+
configDigest: stamp.configDigest,
|
|
384
|
+
installedBy: typeof stamp.installedBy === "string" ? stamp.installedBy : "unknown",
|
|
385
|
+
installedAt: typeof stamp.installedAt === "string" ? stamp.installedAt : "unknown"
|
|
386
|
+
} : null;
|
|
387
|
+
} catch {
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
function agentConfigStatus(dir, shipped = AGENT_CONFIG_DIGEST) {
|
|
392
|
+
const stamp = readInstalledStamp(dir);
|
|
393
|
+
if (stamp === null)
|
|
394
|
+
return "absent";
|
|
395
|
+
return stamp.configDigest === shipped ? "current" : "behind";
|
|
396
|
+
}
|
|
397
|
+
var UPDATE_COMMAND = "ppb setup";
|
|
398
|
+
function agentConfigNotice(status) {
|
|
399
|
+
return status === "behind" ? `This machine's shared agent configuration is not the one this build ships \u2014 run \`${UPDATE_COMMAND}\` to see what would change and update it.` : null;
|
|
400
|
+
}
|
|
401
|
+
|
|
359
402
|
// ../../libs/agent-shared/dist/asset-transfer.js
|
|
360
403
|
import { createHash } from "node:crypto";
|
|
361
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
404
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "node:fs";
|
|
362
405
|
import { basename, extname } from "node:path";
|
|
363
406
|
var MIME_BY_EXT = {
|
|
364
407
|
".png": "image/png",
|
|
@@ -381,9 +424,9 @@ function guessContentType(filename) {
|
|
|
381
424
|
return MIME_BY_EXT[extname(filename).toLowerCase()] ?? "application/octet-stream";
|
|
382
425
|
}
|
|
383
426
|
async function uploadAssetFromPath(client2, apiBaseUrl, input) {
|
|
384
|
-
if (!
|
|
427
|
+
if (!existsSync2(input.path))
|
|
385
428
|
throw new Error(`file not found: ${input.path}`);
|
|
386
|
-
const bytes =
|
|
429
|
+
const bytes = readFileSync2(input.path);
|
|
387
430
|
const filename = basename(input.path);
|
|
388
431
|
const contentType = input.contentType ?? guessContentType(filename);
|
|
389
432
|
const sha256 = createHash("sha256").update(bytes).digest("hex");
|
|
@@ -433,21 +476,14 @@ function apiBindingLabel(apiBaseUrl) {
|
|
|
433
476
|
var DOCTRINE = "# Pipebrain doctrine\n\nHow an agent works inside Pipebrain. These rules ship with the product and read the same in every organization, because each exists where disagreeing breaks something the product guarantees rather than merely changing house style.\n\nHow your organization *writes* is its own text, edited in Settings, and follows this document under a heading naming each scope.\n\n## Decisions are yours; escalate on consequence\n\nInside an approved requirement you choose the design, the libraries, the structure, and the sequencing. Do not ask, report, or seek ratification for the choice. Escalate on consequence, not uncertainty: a choice that changes what a user sees, costs money, or contradicts an approved requirement is filed as a question with the options you propose; everything else you decide and carry on. A technical choice worth keeping is a decision record, never a paragraph.\n\n## Every loose end has one destination, and none of them is prose\n\nAnything you notice that is not the work in front of you goes to exactly one of three places: a question, when the operator has to choose; a new issue at triage, when it is real work outside this item's scope; or nowhere. Nowhere is the default, and the right outcome whenever knowing the thing would change nothing anyone does. Never open a section called notes, caveats, observations, things to know, or next steps in a body or a comment. Filing is the report: an item recorded and then repeated in prose asks the operator to triage it twice.\n\n## A decision you need is filed as a question, never written into a body\n\nAn open decision belongs in a question on the issue, filed with `ask_questions`, carrying the options you propose. It does not belong in a body \u2014 not under a heading, and not in a field. A decision written into prose prompts nobody and records no answer, so the operator has no place to make it and a later session cannot tell whether it ever was. Retract one your own work has since settled, so nothing asks the operator to decide something nobody waits on.\n\n## Search before you create\n\nLook for the existing record before adding one. A duplicate splits one thing's history across two identifiers, and the split is permanent \u2014 nothing merges them afterward.\n\n## Link by key; never paste another record's text\n\nReference a requirement, a decision, or an issue by its key, with its title on first mention. Never paste its text into a body. The source changes and the copy does not, and the stale copy is what gets built.\n\n## Cross-cutting requirements arrive on their own\n\nThey bind every epic automatically and come back with a work-item read, at their latest approved revision. Never restate them in a body and never add them as explicit requirement links. An explicit link pins a document at an approved revision, which is a different act.\n\n## Verification modes follow what can be driven\n\nBefore assigning a mode, establish what this project can drive end to end: a harness, and the command that runs it. A criterion is `auto` when an evaluator can drive the input and read the result against the running system, including behavior shown in an interface wherever such a harness exists. What qualifies it is the evaluator's own run, never the existence of a test the implementer wrote.\n\nMark `manual` only when the verdict needs a person's judgment. A criterion you cannot describe as drivable is usually worded too vaguely: reword it against the observable rather than fall back to a person. A `manual` criterion carries instructions for whoever checks it by hand, written for someone who has not seen the code or the conversation that produced it.\n\nA manual criterion carrying no recorded artifact is the human gate working as designed rather than a coverage gap, so it is neither a finding nor weaker than one carrying evidence.\n\n## The bar for filing a bug\n\nFile a bug only when the observed behavior breaks an approved requirement or a defined acceptance criterion, and name which one before you file. Failing that, it is not a bug: code you find unclear, an edge case nobody specified, a performance number nobody set, and anything you would phrase as \"this could be better\" are opinions about the product, so file each as feedback or not at all. The bar exists because a queue holding both real defects and unshared taste has to be read entirely before any of it can be trusted.\n\n## A decision record is written once\n\nIt states what was decided and why, at the time it was decided, and is not edited as understanding moves. A superseding record replaces it, and the chain of the two is the account of how the thinking changed \u2014 which an edited record destroys. Read the existing records before re-opening a settled question.\n\n## A secret value never travels in a tool call\n\nNo tool here returns one, and that is deliberate rather than an omission to be fixed. Nor should you put one in: a value typed into a tool call is in the transcript before the server can encrypt it, so it is already disclosed and the only remedy left is rotation. Set values with the command-line tool, from a file or on standard input, and inject them at run time rather than reading them into context.\n\n## Setting an instruction replaces the whole body\n\nThere is no partial update. Writing an instruction stores exactly the body you supply and discards everything that was there, so a partial write destroys the rest of the text. The procedure is: read the live value, compose the complete replacement, write it, then read the live value again to confirm what landed.\n\nWhat you are reading now is a snapshot taken when this session started, and so are the tool descriptions. An edit reaches the next session, never this one, so reading the text back after a write shows the pre-edit value. An agent that reports a diff of what it just wrote has read the snapshot instead of the record.\n";
|
|
434
477
|
|
|
435
478
|
// ../../libs/agent-shared/dist/resolve-agent-env.js
|
|
436
|
-
function pick(env,
|
|
437
|
-
const
|
|
438
|
-
|
|
439
|
-
return { value: fromPrimary, source: primary };
|
|
440
|
-
}
|
|
441
|
-
const fromLegacy = env[legacy];
|
|
442
|
-
if (fromLegacy !== void 0 && fromLegacy !== "") {
|
|
443
|
-
return { value: fromLegacy, source: legacy };
|
|
444
|
-
}
|
|
445
|
-
return void 0;
|
|
479
|
+
function pick(env, name) {
|
|
480
|
+
const value = env[name];
|
|
481
|
+
return value !== void 0 && value !== "" ? { value, source: name } : void 0;
|
|
446
482
|
}
|
|
447
483
|
function resolveAgentEnv(env) {
|
|
448
484
|
return {
|
|
449
|
-
apiUrl: pick(env, "PIPEBRAIN_API_URL"
|
|
450
|
-
token: pick(env, "PIPEBRAIN_TOKEN"
|
|
485
|
+
apiUrl: pick(env, "PIPEBRAIN_API_URL"),
|
|
486
|
+
token: pick(env, "PIPEBRAIN_TOKEN")
|
|
451
487
|
};
|
|
452
488
|
}
|
|
453
489
|
|
|
@@ -15044,9 +15080,9 @@ var widgetOriginSchema = external_exports.string().trim().transform((raw) => raw
|
|
|
15044
15080
|
});
|
|
15045
15081
|
var emailSchema = external_exports.email({ message: "enter a valid email address" });
|
|
15046
15082
|
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" });
|
|
15047
|
-
var ACCEPTED_TOKEN_PREFIXES = ["pbt_"
|
|
15083
|
+
var ACCEPTED_TOKEN_PREFIXES = ["pbt_"];
|
|
15048
15084
|
var tokenPrefixSchema = external_exports.string().regex(new RegExp(`^(?:${ACCEPTED_TOKEN_PREFIXES.join("|")})[0-9A-Za-z]{4}$`), {
|
|
15049
|
-
message: "token prefix must be `pbt_`
|
|
15085
|
+
message: "token prefix must be `pbt_` followed by 4 base62 characters"
|
|
15050
15086
|
});
|
|
15051
15087
|
var SECRET_NAME_RE = /^[A-Z_][A-Z0-9_]*$/;
|
|
15052
15088
|
var secretNameSchema = external_exports.string().regex(SECRET_NAME_RE, {
|
|
@@ -15589,6 +15625,22 @@ var orgInstructionSchema = external_exports.object({
|
|
|
15589
15625
|
body: external_exports.string().min(1),
|
|
15590
15626
|
createdBy: uuidSchema.nullable(),
|
|
15591
15627
|
viaTokenId: uuidSchema.nullable(),
|
|
15628
|
+
/**
|
|
15629
|
+
* sha256 of the shipped default this row was SEEDED from, or null when nothing seeded
|
|
15630
|
+
* it — a row written by a person, a row re-created after its scope was cleared, or a row
|
|
15631
|
+
* that predates seeding.
|
|
15632
|
+
*
|
|
15633
|
+
* It is what lets a settings page say the shipped default has MOVED since this
|
|
15634
|
+
* organization got it, without the product ever applying that move: comparing this digest
|
|
15635
|
+
* against the running release's default for the same scope is the whole signal. It is not
|
|
15636
|
+
* updated by an edit, because what it records is what was seeded — which stays true after
|
|
15637
|
+
* the organization rewrites the body, and is exactly then that the comparison is worth
|
|
15638
|
+
* making.
|
|
15639
|
+
*
|
|
15640
|
+
* `.default(null)` rather than a bare nullable, for the tolerance this schema keeps
|
|
15641
|
+
* throughout: a payload from a server that predates the column still parses.
|
|
15642
|
+
*/
|
|
15643
|
+
seededDefaultSha: external_exports.string().nullable().default(null),
|
|
15592
15644
|
createdAt: external_exports.iso.datetime({ offset: true }),
|
|
15593
15645
|
updatedAt: external_exports.iso.datetime({ offset: true }),
|
|
15594
15646
|
deletedAt: external_exports.iso.datetime({ offset: true }).nullable()
|
|
@@ -16105,6 +16157,12 @@ var listEnvelopeSchema = (item) => external_exports.object({ items: external_exp
|
|
|
16105
16157
|
var mintedTokenSchema = apiTokenSchema.extend({ token: external_exports.string().min(1) }).loose();
|
|
16106
16158
|
var tokenListSchema = listEnvelopeSchema(apiTokenSchema);
|
|
16107
16159
|
var orgInstructionListSchema = listEnvelopeSchema(orgInstructionSchema);
|
|
16160
|
+
var instructionDefaultSchema = external_exports.object({
|
|
16161
|
+
scope: external_exports.string().min(1),
|
|
16162
|
+
body: external_exports.string().min(1),
|
|
16163
|
+
sha256: external_exports.string().min(1)
|
|
16164
|
+
}).loose();
|
|
16165
|
+
var instructionDefaultListSchema = listEnvelopeSchema(instructionDefaultSchema);
|
|
16108
16166
|
var ApiClient = class _ApiClient {
|
|
16109
16167
|
baseUrl;
|
|
16110
16168
|
auth;
|
|
@@ -16715,6 +16773,18 @@ var ApiClient = class _ApiClient {
|
|
|
16715
16773
|
const body = await this.request("GET", "/api/v1/org-instructions", {}, orgInstructionListSchema);
|
|
16716
16774
|
return body.items;
|
|
16717
16775
|
}
|
|
16776
|
+
/**
|
|
16777
|
+
* The starting text THIS RELEASE ships, per scope, with the digest a seeded row records.
|
|
16778
|
+
*
|
|
16779
|
+
* Shipped content rather than the org's, so it carries no tenant data and any
|
|
16780
|
+
* authenticated caller may read it. Comparing a row's `seededDefaultSha` against the
|
|
16781
|
+
* `sha256` here is how a settings page says the shipped default has MOVED since this
|
|
16782
|
+
* organization was seeded — a difference the product reports and never applies.
|
|
16783
|
+
*/
|
|
16784
|
+
async listInstructionDefaults() {
|
|
16785
|
+
const body = await this.request("GET", "/api/v1/org-instructions/defaults", {}, instructionDefaultListSchema);
|
|
16786
|
+
return body.items;
|
|
16787
|
+
}
|
|
16718
16788
|
/** Upserts one scope's instruction text (ADMIN+; body ≤ 4000 chars). */
|
|
16719
16789
|
setOrgInstruction(scope, body) {
|
|
16720
16790
|
return this.request("PUT", `/api/v1/org-instructions/${enc(scope)}`, { body: { body } }, orgInstructionSchema);
|
|
@@ -16867,11 +16937,18 @@ function bindingBanner(apiBaseUrl) {
|
|
|
16867
16937
|
return `Pipebrain MCP server, bound to the ${apiBindingLabel(apiBaseUrl)} API at ${apiBaseUrl}. Every tool reads and writes THAT environment \u2014 confirm it is the one you intend before mutating.`;
|
|
16868
16938
|
}
|
|
16869
16939
|
var INDEX_RULES = "Until you have it: an open decision is filed as a question with `ask_questions` and never written into a body; search for an existing record before creating one, because a duplicate splits one thing\u2019s history across two identifiers and nothing merges them afterward; and a secret value never travels in a tool call, because a value in a call is disclosed before the server can encrypt it.";
|
|
16870
|
-
function composeIndex(base) {
|
|
16940
|
+
function composeIndex(base, machineNotice2 = null) {
|
|
16871
16941
|
return [
|
|
16872
16942
|
base,
|
|
16873
16943
|
"Call `get_work_context` before you write anything \u2014 it returns the binding guidance, the product doctrine and this organization\u2019s own writing rules, together with the work. Nothing else delivers that guidance in full; every other read only names this call.",
|
|
16874
|
-
INDEX_RULES
|
|
16944
|
+
INDEX_RULES,
|
|
16945
|
+
// LAST, and usually absent. It is about the MACHINE rather than about the work, so it
|
|
16946
|
+
// must never displace a rule: a squeezed share keeps the prefix, and losing a sentence
|
|
16947
|
+
// about an installer costs less than losing the sentence that recovers everything else.
|
|
16948
|
+
// Present only when this machine's shared configuration is not the one this build ships
|
|
16949
|
+
// (PIPE-354 AC-5) — a machine that never installed it is passed over in silence, since
|
|
16950
|
+
// it has no configuration to be behind with.
|
|
16951
|
+
...machineNotice2 === null ? [] : [machineNotice2]
|
|
16875
16952
|
].join("\n\n");
|
|
16876
16953
|
}
|
|
16877
16954
|
async function fetchOrgInstructionsSafe(client2) {
|
|
@@ -16912,10 +16989,12 @@ ${body}`);
|
|
|
16912
16989
|
}
|
|
16913
16990
|
|
|
16914
16991
|
// ../../libs/mcp-server/dist/server.js
|
|
16915
|
-
|
|
16916
|
-
|
|
16917
|
-
|
|
16918
|
-
|
|
16992
|
+
function machineNotice() {
|
|
16993
|
+
try {
|
|
16994
|
+
return agentConfigNotice(agentConfigStatus(agentConfigDir()));
|
|
16995
|
+
} catch {
|
|
16996
|
+
return null;
|
|
16997
|
+
}
|
|
16919
16998
|
}
|
|
16920
16999
|
var DEFAULT_VERSION = "0.0.0";
|
|
16921
17000
|
function ok(data, appended) {
|
|
@@ -16983,7 +17062,13 @@ function createMcpServer(client2, apiBaseUrl, orgInstructions2 = /* @__PURE__ */
|
|
|
16983
17062
|
// which environment this is bound to, the call that returns the binding guidance,
|
|
16984
17063
|
// and the three rules whose violation cannot be undone. Everything else comes from
|
|
16985
17064
|
// `docs/DOCTRINE.md` and the org's saved texts, on the work-context read.
|
|
16986
|
-
|
|
17065
|
+
//
|
|
17066
|
+
// Plus, only when there is something to say, one line about the MACHINE: its shared
|
|
17067
|
+
// agent configuration is not the one this build ships (PIPE-354 AC-5). Read here
|
|
17068
|
+
// rather than passed in, because the fact is about the filesystem this process is
|
|
17069
|
+
// running on and there is no caller better placed to know it; a read failure
|
|
17070
|
+
// degrades to silence, since a session must not fail to start over an installer.
|
|
17071
|
+
instructions: composeIndex(banner, machineNotice())
|
|
16987
17072
|
});
|
|
16988
17073
|
server2.registerTool("list_projects", {
|
|
16989
17074
|
description: "List all projects with their key, environments, milestones (token, label, active), and open-issue counts \u2014 the project switcher and workflow entry point (\xA77).",
|
|
@@ -17012,10 +17097,17 @@ function createMcpServer(client2, apiBaseUrl, orgInstructions2 = /* @__PURE__ */
|
|
|
17012
17097
|
// path that has no precedent anywhere on this surface. The viewer narrowing the
|
|
17013
17098
|
// web queue offers is filter state on that view, not something an agent picks.
|
|
17014
17099
|
}, () => (
|
|
17015
|
-
// `{ items }`, mirroring the HTTP envelope rather than unwrapping to a bare
|
|
17016
|
-
//
|
|
17017
|
-
//
|
|
17018
|
-
//
|
|
17100
|
+
// `{ items }`, mirroring the HTTP envelope rather than unwrapping to a bare array
|
|
17101
|
+
// like the sibling list tools. The reason is the envelope itself: this tool is a
|
|
17102
|
+
// pass-through of one endpoint's answer, and reshaping it here would be a second
|
|
17103
|
+
// place for that answer's shape to be decided.
|
|
17104
|
+
//
|
|
17105
|
+
// The two surfaces are NOT interchangeable, and nothing here should suggest they
|
|
17106
|
+
// are. This tool takes no narrowing arguments and returns the org's WHOLE queue,
|
|
17107
|
+
// while the web view narrows what it DRAWS — by project, by category, and by viewer
|
|
17108
|
+
// scope. One endpoint decides what counts as a gate, so that definition cannot drift
|
|
17109
|
+
// between them; what an agent reads here is everything waiting, rather than whatever
|
|
17110
|
+
// the operator happens to be looking at.
|
|
17019
17111
|
run(async () => ({ items: await client2.listWaiting() }), GUIDANCE_POINTER)
|
|
17020
17112
|
));
|
|
17021
17113
|
server2.registerTool(
|
|
@@ -17352,11 +17444,11 @@ function createMcpServer(client2, apiBaseUrl, orgInstructions2 = /* @__PURE__ */
|
|
|
17352
17444
|
inputSchema: linkAssetInputSchema.shape
|
|
17353
17445
|
}, (args) => run(() => client2.linkAsset(args)));
|
|
17354
17446
|
server2.registerTool("unlink_asset", {
|
|
17355
|
-
description: "Remove an asset reference by its link id (soft-delete). The asset itself is untouched \u2014 this only drops the reference from its target.",
|
|
17447
|
+
description: "Remove an asset reference by its link id (soft-delete). The asset itself is untouched \u2014 this only drops the reference from its target. A link whose target is a CHAT MESSAGE is author-only: anyone but the person who wrote that message is refused with `not_message_author` (403), so an attachment you did not post is not yours to detach. Every other target type is removable by any member.",
|
|
17356
17448
|
inputSchema: unlinkAssetInputSchema.shape
|
|
17357
17449
|
}, (args) => run(() => client2.unlinkAsset(args.linkId)));
|
|
17358
17450
|
server2.registerTool("set_secret", {
|
|
17359
|
-
description: "Create a secret or add a new version under a project + environment (metadata only \u2014 the response NEVER echoes the value). `name` is UPPER_SNAKE_CASE (e.g. API_KEY); `environment` is the environment slug (dev/prod); optional `description`.",
|
|
17451
|
+
description: "Create a secret or add a new version under a project + environment (metadata only \u2014 the response NEVER echoes the value). `name` is UPPER_SNAKE_CASE (e.g. API_KEY); `environment` is the environment slug (dev/prod); optional `description`. Writing guidance for that description lives in the product doctrine, which `get_work_context` returns.",
|
|
17360
17452
|
inputSchema: {
|
|
17361
17453
|
project: projectKeySchema,
|
|
17362
17454
|
environment: external_exports.string().min(1),
|
|
@@ -17451,14 +17543,14 @@ var apiTokenSchema2 = external_exports.string().min(1).superRefine((value, ctx)
|
|
|
17451
17543
|
if (!isAcceptedTokenShape(value)) {
|
|
17452
17544
|
ctx.addIssue({
|
|
17453
17545
|
code: "custom",
|
|
17454
|
-
message: "is not a Pipebrain API token \u2014 expected `pbt_\u2026` (
|
|
17546
|
+
message: "is not a Pipebrain API token \u2014 expected `pbt_\u2026` (value withheld). Mint one under Settings \u2192 Organization \u2192 API tokens."
|
|
17455
17547
|
});
|
|
17456
17548
|
}
|
|
17457
17549
|
});
|
|
17458
|
-
function validate(schema, resolved,
|
|
17550
|
+
function validate(schema, resolved, name, issues, variables) {
|
|
17459
17551
|
if (resolved === void 0) {
|
|
17460
|
-
issues.push(`${
|
|
17461
|
-
variables.push(
|
|
17552
|
+
issues.push(`${name}: is not set`);
|
|
17553
|
+
variables.push(name);
|
|
17462
17554
|
return void 0;
|
|
17463
17555
|
}
|
|
17464
17556
|
const result = schema.safeParse(resolved.value);
|
|
@@ -17476,8 +17568,8 @@ function loadMcpConfig() {
|
|
|
17476
17568
|
const resolved = resolveAgentEnv(process.env);
|
|
17477
17569
|
const issues = [];
|
|
17478
17570
|
const variables = [];
|
|
17479
|
-
const apiUrl = validate(external_exports.url(), resolved.apiUrl, "PIPEBRAIN_API_URL",
|
|
17480
|
-
const token = validate(apiTokenSchema2, resolved.token, "PIPEBRAIN_TOKEN",
|
|
17571
|
+
const apiUrl = validate(external_exports.url(), resolved.apiUrl, "PIPEBRAIN_API_URL", issues, variables);
|
|
17572
|
+
const token = validate(apiTokenSchema2, resolved.token, "PIPEBRAIN_TOKEN", issues, variables);
|
|
17481
17573
|
if (apiUrl === void 0 || token === void 0) {
|
|
17482
17574
|
throw new EnvValidationError(issues, variables);
|
|
17483
17575
|
}
|
|
@@ -17485,7 +17577,7 @@ function loadMcpConfig() {
|
|
|
17485
17577
|
}
|
|
17486
17578
|
|
|
17487
17579
|
// src/version.ts
|
|
17488
|
-
var VERSION = "0.
|
|
17580
|
+
var VERSION = "0.34.0";
|
|
17489
17581
|
|
|
17490
17582
|
// src/main.ts
|
|
17491
17583
|
var config2 = loadMcpConfig();
|
|
@@ -17500,8 +17592,6 @@ var server = createMcpServer(client, config2.apiUrl, orgInstructions, {
|
|
|
17500
17592
|
});
|
|
17501
17593
|
var transport = new StdioServerTransport();
|
|
17502
17594
|
await server.connect(transport);
|
|
17503
|
-
var notice = successorNotice();
|
|
17504
|
-
if (notice !== "") process.stderr.write(notice);
|
|
17505
17595
|
process.stderr.write(
|
|
17506
17596
|
`pipebrain mcp connected \u2014 ${apiBindingLabel(config2.apiUrl)} API base ${config2.apiUrl}
|
|
17507
17597
|
`
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pipebrain-mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Pipebrain MCP server for Claude Code and Claude Desktop
|
|
3
|
+
"version": "0.34.0",
|
|
4
|
+
"description": "Pipebrain MCP server for Claude Code and Claude Desktop \u2014 issues, requirements, content, assets, and metadata-only secrets over the Pipebrain API.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"main": "./dist/main.js",
|