@productbrain/mcp 0.0.1-beta.9533 → 0.0.1-beta.9534

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.
@@ -43,7 +43,7 @@ import {
43
43
  trackQualityVerdict,
44
44
  trackSessionCaptureRate,
45
45
  trackZeroCaptureAuditFired
46
- } from "./chunk-GXPFXLOF.js";
46
+ } from "./chunk-HOSGKIO6.js";
47
47
 
48
48
  // src/server.ts
49
49
  import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -12223,21 +12223,28 @@ function formatSeatSentence(action, reply, clean) {
12223
12223
  }
12224
12224
  return reply.removed ? `Unseated ${who} from ${roleLabel2} in ${teamLabel}.` : `${who} did not hold ${roleLabel2} in ${teamLabel}; nothing changed.`;
12225
12225
  }
12226
+ function formatInviteSentence(reply, clean) {
12227
+ const who = `${clean(reply.person)} [${reply.personId}]`;
12228
+ return reply.inPlace ? `Invited ${who} at ${clean(reply.email)}; they keep every role they already hold.` : `Invited ${who} at ${clean(reply.email)} as a new person.`;
12229
+ }
12226
12230
 
12227
12231
  // src/tools/role.ts
12228
- var ROLE_ACTIONS = ["accountable", "seat", "unseat"];
12232
+ var ROLE_ACTIONS = ["accountable", "seat", "unseat", "invite"];
12229
12233
  var roleSchema = z31.object({
12230
12234
  action: z31.enum(ROLE_ACTIONS).describe(
12231
- "'accountable': what this role owns (explicit), reports on (reporting), and \u2014 not yet computed \u2014 inherits via Team-Lead vacancy coverage. 'seat': put a person in this role. 'unseat': take this role from a person."
12235
+ "'accountable': what this role owns (explicit), reports on (reporting), and \u2014 not yet computed \u2014 inherits via Team-Lead vacancy coverage. 'seat': put a person in this role. 'unseat': take this role from a person. 'invite': send a login link by email; a placeholder person (seated by name) is invited in place and keeps every role it holds."
12232
12236
  ),
12233
- roleId: z31.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. Required for every action."),
12237
+ roleId: z31.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. Required for every action except 'invite'."),
12234
12238
  canonicalKey: z31.string().max(200).optional().describe("'accountable': narrow to one kind (e.g. objective, key_result, tension)."),
12235
12239
  limit: z31.number().int().positive().max(200).optional().describe("'accountable': max explicit-list rows per page (default and maximum 200)."),
12236
12240
  cursor: z31.number().optional().describe("'accountable': continue the explicit list from an earlier truncated page's nextCursor."),
12237
- person: z31.string().max(200).optional().describe("'seat' / 'unseat': an existing person, by person id or exact email. For 'seat', give this or newPersonName, not both."),
12241
+ person: z31.string().max(200).optional().describe(
12242
+ "'seat' / 'unseat': an existing person, by person id or exact email. For 'seat', give this or newPersonName, not both. 'invite': the placeholder's person id."
12243
+ ),
12238
12244
  newPersonName: z31.string().max(120).optional().describe(
12239
- "'seat': the name of a person who has no login yet; a placeholder person is created once and reused for the same exact name. Refused when the name belongs to someone with an account."
12245
+ "'seat': the name of a person who has no login yet; a placeholder person is created once and reused for the same exact name. Refused when the name belongs to someone with an account. 'invite': the name of a new person to invite, instead of person."
12240
12246
  ),
12247
+ email: z31.string().max(320).optional().describe("'invite': where the login link goes. Required for 'invite'."),
12241
12248
  teamId: z31.string().max(200).optional().describe(
12242
12249
  "'seat' / 'unseat': the team, by chain id or entry id. Only needed for Team Lead, which lives in every team; any other role is seated in its home team."
12243
12250
  )
@@ -12262,11 +12269,18 @@ var unseatVariant = z31.object({
12262
12269
  person: z31.string().max(200),
12263
12270
  teamId: z31.string().max(200).optional()
12264
12271
  });
12265
- var roleActionUnion = z31.discriminatedUnion("action", [accountableVariant, seatVariant, unseatVariant]);
12272
+ var inviteVariant = z31.object({
12273
+ action: z31.literal("invite"),
12274
+ email: z31.string().max(320),
12275
+ person: z31.string().max(200).optional(),
12276
+ newPersonName: z31.string().max(120).optional()
12277
+ });
12278
+ var roleActionUnion = z31.discriminatedUnion("action", [accountableVariant, seatVariant, unseatVariant, inviteVariant]);
12266
12279
  var ROLE_ACTION_SPECS = {
12267
12280
  accountable: { params: ["roleId", "canonicalKey", "limit", "cursor"], description: "roleId is required; canonicalKey/limit/cursor are optional." },
12268
12281
  seat: { params: ["roleId", "person", "newPersonName", "teamId"], description: "roleId is required, plus exactly one of person or newPersonName; teamId only for Team Lead." },
12269
- unseat: { params: ["roleId", "person", "teamId"], description: "roleId and person are required; teamId only for Team Lead." }
12282
+ unseat: { params: ["roleId", "person", "teamId"], description: "roleId and person are required; teamId only for Team Lead." },
12283
+ invite: { params: ["email", "person", "newPersonName"], description: "email is required, plus exactly one of person (a placeholder's id) or newPersonName." }
12270
12284
  };
12271
12285
  function formatHolders(result2) {
12272
12286
  if (result2.holders.length === 0) return [` holders: none${result2.holdersTruncated ? " found, but the membership scan is incomplete" : ""}`];
@@ -12346,6 +12360,22 @@ function handleRoleSeat(roleId, person, newPersonName, teamId) {
12346
12360
  function handleRoleUnseat(roleId, person, teamId) {
12347
12361
  return runSeatAction("unseat", "organisation.unseatFromRole", { roleId, person, teamId });
12348
12362
  }
12363
+ async function handleRoleInvite(email, person, newPersonName) {
12364
+ const params = { email, person, newPersonName };
12365
+ try {
12366
+ requireWriteAccess();
12367
+ const result2 = await kernelMutation("people.invite", {
12368
+ email,
12369
+ targetPersonId: person,
12370
+ displayName: newPersonName,
12371
+ sessionId: getAgentSessionId()
12372
+ });
12373
+ const message = formatInviteSentence(result2, sanitizeSingleLineField);
12374
+ return successResult(message, message, result2);
12375
+ } catch (err) {
12376
+ return roleErrorResult(err, "invite", params);
12377
+ }
12378
+ }
12349
12379
  function roleErrorResult(err, action, params = {}) {
12350
12380
  const classified = classifyError(err);
12351
12381
  const definedParams = Object.fromEntries(Object.entries(params).filter(([, v]) => v !== void 0));
@@ -12363,13 +12393,14 @@ function registerRoleTools(server) {
12363
12393
  const roleHandlers = {
12364
12394
  accountable: (data) => handleRoleAccountable(data.roleId, data.canonicalKey, data.limit, data.cursor),
12365
12395
  seat: (data) => handleRoleSeat(data.roleId, data.person, data.newPersonName, data.teamId),
12366
- unseat: (data) => handleRoleUnseat(data.roleId, data.person, data.teamId)
12396
+ unseat: (data) => handleRoleUnseat(data.roleId, data.person, data.teamId),
12397
+ invite: (data) => handleRoleInvite(data.email, data.person, data.newPersonName)
12367
12398
  };
12368
12399
  server.registerTool(
12369
12400
  "role",
12370
12401
  {
12371
12402
  title: "Role",
12372
- description: "Workspace roles \u2014 who is accountable for what, and who holds it. Three actions:\n\n- **accountable**: what this role owns (`explicit` \u2014 the canonical ownership row for objective/key_result/tension, data.ownerRoleId for domain), reports on (`reporting`, data.reporterRoleId \u2014 key results and tensions only, a separate fact from ownership), and \u2014 always `not-computed` today, never faked as empty \u2014 inherits via Team-Lead vacancy coverage. Also lists `holders`.\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.\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.",
12403
+ description: "Workspace roles \u2014 who is accountable for what, and who holds it. Four actions:\n\n- **accountable**: what this role owns (`explicit` \u2014 the canonical ownership row for objective/key_result/tension, data.ownerRoleId for domain), reports on (`reporting`, data.reporterRoleId \u2014 key results and tensions only, a separate fact from ownership), and \u2014 always `not-computed` today, never faked as empty \u2014 inherits via Team-Lead vacancy coverage. Also lists `holders`.\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.\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.\n- **invite**: send a login link to `email`. Give `person` (a placeholder's id, as `seat` printed it) to invite that person in place: they keep every role they hold. Or give `newPersonName` to invite someone new. A person who already has a login or an invite is refused. Needs a workspace owner or admin.",
12373
12404
  inputSchema: roleSchema,
12374
12405
  // The hints describe the whole tool, so they say the most it can do: unseat removes a role and can
12375
12406
  // delete a membership row (destructive), and a repeated seat still writes history and audit rows
@@ -12731,7 +12762,7 @@ var AGENT_CHEATSHEET = `# Product Brain \u2014 Agent Cheatsheet
12731
12762
  | \`feedback\` | Submit product feedback (any key, no session required); \`list\` reads back your own workspace's feedback (no gate); vendor triage: queue / note / group / status | \`action\`: submit, list, queue, note, group, status |
12732
12763
  | \`shape\` | Review / disposition write-shape (single-concern split) advisories | \`action\`: list, show, agree, dismiss |
12733
12764
  | \`question\` | Routed questions \u2014 create, reassign, answer, force-close, list | \`action\`: create, assign, answer, force-close, list |
12734
- | \`role\` | What a role is accountable for and who holds it; seat a person in a role (by id, email or a new name) or unseat them | \`action\`: accountable, seat, unseat |
12765
+ | \`role\` | What a role owns and who holds it; seat (by id, email or new name), unseat, or invite a placeholder in place | \`action\`: accountable, seat, unseat, invite |
12735
12766
  | \`outcome\` | Record an outcome measurement and read the ones already recorded | \`action\`: record, list, show |
12736
12767
 
12737
12768
  This table lists actions only. See \`tools/list\` for each tool's input schema; a missing or invalid parameter is named in the call's own reply.
@@ -13503,4 +13534,4 @@ export {
13503
13534
  createProductBrainServer,
13504
13535
  initFeatureFlags
13505
13536
  };
13506
- //# sourceMappingURL=chunk-HHH3YC56.js.map
13537
+ //# sourceMappingURL=chunk-APQ2MKVQ.js.map