pipebrain-mcp 0.48.1 → 0.50.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 +14 -5
- package/dist/main.js +76 -99
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,16 +14,24 @@ Register it with Claude Code (no install step — `npx` fetches it), at **user s
|
|
|
14
14
|
every repo on the machine gets the tools:
|
|
15
15
|
|
|
16
16
|
```
|
|
17
|
-
claude mcp add pipebrain -s user -e PIPEBRAIN_API_URL=https://api.pipebrain.dev -e 'PIPEBRAIN_TOKEN=${PIPEBRAIN_TOKEN}' -- npx -y pipebrain-mcp@latest
|
|
17
|
+
claude mcp add pipebrain -s user -e PIPEBRAIN_API_URL=https://api.pipebrain.dev -e 'PIPEBRAIN_TOKEN=${PIPEBRAIN_TOKEN}' -e PIPEBRAIN_ORG=your-org-slug -- npx -y pipebrain-mcp@latest
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
environment of the process that launches it, and refuses to start if either value
|
|
22
|
-
missing or malformed — including an unexpanded `${PIPEBRAIN_TOKEN}` placeholder, which
|
|
23
|
-
the failure that otherwise looks like a revoked token. There is no default API URL (see
|
|
20
|
+
The first two `-e` flags are required, not decoration. The server reads its configuration
|
|
21
|
+
from the environment of the process that launches it, and refuses to start if either value
|
|
22
|
+
is missing or malformed — including an unexpanded `${PIPEBRAIN_TOKEN}` placeholder, which
|
|
23
|
+
is the failure that otherwise looks like a revoked token. There is no default API URL (see
|
|
24
24
|
the table below), so a registration that passes neither flag produces a server that never
|
|
25
25
|
starts.
|
|
26
26
|
|
|
27
|
+
The third flag fails the other way, which is why it is easy to miss. Every call acts inside
|
|
28
|
+
one organization and no credential carries one, so a server registered without
|
|
29
|
+
`PIPEBRAIN_ORG` starts perfectly and is then refused on its first organization-scoped call.
|
|
30
|
+
Replace `your-org-slug` with your own slug or id; `ppb doctor` (from `pipebrain-cli`)
|
|
31
|
+
prints the ones you may name before any of this is wired. A call may still name a
|
|
32
|
+
different organization than the default, and `list_orgs` returns every organization your
|
|
33
|
+
credential may name — the same set the refusal lists.
|
|
34
|
+
|
|
27
35
|
The single quotes around the token are load-bearing: they keep `${PIPEBRAIN_TOKEN}` a
|
|
28
36
|
literal *reference* in Claude's config instead of expanding it as you type, so the token
|
|
29
37
|
value itself never lands in a config file — the server resolves it from your environment
|
|
@@ -35,6 +43,7 @@ at each launch.
|
|
|
35
43
|
| --- | --- |
|
|
36
44
|
| `PIPEBRAIN_TOKEN` | Your `pbt_…` API token (Settings → Organization → API tokens). |
|
|
37
45
|
| `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. |
|
|
46
|
+
| `PIPEBRAIN_ORG` | The organization slug or id calls act in unless a call names another. Optional, and its absence does not stop the server starting — but a server with no default is refused on every organization-scoped call, since no credential carries one. `list_orgs` returns the ones you may name. |
|
|
38
47
|
|
|
39
48
|
No tool here RETURNS a secret value: the secrets tools answer with metadata only, a
|
|
40
49
|
response never echoes a value, and there is deliberately no value-returning tool — no get,
|
package/dist/main.js
CHANGED
|
@@ -384,7 +384,7 @@ var AGENT_CONFIG_ENTRIES = [
|
|
|
384
384
|
{ target: "skills/pipebrain-header-pass/SKILL.md", sha256: "10545e3c1c5215b031e1c76281e65cd2ecf7420b3f07a4221824591a8263c5fc" },
|
|
385
385
|
{ target: "skills/pipebrain-secrets/SKILL.md", sha256: "52c2136a3cca5612981d50b926195eae3e804911e1e9723dd5eba76e66b52d6f" },
|
|
386
386
|
{ target: "skills/pipebrain-work-session/SKILL.md", sha256: "09d5741761b43fc13a12a04117edd8b194aac18974bfedb42dfc76c14030ba87" },
|
|
387
|
-
{ target: "skills/pipebrain-writing/SKILL.md", sha256: "
|
|
387
|
+
{ target: "skills/pipebrain-writing/SKILL.md", sha256: "8e5ed4636afcca0cacc220d8fa5310364d2d6854a9fc68ffa03e4c428989f8b8" },
|
|
388
388
|
{ target: "skills/pipebrain/SKILL.md", sha256: "e53a1cb13d4e2e396c638d8f99055f4082edef030a40ffc8806b1f52f0824b57" },
|
|
389
389
|
{ target: "skills/requirements-altitude/SKILL.md", sha256: "60d1916da22a52138c046fcf4e5cd4cc420b7a5679a6652eb76573b7a69a1cf4" }
|
|
390
390
|
];
|
|
@@ -505,7 +505,7 @@ function apiBindingLabel(apiBaseUrl) {
|
|
|
505
505
|
}
|
|
506
506
|
|
|
507
507
|
// ../../libs/agent-shared/dist/doctrine.js
|
|
508
|
-
var DOCTRINE = "# Pipebrain doctrine\n\nHow an agent works inside Pipebrain. These rules ship with the product and
|
|
508
|
+
var DOCTRINE = "# Pipebrain doctrine\n\nHow an agent works inside Pipebrain. These rules ship with the product and are the same in every organization. Each one protects something the product guarantees.\n\n## How to write here\n\nThe person reading what you write is the operator, deciding something. They have about two minutes. Put the point first. Use short sentences and ordinary words. Headings and bullets are fine. Explain why once, not under every bullet.\n\nA criterion is one sentence: a concrete situation, and what must be true afterward. If you need a general rule, write two or three criteria instead.\n\nEvidence on a criterion opens with the verdict, then how you drove it, then what it does not cover. Keep it to one screen; name the test or command, not every file you read.\n\nA closing comment says what landed, what did not and why, what you filed by key, and whether the tests are green.\n\nAnything else you notice goes to a question, when the operator has to choose; to a new issue at triage, when it is real work; or, most often, nowhere. Never add a notes, caveats, or next-steps section. An item filed and repeated in prose gets triaged twice.\n\nYour organization may add its own writing rules below this document. Most have none.\n\n## Search before you create\n\nLook for an existing record before you add one. A duplicate splits one record's history across two keys, and nothing merges them later. Update or link the record you find.\n\n## Link by key, never paste\n\nRefer to a requirement, decision, or issue by its key. Never paste its text into a body. When the source changes the copy stays stale, and the stale copy is what gets built.\n\n## Most choices are yours\n\nInside an approved requirement you choose the design, the libraries, and the order of work. Ask only when a choice changes what a user sees, costs money, or contradicts a requirement; file that one as a question. Asking about everything makes real questions wait behind choices that were yours. A technical choice worth keeping goes in a decision record.\n\n## File decisions as questions\n\nAn open decision goes in a question on the issue, filed with `ask_questions` and carrying the options you propose. It does not belong in a body, under a heading or in a field. Prose asks nobody and records no answer, so a later session cannot tell whether it was ever decided. Withdraw a question your own work has settled.\n\n## Cross-cutting requirements arrive on their own\n\nThey bind every epic 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. A copy drifts, and an explicit link pins one revision where the read delivers the newest.\n\n## Conventions need a person's approval\n\nApproved conventions come back with the same work-item read and bind every project in the organization. Never restate one in a body and never add one as an explicit link; a copy goes stale.\n\nA convention you create or amend over the agent surface is a proposal. It is not in force until a person approves it in a browser. Until then, do not work from your own unapproved wording.\n\nA statement that stops being true when this project ends is a cross-cutting requirement. One still true on the next project is a convention. File one when an evaluator rejects work or a criterion fails, and rarely otherwise: each reaches every read, after a person reviews it.\n\n## Choosing auto or manual\n\nFirst find out what this project can drive end to end, and with which command. A criterion is `auto` when an evaluator can drive the input and read the result on the running system, including an interface where a browser harness exists. Only the evaluator's own run qualifies it; a test the implementer wrote does not. An auto criterion nobody can drive never gets evidence.\n\nMark a criterion `manual` only when the verdict needs a person's judgment. A manual row a machine could have checked holds the issue open until a person gets to it. If you cannot say how to drive a criterion, it is worded too vaguely; reword it against something observable. A manual criterion carries instructions for a stranger to the code.\n\nA manual criterion with no recorded artifact is the human gate working as designed, and not a finding.\n\n## The bar for filing a bug\n\nFile a bug only when the behavior breaks an approved requirement or a defined acceptance criterion, and name which one first. Everything else, including unclear code and edge cases nobody specified, is feedback or nothing. A queue that mixes defects with taste has to be read in full before any of it can be trusted.\n\n## A decision record is written once\n\nA decision record says what was decided and why, at the time. Do not edit it later; write a superseding record instead. The chain of records shows how the thinking changed, and an edit destroys it. Read existing records before reopening a settled question.\n\n## A secret value never travels in a tool call\n\nNo tool returns a secret value, by design, and none should receive one. A value in a tool call is in the transcript before the server can encrypt it; the only remedy is rotation. Set values with the command-line tool, from a file or standard input, and inject at run time.\n\n## Setting an instruction replaces the whole body\n\nThere is no partial update. An instruction write stores exactly the body you supply and discards everything that was there. Read the live value, compose the complete replacement, write it, then read it again to confirm what landed.\n\nThis guidance and the tool descriptions are a snapshot from the session's start; an edit reaches the next session, never this one. `list_org_instructions` returns the saved rows as they are now; that is the confirming read.\n";
|
|
509
509
|
|
|
510
510
|
// ../../libs/agent-shared/dist/org-reference.js
|
|
511
511
|
var CANONICAL_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -17675,10 +17675,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
17675
17675
|
);
|
|
17676
17676
|
}
|
|
17677
17677
|
registerStrict("list_orgs", {
|
|
17678
|
-
description: "List the organizations this credential may act in
|
|
17679
|
-
// NO inputSchema, and the absence is the point rather than an omission. There is
|
|
17680
|
-
// nothing to narrow: the answer is the caller's own memberships, and a filter over a
|
|
17681
|
-
// list this short would be a second way to ask one question.
|
|
17678
|
+
description: "List the organizations this credential may act in, with each 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 is refused without one, and this is how you find the names you may use. The answer is your memberships right now, which is what that check reads, so a joined organization appears and a left one does not."
|
|
17682
17679
|
}, () => (
|
|
17683
17680
|
// A BARE ARRAY, unlike `list_waiting`'s `{ items }`, and for the reason that tool
|
|
17684
17681
|
// gives rather than against it: the envelope has already been unwrapped by
|
|
@@ -17698,7 +17695,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
17698
17695
|
run(() => client2().unscoped().listOrgs(), GUIDANCE_POINTER)
|
|
17699
17696
|
));
|
|
17700
17697
|
registerStrict("list_projects", {
|
|
17701
|
-
description: "List
|
|
17698
|
+
description: "List every project: key, environments, milestones (an archived one leaves the pickers, staying valid on issues naming it), deliverable targets and open-issue counts. Pages with `limit` (default 200, max 500) and `offset`.",
|
|
17702
17699
|
// Optional pagination (§9) — bounds mirror list_issues/list_requirements; the
|
|
17703
17700
|
// server defaults limit to 200 (max 500) and passes limit/offset as query params.
|
|
17704
17701
|
inputSchema: {
|
|
@@ -17707,7 +17704,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
17707
17704
|
}
|
|
17708
17705
|
}, (args) => run(() => client2().listProjects(args), GUIDANCE_POINTER));
|
|
17709
17706
|
registerStrict("create_project", {
|
|
17710
|
-
description: "Create a
|
|
17707
|
+
description: "Create a project. Nothing can be filed until one exists. `name` is the display label; `key` is 2\u20135 uppercase letters (e.g. `DWB`), unique per organization, and prefixes every display id.",
|
|
17711
17708
|
// Inline shape: @pipeline/domain has no standalone create-project schema — the
|
|
17712
17709
|
// same `{ name, key }` pair lives only inside createOrgInputSchema.project, so
|
|
17713
17710
|
// this mirrors it exactly (key = 2–5 A–Z via projectKeySchema) and matches
|
|
@@ -17718,12 +17715,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
17718
17715
|
}
|
|
17719
17716
|
}, (args) => run(() => client2().createProject(args)));
|
|
17720
17717
|
registerStrict("list_waiting", {
|
|
17721
|
-
description: "Every gate awaiting a
|
|
17722
|
-
// NO NARROWING ARGUMENTS. The one argument this tool does take — `org`, which every
|
|
17723
|
-
// tool takes — is not a narrowing: it says WHICH ORGANIZATION the call acts in, not
|
|
17724
|
-
// which of that organization's gates come back, and the caller's membership there is
|
|
17725
|
-
// checked on the way in (PIPE-410). The viewer narrowing the web queue offers is
|
|
17726
|
-
// filter state on that view, not something an agent picks.
|
|
17718
|
+
description: "Every gate awaiting a person across all projects in the organization named by `org`, longest wait first: a requirement revision awaiting approval, a question awaiting an answer, and work at verify still holding a criterion that has not passed. Each entry names its project, the ref to act on, and when the wait began. Read-only, and it clears nothing. A revision clears with `approve_requirement`. Use it to see what is waiting before you ask."
|
|
17727
17719
|
}, () => (
|
|
17728
17720
|
// `{ items }`, mirroring the HTTP envelope rather than unwrapping to a bare array
|
|
17729
17721
|
// like the sibling list tools. The reason is the envelope itself: this tool is a
|
|
@@ -17746,12 +17738,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
17746
17738
|
// one description loaded into EVERY session by `anthropic/alwaysLoad`. The clauses
|
|
17747
17739
|
// that went were the ones arguing for the rule rather than stating it; what a caller
|
|
17748
17740
|
// needs to predict a refusal all stayed.
|
|
17749
|
-
description:
|
|
17750
|
-
// The WHOLE domain schema rather than its `.shape`: the exactly-one-of rule is a
|
|
17751
|
-
// refinement on the OBJECT, and a raw shape drops it — the SDK validates against
|
|
17752
|
-
// what it is handed, so the shape alone would quietly accept both arguments and
|
|
17753
|
-
// neither. Handing it the schema lets its refusal reach the caller verbatim,
|
|
17754
|
-
// naming the two arguments it refused rather than a reworded stand-in.
|
|
17741
|
+
description: "Everything a session needs before starting work. Call it first: it alone returns the doctrine and the organization\u2019s writing rules in full. Address it by `issue` or `project`, never both. An issue returns its detail plus the text of each requirement it links, comments capped at `commentLimit` (default " + WORK_CONTEXT_COMMENT_LIMIT + "), with `commentCount` the true total. A project returns `available` \u2014 startable work, excluding done, killed, blocked, person-reserved and others\u2019 claims \u2014 capped at `availableLimit` with `availableCount` as the true total. Read-only.",
|
|
17755
17742
|
inputSchema: getWorkContextInputSchema,
|
|
17756
17743
|
// THE TWO ANNOTATIONS PIPE-D30 REQUIRES, and the reason each key is spelled out
|
|
17757
17744
|
// rather than built: an unknown `_meta` key is IGNORED — no error, no warning — so a
|
|
@@ -17792,7 +17779,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
17792
17779
|
}
|
|
17793
17780
|
);
|
|
17794
17781
|
registerStrict("create_milestone", {
|
|
17795
|
-
description: "Create a milestone
|
|
17782
|
+
description: "Create a milestone. `token` is the ref issues point at (e.g. `M1`) and is unique per project; a duplicate is rejected. `label` names it; `position` orders it, 0-based, last by default. `active: true` demotes whichever was active.",
|
|
17796
17783
|
inputSchema: {
|
|
17797
17784
|
project: projectKeySchema,
|
|
17798
17785
|
token: external_exports.string().min(1),
|
|
@@ -17802,7 +17789,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
17802
17789
|
}
|
|
17803
17790
|
}, ({ project, ...input }) => run(() => client2().createMilestone(project, input)));
|
|
17804
17791
|
registerStrict("update_milestone", {
|
|
17805
|
-
description: "Rename, reorder, archive or restore a milestone,
|
|
17792
|
+
description: "Rename, reorder, archive or restore a milestone, by token (`M1`) or uuid. Supply at least one of `token`, `label`, `position` or `archived`; an empty edit is rejected. Renaming a token does not rewrite the issues carrying it, so prefer `label`. The active milestone cannot be archived (`milestone_active`), and an archived one cannot be activated (`milestone_archived`).",
|
|
17806
17793
|
inputSchema: {
|
|
17807
17794
|
project: projectKeySchema,
|
|
17808
17795
|
milestone: external_exports.string().min(1),
|
|
@@ -17813,14 +17800,14 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
17813
17800
|
}
|
|
17814
17801
|
}, ({ project, milestone, ...fields }) => run(async () => client2().updateMilestone(project, await resolveMilestoneId(client2(), project, milestone), fields)));
|
|
17815
17802
|
registerStrict("set_active_milestone", {
|
|
17816
|
-
description: "Make a milestone
|
|
17803
|
+
description: "Make a milestone its project\u2019s active one, by token (e.g. `M2`) or uuid. At most one per project is active, so this clears the previous in the same transaction.",
|
|
17817
17804
|
inputSchema: {
|
|
17818
17805
|
project: projectKeySchema,
|
|
17819
17806
|
milestone: external_exports.string().min(1)
|
|
17820
17807
|
}
|
|
17821
17808
|
}, ({ project, milestone }) => run(async () => client2().activateMilestone(project, await resolveMilestoneId(client2(), project, milestone))));
|
|
17822
17809
|
registerStrict("delete_milestone", {
|
|
17823
|
-
description: "Soft-delete a milestone
|
|
17810
|
+
description: "Soft-delete a milestone by token (e.g. `M3`) or uuid; the token frees up for reuse. Issues naming it are not rewritten and keep a dangling token, so re-point them first.",
|
|
17824
17811
|
inputSchema: {
|
|
17825
17812
|
project: projectKeySchema,
|
|
17826
17813
|
milestone: external_exports.string().min(1)
|
|
@@ -17831,7 +17818,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
17831
17818
|
return { deleted: true, project, milestone, id };
|
|
17832
17819
|
}));
|
|
17833
17820
|
registerStrict("create_deliverable_target", {
|
|
17834
|
-
description: "Add a
|
|
17821
|
+
description: "Add a platform a deliverable can target. `slug` is the immutable id it stores (e.g. `bsky`), unique per project. `label` names it, `position` orders it, `active` (default true) gates new deliverables.",
|
|
17835
17822
|
inputSchema: {
|
|
17836
17823
|
project: projectKeySchema,
|
|
17837
17824
|
slug: platformSlugSchema,
|
|
@@ -17841,7 +17828,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
17841
17828
|
}
|
|
17842
17829
|
}, ({ project, ...input }) => run(() => client2().createDeliverableTarget(project, input)));
|
|
17843
17830
|
registerStrict("update_deliverable_target", {
|
|
17844
|
-
description: "Update a deliverable target
|
|
17831
|
+
description: "Update a deliverable target by slug or uuid. Supply at least one of `label`, `position` or `active`; an empty edit is rejected. The slug is immutable; `active: false` hides it from new deliverables.",
|
|
17845
17832
|
inputSchema: {
|
|
17846
17833
|
project: projectKeySchema,
|
|
17847
17834
|
target: external_exports.string().min(1),
|
|
@@ -17851,7 +17838,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
17851
17838
|
}
|
|
17852
17839
|
}, ({ project, target, ...fields }) => run(async () => client2().updateDeliverableTarget(project, await resolveDeliverableTargetId(client2(), project, target), fields)));
|
|
17853
17840
|
registerStrict("delete_deliverable_target", {
|
|
17854
|
-
description: "Delete a deliverable target
|
|
17841
|
+
description: "Delete a deliverable target by slug or uuid. Only a target with no deliverables can be deleted; one in use is rejected \u2014 deactivate it with `update_deliverable_target` instead.",
|
|
17855
17842
|
inputSchema: {
|
|
17856
17843
|
project: projectKeySchema,
|
|
17857
17844
|
target: external_exports.string().min(1)
|
|
@@ -17864,7 +17851,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
17864
17851
|
registerStrict(
|
|
17865
17852
|
"list_issues",
|
|
17866
17853
|
{
|
|
17867
|
-
description:
|
|
17854
|
+
description: "List issue summaries \u2014 status, priority, milestone, claimer, criteria progress and the header \u2014 filtered by project, status, type, claimer, priority, milestone, text, parent, blocked and needsHeader. With no status filter the default is buildable work only: " + OPEN_STATUSES.join(", ") + '. Terminal work is excluded until you name it, so pass status ["done","killed"] for finished and abandoned work. An explicit filter is used verbatim, never merged. needsHeader true returns the issues whose header is missing or stale.',
|
|
17868
17855
|
inputSchema: listIssuesFilterSchema.shape
|
|
17869
17856
|
},
|
|
17870
17857
|
// The default lives in the API now, not here (PIPE-D35, Listing issues
|
|
@@ -17880,7 +17867,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
17880
17867
|
(args) => run(() => client2().listIssues(args), GUIDANCE_POINTER)
|
|
17881
17868
|
);
|
|
17882
17869
|
registerStrict("get_issue", {
|
|
17883
|
-
description:
|
|
17870
|
+
description: "Full issue detail by key or uuid: header, body, criteria, requirement links, cross-cutting requirements, approved conventions, children and comments. `terminal` is true when the issue is done or killed, so it is not buildable; `projectArchived` is true when the project is retired, and this read still answers while lists and writes do not. `headerStale` is true when no header was written or the body moved. Long records page with `bodyOffset` and `commentOffset`; `omitted` and `next` say what was left out.",
|
|
17884
17871
|
inputSchema: getIssueInputSchema.shape,
|
|
17885
17872
|
// The same result-threshold annotation `get_work_context` declares, and the same
|
|
17886
17873
|
// key spelled out for the same reason (an unknown `_meta` key is silently
|
|
@@ -17908,78 +17895,66 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
17908
17895
|
})
|
|
17909
17896
|
), GUIDANCE_POINTER));
|
|
17910
17897
|
registerStrict("create_issue", {
|
|
17911
|
-
description: "Create an
|
|
17898
|
+
description: "Create an epic, task, bug or feedback. Status defaults to triage; anything but triage or killed needs a milestone, else refused `milestone_required`. Optional parent, priority, milestone, links and ordered criteria. 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 the doctrine `get_work_context` returns, under \u201CSearch before you create\u201D.",
|
|
17912
17899
|
inputSchema: createIssueInputSchema.shape
|
|
17913
17900
|
}, (args) => run(() => client2().createIssue(args)));
|
|
17914
17901
|
registerStrict("promote_feedback", {
|
|
17915
|
-
description: "
|
|
17902
|
+
description: "Retype a feedback report as real work \u2014 an epic or a task \u2014 so it can carry criteria and be built. Feedback itself is refused `criteria_not_supported`, which is what this call resolves. Closing the source feedback is refused `status_guard` when anything beneath it, at any depth, is not done or killed, and the refusal names it. A `manual` criterion needs `howTo`, or it is refused `how_to_required`. See the doctrine `get_work_context` returns, under \u201CThe bar for filing a bug\u201D.",
|
|
17916
17903
|
inputSchema: {
|
|
17917
17904
|
issue: external_exports.string().min(1),
|
|
17918
17905
|
...promoteFeedbackInputSchema.shape
|
|
17919
17906
|
}
|
|
17920
17907
|
}, ({ issue: issue2, ...fields }) => run(() => client2().promoteFeedback(issue2, fields)));
|
|
17921
17908
|
registerStrict("open_review", {
|
|
17922
|
-
description: "Open the
|
|
17923
|
-
// The WHOLE domain schema rather than its `.shape`, for `get_work_context`'s
|
|
17924
|
-
// reason: the exactly-one-of rule is a refinement on the OBJECT, and a raw
|
|
17925
|
-
// shape drops it — the refusal naming the two forms would never reach the
|
|
17926
|
-
// caller, and both arguments (or neither) would be accepted here.
|
|
17909
|
+
description: "Open the completion review of an epic\u2019s whole change, by a session that neither built nor evaluated it. It creates one task at ready carrying the checklist. Pass exactly one of `epic`, or `project` plus `milestone`. One review stands per scope; a second is refused `review_already_open`. An epic-form review also holds that milestone scope; a milestone-form review blocks no epic. File findings as children of the epic, not the review. See the doctrine `get_work_context` returns, under \u201CHow to write here\u201D.",
|
|
17927
17910
|
inputSchema: openReviewInputSchema
|
|
17928
17911
|
}, (args) => run(() => client2().openReview(args)));
|
|
17929
17912
|
registerStrict("update_issue", {
|
|
17930
|
-
description: "
|
|
17913
|
+
description: "Change an issue\u2019s fields, parent and links. Header fields are refused here; criteria are set with `set_criteria`, which epics, tasks and bugs all carry. Moving an open issue onto a done or killed parent is refused `parent_terminal`; one with open work beneath it is refused `parent_terminal_subtree`. A cycle-closing re-parent is refused `parent_cycle`. Attaching open work starts its new parent unless that parent is blocked, unscheduled or under a finished parent. See the doctrine `get_work_context` returns, under \u201CMost choices are yours\u201D.",
|
|
17931
17914
|
inputSchema: updateIssueInputSchema.shape
|
|
17932
17915
|
}, ({ issue: issue2, ...fields }) => run(() => client2().updateIssue(issue2, fields)));
|
|
17933
17916
|
registerStrict("set_issue_header", {
|
|
17934
|
-
description: "Write an issue\u2019s
|
|
17917
|
+
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 the doctrine `get_work_context` returns, under \u201CHow to write here\u201D.",
|
|
17935
17918
|
inputSchema: setIssueHeaderInputSchema.shape
|
|
17936
17919
|
}, ({ issue: issue2, ...fields }) => run(() => client2().setIssueHeader(issue2, fields)));
|
|
17937
17920
|
registerStrict("set_status", {
|
|
17938
|
-
description: "
|
|
17921
|
+
description: "Move an issue to a new status. verify needs every auto criterion passed; done needs every criterion passed, and every issue beneath it, at any depth, done or killed \u2014 epic, task or bug alike \u2014 read from status. An epic with no criteria is refused at both gates; a task or bug with none moves through. A blocked issue cannot reach ready or in_progress until its blockers clear. An issue with no milestone can only be triage or killed.",
|
|
17939
17922
|
inputSchema: setStatusInputSchema.shape
|
|
17940
17923
|
}, (args) => run(() => client2().setStatus(args.issue, args.status)));
|
|
17941
17924
|
registerStrict("claim_issue", {
|
|
17942
|
-
description: "
|
|
17925
|
+
description: "Claim an issue for this session. It succeeds only if unclaimed; under a race one caller wins and the loser is told who holds it. Claiming is separate from status. It is refused `reserved_for_person` when an operator has reserved the work for a person \u2014 that mark refuses the claim and nothing else, so reading, commenting and moving status all still work. Only a person in the browser can lift it; say in a comment why the work looks agent-ready.",
|
|
17943
17926
|
inputSchema: { issue: issueRefSchema }
|
|
17944
17927
|
}, (args) => run(() => client2().claimIssue(args.issue)));
|
|
17945
17928
|
registerStrict("release_issue", {
|
|
17946
|
-
description: "Release an issue
|
|
17929
|
+
description: "Release your claim on an issue, leaving it unclaimed and its status unchanged.",
|
|
17947
17930
|
inputSchema: { issue: issueRefSchema }
|
|
17948
17931
|
}, (args) => run(() => client2().releaseIssue(args.issue)));
|
|
17949
17932
|
registerStrict("add_comment", {
|
|
17950
|
-
description: "Add a markdown comment to an issue, attributed to the operator
|
|
17933
|
+
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 the doctrine `get_work_context` returns, under \u201CHow to write here\u201D.",
|
|
17951
17934
|
inputSchema: commentInputSchema.shape
|
|
17952
17935
|
}, (args) => run(() => client2().addComment(args.issue, args.body)));
|
|
17953
17936
|
registerStrict("set_blocked", {
|
|
17954
|
-
description: "
|
|
17937
|
+
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 a line or two on what the work waits on; what you tried goes in a comment. See the doctrine `get_work_context` returns, under \u201CHow to write here\u201D.",
|
|
17955
17938
|
inputSchema: setBlockedInputSchema.shape
|
|
17956
17939
|
}, (args) => run(() => client2().setBlocked(args.issue, args.reason)));
|
|
17957
17940
|
registerStrict("clear_blocked", {
|
|
17958
|
-
description: "Clear an issue\u2019s blocked mark
|
|
17941
|
+
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` on the returned issue is null.",
|
|
17959
17942
|
inputSchema: { issue: issueRefSchema }
|
|
17960
17943
|
}, (args) => run(() => client2().clearBlocked(args.issue)));
|
|
17961
17944
|
registerStrict("link_blocker", {
|
|
17962
|
-
description: "Record that `issue` is
|
|
17945
|
+
description: "Record that `issue` is waiting on `blocker`. One call writes both sides: the waiting issue lists it under `blockedBy`, the blocker under `blocks`. Both must be in the same project. An issue cannot block itself, and an edge that would close a cycle is refused with nothing recorded.",
|
|
17963
17946
|
inputSchema: linkBlockerInputSchema.shape
|
|
17964
17947
|
}, (args) => run(() => client2().addBlocker(args.issue, args.blocker)));
|
|
17965
17948
|
registerStrict("unlink_blocker", {
|
|
17966
|
-
description: "Remove the blocked-by edge between `issue` and `blocker
|
|
17949
|
+
description: "Remove the blocked-by edge between `issue` and `blocker`; one call clears it from both sides. Unlinking a pair that is not linked is a no-op, not an error.",
|
|
17967
17950
|
inputSchema: linkBlockerInputSchema.shape
|
|
17968
17951
|
}, (args) => run(() => client2().removeBlocker(args.issue, args.blocker)));
|
|
17969
17952
|
registerStrict("set_criteria", {
|
|
17970
|
-
description: "Define or replace the
|
|
17953
|
+
description: "Define or replace the acceptance criteria on an issue. 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. A `manual` criterion needs `howTo`, else refused `how_to_required`; `howTo` on an `auto` criterion is refused by the schema. See the doctrine `get_work_context` returns, under \u201CChoosing auto or manual\u201D.",
|
|
17971
17954
|
inputSchema: setCriteriaInputSchema.shape
|
|
17972
17955
|
}, (args) => run(() => client2().setCriteria(args.issue, args.criteria)));
|
|
17973
17956
|
registerStrict("set_criterion_state", {
|
|
17974
|
-
description: "Set a criterion\u2019s state
|
|
17975
|
-
// `issue` is OPTIONAL: the criterionId-only form needs no issue ref. The
|
|
17976
|
-
// exactly-one-of rule can't be expressed in a raw shape, so the handler
|
|
17977
|
-
// enforces it via the shared `@pipeline/domain` refine (mirror of §7).
|
|
17978
|
-
//
|
|
17979
|
-
// `criterionNumber` takes `int4Schema` so the ADVERTISED JSON Schema carries
|
|
17980
|
-
// the real ceiling (PIPE-149) — hand-rolling it from `positiveIntSchema` told
|
|
17981
|
-
// clients the maximum was 2^53-1 while the column and the API both stop at
|
|
17982
|
-
// 2,147,483,647, so a client refusal that should happen never did.
|
|
17957
|
+
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 the doctrine `get_work_context` returns, under \u201CChoosing auto or manual\u201D.",
|
|
17983
17958
|
inputSchema: {
|
|
17984
17959
|
issue: issueRefSchema.optional(),
|
|
17985
17960
|
criterionId: uuidSchema.optional(),
|
|
@@ -18002,27 +17977,27 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18002
17977
|
});
|
|
18003
17978
|
}));
|
|
18004
17979
|
registerStrict("ask_questions", {
|
|
18005
|
-
description: "Ask the operator
|
|
17980
|
+
description: "Ask the operator one or more questions on an issue. Send the batch in one call \u2014 a half-asked set costs a second pass. Each question gets a stable `Q-{n}` number that is never reused, and `options` are the choices you propose. Answers come back on every later read of the issue, so read them before asking again rather than re-asking a settled decision. Withdraw one with `withdraw_question`. See the doctrine `get_work_context` returns, under \u201CFile decisions as questions\u201D.",
|
|
18006
17981
|
inputSchema: askQuestionsInputSchema.shape
|
|
18007
17982
|
}, (args) => run(() => client2().askQuestions(args.issue, args.questions)));
|
|
18008
17983
|
registerStrict("withdraw_question", {
|
|
18009
|
-
description: "Withdraw a question by its
|
|
17984
|
+
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, so another agent\u2019s question is not yours to clear. Use it when your own work settled the question or it was wrong to ask, rather than leaving the operator to answer something nobody awaits. The question is kept for the audit trail, its number retires, and later reads no longer carry it.",
|
|
18010
17985
|
inputSchema: withdrawQuestionInputSchema.shape
|
|
18011
17986
|
}, (args) => run(() => client2().withdrawQuestion(args.issue, args.number)));
|
|
18012
17987
|
registerStrict("list_requirements", {
|
|
18013
|
-
description: "List requirements filtered by project and text:
|
|
17988
|
+
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.",
|
|
18014
17989
|
inputSchema: listRequirementsFilterSchema.shape
|
|
18015
17990
|
}, (args) => run(() => client2().listRequirements(args), GUIDANCE_POINTER));
|
|
18016
17991
|
registerStrict("get_requirement", {
|
|
18017
|
-
description: "Get a requirement by display id or uuid
|
|
17992
|
+
description: "Get a requirement by display id or uuid: the approved revision by default, the latest when none is approved, or the `revision` you name. Includes lifecycle, approvals and linked issues.",
|
|
18018
17993
|
inputSchema: { requirement: requirementRefSchema, revision: positiveIntSchema.optional() }
|
|
18019
17994
|
}, (args) => run(() => client2().getRequirement(args.requirement, args.revision), GUIDANCE_POINTER));
|
|
18020
17995
|
registerStrict("create_requirement", {
|
|
18021
|
-
description:
|
|
17996
|
+
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 the doctrine `get_work_context` returns, under \u201CSearch before you create\u201D.",
|
|
18022
17997
|
inputSchema: createRequirementInputSchema.shape
|
|
18023
17998
|
}, (args) => run(() => client2().createRequirement(args)));
|
|
18024
17999
|
registerStrict("update_requirement", {
|
|
18025
|
-
description: "Update a requirement
|
|
18000
|
+
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 the doctrine `get_work_context` returns, under \u201CLink by key, never paste\u201D.",
|
|
18026
18001
|
// Inline shape (updateRequirementInputSchema is a refine, no .shape) — add
|
|
18027
18002
|
// summary/area here explicitly (§210) and decisionLinks (PIPE-84 §6); the
|
|
18028
18003
|
// empty-edit refine is enforced by the API, not this shape.
|
|
@@ -18038,23 +18013,23 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18038
18013
|
}
|
|
18039
18014
|
}, ({ requirement, ...fields }) => run(() => client2().updateRequirement(requirement, fields)));
|
|
18040
18015
|
registerStrict("approve_requirement", {
|
|
18041
|
-
description: "Approve
|
|
18016
|
+
description: "Approve a requirement\u2019s latest revision, or the `revision` you name. It enters the approval history and becomes the approved revision reads and links build against.",
|
|
18042
18017
|
inputSchema: approveRequirementInputSchema.shape
|
|
18043
18018
|
}, (args) => run(() => client2().approveRequirement(args.requirement, args.revision)));
|
|
18044
18019
|
registerStrict("list_conventions", {
|
|
18045
|
-
description: "List this organization\u2019s conventions \u2014 standing guidance that binds
|
|
18020
|
+
description: "List this organization\u2019s conventions \u2014 standing guidance that binds every project here. Returns 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.",
|
|
18046
18021
|
inputSchema: listConventionsFilterSchema.shape
|
|
18047
18022
|
}, (args) => run(() => client2().listConventions(args), GUIDANCE_POINTER));
|
|
18048
18023
|
registerStrict("get_convention", {
|
|
18049
|
-
description: "Get one convention by display id (`CONV-{n}`) or uuid:
|
|
18024
|
+
description: "Get one convention by display id (`CONV-{n}`) or uuid: its approved text, the revision and approval history, and what replaced it once retired. One never approved reads here and binds nobody.",
|
|
18050
18025
|
inputSchema: { convention: conventionRefSchema }
|
|
18051
18026
|
}, (args) => run(() => client2().getConvention(args.convention), GUIDANCE_POINTER));
|
|
18052
18027
|
registerStrict("create_convention", {
|
|
18053
|
-
description: "Write a convention:
|
|
18028
|
+
description: "Write a convention: standing guidance for the whole organization, with no project attached. Pass `outlivesAnyProject: true` or the write is refused. Text naming one project is refused `convention_names_project_scope`; file that as a cross-cutting requirement instead. It is a draft that binds nobody until a person approves it in a browser.",
|
|
18054
18029
|
inputSchema: createConventionInputSchema.shape
|
|
18055
18030
|
}, (args) => run(() => client2().createConvention(args)));
|
|
18056
18031
|
registerStrict("update_convention", {
|
|
18057
|
-
description: "Amend a convention
|
|
18032
|
+
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 the doctrine `get_work_context` returns, under \u201CConventions need a person\u2019s approval\u201D.",
|
|
18058
18033
|
// Inline shape (updateConventionInputSchema is a refine, so it has no `.shape`);
|
|
18059
18034
|
// the empty-edit rule is enforced by the API, not by this shape.
|
|
18060
18035
|
inputSchema: {
|
|
@@ -18065,19 +18040,19 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18065
18040
|
}
|
|
18066
18041
|
}, ({ convention, ...fields }) => run(() => client2().updateConvention(convention, fields)));
|
|
18067
18042
|
registerStrict("list_prompts", {
|
|
18068
|
-
description: "List
|
|
18043
|
+
description: "List the organization\u2019s prompt library (`PROMPT-{n}`): display id, title, current text, latest revision and any attached project. With no filter this returns the whole library. `project` narrows to the ones attached there \u2014 an attachment says what a prompt is about, not who may run it. `text` matches title and body.",
|
|
18069
18044
|
inputSchema: listPromptsFilterSchema.shape
|
|
18070
18045
|
}, (args) => run(() => client2().listPrompts(args), GUIDANCE_POINTER));
|
|
18071
18046
|
registerStrict("get_prompt", {
|
|
18072
|
-
description: "Get one prompt by display id (`PROMPT-{n}`) or uuid: its current text
|
|
18047
|
+
description: "Get one prompt by display id (`PROMPT-{n}`) or uuid: its current text, which is simply the latest revision, plus every earlier one with who saved it, when and why.",
|
|
18073
18048
|
inputSchema: { prompt: promptRefSchema }
|
|
18074
18049
|
}, (args) => run(() => client2().getPrompt(args.prompt), GUIDANCE_POINTER));
|
|
18075
18050
|
registerStrict("create_prompt", {
|
|
18076
|
-
description: "Write a prompt: reusable
|
|
18051
|
+
description: "Write a prompt: reusable text held by the organization (`PROMPT-{n}`). `body` is the prompt and must not be empty; `title` names it; `note` says why. Optional `project` records what the prompt is about, not who may use it. Nothing approves or deletes a prompt; later wording is a new revision.",
|
|
18077
18052
|
inputSchema: createPromptInputSchema.shape
|
|
18078
18053
|
}, (args) => run(() => client2().createPrompt(args)));
|
|
18079
18054
|
registerStrict("update_prompt", {
|
|
18080
|
-
description: "Amend a prompt
|
|
18055
|
+
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 the doctrine `get_work_context` returns, under \u201CHow to write here\u201D.",
|
|
18081
18056
|
// Inline shape (updatePromptInputSchema is a refine, so it has no `.shape`),
|
|
18082
18057
|
// mirroring that schema's fields — it is the wording this copy follows, `.min(1)`
|
|
18083
18058
|
// bounds included, so a caller learns a blank body is refused from the advertised
|
|
@@ -18092,51 +18067,53 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18092
18067
|
}
|
|
18093
18068
|
}, ({ prompt, ...fields }) => run(() => client2().updatePrompt(prompt, fields)));
|
|
18094
18069
|
registerStrict("create_decision", {
|
|
18095
|
-
description: "Record
|
|
18070
|
+
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 the doctrine `get_work_context` returns, under \u201CA decision record is written once\u201D.",
|
|
18096
18071
|
inputSchema: createDecisionInputSchema.shape
|
|
18097
18072
|
}, (args) => run(() => client2().createDecision(args)));
|
|
18098
18073
|
registerStrict("get_decision", {
|
|
18099
|
-
description: "Get a decision record by display id (`KEY-D{n}`) or uuid:
|
|
18074
|
+
description: "Get a decision record by display id (`KEY-D{n}`) or uuid: its text, its state (`proposed`/`accepted`/`superseded`), who accepted it, what replaced it, and everything citing it.",
|
|
18100
18075
|
inputSchema: getDecisionInputSchema.shape
|
|
18101
18076
|
}, (args) => run(() => client2().getDecision(args.decision), GUIDANCE_POINTER));
|
|
18102
18077
|
registerStrict("list_decisions", {
|
|
18103
|
-
description: "List decision records filtered by `project` and `text`:
|
|
18078
|
+
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.",
|
|
18104
18079
|
inputSchema: listDecisionsFilterSchema.shape
|
|
18105
18080
|
}, (args) => run(() => client2().listDecisions(args), GUIDANCE_POINTER));
|
|
18106
18081
|
registerStrict("supersede_decision", {
|
|
18107
|
-
description: "Replace an
|
|
18082
|
+
description: "Replace an accepted decision record with another: `decision` becomes `superseded` and points at `replacedBy`, where the current decision lives. Refused while the record is still `proposed` (correct it instead), when a record replaces itself, when the replacement is in another project, or when its chain already leads back to this record.",
|
|
18108
18083
|
inputSchema: supersedeDecisionInputSchema.shape
|
|
18109
18084
|
}, (args) => run(() => client2().supersedeDecision(args.decision, args.replacedBy)));
|
|
18110
18085
|
registerStrict("list_content", {
|
|
18111
|
-
description: "List content drafts
|
|
18086
|
+
description: "List content drafts (`KEY-C{n}`) filtered by project, text, area and author. Content is a project-scoped sibling of requirements, not an issue type.",
|
|
18112
18087
|
inputSchema: listContentFilterSchema.shape
|
|
18113
18088
|
}, (args) => run(() => client2().listContent(args), GUIDANCE_POINTER));
|
|
18114
18089
|
registerStrict("get_content", {
|
|
18115
|
-
description: "Get a content draft by display id (KEY-C{n}) or uuid: its
|
|
18090
|
+
description: "Get a content draft by display id (`KEY-C{n}`) or uuid: its revisions, latest or the `revision` you name, plus every deliverable, linked issue and linked asset.",
|
|
18116
18091
|
inputSchema: { content: contentRefSchema, revision: positiveIntSchema.optional() }
|
|
18117
18092
|
}, (args) => run(() => client2().getContent(args.content, args.revision), GUIDANCE_POINTER));
|
|
18118
18093
|
registerStrict("create_content", {
|
|
18119
|
-
description:
|
|
18094
|
+
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 the doctrine `get_work_context` returns, under \u201CHow to write here\u201D.",
|
|
18120
18095
|
inputSchema: createContentInputSchema.shape
|
|
18121
18096
|
}, (args) => run(() => client2().createContent(args)));
|
|
18122
18097
|
registerStrict("update_content", {
|
|
18123
|
-
description: "Update a content draft
|
|
18098
|
+
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 the doctrine `get_work_context` returns, under \u201CHow to write here\u201D.",
|
|
18124
18099
|
inputSchema: updateContentInputSchema.shape
|
|
18125
18100
|
}, ({ content, ...fields }) => run(() => client2().updateContent(content, fields)));
|
|
18126
18101
|
registerStrict("create_deliverable", {
|
|
18127
|
-
description: "Add a per-platform deliverable to a draft
|
|
18102
|
+
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 the doctrine `get_work_context` returns, under \u201CHow to write here\u201D.",
|
|
18128
18103
|
inputSchema: createDeliverableInputSchema.shape
|
|
18129
18104
|
}, ({ content, ...fields }) => run(() => client2().createDeliverable(content, fields)));
|
|
18130
18105
|
registerStrict("update_deliverable", {
|
|
18131
|
-
description: "Append a
|
|
18106
|
+
description: "Append a revision of the post text to a deliverable, addressed `DRAFT-KEY/platform`; `note` says why. The platform itself is immutable. See the doctrine `get_work_context` returns, under \u201CHow to write here\u201D.",
|
|
18132
18107
|
inputSchema: updateDeliverableInputSchema.shape
|
|
18133
18108
|
}, ({ deliverable, ...fields }) => run(() => client2().updateDeliverable(deliverable, fields)));
|
|
18134
18109
|
registerStrict("record_post", {
|
|
18135
|
-
description: "Record that a deliverable (DRAFT-KEY/platform) was posted: its public url and
|
|
18110
|
+
description: "Record that a deliverable (`DRAFT-KEY/platform`) was posted: its public url and `postedAt` (now by default), pinning its current revision as posted. Pipebrain never posts; a person does, then records it here.",
|
|
18136
18111
|
inputSchema: recordPostInputSchema.shape
|
|
18137
18112
|
}, ({ deliverable, ...fields }) => run(() => client2().recordPost(deliverable, fields)));
|
|
18138
18113
|
registerStrict("promote_deliverable", {
|
|
18139
|
-
|
|
18114
|
+
// The paste exception is stated because without it an agent holding the doctrine
|
|
18115
|
+
// would refuse this tool's whole purpose.
|
|
18116
|
+
description: "Create a NEW issue from a deliverable (`DRAFT-KEY/platform`) with a protected link pinning its current revision, which cannot be removed. Needs `title` and `type`; `status` defaults to triage, `body` seeds the issue. A `status` past triage or killed needs `milestone`, or it is refused `milestone_required`. Seeding `body` with the post text is the exception to linking rather than pasting. See the doctrine `get_work_context` returns, under \u201CLink by key, never paste\u201D.",
|
|
18140
18117
|
inputSchema: {
|
|
18141
18118
|
deliverable: external_exports.string().min(1),
|
|
18142
18119
|
title: external_exports.string().min(1),
|
|
@@ -18160,33 +18137,33 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18160
18137
|
}
|
|
18161
18138
|
}, ({ deliverable, ...fields }) => run(() => client2().promoteDeliverable(deliverable, fields)));
|
|
18162
18139
|
registerStrict("list_assets", {
|
|
18163
|
-
description: "List project assets
|
|
18140
|
+
description: "List project assets, filtered by status (pending/active/rejected), category (image/video/document) and filename text, sorted by date or size. No bytes cross this result.",
|
|
18164
18141
|
inputSchema: listAssetsFilterSchema.shape
|
|
18165
18142
|
}, (args) => run(() => client2().listAssets(args), GUIDANCE_POINTER));
|
|
18166
18143
|
registerStrict("get_asset", {
|
|
18167
|
-
description: "Get an asset\u2019s metadata by uuid: filename, contentType, size, sha256
|
|
18144
|
+
description: "Get an asset\u2019s metadata by uuid: filename, contentType, size, sha256, status and linkCount. No bytes and no URL come back; use `download_asset` or `ppb assets download`.",
|
|
18168
18145
|
inputSchema: { asset: uuidSchema }
|
|
18169
18146
|
}, (args) => run(() => client2().getAsset(args.asset), GUIDANCE_POINTER));
|
|
18170
18147
|
if (surface === "local") {
|
|
18171
18148
|
registerStrict("upload_asset", {
|
|
18172
|
-
description: "Upload a
|
|
18149
|
+
description: "Upload a local file as a project asset and activate it. Only the `path` crosses the wire \u2014 never base64 or file contents. `contentType` is guessed from the extension when omitted. Returns the asset metadata; `link_asset` then attaches it to a draft or a deliverable revision.",
|
|
18173
18150
|
inputSchema: uploadAssetInputSchema.shape
|
|
18174
18151
|
}, (args) => run(() => uploadAssetFromPath(client2(), apiBaseUrl, args)));
|
|
18175
18152
|
registerStrict("download_asset", {
|
|
18176
|
-
description: "
|
|
18153
|
+
description: "Write an ACTIVE asset\u2019s bytes to a local file at `path`, created or overwritten, so you can read an uploaded image or document. Addressed by asset uuid. The bytes are checked against the recorded sha256; the result is metadata and the path.",
|
|
18177
18154
|
inputSchema: downloadAssetInputSchema.shape
|
|
18178
18155
|
}, (args) => run(() => downloadAssetToPath(client2(), args)));
|
|
18179
18156
|
}
|
|
18180
18157
|
registerStrict("link_asset", {
|
|
18181
|
-
description: "Reference an active asset
|
|
18158
|
+
description: "Reference an active asset from a `deliverable_revision` (a content_deliverable_revision uuid) or a `content_draft` (a content_draft uuid, copied onto later deliverables). A non-active asset or a cross-project link is rejected.",
|
|
18182
18159
|
inputSchema: linkAssetInputSchema.shape
|
|
18183
18160
|
}, (args) => run(() => client2().linkAsset(args)));
|
|
18184
18161
|
registerStrict("unlink_asset", {
|
|
18185
|
-
description: "Remove an asset reference by its link id
|
|
18162
|
+
description: "Remove an asset reference by its link id. This drops the reference only; the asset itself is untouched. A link on a chat message is author-only \u2014 anyone else is refused `not_message_author`. Every other target type any member can remove.",
|
|
18186
18163
|
inputSchema: unlinkAssetInputSchema.shape
|
|
18187
18164
|
}, (args) => run(() => client2().unlinkAsset(args.linkId)));
|
|
18188
18165
|
registerStrict("set_secret", {
|
|
18189
|
-
description: "Create a secret or add a new version under a project
|
|
18166
|
+
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 the doctrine `get_work_context` returns, under \u201CA secret value never travels in a tool call\u201D.",
|
|
18190
18167
|
inputSchema: {
|
|
18191
18168
|
project: projectKeySchema,
|
|
18192
18169
|
environment: external_exports.string().min(1),
|
|
@@ -18196,14 +18173,14 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18196
18173
|
}
|
|
18197
18174
|
}, ({ project, environment, name, value, description }) => run(() => client2().setSecret(project, { env: environment, name, value, description })));
|
|
18198
18175
|
registerStrict("list_secrets", {
|
|
18199
|
-
description: "List a project
|
|
18176
|
+
description: "List a project\u2019s secrets: id, name, environment, description, current version, timestamps. No value comes back. Optional `environment` slug narrows it.",
|
|
18200
18177
|
inputSchema: {
|
|
18201
18178
|
project: projectKeySchema,
|
|
18202
18179
|
environment: external_exports.string().min(1).optional()
|
|
18203
18180
|
}
|
|
18204
18181
|
}, ({ project, environment }) => run(() => client2().listSecrets(project, { env: environment }), GUIDANCE_POINTER));
|
|
18205
18182
|
registerStrict("describe_secret", {
|
|
18206
|
-
description: "Describe one secret: its metadata plus
|
|
18183
|
+
description: "Describe one secret: its metadata plus each version \u2014 when, by whom, and whether it was crypto-shredded. Nothing here returns a value.",
|
|
18207
18184
|
inputSchema: {
|
|
18208
18185
|
project: projectKeySchema,
|
|
18209
18186
|
environment: external_exports.string().min(1),
|
|
@@ -18211,7 +18188,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18211
18188
|
}
|
|
18212
18189
|
}, ({ project, environment, name }) => run(() => client2().describeSecret(project, name, { env: environment }), GUIDANCE_POINTER));
|
|
18213
18190
|
registerStrict("rotate_secret", {
|
|
18214
|
-
description: "
|
|
18191
|
+
description: "Add a new version to an existing secret and advance the pointer. Same value rules as `set_secret`: a value crosses inbound only and nothing here returns one, so set a real credential with `ppb secrets set`, from a file or stdin.",
|
|
18215
18192
|
inputSchema: {
|
|
18216
18193
|
project: projectKeySchema,
|
|
18217
18194
|
environment: external_exports.string().min(1),
|
|
@@ -18220,7 +18197,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18220
18197
|
}
|
|
18221
18198
|
}, ({ project, environment, name, value }) => run(() => client2().rotateSecret(project, name, { env: environment, value })));
|
|
18222
18199
|
registerStrict("update_secret_description", {
|
|
18223
|
-
description: "
|
|
18200
|
+
description: "Change a secret\u2019s description \u2014 metadata only: no new version, and nothing here returns a value. See the doctrine `get_work_context` returns, under \u201CHow to write here\u201D.",
|
|
18224
18201
|
inputSchema: {
|
|
18225
18202
|
project: projectKeySchema,
|
|
18226
18203
|
environment: external_exports.string().min(1),
|
|
@@ -18229,7 +18206,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18229
18206
|
}
|
|
18230
18207
|
}, ({ project, environment, name, description }) => run(() => client2().updateSecretDescription(project, name, { env: environment, description })));
|
|
18231
18208
|
registerStrict("delete_secret", {
|
|
18232
|
-
description: "Delete a secret: soft-delete the entry and crypto-shred
|
|
18209
|
+
description: "Delete a secret: soft-delete the entry and crypto-shred every version, so the value is unrecoverable. Returns confirmation metadata only.",
|
|
18233
18210
|
inputSchema: {
|
|
18234
18211
|
project: projectKeySchema,
|
|
18235
18212
|
environment: external_exports.string().min(1),
|
|
@@ -18240,14 +18217,14 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
|
|
|
18240
18217
|
return { deleted: true, project, environment, name };
|
|
18241
18218
|
}));
|
|
18242
18219
|
registerStrict("list_org_instructions", {
|
|
18243
|
-
description: "List
|
|
18220
|
+
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."
|
|
18244
18221
|
}, () => run(() => client2().listOrgInstructions(), GUIDANCE_POINTER));
|
|
18245
18222
|
registerStrict("set_org_instruction", {
|
|
18246
|
-
description: "Set
|
|
18223
|
+
description: "Set one scope\u2019s organization-level agent instruction, up to 4000 characters; over that 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, so a contributor is refused. New sessions pick it up; running ones keep what they started with. See the doctrine `get_work_context` returns, under \u201CSetting an instruction replaces the whole body\u201D.",
|
|
18247
18224
|
inputSchema: setOrgInstructionInputSchema.shape
|
|
18248
18225
|
}, (args) => run(() => client2().setOrgInstruction(args.scope, args.body)));
|
|
18249
18226
|
registerStrict("delete_org_instruction", {
|
|
18250
|
-
description: "Clear one scope\u2019s
|
|
18227
|
+
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, so a contributor is refused. New sessions pick up the change; running ones keep what they started with.",
|
|
18251
18228
|
inputSchema: deleteOrgInstructionInputSchema.shape
|
|
18252
18229
|
}, (args) => run(() => client2().deleteOrgInstruction(args.scope)));
|
|
18253
18230
|
return server2;
|
|
@@ -18316,7 +18293,7 @@ function loadMcpConfig() {
|
|
|
18316
18293
|
}
|
|
18317
18294
|
|
|
18318
18295
|
// src/version.ts
|
|
18319
|
-
var VERSION = "0.
|
|
18296
|
+
var VERSION = "0.50.0";
|
|
18320
18297
|
|
|
18321
18298
|
// src/main.ts
|
|
18322
18299
|
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.50.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",
|