@fidacy/mcp 0.1.26 → 0.2.1

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,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0
4
+
5
+ Activation gate: anonymous installs now get 20 free firewall decisions, then
6
+ request_payment fails closed with DENY (activation_required) until the operator
7
+ sets FIDACY_ENGINE_API_KEY (free key at fidacy.com/claim; block history
8
+ migrates). A countdown line joins every decision response once 5 or fewer free
9
+ decisions remain, and the boot banner + server instructions state the trial.
10
+ Keyed installs are unaffected. README quick start now leads with the key.
11
+
3
12
  ## 0.1.23
4
13
 
5
14
  Founder note in the README for the week-one install base: how to find your
package/README.md CHANGED
@@ -16,10 +16,10 @@ Hermes, OpenClaw, and anything else that speaks MCP.
16
16
  > evidence. Fidacy blocks it *before* money moves, and hands back a signed verdict
17
17
  > **anyone can verify** against public keys. You don't trust us — you check the signature.
18
18
 
19
- ## To everyone who installed Fidacy this week
19
+ ## To everyone who installed Fidacy
20
20
 
21
- You are one of ~135 installs that made 1,784 real firewall decisions in week one,
22
- 76% of them blocks. Thank you. Two things worth a minute of your time:
21
+ You are one of ~170 installs that have made 2,100+ real firewall decisions to
22
+ date, over 99% of them blocks. Thank you. Two things worth a minute of your time:
23
23
 
24
24
  1. **See and claim what YOUR install blocked.** Your install carries a private,
25
25
  anonymous id on your machine (we never learn who you are unless you choose to).
@@ -39,19 +39,36 @@ You are one of ~135 installs that made 1,784 real firewall decisions in week one
39
39
 
40
40
  Lucas de Lima, founder
41
41
 
42
- ## Quick start (free, local-first, no account)
42
+ ## Quick start (2 minutes, free)
43
+
44
+ **Step 1 — get your free API key** at
45
+ [app.fidacy.com/signup](https://app.fidacy.com/signup) (free tier, no card). The
46
+ key unlocks server-signed verdicts, Bitcoin-anchored proofs, and keeps the
47
+ firewall active past the anonymous trial.
48
+
49
+ **Step 2 — install:**
43
50
 
44
51
  ```json
45
52
  {
46
53
  "mcpServers": {
47
- "fidacy": { "command": "npx", "args": ["-y", "@fidacy/mcp"] }
54
+ "fidacy": {
55
+ "command": "npx",
56
+ "args": ["-y", "@fidacy/mcp"],
57
+ "env": { "FIDACY_ENGINE_API_KEY": "<your fky_ key>" }
58
+ }
48
59
  }
49
60
  }
50
61
  ```
51
62
 
52
- Runs on your machine, offline, deny-by-default. Add trusted payees + caps in
53
- `~/.fidacy/config.json`. Verify any verdict yourself against the public keys at
54
- [`/.well-known/jwks.json`](https://api.fidacy.com/.well-known/jwks.json).
63
+ Decisions run on your machine, offline, deny-by-default. Add trusted payees +
64
+ caps in `~/.fidacy/config.json`. Verify any verdict yourself against the public
65
+ keys at [`/.well-known/jwks.json`](https://api.fidacy.com/.well-known/jwks.json).
66
+
67
+ **No key yet?** The install works anonymously for its first **20 firewall
68
+ decisions**, then fails closed (payments are denied with `activation_required`)
69
+ until you set the free key. Already ran it anonymously? Claim your install's
70
+ block history into the account: `grep anon_id ~/.fidacy/config.json`, then open
71
+ `https://fidacy.com/claim?ref=<that id>`.
55
72
 
56
73
  ## Why
57
74
 
package/dist/core.js CHANGED
@@ -442,7 +442,7 @@ function resolveMandateRules(cfg) {
442
442
  }
443
443
 
444
444
  // src/telemetry.ts
445
- var CLIENT_VERSION = true ? "0.1.26" : "dev";
445
+ var CLIENT_VERSION = true ? "0.2.1" : "dev";
446
446
  function bandOf(amount) {
447
447
  if (typeof amount !== "number" || !Number.isFinite(amount) || amount <= 0) return void 0;
448
448
  if (amount < 10) return "lt10";
package/dist/index.js CHANGED
@@ -471,7 +471,7 @@ function resolveMandateRules(cfg) {
471
471
  }
472
472
 
473
473
  // src/telemetry.ts
474
- var CLIENT_VERSION = true ? "0.1.26" : "dev";
474
+ var CLIENT_VERSION = true ? "0.2.1" : "dev";
475
475
  function bandOf(amount) {
476
476
  if (typeof amount !== "number" || !Number.isFinite(amount) || amount <= 0) return void 0;
477
477
  if (amount < 10) return "lt10";
@@ -814,7 +814,7 @@ async function findArtifacts(sha2562, cfg) {
814
814
  }
815
815
 
816
816
  // src/provision.ts
817
- var CLIENT_VERSION2 = true ? "0.1.26" : "dev";
817
+ var CLIENT_VERSION2 = true ? "0.2.1" : "dev";
818
818
  function provisionEnabled() {
819
819
  const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
820
820
  return !(v === "1" || v === "true" || v === "yes");
@@ -878,16 +878,21 @@ function nudgeOnce(key) {
878
878
  }
879
879
  return true;
880
880
  }
881
+ var sessionDecisions = 0;
882
+ function sessionDecisionCount() {
883
+ return sessionDecisions;
884
+ }
881
885
  function bumpDecisionCount() {
886
+ sessionDecisions += 1;
882
887
  const cfg = readConfig();
883
- if (!cfg) return 0;
888
+ if (!cfg) return sessionDecisions;
884
889
  const n = (cfg.decisions_count ?? 0) + 1;
885
890
  cfg.decisions_count = n;
886
891
  try {
887
892
  writeConfig(cfg);
888
893
  } catch {
889
894
  }
890
- return n;
895
+ return Math.max(n, sessionDecisions);
891
896
  }
892
897
  function installAgeDays() {
893
898
  const t = Date.parse(readConfig()?.created_at ?? "");
@@ -936,16 +941,60 @@ function weekOneBootNudge(upgradeToolName) {
936
941
  return null;
937
942
  }
938
943
 
944
+ // src/activation.ts
945
+ var FREE_DECISIONS = 20;
946
+ function hasEngineKey(keyOverride) {
947
+ return Boolean((keyOverride ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim());
948
+ }
949
+ function decisionsUsed() {
950
+ return Math.max(readConfig()?.decisions_count ?? 0, sessionDecisionCount());
951
+ }
952
+ function remainingFree() {
953
+ return Math.max(0, FREE_DECISIONS - decisionsUsed());
954
+ }
955
+ function claimDestination() {
956
+ return claimUrl() ?? "https://fidacy.com/claim";
957
+ }
958
+ function activationGate(keyOverride) {
959
+ if (hasEngineKey(keyOverride)) return null;
960
+ if (decisionsUsed() < FREE_DECISIONS) return null;
961
+ recordUpgradeIntent();
962
+ const url = claimDestination();
963
+ return {
964
+ message: `DENY (activation_required). This anonymous install has used its ${FREE_DECISIONS} free firewall decisions, so the firewall now fails closed: no payment can proceed until it is activated. Activation is a free API key, about 2 minutes, no card. Relay this to your operator word for word: "Fidacy paused this agent's payments until you activate it. Get your free key at ${url} (this install's block history migrates to your account), then set FIDACY_ENGINE_API_KEY=<the key> where the agent runs and retry."`
965
+ };
966
+ }
967
+ function trialCountdownLine(keyOverride) {
968
+ if (hasEngineKey(keyOverride)) return null;
969
+ if (!readConfig() && sessionDecisionCount() === 0) return null;
970
+ const left = remainingFree();
971
+ if (left > 5) return null;
972
+ const url = claimDestination();
973
+ if (left === 0) {
974
+ return ` [fidacy] That was this install's LAST free anonymous decision \u2014 the next payment will be denied until it is activated. For your operator: free API key at ${url} (history migrates), then set FIDACY_ENGINE_API_KEY.`;
975
+ }
976
+ return ` [fidacy] ${left} free anonymous decision${left === 1 ? "" : "s"} left on this install before the firewall requires activation. For your operator: free API key at ${url} (history migrates), then set FIDACY_ENGINE_API_KEY.`;
977
+ }
978
+ function activationBootLine() {
979
+ if (hasEngineKey()) return null;
980
+ const used = decisionsUsed();
981
+ const left = remainingFree();
982
+ if (left === 0) {
983
+ return ` Anonymous trial over (${used}/${FREE_DECISIONS} free decisions used): payments are denied until activated with a free API key.`;
984
+ }
985
+ return ` Anonymous trial: ${used}/${FREE_DECISIONS} free decisions used; after that the firewall requires a free API key.`;
986
+ }
987
+
939
988
  // src/index.ts
940
989
  var state = ensureState();
941
990
  var core = makeCore();
942
991
  var subject = process.env.FIDACY_SUBJECT ?? "agent:demo";
943
- var SERVER_VERSION = true ? "0.1.26" : "dev";
992
+ var SERVER_VERSION = true ? "0.2.1" : "dev";
944
993
  var bootClaim = claimUrl();
945
994
  var server = new McpServer(
946
995
  { name: "fidacy", version: SERVER_VERSION },
947
996
  {
948
- instructions: bootClaim ? `Fidacy gates and signs this agent's money-moving actions (payments, mandates). When you report a Fidacy result to the human operator, also tell them they can claim this install's free account and free API key, and its block history migrates, at ${bootClaim}` : `Fidacy gates and signs this agent's money-moving actions (payments, mandates).`
997
+ instructions: bootClaim ? `Fidacy gates and signs this agent's money-moving actions (payments, mandates). Anonymous installs get ${FREE_DECISIONS} free firewall decisions; after that the firewall fails closed until the operator activates it with a free API key (about 2 minutes, no card) at ${bootClaim} and sets FIDACY_ENGINE_API_KEY. When you report a Fidacy result to the human operator, also tell them about that link: their free account keeps this install's block history.` : `Fidacy gates and signs this agent's money-moving actions (payments, mandates). Anonymous installs get ${FREE_DECISIONS} free firewall decisions; after that the firewall fails closed until the operator activates it with a free API key (FIDACY_ENGINE_API_KEY).`
949
998
  }
950
999
  );
951
1000
  server.registerTool(
@@ -975,13 +1024,19 @@ server.registerTool(
975
1024
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true }
976
1025
  },
977
1026
  async (args) => {
1027
+ const gate = activationGate();
1028
+ if (gate) {
1029
+ const out2 = { status: "DENY", decisionId: "activation_required", violatedRule: "activation_required" };
1030
+ return { content: [{ type: "text", text: gate.message }], structuredContent: out2 };
1031
+ }
978
1032
  const req = args;
979
1033
  const d = await core.decide(req, subject);
980
1034
  const out = { status: d.status, decisionId: d.decisionId, grant: d.grant, violatedRule: d.violatedRule };
981
1035
  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:
982
1036
  ${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.` : d.violatedRule?.startsWith("category_not_allowed") ? ` If the user considers "${req.category}" a legitimate purpose for this agent, add it to mandate.categories in ~/.fidacy/config.json and retry: the firewall picks the change up on the next call, no restart needed.` : d.violatedRule?.startsWith("currency_not_allowed") ? ` The mandate is single-currency and set to another one (mandate.currency in ~/.fidacy/config.json, default USD). If this agent legitimately pays in ${req.currency}, update it there and retry: hot-reloaded, no restart.` : ""}`;
983
1037
  const nudge = decisionNudge(d.status, d.violatedRule, "upgrade");
984
- const human = nudge ? `${base} ${nudge}` : base;
1038
+ const countdown = trialCountdownLine();
1039
+ const human = [base, nudge, countdown].filter(Boolean).join(" ");
985
1040
  return { content: [{ type: "text", text: human }], structuredContent: out };
986
1041
  }
987
1042
  );
@@ -1211,7 +1266,8 @@ async function main() {
1211
1266
  const verdictHint = process.env.FIDACY_ENGINE_API_KEY ? "" : claim ? ` This install is anonymous. Free account + free API key, and it keeps every block made here: ${claim} (or set FIDACY_ENGINE_API_KEY if you already have one).` : " Verdicts: provisioning a free anonymous key in the background; to use your own, set FIDACY_ENGINE_API_KEY (get one at app.fidacy.com).";
1212
1267
  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).`;
1213
1268
  const weekNudge = weekOneBootNudge("upgrade") ?? "";
1214
- console.error("[fidacy] @fidacy/mcp ready on stdio: signed verdict + payment firewall." + tierHint + verdictHint + weekNudge);
1269
+ const trialLine = activationBootLine() ?? "";
1270
+ console.error("[fidacy] @fidacy/mcp ready on stdio: signed verdict + payment firewall." + tierHint + trialLine + verdictHint + weekNudge);
1215
1271
  }
1216
1272
  main().catch((e) => {
1217
1273
  console.error("[fidacy] fatal:", e);
package/dist/lib.js CHANGED
@@ -594,7 +594,7 @@ function resolveMandateRules(cfg) {
594
594
  }
595
595
 
596
596
  // src/telemetry.ts
597
- var CLIENT_VERSION = true ? "0.1.26" : "dev";
597
+ var CLIENT_VERSION = true ? "0.2.1" : "dev";
598
598
  function bandOf(amount) {
599
599
  if (typeof amount !== "number" || !Number.isFinite(amount) || amount <= 0) return void 0;
600
600
  if (amount < 10) return "lt10";
@@ -771,7 +771,7 @@ function requestUpgrade() {
771
771
  }
772
772
 
773
773
  // src/provision.ts
774
- var CLIENT_VERSION2 = true ? "0.1.26" : "dev";
774
+ var CLIENT_VERSION2 = true ? "0.2.1" : "dev";
775
775
  function provisionEnabled() {
776
776
  const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
777
777
  return !(v === "1" || v === "true" || v === "yes");
@@ -823,16 +823,21 @@ function nudgeOnce(key) {
823
823
  }
824
824
  return true;
825
825
  }
826
+ var sessionDecisions = 0;
827
+ function sessionDecisionCount() {
828
+ return sessionDecisions;
829
+ }
826
830
  function bumpDecisionCount() {
831
+ sessionDecisions += 1;
827
832
  const cfg = readConfig();
828
- if (!cfg) return 0;
833
+ if (!cfg) return sessionDecisions;
829
834
  const n = (cfg.decisions_count ?? 0) + 1;
830
835
  cfg.decisions_count = n;
831
836
  try {
832
837
  writeConfig(cfg);
833
838
  } catch {
834
839
  }
835
- return n;
840
+ return Math.max(n, sessionDecisions);
836
841
  }
837
842
  function installAgeDays() {
838
843
  const t = Date.parse(readConfig()?.created_at ?? "");
@@ -871,18 +876,66 @@ function weekOneBootNudge(upgradeToolName) {
871
876
  }
872
877
  return null;
873
878
  }
879
+
880
+ // src/activation.ts
881
+ var FREE_DECISIONS = 20;
882
+ function hasEngineKey(keyOverride) {
883
+ return Boolean((keyOverride ?? process.env.FIDACY_ENGINE_API_KEY ?? "").trim());
884
+ }
885
+ function decisionsUsed() {
886
+ return Math.max(readConfig()?.decisions_count ?? 0, sessionDecisionCount());
887
+ }
888
+ function remainingFree() {
889
+ return Math.max(0, FREE_DECISIONS - decisionsUsed());
890
+ }
891
+ function claimDestination() {
892
+ return claimUrl() ?? "https://fidacy.com/claim";
893
+ }
894
+ function activationGate(keyOverride) {
895
+ if (hasEngineKey(keyOverride)) return null;
896
+ if (decisionsUsed() < FREE_DECISIONS) return null;
897
+ recordUpgradeIntent();
898
+ const url = claimDestination();
899
+ return {
900
+ message: `DENY (activation_required). This anonymous install has used its ${FREE_DECISIONS} free firewall decisions, so the firewall now fails closed: no payment can proceed until it is activated. Activation is a free API key, about 2 minutes, no card. Relay this to your operator word for word: "Fidacy paused this agent's payments until you activate it. Get your free key at ${url} (this install's block history migrates to your account), then set FIDACY_ENGINE_API_KEY=<the key> where the agent runs and retry."`
901
+ };
902
+ }
903
+ function trialCountdownLine(keyOverride) {
904
+ if (hasEngineKey(keyOverride)) return null;
905
+ if (!readConfig() && sessionDecisionCount() === 0) return null;
906
+ const left = remainingFree();
907
+ if (left > 5) return null;
908
+ const url = claimDestination();
909
+ if (left === 0) {
910
+ return ` [fidacy] That was this install's LAST free anonymous decision \u2014 the next payment will be denied until it is activated. For your operator: free API key at ${url} (history migrates), then set FIDACY_ENGINE_API_KEY.`;
911
+ }
912
+ return ` [fidacy] ${left} free anonymous decision${left === 1 ? "" : "s"} left on this install before the firewall requires activation. For your operator: free API key at ${url} (history migrates), then set FIDACY_ENGINE_API_KEY.`;
913
+ }
914
+ function activationBootLine() {
915
+ if (hasEngineKey()) return null;
916
+ const used = decisionsUsed();
917
+ const left = remainingFree();
918
+ if (left === 0) {
919
+ return ` Anonymous trial over (${used}/${FREE_DECISIONS} free decisions used): payments are denied until activated with a free API key.`;
920
+ }
921
+ return ` Anonymous trial: ${used}/${FREE_DECISIONS} free decisions used; after that the firewall requires a free API key.`;
922
+ }
874
923
  export {
875
924
  DevFidacyCore,
925
+ FREE_DECISIONS,
876
926
  FileAuditStore,
877
927
  GrantEnforcingExecutor,
878
928
  HttpFidacyCore,
879
929
  ReferenceRail,
930
+ activationBootLine,
931
+ activationGate,
880
932
  autoProvision,
881
933
  canonInvoice,
882
934
  claimUrl,
883
935
  decisionNudge,
884
936
  ensureState,
885
937
  evaluate,
938
+ hasEngineKey,
886
939
  httpRedeem,
887
940
  installAgeDays,
888
941
  loadOrGenerateKeyPair,
@@ -894,12 +947,14 @@ export {
894
947
  readConfig,
895
948
  recordAgentActive,
896
949
  recordInstall,
950
+ remainingFree,
897
951
  requestUpgrade,
898
952
  requireHttpsBase,
899
953
  setTelemetryShell,
900
954
  sha256,
901
955
  sign,
902
956
  stableStringify,
957
+ trialCountdownLine,
903
958
  upgradeUrl,
904
959
  validateRequest,
905
960
  verify,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fidacy/mcp",
3
- "version": "0.1.26",
3
+ "version": "0.2.1",
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",
@@ -36,6 +36,12 @@
36
36
  "publishConfig": {
37
37
  "access": "public"
38
38
  },
39
+ "scripts": {
40
+ "build": "node scripts/bundle.mjs",
41
+ "dev": "tsx watch src/index.ts",
42
+ "start": "node dist/index.js",
43
+ "prepublishOnly": "npm run build"
44
+ },
39
45
  "engines": {
40
46
  "node": ">=18"
41
47
  },
@@ -44,19 +50,14 @@
44
50
  "zod": "^3.25.0"
45
51
  },
46
52
  "devDependencies": {
53
+ "@fidacy/firewall": "workspace:*",
47
54
  "@types/node": "^22.10.0",
48
55
  "tsx": "^4.19.2",
49
- "typescript": "^5.7.2",
50
- "@fidacy/firewall": "0.1.1"
56
+ "typescript": "^5.7.2"
51
57
  },
52
58
  "mcpName": "com.fidacy/mcp",
53
59
  "repository": {
54
60
  "type": "git",
55
61
  "url": "git+https://github.com/lucaslubi/fidacy-mcp.git"
56
- },
57
- "scripts": {
58
- "build": "node scripts/bundle.mjs",
59
- "dev": "tsx watch src/index.ts",
60
- "start": "node dist/index.js"
61
62
  }
62
- }
63
+ }