@fidacy/mcp 0.2.9 → 0.3.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/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 0a1bf9e: Anonymous telemetry now counts tool usage: a `tool_use` event with which tool ran (assess, anchor, check, verify, explain) and nothing else. Same privacy invariant as every other event: an allowlisted enum on the wire, never the tool's content, payee, amount, hash or mandate. Opt out as always with FIDACY_DISABLE_TELEMETRY=1.
8
+
9
+ ### Patch Changes
10
+
11
+ - 0a1bf9e: Fix the free-trial decision counter being wiped by the background auto-provision write. The provision task captured config.json before its network call and wrote that stale snapshot back after, erasing every decision counted in between, so the 20-decision anonymous trial reset on every process restart. The provision write now merges the key onto a fresh read, and the counter write self-heals from the in-process floor. The wall now holds across restarts: 20 free decisions per install, as designed.
12
+
13
+ ## 0.2.10
14
+
15
+ ### Patch Changes
16
+
17
+ - 2e846a7: Funnel: move the strongest claim nudge before the activation wall, and make the
18
+ first-block nudge scary-but-true.
19
+
20
+ The proof-teaser nudge fired at decision 25, stranded behind the 20-decision
21
+ activation wall: once the firewall fails closed at 20, denied calls stop
22
+ advancing the counter, so 25 was never reached and the single strongest reason
23
+ to claim (the history you already built) never showed. It now fires at 15, five
24
+ decisions before the wall — carrot before stick, which is the highest-converting
25
+ moment the PLG research points to.
26
+
27
+ The first-deny nudge now cites the real $441,788 agent incident. Grounded fear,
28
+ never manufactured: for a company whose pitch is verify-don't-trust, a cited
29
+ fact converts and a fabricated panic would be self-refuting.
30
+
3
31
  ## 0.2.9
4
32
 
5
33
  ### Patch Changes
package/dist/core.js CHANGED
@@ -461,7 +461,7 @@ function resolveMandateRules(cfg) {
461
461
  }
462
462
 
463
463
  // src/telemetry.ts
464
- var CLIENT_VERSION = true ? "0.2.9" : "dev";
464
+ var CLIENT_VERSION = true ? "0.3.0" : "dev";
465
465
  function bandOf(amount) {
466
466
  if (typeof amount !== "number" || !Number.isFinite(amount) || amount <= 0) return void 0;
467
467
  if (amount < 10) return "lt10";
@@ -509,7 +509,7 @@ function resultOf(status, violatedRule) {
509
509
  if (r.startsWith("non_positive_amount") || r.startsWith("invalid_")) return "deny_invalid";
510
510
  return "deny_scope";
511
511
  }
512
- function record(type, result, band, capRatio) {
512
+ function record(type, result, band, capRatio, action) {
513
513
  if (!telemetryEnabled()) return;
514
514
  buffer.push({
515
515
  type,
@@ -518,7 +518,8 @@ function record(type, result, band, capRatio) {
518
518
  shell: currentShell,
519
519
  ...result ? { result } : {},
520
520
  ...band ? { band } : {},
521
- ...capRatio ? { cap_ratio: capRatio } : {}
521
+ ...capRatio ? { cap_ratio: capRatio } : {},
522
+ ...action ? { action } : {}
522
523
  });
523
524
  if (!flushTimer) {
524
525
  flushTimer = setTimeout(() => {
package/dist/index.js CHANGED
@@ -490,7 +490,7 @@ function resolveMandateRules(cfg) {
490
490
  }
491
491
 
492
492
  // src/telemetry.ts
493
- var CLIENT_VERSION = true ? "0.2.9" : "dev";
493
+ var CLIENT_VERSION = true ? "0.3.0" : "dev";
494
494
  function bandOf(amount) {
495
495
  if (typeof amount !== "number" || !Number.isFinite(amount) || amount <= 0) return void 0;
496
496
  if (amount < 10) return "lt10";
@@ -538,7 +538,7 @@ function resultOf(status, violatedRule) {
538
538
  if (r.startsWith("non_positive_amount") || r.startsWith("invalid_")) return "deny_invalid";
539
539
  return "deny_scope";
540
540
  }
541
- function record(type, result, band, capRatio) {
541
+ function record(type, result, band, capRatio, action) {
542
542
  if (!telemetryEnabled()) return;
543
543
  buffer.push({
544
544
  type,
@@ -547,7 +547,8 @@ function record(type, result, band, capRatio) {
547
547
  shell: currentShell,
548
548
  ...result ? { result } : {},
549
549
  ...band ? { band } : {},
550
- ...capRatio ? { cap_ratio: capRatio } : {}
550
+ ...capRatio ? { cap_ratio: capRatio } : {},
551
+ ...action ? { action } : {}
551
552
  });
552
553
  if (!flushTimer) {
553
554
  flushTimer = setTimeout(() => {
@@ -560,6 +561,7 @@ var recordInstall = () => record("install");
560
561
  var recordAgentActive = () => record("agent_active");
561
562
  var recordDecision = (status, violatedRule, amount, perTxMax) => record("decision", resultOf(status, violatedRule), bandOf(amount), capRatioOf(amount, perTxMax));
562
563
  var recordUpgradeIntent = () => record("upgrade_intent");
564
+ var recordToolUse = (action) => record("tool_use", void 0, void 0, void 0, action);
563
565
  async function flush() {
564
566
  if (flushTimer) {
565
567
  clearTimeout(flushTimer);
@@ -834,7 +836,7 @@ async function findArtifacts(sha2562, cfg) {
834
836
  }
835
837
 
836
838
  // src/provision.ts
837
- var CLIENT_VERSION2 = true ? "0.2.9" : "dev";
839
+ var CLIENT_VERSION2 = true ? "0.3.0" : "dev";
838
840
  function provisionEnabled() {
839
841
  const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
840
842
  return !(v === "1" || v === "true" || v === "yes");
@@ -858,8 +860,9 @@ async function autoProvision() {
858
860
  if (!res.ok) return "failed";
859
861
  const data = await res.json();
860
862
  if (typeof data.api_key !== "string" || !data.api_key) return "failed";
863
+ const fresh = readConfig() ?? cfg;
861
864
  writeConfig({
862
- ...cfg,
865
+ ...fresh,
863
866
  api_key: data.api_key,
864
867
  tier: data.tier === "paid" ? "paid" : "free"
865
868
  });
@@ -906,7 +909,7 @@ function bumpDecisionCount() {
906
909
  sessionDecisions += 1;
907
910
  const cfg = readConfig();
908
911
  if (!cfg) return sessionDecisions;
909
- const n = (cfg.decisions_count ?? 0) + 1;
912
+ const n = Math.max((cfg.decisions_count ?? 0) + 1, sessionDecisions);
910
913
  cfg.decisions_count = n;
911
914
  try {
912
915
  writeConfig(cfg);
@@ -927,13 +930,13 @@ function decisionNudge(status, violatedRule, upgradeToolName) {
927
930
  return `That was a duplicate-invoice (BEC) catch, the #1 payment fraud. For your operator: ${claimPath}. On a hosted account every block like this is server-signed and anchored to Bitcoin: court-grade evidence.`;
928
931
  }
929
932
  if (status === "DENY" && nudgeOnce("first_deny")) {
930
- return `Fidacy just blocked this for you. For your operator: ${claimPath}.`;
933
+ return `Fidacy just blocked this payment before it moved. This is the failure it exists for: last week an AI agent sent $441,788 to a stranger who posted a fake story with a wallet address. Nothing was checking it. For your operator: ${claimPath}.`;
931
934
  }
932
935
  if (status === "ALLOW" && nudgeOnce("first_allow")) {
933
936
  return `Fidacy authorized this and signed the decision. For your operator: ${claimPath}.`;
934
937
  }
935
- if (count === 25 && nudgeOnce("proof_teaser")) {
936
- return `Your local chain now holds ${count} decisions only you can read. Anchor them and anyone can verify at fidacy.com/verify, no account needed on their side. For your operator: ${claimPath}.`;
938
+ if (count === 15 && nudgeOnce("proof_teaser")) {
939
+ return `Fidacy has now checked ${count} payments on this agent and stands between it and the wrong one, the prompt-injected payee, the duplicate, the over-limit charge. That protection pauses after 20 decisions without a free key. Keep the agent covered: ${claimPath}.`;
937
940
  }
938
941
  if (count === 10 && nudgeOnce("milestone_10")) {
939
942
  return `That's 10 actions gated on this install. For your operator: ${claimPath}.`;
@@ -1264,7 +1267,7 @@ function renderAlertLine(alerts) {
1264
1267
  var state = ensureState();
1265
1268
  var core = makeCore();
1266
1269
  var subject = process.env.FIDACY_SUBJECT ?? "agent:demo";
1267
- var SERVER_VERSION = true ? "0.2.9" : "dev";
1270
+ var SERVER_VERSION = true ? "0.3.0" : "dev";
1268
1271
  var bootClaim = claimUrl();
1269
1272
  var server = new McpServer(
1270
1273
  { name: "fidacy", version: SERVER_VERSION },
@@ -1331,6 +1334,7 @@ server.registerTool(
1331
1334
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
1332
1335
  },
1333
1336
  async () => {
1337
+ recordToolUse("verify");
1334
1338
  const m = await core.getMandate(subject);
1335
1339
  const payload = { mandate: m, fidacyPublicKey: core.publicKey() };
1336
1340
  const claim = process.env.FIDACY_ENGINE_API_KEY ? null : claimUrl();
@@ -1409,6 +1413,7 @@ server.registerTool(
1409
1413
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
1410
1414
  },
1411
1415
  async ({ decisionId }) => {
1416
+ recordToolUse("explain");
1412
1417
  const records = await core.history(5e3);
1413
1418
  const record2 = records.find((r) => r.decisionId === decisionId);
1414
1419
  if (!record2) {
@@ -1469,6 +1474,7 @@ server.registerTool(
1469
1474
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }
1470
1475
  },
1471
1476
  async ({ kind, mandate, mandateType, idempotencyKey, spendingMandate, a2a }) => {
1477
+ recordToolUse("assess");
1472
1478
  const engineUrl = process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
1473
1479
  const apiKey = (process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
1474
1480
  if (!apiKey) {
@@ -1519,6 +1525,7 @@ server.registerTool(
1519
1525
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }
1520
1526
  },
1521
1527
  async ({ path, sha256: sha2562, kind, label, subject: subj }) => {
1528
+ recordToolUse("anchor");
1522
1529
  const engineUrl = process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
1523
1530
  const apiKey = (process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
1524
1531
  if (!apiKey) {
@@ -1573,6 +1580,7 @@ server.registerTool(
1573
1580
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }
1574
1581
  },
1575
1582
  async ({ path, sha256: sha2562 }) => {
1583
+ recordToolUse("check");
1576
1584
  const engineUrl = process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
1577
1585
  const apiKey = (process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
1578
1586
  if (!apiKey) {
package/dist/lib.js CHANGED
@@ -613,7 +613,7 @@ function resolveMandateRules(cfg) {
613
613
  }
614
614
 
615
615
  // src/telemetry.ts
616
- var CLIENT_VERSION = true ? "0.2.9" : "dev";
616
+ var CLIENT_VERSION = true ? "0.3.0" : "dev";
617
617
  function bandOf(amount) {
618
618
  if (typeof amount !== "number" || !Number.isFinite(amount) || amount <= 0) return void 0;
619
619
  if (amount < 10) return "lt10";
@@ -664,7 +664,7 @@ function resultOf(status, violatedRule) {
664
664
  if (r.startsWith("non_positive_amount") || r.startsWith("invalid_")) return "deny_invalid";
665
665
  return "deny_scope";
666
666
  }
667
- function record(type, result, band, capRatio) {
667
+ function record(type, result, band, capRatio, action) {
668
668
  if (!telemetryEnabled()) return;
669
669
  buffer.push({
670
670
  type,
@@ -673,7 +673,8 @@ function record(type, result, band, capRatio) {
673
673
  shell: currentShell,
674
674
  ...result ? { result } : {},
675
675
  ...band ? { band } : {},
676
- ...capRatio ? { cap_ratio: capRatio } : {}
676
+ ...capRatio ? { cap_ratio: capRatio } : {},
677
+ ...action ? { action } : {}
677
678
  });
678
679
  if (!flushTimer) {
679
680
  flushTimer = setTimeout(() => {
@@ -791,7 +792,7 @@ function requestUpgrade() {
791
792
  }
792
793
 
793
794
  // src/provision.ts
794
- var CLIENT_VERSION2 = true ? "0.2.9" : "dev";
795
+ var CLIENT_VERSION2 = true ? "0.3.0" : "dev";
795
796
  function provisionEnabled() {
796
797
  const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
797
798
  return !(v === "1" || v === "true" || v === "yes");
@@ -815,8 +816,9 @@ async function autoProvision() {
815
816
  if (!res.ok) return "failed";
816
817
  const data = await res.json();
817
818
  if (typeof data.api_key !== "string" || !data.api_key) return "failed";
819
+ const fresh = readConfig() ?? cfg;
818
820
  writeConfig({
819
- ...cfg,
821
+ ...fresh,
820
822
  api_key: data.api_key,
821
823
  tier: data.tier === "paid" ? "paid" : "free"
822
824
  });
@@ -851,7 +853,7 @@ function bumpDecisionCount() {
851
853
  sessionDecisions += 1;
852
854
  const cfg = readConfig();
853
855
  if (!cfg) return sessionDecisions;
854
- const n = (cfg.decisions_count ?? 0) + 1;
856
+ const n = Math.max((cfg.decisions_count ?? 0) + 1, sessionDecisions);
855
857
  cfg.decisions_count = n;
856
858
  try {
857
859
  writeConfig(cfg);
@@ -872,13 +874,13 @@ function decisionNudge(status, violatedRule, upgradeToolName) {
872
874
  return `That was a duplicate-invoice (BEC) catch, the #1 payment fraud. For your operator: ${claimPath}. On a hosted account every block like this is server-signed and anchored to Bitcoin: court-grade evidence.`;
873
875
  }
874
876
  if (status === "DENY" && nudgeOnce("first_deny")) {
875
- return `Fidacy just blocked this for you. For your operator: ${claimPath}.`;
877
+ return `Fidacy just blocked this payment before it moved. This is the failure it exists for: last week an AI agent sent $441,788 to a stranger who posted a fake story with a wallet address. Nothing was checking it. For your operator: ${claimPath}.`;
876
878
  }
877
879
  if (status === "ALLOW" && nudgeOnce("first_allow")) {
878
880
  return `Fidacy authorized this and signed the decision. For your operator: ${claimPath}.`;
879
881
  }
880
- if (count === 25 && nudgeOnce("proof_teaser")) {
881
- return `Your local chain now holds ${count} decisions only you can read. Anchor them and anyone can verify at fidacy.com/verify, no account needed on their side. For your operator: ${claimPath}.`;
882
+ if (count === 15 && nudgeOnce("proof_teaser")) {
883
+ return `Fidacy has now checked ${count} payments on this agent and stands between it and the wrong one, the prompt-injected payee, the duplicate, the over-limit charge. That protection pauses after 20 decisions without a free key. Keep the agent covered: ${claimPath}.`;
882
884
  }
883
885
  if (count === 10 && nudgeOnce("milestone_10")) {
884
886
  return `That's 10 actions gated on this install. For your operator: ${claimPath}.`;
@@ -1,4 +1,7 @@
1
- export type TelemetryEventType = "install" | "agent_active" | "decision" | "upgrade_intent";
1
+ export type TelemetryEventType = "install" | "agent_active" | "decision" | "upgrade_intent" | "tool_use";
2
+ /** Which tool a tool_use event counts (0059). An ENUM mirrored by the ingest
3
+ * allowlist: which tool ran, never what it ran on. */
4
+ export type ToolAction = "assess" | "anchor" | "check" | "verify" | "explain";
2
5
  export type DecisionResult = "allow" | "deny_cap" | "deny_payee" | "deny_scope" | "deny_lookalike" | "deny_duplicate" | "deny_window" | "deny_invalid";
3
6
  /** Which shell this engine is embedded in. An ENUM mirrored by the ingest allowlist
4
7
  * and the DB CHECK — never a free-form string, so it can't become a PII channel. */
@@ -18,10 +21,12 @@ export declare function telemetryEnabled(): boolean;
18
21
  /** Map a firewall violatedRule to the coarse, PII-free decision result enum. */
19
22
  export declare function resultOf(status: "ALLOW" | "DENY", violatedRule?: string): DecisionResult;
20
23
  /** Buffer an event (sync, never blocks, never throws). Auto-flushes shortly after. */
21
- export declare function record(type: TelemetryEventType, result?: DecisionResult, band?: AmountBand, capRatio?: CapRatioBand): void;
24
+ export declare function record(type: TelemetryEventType, result?: DecisionResult, band?: AmountBand, capRatio?: CapRatioBand, action?: ToolAction): void;
22
25
  export declare const recordInstall: () => void;
23
26
  export declare const recordAgentActive: () => void;
24
27
  export declare const recordDecision: (status: "ALLOW" | "DENY", violatedRule?: string, amount?: number, perTxMax?: number) => void;
25
28
  export declare const recordUpgradeIntent: () => void;
29
+ /** Count a tool invocation (which tool, never its content) — the agent-activity profile. */
30
+ export declare const recordToolUse: (action: ToolAction) => void;
26
31
  /** Send the buffered batch fire-and-forget. Swallows all errors. Best-effort. */
27
32
  export declare function flush(): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fidacy/mcp",
3
- "version": "0.2.9",
3
+ "version": "0.3.0",
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
  "author": "Fidacy (ZeepCode Group Technology LLC) <hello@fidacy.com> (https://fidacy.com)",