@ratifia/mcp 0.11.0 → 0.12.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.
Files changed (3) hide show
  1. package/README.md +2 -2
  2. package/dist/index.js +25 -10
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -57,8 +57,8 @@ real decisions. CI runs it on every push.
57
57
  | `RATIFIA_API_KEY` | ✅ | — | Org-scoped worker API key. |
58
58
  | `RATIFIA_API_URL` | | `https://api.ratifia.com` | Ratifia API base. |
59
59
  | `RATIFIA_APP_URL` | | `https://app.ratifia.com` | Dashboard base for decision links. |
60
- | `RATIFIA_APPROVER_EMAIL` | | — | Who the decision is assigned to / notified. |
61
- | `RATIFIA_POLICY` | | — | The **key** of a policy deciding **who** reviews and the quorum they need. It must exist in your org: an unknown key is a tool error, and the `gate` hook denies. |
60
+ | `RATIFIA_APPROVER_EMAIL` | | — | Without a policy: who can answer, one email or a comma-separated list. Ignored when `RATIFIA_POLICY` is set. |
61
+ | `RATIFIA_POLICY` | | — | The **key** of a policy deciding **who** reviews, the quorum they need, and the expiry (`timeout_minutes` is then not sent). It must exist in your org: an unknown key is a tool error, and the `gate` hook denies. |
62
62
  | `RATIFIA_GATE_INCLUDE_PROMPT` | | off | `gate` hook only: `1` includes the user's last request (from the transcript) in the decision. Off by default — the gate otherwise sends only metadata, never your conversation. |
63
63
  | `RATIFIA_WORKFLOW` | | `ratifia-mcp` (`ratifia-gate` for the hook) | The **workflow** decisions are filed under — the category the dashboard's Workflow filter groups by, e.g. `deploys` or `billing-agent`. A `request_decision` call can override it. |
64
64
 
package/dist/index.js CHANGED
@@ -24,8 +24,11 @@
24
24
  * RATIFIA_API_URL API base (default https://api.ratifia.com)
25
25
  * RATIFIA_API_KEY org-scoped worker API key (required)
26
26
  * RATIFIA_APP_URL dashboard base for the decision link (default https://app.ratifia.com)
27
- * RATIFIA_APPROVER_EMAIL who the decision is assigned to / notified
28
- * RATIFIA_POLICY optional: policy key deciding who reviews (must exist in the org)
27
+ * RATIFIA_APPROVER_EMAIL who may answer (comma-separated for several); used only
28
+ * when RATIFIA_POLICY is unset
29
+ * RATIFIA_POLICY optional: policy key (must exist in the org). When set, the
30
+ * policy decides who approves and when it expires, and
31
+ * RATIFIA_APPROVER_EMAIL / timeout_minutes are not sent
29
32
  * (e.g. a route to your phone)
30
33
  * RATIFIA_GATE_INCLUDE_PROMPT gate hook only: 1 = include the user's last request from the
31
34
  * transcript in the decision (off by default — it is case content)
@@ -42,8 +45,21 @@ import { z } from 'zod';
42
45
  const API_URL = (process.env.RATIFIA_API_URL ?? 'https://api.ratifia.com').replace(/\/+$/, '');
43
46
  const API_KEY = process.env.RATIFIA_API_KEY;
44
47
  const APP_URL = (process.env.RATIFIA_APP_URL ?? 'https://app.ratifia.com').replace(/\/+$/, '');
45
- const APPROVER_EMAIL = process.env.RATIFIA_APPROVER_EMAIL;
46
- const POLICY = process.env.RATIFIA_POLICY;
48
+ const APPROVERS = (process.env.RATIFIA_APPROVER_EMAIL ?? '')
49
+ .split(',')
50
+ .map((s) => s.trim())
51
+ .filter(Boolean);
52
+ const POLICY = process.env.RATIFIA_POLICY || undefined;
53
+ /**
54
+ * Who answers (Ratifia PD-003): a policy owns its approvers and expiry, so with
55
+ * one set only its key is sent (approvers alongside it are refused, 422
56
+ * field_owned_by_policy). Without one, the decision is ad hoc to APPROVERS.
57
+ */
58
+ const WHO = POLICY
59
+ ? { policy: { key: POLICY } }
60
+ : APPROVERS.length
61
+ ? { approvers: APPROVERS }
62
+ : {};
47
63
  const WORKFLOW = process.env.RATIFIA_WORKFLOW;
48
64
  /**
49
65
  * Where a decision is filed. `workflowRef` is the category (the dashboard's
@@ -410,8 +426,7 @@ async function runGate() {
410
426
  externalRunId,
411
427
  stepRef: `tool:${toolName}`.slice(0, 80),
412
428
  workflowRef: label(WORKFLOW, 'ratifia-gate', 100),
413
- ...(APPROVER_EMAIL ? { assigneeEmail: APPROVER_EMAIL } : {}),
414
- ...(POLICY ? { policy: POLICY } : {}),
429
+ ...WHO,
415
430
  required: true,
416
431
  context: {
417
432
  prompt: `An agent wants to run the tool "${toolName}"${where ? ` in ${where}` : ''}. Approve?`,
@@ -493,7 +508,7 @@ server.registerTool('request_decision', {
493
508
  .int()
494
509
  .positive()
495
510
  .optional()
496
- .describe('Auto-expire (and reject) if unanswered after N minutes. Omit to wait indefinitely.'),
511
+ .describe("Auto-expire (and reject) if unanswered after N minutes. Omit to wait indefinitely. Ignored when this server is configured with a policy (RATIFIA_POLICY): the policy's expiry applies."),
497
512
  response_type: z
498
513
  .enum(['approval', 'select', 'text'])
499
514
  .optional()
@@ -575,10 +590,10 @@ server.registerTool('request_decision', {
575
590
  externalRunId,
576
591
  stepRef: label(step, title, 80),
577
592
  workflowRef: label(workflow, label(WORKFLOW, 'ratifia-mcp', 100), 100),
578
- ...(APPROVER_EMAIL ? { assigneeEmail: APPROVER_EMAIL } : {}),
579
- ...(POLICY ? { policy: POLICY } : {}),
593
+ ...WHO,
580
594
  required: true,
581
- ...(timeout_minutes ? { timeoutSec: timeout_minutes * 60 } : {}),
595
+ // With a policy, the policy sets the expiry (a timeoutSec would be refused).
596
+ ...(timeout_minutes && !POLICY ? { timeoutSec: timeout_minutes * 60 } : {}),
582
597
  ...(responseSpec ? { responseSpec } : {}),
583
598
  ...(proposed_output
584
599
  ? { proposedOutput: { title: proposed_output.title, format: 'text', content: proposed_output.content } }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ratifia/mcp",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "type": "module",
5
5
  "description": "Ratifia MCP server — let an AI agent request a human decision/approval (HITL) mid-task and resume once it's answered. The agent becomes a Ratifia 'engine'.",
6
6
  "keywords": [