@practicemakes/mcp 26.823.1 → 26.823.2

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 CHANGED
@@ -55,7 +55,7 @@ Versioned on the monorepo's calendar scheme (`26.MMDD.patch`). Scoped public pac
55
55
  yarn workspace @practicemakes/mcp publish # runs prepublishOnly (clean + build) first
56
56
  ```
57
57
 
58
- ## Tools (v1, read-only)
58
+ ## Tools (read)
59
59
 
60
60
  | Tool | What it returns |
61
61
  |---|---|
@@ -69,7 +69,7 @@ yarn workspace @practicemakes/mcp publish # runs prepublishOnly (clean + build
69
69
  | `get_task` | One task by UUID |
70
70
  | `list_workflows` | Your Planner workflows |
71
71
  | `get_workflow` | One workflow, hydrated with run state |
72
- | `get_billing_config` | Billing/entitlement config for your org |
72
+ | `get_billing_config` | Billing/entitlement config for your org (takes a required `platform`) |
73
73
  | `list_servers` | Monitored servers + both identifiers the tools below need |
74
74
  | `get_server` | One server's config, including its maintenance windows |
75
75
  | `list_server_statuses` | Live status of **all** your servers (is anything down now) |
@@ -89,8 +89,43 @@ one returns a 404 that reads like a missing server. `list_servers` returns both:
89
89
 
90
90
  Neither is a UUID — `serverId` is the operator-assigned id (e.g. `web-01`).
91
91
 
92
+ ## Tools (write)
93
+
94
+ Every write tool asks you to confirm the exact request before it sends anything.
95
+
96
+ | Tool | What it does |
97
+ |---|---|
98
+ | `create_task` | Creates a Planner task in your current org context (needs ADMIN there) |
99
+ | `complete_task` | Sets `completedAt` on a task — the tool that closes the Planner-driven-dev loop |
100
+ | `switch_org_context` | Switches your active org context (SYSADMIN → any org, otherwise invite-only) |
101
+
102
+ ### Read this before enabling write tools
103
+
104
+ **These tools run as you, with all of your permissions.** Authentication is a per-user Firebase
105
+ ID token, not a service account — there is no read-only role, no separate service identity, and
106
+ no server-side allow-list behind them. If your account is SYSADMIN, a write tool can write across
107
+ every tenant on the platform. The tool list and the confirmation prompt are the entire safety
108
+ boundary.
109
+
110
+ **Confirmation needs client support.** The gate uses MCP *elicitation*. If your client did not
111
+ advertise elicitation support during the handshake, there is no channel to ask you on, and every
112
+ write tool **refuses and sends nothing** — it will not fall back to writing unconfirmed. The
113
+ error names the mutation it declined to perform. This is deliberate: silently proceeding would
114
+ disable the guardrail for exactly the clients that never agreed to be asked.
115
+
116
+ **`switch_org_context` is session-global.** It mutates your profile, so it changes what *every
117
+ later read tool* returns for the rest of the session — not just the next call.
118
+
119
+ **`complete_task` is a read-modify-write.** There is no dedicated completion endpoint; the tool
120
+ reads the task, sets `completedAt`, and re-posts it, carrying the read's `ETag` back as
121
+ `If-Match` so a concurrent edit is not silently clobbered.
122
+
123
+ `yarn smoke` is unaffected — it issues three fixed read-only requests and never touches the tool
124
+ registry, so no write can reach production through it.
125
+
92
126
  ## Roadmap
93
127
 
94
- - **Write tools** (complete/create a task, switch org context, admin actions) with per-call confirmation.
128
+ - **Admin actions** (e.g. the ARCH-153 org/user purge) — blocked: no executable endpoint exists
129
+ yet, only a `GET .../data-inventory` that reports what a purge *would* do and deletes nothing.
95
130
  - **OpenAPI-driven** tool generation from the backend's grouped specs (the monorepo already uses `openapi-typescript` in `packages/core`).
96
131
  - **Hosted remote server** (streamable-HTTP + per-user OAuth) so users connect to a URL with nothing to install.
@@ -13,6 +13,18 @@ export declare class PmClient {
13
13
  private tokens;
14
14
  constructor(baseUrl: string, tokens: TokenManager);
15
15
  get(path: string): Promise<unknown>;
16
- post(path: string, body: unknown): Promise<unknown>;
16
+ /**
17
+ * A GET that also surfaces the response's `ETag`.
18
+ *
19
+ * The concurrency token travels in headers only: the backend's `ConcurrencyGuard` documents
20
+ * that a body's `updatedAt` serializes at second precision and so cannot reproduce the
21
+ * millisecond token. A read-modify-write therefore has no other way to obtain the `If-Match`
22
+ * value it has to send back.
23
+ */
24
+ getWithEtag(path: string): Promise<{
25
+ data: unknown;
26
+ etag: string | null;
27
+ }>;
28
+ post(path: string, body?: unknown, headers?: Record<string, string>): Promise<unknown>;
17
29
  private request;
18
30
  }
package/dist/pmClient.js CHANGED
@@ -24,12 +24,23 @@ export class PmClient {
24
24
  this.tokens = tokens;
25
25
  }
26
26
  async get(path) {
27
+ return (await this.request("GET", path)).data;
28
+ }
29
+ /**
30
+ * A GET that also surfaces the response's `ETag`.
31
+ *
32
+ * The concurrency token travels in headers only: the backend's `ConcurrencyGuard` documents
33
+ * that a body's `updatedAt` serializes at second precision and so cannot reproduce the
34
+ * millisecond token. A read-modify-write therefore has no other way to obtain the `If-Match`
35
+ * value it has to send back.
36
+ */
37
+ async getWithEtag(path) {
27
38
  return this.request("GET", path);
28
39
  }
29
- async post(path, body) {
30
- return this.request("POST", path, body);
40
+ async post(path, body, headers) {
41
+ return (await this.request("POST", path, body, headers)).data;
31
42
  }
32
- async request(method, path, body) {
43
+ async request(method, path, body, headers) {
33
44
  const idToken = await this.tokens.getIdToken();
34
45
  const res = await fetch(`${this.baseUrl}${path}`, {
35
46
  method,
@@ -37,6 +48,7 @@ export class PmClient {
37
48
  Authorization: `Bearer ${idToken}`,
38
49
  Accept: "application/json",
39
50
  ...(body !== undefined ? { "Content-Type": "application/json" } : {}),
51
+ ...(headers ?? {}),
40
52
  },
41
53
  body: body !== undefined ? JSON.stringify(body) : undefined,
42
54
  signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
@@ -45,10 +57,14 @@ export class PmClient {
45
57
  if (!res.ok) {
46
58
  throw new PmApiError(res.status, text, method, path);
47
59
  }
60
+ const etag = res.headers.get("etag");
48
61
  if (!text)
49
- return null;
62
+ return { data: null, etag };
50
63
  const contentType = res.headers.get("content-type") ?? "";
51
- return contentType.includes("application/json") ? JSON.parse(text) : text;
64
+ return {
65
+ data: contentType.includes("application/json") ? JSON.parse(text) : text,
66
+ etag,
67
+ };
52
68
  }
53
69
  }
54
70
  //# sourceMappingURL=pmClient.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pmClient.js","sourceRoot":"","sources":["../src/pmClient.ts"],"names":[],"mappings":"AAEA,mGAAmG;AACnG,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC,8FAA8F;AAC9F,MAAM,OAAO,UAAW,SAAQ,KAAK;IAE1B;IACA;IACA;IACA;IAJT,YACS,MAAc,EACd,QAAgB,EAChB,MAAc,EACd,IAAY;QAEnB,KAAK,CAAC,GAAG,MAAM,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QAL5D,WAAM,GAAN,MAAM,CAAQ;QACd,aAAQ,GAAR,QAAQ,CAAQ;QAChB,WAAM,GAAN,MAAM,CAAQ;QACd,SAAI,GAAJ,IAAI,CAAQ;QAGnB,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;IAC3B,CAAC;CACF;AAED,wEAAwE;AACxE,MAAM,OAAO,QAAQ;IAET;IACA;IAFV,YACU,OAAe,EACf,MAAoB;QADpB,YAAO,GAAP,OAAO,CAAQ;QACf,WAAM,GAAN,MAAM,CAAc;IAC3B,CAAC;IAEJ,KAAK,CAAC,GAAG,CAAC,IAAY;QACpB,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAY,EAAE,IAAa;QACpC,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC1C,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,MAAc,EAAE,IAAY,EAAE,IAAc;QAChE,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;QAC/C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,EAAE;YAChD,MAAM;YACN,OAAO,EAAE;gBACP,aAAa,EAAE,UAAU,OAAO,EAAE;gBAClC,MAAM,EAAE,kBAAkB;gBAC1B,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACtE;YACD,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;YAC3D,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,kBAAkB,CAAC;SAChD,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QACvD,CAAC;QACD,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QACvB,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;QAC1D,OAAO,WAAW,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5E,CAAC;CACF"}
1
+ {"version":3,"file":"pmClient.js","sourceRoot":"","sources":["../src/pmClient.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,27 @@
1
+ import { z } from "zod";
1
2
  import { runTool } from "./shared.js";
2
3
  export function registerBillingTools(server, client) {
3
4
  server.registerTool("get_billing_config", {
4
5
  title: "Get billing config",
5
- description: "Fetch the billing/entitlement configuration for your current organization context (products, entitlements, plan state).",
6
- inputSchema: {},
7
- }, async () => runTool(() => client.get("/api/billing/config")));
6
+ description: "Fetch the billing/entitlement configuration for your current organization context (products, entitlements, plan state). Billing config is per-platform, so the platform must be named.",
7
+ inputSchema: {
8
+ // `platform` is a REQUIRED query param on GET /api/billing/config (operation
9
+ // `getConfig` in the committed spec). This tool used to declare no inputs and call
10
+ // the bare path, so every call was malformed.
11
+ platform: z
12
+ .string()
13
+ .min(1)
14
+ .describe("Store platform the config is for, e.g. 'ios', 'android', 'web'."),
15
+ useTestStore: z
16
+ .boolean()
17
+ .optional()
18
+ .describe("Read the store's sandbox/test catalogue instead of production."),
19
+ },
20
+ }, async ({ platform, useTestStore }) => {
21
+ const query = new URLSearchParams({ platform });
22
+ if (useTestStore !== undefined)
23
+ query.set("useTestStore", String(useTestStore));
24
+ return runTool(() => client.get(`/api/billing/config?${query.toString()}`));
25
+ });
8
26
  }
9
27
  //# sourceMappingURL=billing.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"billing.js","sourceRoot":"","sources":["../../src/tools/billing.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,MAAM,UAAU,oBAAoB,CAAC,MAAiB,EAAE,MAAgB;IACtE,MAAM,CAAC,YAAY,CACjB,oBAAoB,EACpB;QACE,KAAK,EAAE,oBAAoB;QAC3B,WAAW,EACT,yHAAyH;QAC3H,WAAW,EAAE,EAAE;KAChB,EACD,KAAK,IAAI,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC,CAC7D,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"billing.js","sourceRoot":"","sources":["../../src/tools/billing.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,MAAM,UAAU,oBAAoB,CAAC,MAAiB,EAAE,MAAgB;IACtE,MAAM,CAAC,YAAY,CACjB,oBAAoB,EACpB;QACE,KAAK,EAAE,oBAAoB;QAC3B,WAAW,EACT,wLAAwL;QAC1L,WAAW,EAAE;YACX,6EAA6E;YAC7E,mFAAmF;YACnF,8CAA8C;YAC9C,QAAQ,EAAE,CAAC;iBACR,MAAM,EAAE;iBACR,GAAG,CAAC,CAAC,CAAC;iBACN,QAAQ,CAAC,iEAAiE,CAAC;YAC9E,YAAY,EAAE,CAAC;iBACZ,OAAO,EAAE;iBACT,QAAQ,EAAE;iBACV,QAAQ,CAAC,gEAAgE,CAAC;SAC9E;KACF,EACD,KAAK,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE;QACnC,MAAM,KAAK,GAAG,IAAI,eAAe,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;QAChD,IAAI,YAAY,KAAK,SAAS;YAAE,KAAK,CAAC,GAAG,CAAC,cAAc,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;QAChF,OAAO,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,uBAAuB,KAAK,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC;IAC9E,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ /** The mutation a write tool intends to perform, rendered for a human to accept or reject. */
3
+ export interface WriteIntent {
4
+ /** Imperative one-liner, e.g. `Complete Planner task "Ship ARCH-173"`. */
5
+ summary: string;
6
+ /** The exact request issued on accept — this is what the human is agreeing to. */
7
+ request: {
8
+ method: "POST";
9
+ path: string;
10
+ body?: unknown;
11
+ };
12
+ }
13
+ /**
14
+ * Runs [perform] only after the human accepts [buildIntent]'s description of it.
15
+ *
16
+ * When the client cannot be asked, the write is refused rather than performed. That is the
17
+ * deliberate call: the alternative — proceeding unconfirmed — turns the ticket's one hard
18
+ * requirement off for exactly the clients that never opted into being asked.
19
+ */
20
+ export declare function withConfirmation(server: McpServer, toolName: string, buildIntent: () => WriteIntent | Promise<WriteIntent>, perform: (intent: WriteIntent) => Promise<unknown>): Promise<{
21
+ content: {
22
+ type: "text";
23
+ text: string;
24
+ }[];
25
+ }>;
@@ -0,0 +1,69 @@
1
+ import { errorResult, jsonResult, runTool } from "./shared.js";
2
+ /**
3
+ * Per-call confirmation for write tools (ARCH-173).
4
+ *
5
+ * Why this exists at all: authentication is a per-user Firebase ID token, not a service
6
+ * account, so a write tool inherits everything the connected human can do — up to
7
+ * cross-tenant SYSADMIN. There is no read-only role and no server-side allow-list behind
8
+ * this. The tool list plus this gate are the entire safety boundary.
9
+ */
10
+ /** How much of a request body to show in the prompt before truncating. */
11
+ const MAX_BODY_PREVIEW = 1200;
12
+ /**
13
+ * The SDK gates `elicitInput` on `elicitation.form` and *throws* when it is absent
14
+ * (`server/index.js`: "Client does not support form elicitation"). It does not silently
15
+ * no-op. Reading the same flag first lets the refusal name the write that was blocked
16
+ * instead of surfacing a bare SDK message.
17
+ */
18
+ function supportsFormElicitation(server) {
19
+ const elicitation = server.server.getClientCapabilities()?.elicitation;
20
+ return Boolean(elicitation?.form);
21
+ }
22
+ function renderRequest(intent) {
23
+ const line = `${intent.request.method} ${intent.request.path}`;
24
+ if (intent.request.body === undefined)
25
+ return line;
26
+ const body = JSON.stringify(intent.request.body, null, 2);
27
+ const shown = body.length > MAX_BODY_PREVIEW ? `${body.slice(0, MAX_BODY_PREVIEW)}\n… (truncated)` : body;
28
+ return `${line}\n${shown}`;
29
+ }
30
+ /**
31
+ * Runs [perform] only after the human accepts [buildIntent]'s description of it.
32
+ *
33
+ * When the client cannot be asked, the write is refused rather than performed. That is the
34
+ * deliberate call: the alternative — proceeding unconfirmed — turns the ticket's one hard
35
+ * requirement off for exactly the clients that never opted into being asked.
36
+ */
37
+ export async function withConfirmation(server, toolName, buildIntent, perform) {
38
+ let intent;
39
+ try {
40
+ intent = await buildIntent();
41
+ }
42
+ catch (err) {
43
+ return errorResult(err);
44
+ }
45
+ if (!supportsFormElicitation(server)) {
46
+ return errorResult(new Error(`${toolName} performs a write and requires per-call confirmation, but this MCP client ` +
47
+ `did not advertise elicitation support, so there is no way to ask you.\n\n` +
48
+ `Intended mutation:\n${intent.summary}\n${renderRequest(intent)}\n\n` +
49
+ `No request was sent.`));
50
+ }
51
+ let outcome;
52
+ try {
53
+ outcome = await server.server.elicitInput({
54
+ message: `${toolName} wants to write to PracticeMakes.\n\n` +
55
+ `${intent.summary}\n\n${renderRequest(intent)}\n\n` +
56
+ `This runs as your own account, with your own permissions. Approve?`,
57
+ requestedSchema: { type: "object", properties: {} },
58
+ });
59
+ }
60
+ catch (err) {
61
+ // Transport/protocol failure while asking. Nothing was sent, and it must stay that way.
62
+ return errorResult(err);
63
+ }
64
+ if (outcome.action !== "accept") {
65
+ return jsonResult(`${toolName}: ${outcome.action} — no request was sent.`);
66
+ }
67
+ return runTool(() => perform(intent));
68
+ }
69
+ //# sourceMappingURL=confirm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"confirm.js","sourceRoot":"","sources":["../../src/tools/confirm.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE/D;;;;;;;GAOG;AAEH,0EAA0E;AAC1E,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAU9B;;;;;GAKG;AACH,SAAS,uBAAuB,CAAC,MAAiB;IAChD,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,qBAAqB,EAAE,EAAE,WAE9C,CAAC;IACd,OAAO,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,aAAa,CAAC,MAAmB;IACxC,MAAM,IAAI,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;IAC/D,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACnD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC1D,MAAM,KAAK,GACT,IAAI,CAAC,MAAM,GAAG,gBAAgB,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9F,OAAO,GAAG,IAAI,KAAK,KAAK,EAAE,CAAC;AAC7B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAiB,EACjB,QAAgB,EAChB,WAAqD,EACrD,OAAkD;IAElD,IAAI,MAAmB,CAAC;IACxB,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,WAAW,EAAE,CAAC;IAC/B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAED,IAAI,CAAC,uBAAuB,CAAC,MAAM,CAAC,EAAE,CAAC;QACrC,OAAO,WAAW,CAChB,IAAI,KAAK,CACP,GAAG,QAAQ,4EAA4E;YACrF,2EAA2E;YAC3E,uBAAuB,MAAM,CAAC,OAAO,KAAK,aAAa,CAAC,MAAM,CAAC,MAAM;YACrE,sBAAsB,CACzB,CACF,CAAC;IACJ,CAAC;IAED,IAAI,OAA2B,CAAC;IAChC,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC;YACxC,OAAO,EACL,GAAG,QAAQ,uCAAuC;gBAClD,GAAG,MAAM,CAAC,OAAO,OAAO,aAAa,CAAC,MAAM,CAAC,MAAM;gBACnD,oEAAoE;YACtE,eAAe,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAE;SACpD,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,wFAAwF;QACxF,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QAChC,OAAO,UAAU,CAAC,GAAG,QAAQ,KAAK,OAAO,CAAC,MAAM,yBAAyB,CAAC,CAAC;IAC7E,CAAC;IAED,OAAO,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;AACxC,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { withConfirmation } from "./confirm.js";
2
3
  import { runTool } from "./shared.js";
3
4
  export function registerOrganizationTools(server, client) {
4
5
  server.registerTool("list_organizations", {
@@ -13,5 +14,17 @@ export function registerOrganizationTools(server, client) {
13
14
  organizationId: z.string().uuid().describe("Organization UUID"),
14
15
  },
15
16
  }, async ({ organizationId }) => runTool(() => client.get(`/api/organization/${organizationId}`)));
17
+ server.registerTool("switch_org_context", {
18
+ title: "Switch organization context",
19
+ description: "Switch your active organization context. WRITE: this mutates your profile, so it changes what EVERY subsequent read tool returns for the rest of the session, not just the next call. Prompts you for confirmation before sending. A SYSADMIN can switch into any organization; everyone else needs an invite to the target.",
20
+ inputSchema: {
21
+ organizationId: z.string().uuid().describe("Organization UUID to switch into"),
22
+ },
23
+ }, async ({ organizationId }) => withConfirmation(server, "switch_org_context", () => ({
24
+ summary: `Switch your active organization context to ${organizationId}. ` +
25
+ `Every later read tool in this session will return that org's data.`,
26
+ // The endpoint takes no request body — the id is the path variable.
27
+ request: { method: "POST", path: `/api/organization/context/${organizationId}` },
28
+ }), () => client.post(`/api/organization/context/${organizationId}`)));
16
29
  }
17
30
  //# sourceMappingURL=organizations.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"organizations.js","sourceRoot":"","sources":["../../src/tools/organizations.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,MAAM,UAAU,yBAAyB,CAAC,MAAiB,EAAE,MAAgB;IAC3E,MAAM,CAAC,YAAY,CACjB,oBAAoB,EACpB;QACE,KAAK,EAAE,oBAAoB;QAC3B,WAAW,EACT,gJAAgJ;QAClJ,WAAW,EAAE,EAAE;KAChB,EACD,KAAK,IAAI,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC,CAC/D,CAAC;IAEF,MAAM,CAAC,YAAY,CACjB,kBAAkB,EAClB;QACE,KAAK,EAAE,kBAAkB;QACzB,WAAW,EAAE,0CAA0C;QACvD,WAAW,EAAE;YACX,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC;SAChE;KACF,EACD,KAAK,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,CAC3B,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,qBAAqB,cAAc,EAAE,CAAC,CAAC,CACnE,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"organizations.js","sourceRoot":"","sources":["../../src/tools/organizations.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,MAAM,UAAU,yBAAyB,CAAC,MAAiB,EAAE,MAAgB;IAC3E,MAAM,CAAC,YAAY,CACjB,oBAAoB,EACpB;QACE,KAAK,EAAE,oBAAoB;QAC3B,WAAW,EACT,gJAAgJ;QAClJ,WAAW,EAAE,EAAE;KAChB,EACD,KAAK,IAAI,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC,CAC/D,CAAC;IAEF,MAAM,CAAC,YAAY,CACjB,kBAAkB,EAClB;QACE,KAAK,EAAE,kBAAkB;QACzB,WAAW,EAAE,0CAA0C;QACvD,WAAW,EAAE;YACX,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,mBAAmB,CAAC;SAChE;KACF,EACD,KAAK,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,CAC3B,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,qBAAqB,cAAc,EAAE,CAAC,CAAC,CACnE,CAAC;IAEF,MAAM,CAAC,YAAY,CACjB,oBAAoB,EACpB;QACE,KAAK,EAAE,6BAA6B;QACpC,WAAW,EACT,8TAA8T;QAChU,WAAW,EAAE;YACX,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,kCAAkC,CAAC;SAC/E;KACF,EACD,KAAK,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,CAC3B,gBAAgB,CACd,MAAM,EACN,oBAAoB,EACpB,GAAG,EAAE,CAAC,CAAC;QACL,OAAO,EACL,8CAA8C,cAAc,IAAI;YAChE,oEAAoE;QACtE,oEAAoE;QACpE,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,6BAA6B,cAAc,EAAE,EAAE;KACjF,CAAC,EACF,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,6BAA6B,cAAc,EAAE,CAAC,CACjE,CACJ,CAAC;AACJ,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
- import { runTool } from "./shared.js";
2
+ import { withConfirmation } from "./confirm.js";
3
+ import { errorResult, jsonResult, runTool } from "./shared.js";
3
4
  export function registerPlannerTools(server, client) {
4
5
  server.registerTool("get_my_planner_tasks", {
5
6
  title: "Get my Planner tasks",
@@ -46,5 +47,85 @@ export function registerPlannerTools(server, client) {
46
47
  workflowId: z.string().uuid().describe("Workflow UUID"),
47
48
  },
48
49
  }, async ({ workflowId }) => runTool(() => client.get(`/api/workflow/${workflowId}`)));
50
+ server.registerTool("create_task", {
51
+ title: "Create Planner task",
52
+ description: "Create a new Planner task in your current organization context. WRITE: prompts you for confirmation before sending, and runs as your own account with your own permissions. Requires ADMIN in the active org context.",
53
+ inputSchema: {
54
+ name: z.string().min(1).describe("Task name — what the task is."),
55
+ dueAt: z
56
+ .string()
57
+ .datetime()
58
+ .optional()
59
+ .describe("ISO-8601 due timestamp, e.g. 2026-09-01T17:00:00Z."),
60
+ priorityValue: z
61
+ .number()
62
+ .int()
63
+ .optional()
64
+ .describe("Backlog priority; ascending, so a lower value sorts higher."),
65
+ durationEstimate: z.number().int().optional().describe("Estimated duration, in minutes."),
66
+ requiresCompletion: z
67
+ .boolean()
68
+ .optional()
69
+ .describe("Whether the task must be completed rather than expiring."),
70
+ },
71
+ }, async ({ name, dueAt, priorityValue, durationEstimate, requiresCompletion }) => {
72
+ // `tags` and `taskLinks` are non-nullable on the Task schema, so a create has to send
73
+ // them even when empty; omitting them fails validation before it reaches the funnel.
74
+ const body = {
75
+ name,
76
+ tags: [],
77
+ taskLinks: [],
78
+ ...(dueAt !== undefined ? { dueAt } : {}),
79
+ ...(priorityValue !== undefined ? { priorityValue } : {}),
80
+ ...(durationEstimate !== undefined ? { durationEstimate } : {}),
81
+ ...(requiresCompletion !== undefined ? { requiresCompletion } : {}),
82
+ };
83
+ return withConfirmation(server, "create_task", () => ({
84
+ summary: `Create Planner task "${name}" in your current organization context.`,
85
+ request: { method: "POST", path: "/api/task", body },
86
+ }),
87
+ // A create has no prior version, so the backend does not consult If-Match here.
88
+ () => client.post("/api/task", body));
89
+ });
90
+ server.registerTool("complete_task", {
91
+ title: "Complete Planner task",
92
+ description: "Mark a Planner task complete by setting its completedAt. This is the tool that closes the Planner-driven-dev loop. WRITE: prompts you for confirmation before sending, and runs as your own account with your own permissions.",
93
+ inputSchema: {
94
+ taskId: z.string().uuid().describe("Task UUID"),
95
+ },
96
+ }, async ({ taskId }) => {
97
+ // Completion is a read-modify-write over POST /api/task — there is no dedicated
98
+ // complete endpoint. The ETag from this read is the If-Match token for the write.
99
+ let fetched;
100
+ try {
101
+ fetched = await client.getWithEtag(`/api/task/${taskId}`);
102
+ }
103
+ catch (err) {
104
+ return errorResult(err);
105
+ }
106
+ const task = fetched.data;
107
+ if (!task) {
108
+ return errorResult(new Error(`Task ${taskId} was not found.`));
109
+ }
110
+ if (task.completedAt) {
111
+ // Nothing to change: re-posting would spend a confirmation to rewrite the same row.
112
+ return jsonResult(`Task ${taskId} is already complete (completedAt ${String(task.completedAt)}). No write needed.`);
113
+ }
114
+ // Echo the whole read back rather than patching {id, completedAt}. POST /api/task is a
115
+ // full-representation update: SafeWriteService copies every field on its writable list
116
+ // across unconditionally, so anything omitted here is blanked on the stored row — and
117
+ // requireClearingWasRequested rejects a body that drops a non-empty collection outright
118
+ // rather than silently detaching it (ARCH-416). The server-managed fields that ride along
119
+ // are inert: `id` is what routes this to an update instead of a create, and the rest are
120
+ // absent from the writable list, so the funnel ignores them.
121
+ const body = { ...task, completedAt: new Date().toISOString() };
122
+ return withConfirmation(server, "complete_task", () => ({
123
+ summary: `Complete Planner task "${String(task.name ?? taskId)}".`,
124
+ request: { method: "POST", path: "/api/task", body },
125
+ }), () => client.post("/api/task", body,
126
+ // Without this the backend's ConcurrencyGuard records a MISSING_TOKEN stale
127
+ // write to Sentry on every call, and would reject it once enforcement is on.
128
+ fetched.etag ? { "If-Match": fetched.etag } : undefined));
129
+ });
49
130
  }
50
131
  //# sourceMappingURL=planner.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"planner.js","sourceRoot":"","sources":["../../src/tools/planner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"planner.js","sourceRoot":"","sources":["../../src/tools/planner.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@practicemakes/mcp",
3
- "version": "26.823.1",
3
+ "version": "26.823.2",
4
4
  "description": "MCP server exposing PracticeMakes prod data (read-only) to Claude, authenticated as the calling user's own Firebase identity.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",