@fidacy/mcp 0.1.10 → 0.1.11

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
@@ -85,3 +85,13 @@ semantic versioning.
85
85
  - Onboarding: first-run `~/.fidacy/config.json` now writes the mandate template
86
86
  explicitly (payees/categories/currency/caps) — adding a trusted payee is a
87
87
  visible one-line edit; friendlier first-boot message (no more "dev mode" scare).
88
+
89
+ ## 0.1.11 — 2026-07-02
90
+
91
+ - Upgrade micro-triggers, elegant by doctrine: fire only at value-proven moments
92
+ (first block, first duplicate-invoice/BEC catch, 10th gated action, one week
93
+ protected), each ONCE per install ever (persisted in config.json) — the free
94
+ tier stays fully usable in silence. The 0.1.10 every-DENY nudge is replaced.
95
+ - verify_mandate/get_audit_proof gain one informational closing line each;
96
+ assess_action's missing-key error now offers the free-key path.
97
+ - config.json gains created_at + nudge/decision state (preserved on read).
package/dist/core.js CHANGED
@@ -337,7 +337,12 @@ function readConfig() {
337
337
  anon_id: raw.anon_id,
338
338
  tier: raw.tier === "paid" ? "paid" : "free",
339
339
  api_key: typeof raw.api_key === "string" ? raw.api_key : null,
340
- mandate: raw.mandate
340
+ mandate: raw.mandate,
341
+ // Install-state passthrough: dropping these on a read→write cycle would
342
+ // reset every once-per-install nudge into an every-time nag.
343
+ created_at: typeof raw.created_at === "string" ? raw.created_at : void 0,
344
+ nudges: raw.nudges && typeof raw.nudges === "object" ? raw.nudges : void 0,
345
+ decisions_count: typeof raw.decisions_count === "number" ? raw.decisions_count : void 0
341
346
  };
342
347
  } catch {
343
348
  return null;
@@ -357,7 +362,7 @@ function resolveMandateRules(cfg) {
357
362
  }
358
363
 
359
364
  // src/telemetry.ts
360
- var CLIENT_VERSION = true ? "0.1.10" : "dev";
365
+ var CLIENT_VERSION = true ? "0.1.11" : "dev";
361
366
  var currentShell = "mcp";
362
367
  function telemetryEnabled() {
363
368
  const v = (process.env.FIDACY_DISABLE_TELEMETRY ?? "").trim().toLowerCase();
package/dist/index.js CHANGED
@@ -345,7 +345,12 @@ function readConfig() {
345
345
  anon_id: raw.anon_id,
346
346
  tier: raw.tier === "paid" ? "paid" : "free",
347
347
  api_key: typeof raw.api_key === "string" ? raw.api_key : null,
348
- mandate: raw.mandate
348
+ mandate: raw.mandate,
349
+ // Install-state passthrough: dropping these on a read→write cycle would
350
+ // reset every once-per-install nudge into an every-time nag.
351
+ created_at: typeof raw.created_at === "string" ? raw.created_at : void 0,
352
+ nudges: raw.nudges && typeof raw.nudges === "object" ? raw.nudges : void 0,
353
+ decisions_count: typeof raw.decisions_count === "number" ? raw.decisions_count : void 0
349
354
  };
350
355
  } catch {
351
356
  return null;
@@ -363,7 +368,8 @@ function ensureState() {
363
368
  anon_id: randomUUID2(),
364
369
  tier: "free",
365
370
  api_key: null,
366
- mandate: { payees: [], categories: ["*"], currency: "USD", perTxMax: 2500, maxTotal: 1e4 }
371
+ mandate: { payees: [], categories: ["*"], currency: "USD", perTxMax: 2500, maxTotal: 1e4 },
372
+ created_at: (/* @__PURE__ */ new Date()).toISOString()
367
373
  };
368
374
  try {
369
375
  writeConfig(config);
@@ -385,7 +391,7 @@ function resolveMandateRules(cfg) {
385
391
  }
386
392
 
387
393
  // src/telemetry.ts
388
- var CLIENT_VERSION = true ? "0.1.10" : "dev";
394
+ var CLIENT_VERSION = true ? "0.1.11" : "dev";
389
395
  var currentShell = "mcp";
390
396
  function telemetryEnabled() {
391
397
  const v = (process.env.FIDACY_DISABLE_TELEMETRY ?? "").trim().toLowerCase();
@@ -609,7 +615,7 @@ async function postOnce(fetchImpl, url, headers, payload, timeoutMs) {
609
615
  }
610
616
 
611
617
  // src/provision.ts
612
- var CLIENT_VERSION2 = true ? "0.1.10" : "dev";
618
+ var CLIENT_VERSION2 = true ? "0.1.11" : "dev";
613
619
  function provisionEnabled() {
614
620
  const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
615
621
  return !(v === "1" || v === "true" || v === "yes");
@@ -656,6 +662,55 @@ function requestUpgrade() {
656
662
  return { url: upgradeUrl(), anonId: anonId2 };
657
663
  }
658
664
 
665
+ // src/nudges.ts
666
+ function nudgeOnce(key) {
667
+ const cfg = readConfig();
668
+ if (!cfg) return false;
669
+ if (cfg.nudges?.[key]) return false;
670
+ cfg.nudges = { ...cfg.nudges ?? {}, [key]: true };
671
+ try {
672
+ writeConfig(cfg);
673
+ } catch {
674
+ return false;
675
+ }
676
+ return true;
677
+ }
678
+ function bumpDecisionCount() {
679
+ const cfg = readConfig();
680
+ if (!cfg) return 0;
681
+ const n = (cfg.decisions_count ?? 0) + 1;
682
+ cfg.decisions_count = n;
683
+ try {
684
+ writeConfig(cfg);
685
+ } catch {
686
+ }
687
+ return n;
688
+ }
689
+ function installAgeDays() {
690
+ const t = Date.parse(readConfig()?.created_at ?? "");
691
+ if (Number.isNaN(t)) return 0;
692
+ return Math.floor((Date.now() - t) / 864e5);
693
+ }
694
+ function decisionNudge(status, violatedRule, upgradeToolName) {
695
+ const count = bumpDecisionCount();
696
+ if (status === "DENY" && violatedRule?.startsWith("duplicate_invoice") && nudgeOnce("bec_catch")) {
697
+ return `That was a duplicate-invoice (BEC) catch \u2014 the #1 payment fraud. On a free account every block like this is server-signed and anchored to Bitcoin: court-grade evidence. Call the ${upgradeToolName} tool to start.`;
698
+ }
699
+ if (status === "DENY" && nudgeOnce("first_deny")) {
700
+ return `Fidacy just blocked this for you \u2014 for server-signed, anchored proof of every block (free account, local history migrates), call the ${upgradeToolName} tool.`;
701
+ }
702
+ if (count === 10 && nudgeOnce("milestone_10")) {
703
+ return `That's 10 actions gated on this install. For a signed, Bitcoin-anchored history of all of them (free account), call the ${upgradeToolName} tool.`;
704
+ }
705
+ return null;
706
+ }
707
+ function weekOneBootNudge(upgradeToolName) {
708
+ if (installAgeDays() >= 7 && nudgeOnce("week_1")) {
709
+ return ` One week protected \u2014 anchor your history to Bitcoin with a free account: the ${upgradeToolName} tool starts it.`;
710
+ }
711
+ return null;
712
+ }
713
+
659
714
  // src/index.ts
660
715
  var state = ensureState();
661
716
  var core = makeCore();
@@ -689,8 +744,10 @@ server.registerTool(
689
744
  const req = args;
690
745
  const d = await core.decide(req, subject);
691
746
  const out = { status: d.status, decisionId: d.decisionId, grant: d.grant, violatedRule: d.violatedRule };
692
- const human = 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:
693
- ${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. Fidacy just blocked this for you \u2014 for server-signed, anchored proof of every block (free account, local history migrates), call the upgrade tool.`;
747
+ 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:
748
+ ${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.`;
749
+ const nudge = decisionNudge(d.status, d.violatedRule, "upgrade");
750
+ const human = nudge ? `${base} ${nudge}` : base;
694
751
  return { content: [{ type: "text", text: human }], structuredContent: out };
695
752
  }
696
753
  );
@@ -705,7 +762,8 @@ server.registerTool(
705
762
  async () => {
706
763
  const m = await core.getMandate(subject);
707
764
  const payload = { mandate: m, fidacyPublicKey: core.publicKey() };
708
- return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }], structuredContent: payload };
765
+ const text = JSON.stringify(payload, null, 2) + "\n\nThis mandate is local (free tier). Hosted mandates add team governance and server-signed verdicts \u2014 the upgrade tool starts a free account.";
766
+ return { content: [{ type: "text", text }], structuredContent: payload };
709
767
  }
710
768
  );
711
769
  server.registerTool(
@@ -719,7 +777,8 @@ server.registerTool(
719
777
  async ({ decisionId }) => {
720
778
  const proof = await core.getProof(decisionId);
721
779
  if (!proof) return { content: [{ type: "text", text: `No proof found for ${decisionId}` }], isError: true };
722
- return { content: [{ type: "text", text: JSON.stringify(proof, null, 2) }], structuredContent: proof };
780
+ const text = JSON.stringify(proof, null, 2) + "\n\nThis proof lives on this machine. On a free hosted account the chain is anchored to Bitcoin \u2014 verifiable by anyone, forever, even against Fidacy (the upgrade tool starts it).";
781
+ return { content: [{ type: "text", text }], structuredContent: proof };
723
782
  }
724
783
  );
725
784
  server.registerTool(
@@ -745,7 +804,7 @@ server.registerTool(
745
804
  content: [
746
805
  {
747
806
  type: "text",
748
- text: "assess_action requires FIDACY_ENGINE_API_KEY (an fky_live_/fky_test_ key with assess:write). Set it to enable signed verdicts."
807
+ text: "assess_action needs an engine key \u2014 free in about a minute: call the upgrade tool (your usage history migrates), or set FIDACY_ENGINE_API_KEY if you already have one."
749
808
  }
750
809
  ],
751
810
  isError: true
@@ -805,7 +864,8 @@ async function main() {
805
864
  void autoProvision();
806
865
  const verdictHint = process.env.FIDACY_ENGINE_API_KEY ? "" : " Set FIDACY_ENGINE_API_KEY to enable verdicts (assess_action).";
807
866
  const tierHint = state.firstRun ? " First run: free local tier active (deny-unknown payee + cap). Add trusted payees in ~/.fidacy/config.json." : ` Tier: ${state.config.tier} (local-first).`;
808
- console.error("[fidacy] @fidacy/mcp ready on stdio: signed verdict + payment firewall." + tierHint + verdictHint);
867
+ const weekNudge = weekOneBootNudge("upgrade") ?? "";
868
+ console.error("[fidacy] @fidacy/mcp ready on stdio: signed verdict + payment firewall." + tierHint + verdictHint + weekNudge);
809
869
  }
810
870
  main().catch((e) => {
811
871
  console.error("[fidacy] fatal:", e);
package/dist/lib.js CHANGED
@@ -464,7 +464,12 @@ function readConfig() {
464
464
  anon_id: raw.anon_id,
465
465
  tier: raw.tier === "paid" ? "paid" : "free",
466
466
  api_key: typeof raw.api_key === "string" ? raw.api_key : null,
467
- mandate: raw.mandate
467
+ mandate: raw.mandate,
468
+ // Install-state passthrough: dropping these on a read→write cycle would
469
+ // reset every once-per-install nudge into an every-time nag.
470
+ created_at: typeof raw.created_at === "string" ? raw.created_at : void 0,
471
+ nudges: raw.nudges && typeof raw.nudges === "object" ? raw.nudges : void 0,
472
+ decisions_count: typeof raw.decisions_count === "number" ? raw.decisions_count : void 0
468
473
  };
469
474
  } catch {
470
475
  return null;
@@ -482,7 +487,8 @@ function ensureState() {
482
487
  anon_id: randomUUID3(),
483
488
  tier: "free",
484
489
  api_key: null,
485
- mandate: { payees: [], categories: ["*"], currency: "USD", perTxMax: 2500, maxTotal: 1e4 }
490
+ mandate: { payees: [], categories: ["*"], currency: "USD", perTxMax: 2500, maxTotal: 1e4 },
491
+ created_at: (/* @__PURE__ */ new Date()).toISOString()
486
492
  };
487
493
  try {
488
494
  writeConfig(config);
@@ -504,7 +510,7 @@ function resolveMandateRules(cfg) {
504
510
  }
505
511
 
506
512
  // src/telemetry.ts
507
- var CLIENT_VERSION = true ? "0.1.10" : "dev";
513
+ var CLIENT_VERSION = true ? "0.1.11" : "dev";
508
514
  var currentShell = "mcp";
509
515
  function setTelemetryShell(shell) {
510
516
  currentShell = shell;
@@ -627,7 +633,7 @@ function requestUpgrade() {
627
633
  }
628
634
 
629
635
  // src/provision.ts
630
- var CLIENT_VERSION2 = true ? "0.1.10" : "dev";
636
+ var CLIENT_VERSION2 = true ? "0.1.11" : "dev";
631
637
  function provisionEnabled() {
632
638
  const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
633
639
  return !(v === "1" || v === "true" || v === "yes");
@@ -661,6 +667,55 @@ async function autoProvision() {
661
667
  return "failed";
662
668
  }
663
669
  }
670
+
671
+ // src/nudges.ts
672
+ function nudgeOnce(key) {
673
+ const cfg = readConfig();
674
+ if (!cfg) return false;
675
+ if (cfg.nudges?.[key]) return false;
676
+ cfg.nudges = { ...cfg.nudges ?? {}, [key]: true };
677
+ try {
678
+ writeConfig(cfg);
679
+ } catch {
680
+ return false;
681
+ }
682
+ return true;
683
+ }
684
+ function bumpDecisionCount() {
685
+ const cfg = readConfig();
686
+ if (!cfg) return 0;
687
+ const n = (cfg.decisions_count ?? 0) + 1;
688
+ cfg.decisions_count = n;
689
+ try {
690
+ writeConfig(cfg);
691
+ } catch {
692
+ }
693
+ return n;
694
+ }
695
+ function installAgeDays() {
696
+ const t = Date.parse(readConfig()?.created_at ?? "");
697
+ if (Number.isNaN(t)) return 0;
698
+ return Math.floor((Date.now() - t) / 864e5);
699
+ }
700
+ function decisionNudge(status, violatedRule, upgradeToolName) {
701
+ const count = bumpDecisionCount();
702
+ if (status === "DENY" && violatedRule?.startsWith("duplicate_invoice") && nudgeOnce("bec_catch")) {
703
+ return `That was a duplicate-invoice (BEC) catch \u2014 the #1 payment fraud. On a free account every block like this is server-signed and anchored to Bitcoin: court-grade evidence. Call the ${upgradeToolName} tool to start.`;
704
+ }
705
+ if (status === "DENY" && nudgeOnce("first_deny")) {
706
+ return `Fidacy just blocked this for you \u2014 for server-signed, anchored proof of every block (free account, local history migrates), call the ${upgradeToolName} tool.`;
707
+ }
708
+ if (count === 10 && nudgeOnce("milestone_10")) {
709
+ return `That's 10 actions gated on this install. For a signed, Bitcoin-anchored history of all of them (free account), call the ${upgradeToolName} tool.`;
710
+ }
711
+ return null;
712
+ }
713
+ function weekOneBootNudge(upgradeToolName) {
714
+ if (installAgeDays() >= 7 && nudgeOnce("week_1")) {
715
+ return ` One week protected \u2014 anchor your history to Bitcoin with a free account: the ${upgradeToolName} tool starts it.`;
716
+ }
717
+ return null;
718
+ }
664
719
  export {
665
720
  DevFidacyCore,
666
721
  FileAuditStore,
@@ -669,12 +724,15 @@ export {
669
724
  ReferenceRail,
670
725
  autoProvision,
671
726
  canonInvoice,
727
+ decisionNudge,
672
728
  ensureState,
673
729
  evaluate,
674
730
  httpRedeem,
731
+ installAgeDays,
675
732
  loadOrGenerateKeyPair,
676
733
  lookalikePayee,
677
734
  makeCore,
735
+ nudgeOnce,
678
736
  publicKeyPem,
679
737
  readConfig,
680
738
  recordAgentActive,
@@ -688,5 +746,6 @@ export {
688
746
  upgradeUrl,
689
747
  validateRequest,
690
748
  verify,
691
- verifyGrant
749
+ verifyGrant,
750
+ weekOneBootNudge
692
751
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fidacy/mcp",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
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",