@fidacy/mcp 0.1.16 → 0.1.17

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/CHANGELOG.md CHANGED
@@ -3,6 +3,14 @@
3
3
  All notable changes to `@fidacy/mcp` are documented here. This project follows
4
4
  semantic versioning.
5
5
 
6
+ ## 0.1.17 — 2026-07-03
7
+
8
+ - Config HOT-RELOAD: editing `~/.fidacy/config.json` (payees, caps) takes effect
9
+ on the next call, no host restart. Spend and invoice-dedup state is rebuilt
10
+ from the audit log on reload, so a reload never weakens the firewall.
11
+ - The `payee_not_in_allowlist` DENY now tells the agent exactly how to allow a
12
+ trusted payee (friction fix from a clean-room install test).
13
+
6
14
  ## 0.1.16 — 2026-07-03
7
15
 
8
16
  - `anchor_artifact`/`check_artifact`: new kind `conversation` — anchor chatbot
package/dist/core.js CHANGED
@@ -1,3 +1,6 @@
1
+ // src/core.ts
2
+ import { statSync } from "node:fs";
3
+
1
4
  // ../firewall/dist/util.js
2
5
  function stableStringify(obj) {
3
6
  if (obj === null || typeof obj !== "object")
@@ -189,6 +192,19 @@ var DevFidacyCore = class {
189
192
  this.spent += r.amount;
190
193
  }
191
194
  }
195
+ /**
196
+ * Swap the active mandate (shell config hot-reload: edit config.json, the next
197
+ * call picks it up, no host restart). Decision state is REBUILT from the audit
198
+ * log under the new window/currency, so a reload can never reset the BEC
199
+ * invoice dedup or undercount spend: a paid invoice stays paid, spent is
200
+ * recomputed.
201
+ */
202
+ setMandate(m) {
203
+ this.mandate = m;
204
+ this.spent = 0;
205
+ this.claimedInvoices.clear();
206
+ this.rehydrate();
207
+ }
192
208
  async getMandate() {
193
209
  return this.mandate;
194
210
  }
@@ -423,7 +439,7 @@ function resolveMandateRules(cfg) {
423
439
  }
424
440
 
425
441
  // src/telemetry.ts
426
- var CLIENT_VERSION = true ? "0.1.16" : "dev";
442
+ var CLIENT_VERSION = true ? "0.1.17" : "dev";
427
443
  var currentShell = "mcp";
428
444
  function telemetryEnabled() {
429
445
  const v = (process.env.FIDACY_DISABLE_TELEMETRY ?? "").trim().toLowerCase();
@@ -525,11 +541,32 @@ function makeCore() {
525
541
  if (!url || !key) throw new Error("FIDACY_MODE=http requires FIDACY_API_URL and FIDACY_API_KEY");
526
542
  return new HttpFidacyCore(url, key, process.env.FIDACY_PUBLIC_KEY_PEM ?? "");
527
543
  }
528
- return new DevFidacyCore({
544
+ const dev = new DevFidacyCore({
529
545
  mandate: buildMandate(),
530
546
  auditLogPath: auditLogPath(),
531
547
  onDecision: (d) => recordDecision(d.status, d.violatedRule)
532
548
  });
549
+ const mtimeOf = () => statSync(configPath(), { throwIfNoEntry: false })?.mtimeMs ?? 0;
550
+ let seenMtime = mtimeOf();
551
+ const maybeReload = () => {
552
+ const m = mtimeOf();
553
+ if (m !== seenMtime) {
554
+ seenMtime = m;
555
+ dev.setMandate(buildMandate());
556
+ }
557
+ };
558
+ return {
559
+ getMandate: () => {
560
+ maybeReload();
561
+ return dev.getMandate();
562
+ },
563
+ decide: (req, subject) => {
564
+ maybeReload();
565
+ return dev.decide(req, subject);
566
+ },
567
+ getProof: (decisionId) => dev.getProof(decisionId),
568
+ publicKey: () => dev.publicKey()
569
+ };
533
570
  }
534
571
  export {
535
572
  makeCore
package/dist/index.js CHANGED
@@ -5,6 +5,9 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
6
  import { z } from "zod";
7
7
 
8
+ // src/core.ts
9
+ import { statSync } from "node:fs";
10
+
8
11
  // ../firewall/dist/util.js
9
12
  function stableStringify(obj) {
10
13
  if (obj === null || typeof obj !== "object")
@@ -196,6 +199,19 @@ var DevFidacyCore = class {
196
199
  this.spent += r.amount;
197
200
  }
198
201
  }
202
+ /**
203
+ * Swap the active mandate (shell config hot-reload: edit config.json, the next
204
+ * call picks it up, no host restart). Decision state is REBUILT from the audit
205
+ * log under the new window/currency, so a reload can never reset the BEC
206
+ * invoice dedup or undercount spend: a paid invoice stays paid, spent is
207
+ * recomputed.
208
+ */
209
+ setMandate(m) {
210
+ this.mandate = m;
211
+ this.spent = 0;
212
+ this.claimedInvoices.clear();
213
+ this.rehydrate();
214
+ }
199
215
  async getMandate() {
200
216
  return this.mandate;
201
217
  }
@@ -452,7 +468,7 @@ function resolveMandateRules(cfg) {
452
468
  }
453
469
 
454
470
  // src/telemetry.ts
455
- var CLIENT_VERSION = true ? "0.1.16" : "dev";
471
+ var CLIENT_VERSION = true ? "0.1.17" : "dev";
456
472
  var currentShell = "mcp";
457
473
  function telemetryEnabled() {
458
474
  const v = (process.env.FIDACY_DISABLE_TELEMETRY ?? "").trim().toLowerCase();
@@ -557,11 +573,32 @@ function makeCore() {
557
573
  if (!url || !key) throw new Error("FIDACY_MODE=http requires FIDACY_API_URL and FIDACY_API_KEY");
558
574
  return new HttpFidacyCore(url, key, process.env.FIDACY_PUBLIC_KEY_PEM ?? "");
559
575
  }
560
- return new DevFidacyCore({
576
+ const dev = new DevFidacyCore({
561
577
  mandate: buildMandate(),
562
578
  auditLogPath: auditLogPath(),
563
579
  onDecision: (d) => recordDecision(d.status, d.violatedRule)
564
580
  });
581
+ const mtimeOf = () => statSync(configPath(), { throwIfNoEntry: false })?.mtimeMs ?? 0;
582
+ let seenMtime = mtimeOf();
583
+ const maybeReload = () => {
584
+ const m = mtimeOf();
585
+ if (m !== seenMtime) {
586
+ seenMtime = m;
587
+ dev.setMandate(buildMandate());
588
+ }
589
+ };
590
+ return {
591
+ getMandate: () => {
592
+ maybeReload();
593
+ return dev.getMandate();
594
+ },
595
+ decide: (req, subject2) => {
596
+ maybeReload();
597
+ return dev.decide(req, subject2);
598
+ },
599
+ getProof: (decisionId) => dev.getProof(decisionId),
600
+ publicKey: () => dev.publicKey()
601
+ };
565
602
  }
566
603
 
567
604
  // src/assess.ts
@@ -746,7 +783,7 @@ async function findArtifacts(sha2562, cfg) {
746
783
  }
747
784
 
748
785
  // src/provision.ts
749
- var CLIENT_VERSION2 = true ? "0.1.16" : "dev";
786
+ var CLIENT_VERSION2 = true ? "0.1.17" : "dev";
750
787
  function provisionEnabled() {
751
788
  const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
752
789
  return !(v === "1" || v === "true" || v === "yes");
@@ -846,7 +883,7 @@ function weekOneBootNudge(upgradeToolName) {
846
883
  var state = ensureState();
847
884
  var core = makeCore();
848
885
  var subject = process.env.FIDACY_SUBJECT ?? "agent:demo";
849
- var SERVER_VERSION = true ? "0.1.16" : "dev";
886
+ var SERVER_VERSION = true ? "0.1.17" : "dev";
850
887
  var server = new McpServer({ name: "fidacy", version: SERVER_VERSION });
851
888
  server.registerTool(
852
889
  "request_payment",
@@ -877,7 +914,7 @@ server.registerTool(
877
914
  const d = await core.decide(req, subject);
878
915
  const out = { status: d.status, decisionId: d.decisionId, grant: d.grant, violatedRule: d.violatedRule };
879
916
  const base = d.status === "ALLOW" ? `ALLOW (decision ${d.decisionId})${req.invoiceRef ? ` for invoice ${req.invoiceRef}` : ""}. To settle, call execute_payment with the SAME payee, amount, currency, and idempotencyKey, and set "grant" to EXACTLY this signed value:
880
- ${d.grant}` : `DENY (decision ${d.decisionId}). Rule violated: ${d.violatedRule}. No grant issued, this payment cannot proceed. The denial itself is recorded in the tamper-evident, hash-chained audit: call get_audit_proof with decisionId ${d.decisionId} for the proof of what was blocked.`;
917
+ ${d.grant}` : `DENY (decision ${d.decisionId}). Rule violated: ${d.violatedRule}. No grant issued, this payment cannot proceed. The denial itself is recorded in the tamper-evident, hash-chained audit: call get_audit_proof with decisionId ${d.decisionId} for the proof of what was blocked.${d.violatedRule?.startsWith("payee_not_in_allowlist") ? ` If the user trusts this payee, add "${req.payee}" to mandate.payees in ~/.fidacy/config.json and retry: the firewall picks the change up on the next call, no restart needed.` : ""}`;
881
918
  const nudge = decisionNudge(d.status, d.violatedRule, "upgrade");
882
919
  const human = nudge ? `${base} ${nudge}` : base;
883
920
  return { content: [{ type: "text", text: human }], structuredContent: out };
package/dist/lib.js CHANGED
@@ -230,6 +230,19 @@ var DevFidacyCore = class {
230
230
  this.spent += r.amount;
231
231
  }
232
232
  }
233
+ /**
234
+ * Swap the active mandate (shell config hot-reload: edit config.json, the next
235
+ * call picks it up, no host restart). Decision state is REBUILT from the audit
236
+ * log under the new window/currency, so a reload can never reset the BEC
237
+ * invoice dedup or undercount spend: a paid invoice stays paid, spent is
238
+ * recomputed.
239
+ */
240
+ setMandate(m) {
241
+ this.mandate = m;
242
+ this.spent = 0;
243
+ this.claimedInvoices.clear();
244
+ this.rehydrate();
245
+ }
233
246
  async getMandate() {
234
247
  return this.mandate;
235
248
  }
@@ -490,6 +503,9 @@ var GrantEnforcingExecutor = class {
490
503
  }
491
504
  };
492
505
 
506
+ // src/core.ts
507
+ import { statSync } from "node:fs";
508
+
493
509
  // src/config.ts
494
510
  import { randomUUID as randomUUID3 } from "node:crypto";
495
511
  import { homedir } from "node:os";
@@ -571,7 +587,7 @@ function resolveMandateRules(cfg) {
571
587
  }
572
588
 
573
589
  // src/telemetry.ts
574
- var CLIENT_VERSION = true ? "0.1.16" : "dev";
590
+ var CLIENT_VERSION = true ? "0.1.17" : "dev";
575
591
  var currentShell = "mcp";
576
592
  function setTelemetryShell(shell) {
577
593
  currentShell = shell;
@@ -679,11 +695,32 @@ function makeCore() {
679
695
  if (!url || !key) throw new Error("FIDACY_MODE=http requires FIDACY_API_URL and FIDACY_API_KEY");
680
696
  return new HttpFidacyCore(url, key, process.env.FIDACY_PUBLIC_KEY_PEM ?? "");
681
697
  }
682
- return new DevFidacyCore({
698
+ const dev = new DevFidacyCore({
683
699
  mandate: buildMandate(),
684
700
  auditLogPath: auditLogPath(),
685
701
  onDecision: (d) => recordDecision(d.status, d.violatedRule)
686
702
  });
703
+ const mtimeOf = () => statSync(configPath(), { throwIfNoEntry: false })?.mtimeMs ?? 0;
704
+ let seenMtime = mtimeOf();
705
+ const maybeReload = () => {
706
+ const m = mtimeOf();
707
+ if (m !== seenMtime) {
708
+ seenMtime = m;
709
+ dev.setMandate(buildMandate());
710
+ }
711
+ };
712
+ return {
713
+ getMandate: () => {
714
+ maybeReload();
715
+ return dev.getMandate();
716
+ },
717
+ decide: (req, subject) => {
718
+ maybeReload();
719
+ return dev.decide(req, subject);
720
+ },
721
+ getProof: (decisionId) => dev.getProof(decisionId),
722
+ publicKey: () => dev.publicKey()
723
+ };
687
724
  }
688
725
 
689
726
  // src/upgrade.ts
@@ -699,7 +736,7 @@ function requestUpgrade() {
699
736
  }
700
737
 
701
738
  // src/provision.ts
702
- var CLIENT_VERSION2 = true ? "0.1.16" : "dev";
739
+ var CLIENT_VERSION2 = true ? "0.1.17" : "dev";
703
740
  function provisionEnabled() {
704
741
  const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
705
742
  return !(v === "1" || v === "true" || v === "yes");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fidacy/mcp",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "description": "Fidacy action firewall for AI agents. Mandate-gated payment authorization as an MCP server.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://fidacy.com",