@productbrain/mcp 0.0.1-beta.11701 → 0.0.1-beta.11709
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-SFMHXRAR.js → chunk-H2SLL2NX.js} +18 -5
- package/dist/chunk-H2SLL2NX.js.map +1 -0
- package/dist/{chunk-CMRO7CUV.js → chunk-VW2VE3H6.js} +48 -25
- package/dist/chunk-VW2VE3H6.js.map +1 -0
- package/dist/cli/index.js +1 -1
- package/dist/http.js +2 -2
- package/dist/index.js +2 -2
- package/dist/{setup-E2EUL2GQ.js → setup-CSHFV46V.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-CMRO7CUV.js.map +0 -1
- package/dist/chunk-SFMHXRAR.js.map +0 -1
- /package/dist/{setup-E2EUL2GQ.js.map → setup-CSHFV46V.js.map} +0 -0
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
runWithToolContext,
|
|
37
37
|
setSessionOriented,
|
|
38
38
|
startAgentSession
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-H2SLL2NX.js";
|
|
40
40
|
|
|
41
41
|
// src/server.ts
|
|
42
42
|
import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -2430,7 +2430,7 @@ var PRODUCT_NAME = "Product Brain";
|
|
|
2430
2430
|
var PLATFORM_TERM_LABEL = "Platform";
|
|
2431
2431
|
var PLATFORM_GLOSSARY_TERMS = [
|
|
2432
2432
|
{ name: "Team", definition: "A group of people and agents working toward one purpose; its roles own its decisions, rules and work." },
|
|
2433
|
-
{ name: "Team Lead", definition: "The role accountable for a team's purpose; it
|
|
2433
|
+
{ name: "Team Lead", definition: "The role accountable for a team's purpose; it holds every role in its team with no one seated, covering it." },
|
|
2434
2434
|
{ name: "Role", definition: "A named set of responsibilities a person holds; whoever sits in it decides and accepts the entries it owns." },
|
|
2435
2435
|
{ name: "Tension", definition: "A gap someone notices between how things are and how they should be; linking one to an entry is how you say you disagree with it." }
|
|
2436
2436
|
];
|
|
@@ -2744,7 +2744,7 @@ async function handleGet(entryId) {
|
|
|
2744
2744
|
if (e.sourceExcerpt) lines.push(`**Source excerpt:** ${e.sourceExcerpt}`);
|
|
2745
2745
|
const parsedOwner = parseOwnerOutput(e.owner);
|
|
2746
2746
|
if (parsedOwner) lines.push(formatOwnerLine2(parsedOwner));
|
|
2747
|
-
for (const line of [e.pendingMove, e.qualityOverride, e.spineLine]) if (typeof line === "string") lines.push(sanitizeSingleLineField(line));
|
|
2747
|
+
for (const line of [e.heldLine, e.pendingMove, e.qualityOverride, e.spineLine]) if (typeof line === "string") lines.push(sanitizeSingleLineField(line));
|
|
2748
2748
|
const restsOnChanged = Array.isArray(e.restsOnChanged) ? e.restsOnChanged : [];
|
|
2749
2749
|
for (const item of restsOnChanged) lines.push(sanitizeSingleLineField(restsOnLine(item)));
|
|
2750
2750
|
if (epistemic) lines.push(formatEpistemicLine(epistemic));
|
|
@@ -2793,7 +2793,9 @@ async function handleGet(entryId) {
|
|
|
2793
2793
|
...e.sourceExcerpt ? { sourceExcerpt: String(e.sourceExcerpt) } : {},
|
|
2794
2794
|
...parsedOwner ? { owner: parsedOwner } : {},
|
|
2795
2795
|
// TASK-954, TASK-1027: the kernel's lines as served
|
|
2796
|
-
...Object.fromEntries(["pendingMove", "qualityOverride", "spineLine"].filter((k) => typeof e[k] === "string").map((k) => [k, e[k]])),
|
|
2796
|
+
...Object.fromEntries(["heldLine", "pendingMove", "qualityOverride", "spineLine"].filter((k) => typeof e[k] === "string").map((k) => [k, e[k]])),
|
|
2797
|
+
...e.coveredBy !== void 0 ? { coveredBy: e.coveredBy } : {},
|
|
2798
|
+
// DEC-2257: the Team Lead holding a role with no one seated
|
|
2797
2799
|
...restsOnChanged.length > 0 ? { restsOnChanged } : {},
|
|
2798
2800
|
// TEN-2191: surface the WHY and its quality in the structured payload too.
|
|
2799
2801
|
...why.length > 0 ? { why } : {},
|
|
@@ -3644,7 +3646,8 @@ var ORGANISATION_VALIDATION_REFUSALS = /* @__PURE__ */ new Set([
|
|
|
3644
3646
|
"move_not_needed",
|
|
3645
3647
|
"no_pending_move",
|
|
3646
3648
|
"why_required",
|
|
3647
|
-
"role_unplaced"
|
|
3649
|
+
"role_unplaced",
|
|
3650
|
+
"role_work_target_invalid"
|
|
3648
3651
|
]);
|
|
3649
3652
|
|
|
3650
3653
|
// src/tools/people.ts
|
|
@@ -4037,13 +4040,13 @@ function registerOutcomeTools(server) {
|
|
|
4037
4040
|
|
|
4038
4041
|
// src/tools/role.ts
|
|
4039
4042
|
import { z as z18 } from "zod/v3";
|
|
4040
|
-
var ROLE_ACTIONS = ["seat", "unseat", "release_move", "accept_move", "refuse_move"];
|
|
4043
|
+
var ROLE_ACTIONS = ["seat", "unseat", "release_move", "accept_move", "refuse_move", "move_work"];
|
|
4041
4044
|
var ROLE_READS_MOVED = {
|
|
4042
4045
|
accountable: "role only writes. Read what a role owns and who holds it with the read-only context tool: context action=accountable roleId=..."
|
|
4043
4046
|
};
|
|
4044
4047
|
var roleSchema = z18.object({
|
|
4045
4048
|
action: actionEnum(ROLE_ACTIONS, ROLE_READS_MOVED).describe(
|
|
4046
|
-
"'seat': put a person in this role. 'unseat': take this role from a person. 'release_move' / 'accept_move' / 'refuse_move': move a role or team to a team with another Team Lead."
|
|
4049
|
+
"'seat': put a person in this role. 'unseat': take this role from a person. 'release_move' / 'accept_move' / 'refuse_move': move a role or team to a team with another Team Lead. 'move_work': move every open item a role owns to another role."
|
|
4047
4050
|
),
|
|
4048
4051
|
roleId: z18.string().max(200).optional().describe("The role, by the human chain id `chain.getEntry` or `pb get` would print for it, or a raw entry id."),
|
|
4049
4052
|
person: z18.string().max(200).optional().describe(
|
|
@@ -4059,7 +4062,10 @@ var roleSchema = z18.object({
|
|
|
4059
4062
|
"'seat', Team Lead only: as the team's Team Lead, seat a second Team Lead who holds the seat on your authority until you unseat them."
|
|
4060
4063
|
),
|
|
4061
4064
|
childId: z18.string().max(200).optional().describe("'release_move' / 'accept_move' / 'refuse_move': the role or team that moves, by chain id or entry id."),
|
|
4062
|
-
why: z18.string().max(1e3).optional().describe("'refuse_move': why the move is refused; it becomes a tension for the old team's Team Lead.")
|
|
4065
|
+
why: z18.string().max(1e3).optional().describe("'refuse_move': why the move is refused; it becomes a tension for the old team's Team Lead."),
|
|
4066
|
+
fromRoleId: z18.string().max(200).optional().describe("'move_work': the role whose open work moves, by chain id or entry id."),
|
|
4067
|
+
toRoleId: z18.string().max(200).optional().describe("'move_work': the receiving role; without it, the nearest Team Lead of the old role's home team."),
|
|
4068
|
+
dryRun: z18.boolean().optional().describe("'move_work': return the receipt and change nothing.")
|
|
4063
4069
|
});
|
|
4064
4070
|
var seatVariant = z18.object({
|
|
4065
4071
|
action: z18.literal("seat"),
|
|
@@ -4078,12 +4084,14 @@ var unseatVariant = z18.object({
|
|
|
4078
4084
|
var releaseMoveVariant = z18.object({ action: z18.literal("release_move"), childId: z18.string().max(200), teamId: z18.string().max(200) });
|
|
4079
4085
|
var acceptMoveVariant = z18.object({ action: z18.literal("accept_move"), childId: z18.string().max(200) });
|
|
4080
4086
|
var refuseMoveVariant = z18.object({ action: z18.literal("refuse_move"), childId: z18.string().max(200), why: z18.string().max(1e3) });
|
|
4087
|
+
var moveWorkVariant = z18.object({ action: z18.literal("move_work"), fromRoleId: z18.string().max(200), toRoleId: z18.string().max(200).optional(), dryRun: z18.boolean().optional() });
|
|
4081
4088
|
var roleActionUnion = z18.discriminatedUnion("action", [
|
|
4082
4089
|
seatVariant,
|
|
4083
4090
|
unseatVariant,
|
|
4084
4091
|
releaseMoveVariant,
|
|
4085
4092
|
acceptMoveVariant,
|
|
4086
|
-
refuseMoveVariant
|
|
4093
|
+
refuseMoveVariant,
|
|
4094
|
+
moveWorkVariant
|
|
4087
4095
|
]);
|
|
4088
4096
|
var ROLE_ACTION_SPECS = {
|
|
4089
4097
|
seat: {
|
|
@@ -4093,18 +4101,12 @@ var ROLE_ACTION_SPECS = {
|
|
|
4093
4101
|
unseat: { params: ["roleId", "person", "teamId"], description: "roleId and person are required; teamId only for Team Lead." },
|
|
4094
4102
|
release_move: { params: ["childId", "teamId"], description: "childId and teamId (the team it moves to) are required." },
|
|
4095
4103
|
accept_move: { params: ["childId"], description: "childId is required." },
|
|
4096
|
-
refuse_move: { params: ["childId", "why"], description: "childId and why are required." }
|
|
4104
|
+
refuse_move: { params: ["childId", "why"], description: "childId and why are required." },
|
|
4105
|
+
move_work: { params: ["fromRoleId", "toRoleId", "dryRun"], description: "fromRoleId is required; toRoleId and dryRun are optional." }
|
|
4097
4106
|
};
|
|
4098
|
-
function
|
|
4099
|
-
if (result2.holders.length === 0) return [` holders: none${result2.holdersTruncated ? " found, but the membership scan is incomplete" : ""}`];
|
|
4107
|
+
function formatHeld(result2) {
|
|
4100
4108
|
return [
|
|
4101
|
-
|
|
4102
|
-
...result2.holders.map((h) => {
|
|
4103
|
-
const word = personStatusTag(h.status);
|
|
4104
|
-
const tag = word ? ` (${word})` : "";
|
|
4105
|
-
const team = h.teamName ? ` in ${sanitizeSingleLineField(h.teamName)}` : "";
|
|
4106
|
-
return ` ${sanitizeSingleLineField(h.displayName)}${tag}${team}`;
|
|
4107
|
-
}),
|
|
4109
|
+
...result2.heldLine ? [` ${sanitizeSingleLineField(result2.heldLine)}`] : [],
|
|
4108
4110
|
...result2.holdersTruncated ? [" (membership scan cut at 500 rows, more holders may exist)"] : []
|
|
4109
4111
|
];
|
|
4110
4112
|
}
|
|
@@ -4125,10 +4127,9 @@ function formatRoleAccountable(result2) {
|
|
|
4125
4127
|
const explicitCursorHint = result2.nextCursor !== null ? `raise limit or continue with cursor ${result2.nextCursor}` : "raise limit";
|
|
4126
4128
|
return [
|
|
4127
4129
|
`${roleLabel2} \u2014 accountability:`,
|
|
4128
|
-
...
|
|
4130
|
+
...formatHeld(result2),
|
|
4129
4131
|
...formatSection("explicit (owns)", result2.explicit, result2.explicitTruncated, explicitCursorHint),
|
|
4130
|
-
...formatSection("reporting (reports on)", result2.reporting, result2.reportingTruncated)
|
|
4131
|
-
` inherited: not-computed \u2014 ${result2.inheritedNotComputedReason}`
|
|
4132
|
+
...formatSection("reporting (reports on)", result2.reporting, result2.reportingTruncated)
|
|
4132
4133
|
].join("\n");
|
|
4133
4134
|
}
|
|
4134
4135
|
async function handleRoleAccountable(roleId, canonicalKey, limit, cursor) {
|
|
@@ -4199,6 +4200,27 @@ async function handleMove(action, params) {
|
|
|
4199
4200
|
return roleErrorResult(err, action, params);
|
|
4200
4201
|
}
|
|
4201
4202
|
}
|
|
4203
|
+
async function handleMoveWork(params) {
|
|
4204
|
+
try {
|
|
4205
|
+
requireWriteAccess();
|
|
4206
|
+
const raw = await kernelMutation("organisation.setOwnership", {
|
|
4207
|
+
fromRoleRef: params.fromRoleId,
|
|
4208
|
+
roleRef: params.toRoleId,
|
|
4209
|
+
...params.dryRun ? { dryRun: true } : {},
|
|
4210
|
+
sessionId: getAgentSessionId()
|
|
4211
|
+
});
|
|
4212
|
+
if (raw.outcome === "refused") {
|
|
4213
|
+
const recovery = authorityRecovery(raw.refusal) ?? raw.refusal.requiredAction ?? "Ask the Team Lead the refusal names.";
|
|
4214
|
+
const message2 = `Refused: ${sanitizeSingleLineField(raw.refusal.reason)} ${recovery}`;
|
|
4215
|
+
const code = ORGANISATION_VALIDATION_REFUSALS.has(raw.refusal.code) ? "VALIDATION_ERROR" : "PERMISSION_DENIED";
|
|
4216
|
+
return failureResult(message2, code, message2, recovery, void 0, raw);
|
|
4217
|
+
}
|
|
4218
|
+
const message = raw.text;
|
|
4219
|
+
return successResult(message, message, raw);
|
|
4220
|
+
} catch (err) {
|
|
4221
|
+
return roleErrorResult(err, "move_work", params);
|
|
4222
|
+
}
|
|
4223
|
+
}
|
|
4202
4224
|
function roleErrorResult(err, action, params = {}) {
|
|
4203
4225
|
const classified = classifyError(err);
|
|
4204
4226
|
const definedParams = Object.fromEntries(Object.entries(params).filter(([, v]) => v !== void 0));
|
|
@@ -4218,13 +4240,14 @@ function registerRoleTools(server) {
|
|
|
4218
4240
|
unseat: (data) => handleRoleUnseat(data.roleId, data.person, data.teamId),
|
|
4219
4241
|
release_move: (data) => handleMove("release_move", data),
|
|
4220
4242
|
accept_move: (data) => handleMove("accept_move", data),
|
|
4221
|
-
refuse_move: (data) => handleMove("refuse_move", data)
|
|
4243
|
+
refuse_move: (data) => handleMove("refuse_move", data),
|
|
4244
|
+
move_work: (data) => handleMoveWork(data)
|
|
4222
4245
|
};
|
|
4223
4246
|
server.registerTool(
|
|
4224
4247
|
"role",
|
|
4225
4248
|
{
|
|
4226
4249
|
title: "Role",
|
|
4227
|
-
description: "Workspace roles \u2014 who holds a role and
|
|
4250
|
+
description: "Workspace roles \u2014 who holds a role, where it sits and what it owns. Six actions:\n\n- **seat**: put a person in this role, by person id or email, or by `newPersonName` for someone with no login yet (a placeholder person, created once per exact name). The role's home team is used; name `teamId` only for Team Lead. A held role refuses a second holder and names the holder; the person keeps their other roles in the team. Needs the team's Team Lead. With `onMyAuthority`, the team's Team Lead seats a second Team Lead on their own authority; unseating them revokes it.\n- **unseat**: take this role from a person; their other roles in the team stay. The last active Team Lead of a team is refused. Invite a person, or take them out of a team, with the `people` tool.\n- **release_move** / **accept_move** / **refuse_move**: move a role or team (`childId`) to a team whose Team Lead is another seat. Acting as Team Lead of its current team, release it toward `teamId`; acting as Team Lead of that team, accept it, or refuse it with `why` (it stays, and a tension goes to its current team's Team Lead).\n- **move_work**: move every open item `fromRoleId` owns (drafts, current entries, open questions) to `toRoleId`, each checked as a one-entry ownership change; an item the receiver may not own goes to the nearest Team Lead of the old role's home team, and past items stay as history. Each call moves a capped number and says how many remain; call again to finish. `dryRun` returns the receipt and changes nothing.\n\nRead what a role owns with `context action=accountable`.",
|
|
4228
4251
|
inputSchema: roleSchema,
|
|
4229
4252
|
// The hints describe the whole tool, so they say the most it can do: unseat removes a role and can
|
|
4230
4253
|
// delete a membership row (destructive), and a repeated seat still writes history and audit rows
|
|
@@ -11848,4 +11871,4 @@ export {
|
|
|
11848
11871
|
SERVER_VERSION,
|
|
11849
11872
|
createProductBrainServer
|
|
11850
11873
|
};
|
|
11851
|
-
//# sourceMappingURL=chunk-
|
|
11874
|
+
//# sourceMappingURL=chunk-VW2VE3H6.js.map
|