@ratifia/mcp 0.10.1 → 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.
- package/README.md +3 -3
- package/dist/index.js +29 -10
- 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` | | — |
|
|
61
|
-
| `RATIFIA_POLICY` | | — | The **key** of a policy deciding **who** reviews
|
|
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
|
|
|
@@ -205,7 +205,7 @@ Three properties worth knowing, because they shape how you should use it:
|
|
|
205
205
|
|
|
206
206
|
1. The agent calls `request_decision` before a consequential/irreversible step (deploy, delete, spend, send externally), an ambiguous call it shouldn't guess, or to get a human to pick/refine something.
|
|
207
207
|
2. Ratifia notifies the human (even away from keyboard) per the org's routing.
|
|
208
|
-
3. The agent `await_decision`s (or polls `check_decision`) — read `verdict`/`note`, plus `response_value` (select/text) or `output` (a refined draft).
|
|
208
|
+
3. The agent `await_decision`s (or polls `check_decision`) — read `verdict`/`note`, plus `response_value` (select/text) or `output` (a refined draft). `policy_version` / `policy_version_id` say which version of the policy the decision ran under (null when no policy applied).
|
|
209
209
|
4. The agent proceeds only on `approved`; on `rejected`/`expired` it adapts.
|
|
210
210
|
|
|
211
211
|
Learn more at [ratifia.com](https://ratifia.com).
|
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
|
|
28
|
-
*
|
|
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
|
|
46
|
-
|
|
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
|
|
@@ -188,6 +204,10 @@ const formatDecision = (d) => ({
|
|
|
188
204
|
...(d.output ? { output: d.output, refined: Boolean(d.refinedOutput) } : {}),
|
|
189
205
|
decided_by: d.decidedBy,
|
|
190
206
|
decided_at: d.decidedAt,
|
|
207
|
+
// FN-316: which version of the policy governed this decision, so an agent can
|
|
208
|
+
// cite it ("approved under refunds v3"). Stays put when the policy is edited.
|
|
209
|
+
policy_version: d.policyVersion ?? null,
|
|
210
|
+
policy_version_id: d.policyVersionId ?? null,
|
|
191
211
|
});
|
|
192
212
|
const asToolResult = (payload) => ({
|
|
193
213
|
content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }],
|
|
@@ -406,8 +426,7 @@ async function runGate() {
|
|
|
406
426
|
externalRunId,
|
|
407
427
|
stepRef: `tool:${toolName}`.slice(0, 80),
|
|
408
428
|
workflowRef: label(WORKFLOW, 'ratifia-gate', 100),
|
|
409
|
-
...
|
|
410
|
-
...(POLICY ? { policy: POLICY } : {}),
|
|
429
|
+
...WHO,
|
|
411
430
|
required: true,
|
|
412
431
|
context: {
|
|
413
432
|
prompt: `An agent wants to run the tool "${toolName}"${where ? ` in ${where}` : ''}. Approve?`,
|
|
@@ -489,7 +508,7 @@ server.registerTool('request_decision', {
|
|
|
489
508
|
.int()
|
|
490
509
|
.positive()
|
|
491
510
|
.optional()
|
|
492
|
-
.describe(
|
|
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."),
|
|
493
512
|
response_type: z
|
|
494
513
|
.enum(['approval', 'select', 'text'])
|
|
495
514
|
.optional()
|
|
@@ -571,10 +590,10 @@ server.registerTool('request_decision', {
|
|
|
571
590
|
externalRunId,
|
|
572
591
|
stepRef: label(step, title, 80),
|
|
573
592
|
workflowRef: label(workflow, label(WORKFLOW, 'ratifia-mcp', 100), 100),
|
|
574
|
-
...
|
|
575
|
-
...(POLICY ? { policy: POLICY } : {}),
|
|
593
|
+
...WHO,
|
|
576
594
|
required: true,
|
|
577
|
-
|
|
595
|
+
// With a policy, the policy sets the expiry (a timeoutSec would be refused).
|
|
596
|
+
...(timeout_minutes && !POLICY ? { timeoutSec: timeout_minutes * 60 } : {}),
|
|
578
597
|
...(responseSpec ? { responseSpec } : {}),
|
|
579
598
|
...(proposed_output
|
|
580
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.
|
|
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": [
|