@fidacy/mcp 0.1.8 → 0.1.10
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 +11 -0
- package/dist/core.js +4 -3
- package/dist/index.js +12 -6
- package/dist/lib.js +120 -4
- package/package.json +14 -10
package/CHANGELOG.md
CHANGED
|
@@ -74,3 +74,14 @@ semantic versioning.
|
|
|
74
74
|
- One install delivers both the signed verdict (assess_action) and the
|
|
75
75
|
non-custodial payment firewall (request_payment), with the same signed,
|
|
76
76
|
publicly verifiable proof as the SDK.
|
|
77
|
+
|
|
78
|
+
## 0.1.10 — 2026-07-02
|
|
79
|
+
|
|
80
|
+
- Telemetry: `shell` channel attribution (mcp | openclaw-plugin | crabtrap | sdk)
|
|
81
|
+
and `client_version` injected from package.json at build time (0.1.5 had shipped
|
|
82
|
+
stale through 0.1.9 in telemetry AND provision).
|
|
83
|
+
- DENY responses now point to the `upgrade` tool (free account, signed anchored
|
|
84
|
+
proof of every block).
|
|
85
|
+
- Onboarding: first-run `~/.fidacy/config.json` now writes the mandate template
|
|
86
|
+
explicitly (payees/categories/currency/caps) — adding a trusted payee is a
|
|
87
|
+
visible one-line edit; friendlier first-boot message (no more "dev mode" scare).
|
package/dist/core.js
CHANGED
|
@@ -213,7 +213,7 @@ var DevFidacyCore = class {
|
|
|
213
213
|
this.priv = kp.privateKey;
|
|
214
214
|
this.pubPem = publicKeyPem(kp.publicKey);
|
|
215
215
|
if (kp.ephemeral)
|
|
216
|
-
console.error("[fidacy]
|
|
216
|
+
console.error("[fidacy] local firewall active, deny-by-default. Grants are signed with a per-session key (fine for local use); set FIDACY_SIGNING_KEY_B64 for a stable key, or FIDACY_MODE=http to use the hosted core.");
|
|
217
217
|
this.mandate = opts.mandate;
|
|
218
218
|
this.store = new FileAuditStore(opts.auditLogPath ?? "./fidacy-audit.log");
|
|
219
219
|
this.onDecision = opts.onDecision;
|
|
@@ -357,7 +357,8 @@ function resolveMandateRules(cfg) {
|
|
|
357
357
|
}
|
|
358
358
|
|
|
359
359
|
// src/telemetry.ts
|
|
360
|
-
var CLIENT_VERSION = "0.1.
|
|
360
|
+
var CLIENT_VERSION = true ? "0.1.10" : "dev";
|
|
361
|
+
var currentShell = "mcp";
|
|
361
362
|
function telemetryEnabled() {
|
|
362
363
|
const v = (process.env.FIDACY_DISABLE_TELEMETRY ?? "").trim().toLowerCase();
|
|
363
364
|
return !(v === "1" || v === "true" || v === "yes");
|
|
@@ -383,7 +384,7 @@ function resultOf(status, violatedRule) {
|
|
|
383
384
|
}
|
|
384
385
|
function record(type, result) {
|
|
385
386
|
if (!telemetryEnabled()) return;
|
|
386
|
-
buffer.push({ type, ts: (/* @__PURE__ */ new Date()).toISOString(), client_version: CLIENT_VERSION, ...result ? { result } : {} });
|
|
387
|
+
buffer.push({ type, ts: (/* @__PURE__ */ new Date()).toISOString(), client_version: CLIENT_VERSION, shell: currentShell, ...result ? { result } : {} });
|
|
387
388
|
if (!flushTimer) {
|
|
388
389
|
flushTimer = setTimeout(() => {
|
|
389
390
|
void flush();
|
package/dist/index.js
CHANGED
|
@@ -220,7 +220,7 @@ var DevFidacyCore = class {
|
|
|
220
220
|
this.priv = kp.privateKey;
|
|
221
221
|
this.pubPem = publicKeyPem(kp.publicKey);
|
|
222
222
|
if (kp.ephemeral)
|
|
223
|
-
console.error("[fidacy]
|
|
223
|
+
console.error("[fidacy] local firewall active, deny-by-default. Grants are signed with a per-session key (fine for local use); set FIDACY_SIGNING_KEY_B64 for a stable key, or FIDACY_MODE=http to use the hosted core.");
|
|
224
224
|
this.mandate = opts.mandate;
|
|
225
225
|
this.store = new FileAuditStore(opts.auditLogPath ?? "./fidacy-audit.log");
|
|
226
226
|
this.onDecision = opts.onDecision;
|
|
@@ -359,7 +359,12 @@ function writeConfig(cfg) {
|
|
|
359
359
|
function ensureState() {
|
|
360
360
|
const existing = readConfig();
|
|
361
361
|
if (existing) return { config: existing, firstRun: false };
|
|
362
|
-
const config = {
|
|
362
|
+
const config = {
|
|
363
|
+
anon_id: randomUUID2(),
|
|
364
|
+
tier: "free",
|
|
365
|
+
api_key: null,
|
|
366
|
+
mandate: { payees: [], categories: ["*"], currency: "USD", perTxMax: 2500, maxTotal: 1e4 }
|
|
367
|
+
};
|
|
363
368
|
try {
|
|
364
369
|
writeConfig(config);
|
|
365
370
|
} catch {
|
|
@@ -380,7 +385,8 @@ function resolveMandateRules(cfg) {
|
|
|
380
385
|
}
|
|
381
386
|
|
|
382
387
|
// src/telemetry.ts
|
|
383
|
-
var CLIENT_VERSION = "0.1.
|
|
388
|
+
var CLIENT_VERSION = true ? "0.1.10" : "dev";
|
|
389
|
+
var currentShell = "mcp";
|
|
384
390
|
function telemetryEnabled() {
|
|
385
391
|
const v = (process.env.FIDACY_DISABLE_TELEMETRY ?? "").trim().toLowerCase();
|
|
386
392
|
return !(v === "1" || v === "true" || v === "yes");
|
|
@@ -406,7 +412,7 @@ function resultOf(status, violatedRule) {
|
|
|
406
412
|
}
|
|
407
413
|
function record(type, result) {
|
|
408
414
|
if (!telemetryEnabled()) return;
|
|
409
|
-
buffer.push({ type, ts: (/* @__PURE__ */ new Date()).toISOString(), client_version: CLIENT_VERSION, ...result ? { result } : {} });
|
|
415
|
+
buffer.push({ type, ts: (/* @__PURE__ */ new Date()).toISOString(), client_version: CLIENT_VERSION, shell: currentShell, ...result ? { result } : {} });
|
|
410
416
|
if (!flushTimer) {
|
|
411
417
|
flushTimer = setTimeout(() => {
|
|
412
418
|
void flush();
|
|
@@ -603,7 +609,7 @@ async function postOnce(fetchImpl, url, headers, payload, timeoutMs) {
|
|
|
603
609
|
}
|
|
604
610
|
|
|
605
611
|
// src/provision.ts
|
|
606
|
-
var CLIENT_VERSION2 = "0.1.
|
|
612
|
+
var CLIENT_VERSION2 = true ? "0.1.10" : "dev";
|
|
607
613
|
function provisionEnabled() {
|
|
608
614
|
const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
|
|
609
615
|
return !(v === "1" || v === "true" || v === "yes");
|
|
@@ -684,7 +690,7 @@ server.registerTool(
|
|
|
684
690
|
const d = await core.decide(req, subject);
|
|
685
691
|
const out = { status: d.status, decisionId: d.decisionId, grant: d.grant, violatedRule: d.violatedRule };
|
|
686
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:
|
|
687
|
-
${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.`;
|
|
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.`;
|
|
688
694
|
return { content: [{ type: "text", text: human }], structuredContent: out };
|
|
689
695
|
}
|
|
690
696
|
);
|
package/dist/lib.js
CHANGED
|
@@ -146,13 +146,34 @@ var ReferenceRail = class {
|
|
|
146
146
|
return { railRef };
|
|
147
147
|
}
|
|
148
148
|
};
|
|
149
|
+
function httpRedeem(coreUrl, apiKey) {
|
|
150
|
+
const base = String(coreUrl ?? "").replace(/\/+$/, "");
|
|
151
|
+
const u = new URL(base);
|
|
152
|
+
if (u.protocol !== "https:" && !(u.protocol === "http:" && (u.hostname === "localhost" || u.hostname === "127.0.0.1"))) {
|
|
153
|
+
throw new Error("core URL must be https:// (the API key is sent to it)");
|
|
154
|
+
}
|
|
155
|
+
return async (grant) => {
|
|
156
|
+
const res = await fetch(`${base}/v1/grant/redeem`, {
|
|
157
|
+
method: "POST",
|
|
158
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${apiKey}` },
|
|
159
|
+
body: JSON.stringify({ grant })
|
|
160
|
+
});
|
|
161
|
+
if (res.status === 200)
|
|
162
|
+
return true;
|
|
163
|
+
if (res.status === 409)
|
|
164
|
+
return false;
|
|
165
|
+
throw new Error(`redeem -> ${res.status}`);
|
|
166
|
+
};
|
|
167
|
+
}
|
|
149
168
|
var GrantEnforcingExecutor = class {
|
|
150
169
|
rail;
|
|
151
170
|
used = /* @__PURE__ */ new Set();
|
|
152
171
|
pub;
|
|
153
|
-
|
|
172
|
+
redeem;
|
|
173
|
+
constructor(publicKeyPem2, rail, opts) {
|
|
154
174
|
this.rail = rail;
|
|
155
175
|
this.pub = createPublicKey(publicKeyPem2);
|
|
176
|
+
this.redeem = opts?.redeem;
|
|
156
177
|
}
|
|
157
178
|
async execute(req, grant) {
|
|
158
179
|
if (!grant)
|
|
@@ -183,6 +204,16 @@ var GrantEnforcingExecutor = class {
|
|
|
183
204
|
if (req.invoiceRef != null && req.invoiceRef !== (p.invoiceRef ?? null)) {
|
|
184
205
|
return { status: "REFUSED", reason: "invoice_mismatch" };
|
|
185
206
|
}
|
|
207
|
+
if (this.redeem) {
|
|
208
|
+
let first;
|
|
209
|
+
try {
|
|
210
|
+
first = await this.redeem(grant);
|
|
211
|
+
} catch {
|
|
212
|
+
return { status: "REFUSED", reason: "redeem_unavailable" };
|
|
213
|
+
}
|
|
214
|
+
if (!first)
|
|
215
|
+
return { status: "REFUSED", reason: "grant_replayed" };
|
|
216
|
+
}
|
|
186
217
|
this.used.add(p.decisionId);
|
|
187
218
|
const { railRef } = await this.rail.execute(req);
|
|
188
219
|
return { status: "EXECUTED", railRef, decisionId: p.decisionId };
|
|
@@ -308,7 +339,7 @@ var DevFidacyCore = class {
|
|
|
308
339
|
this.priv = kp.privateKey;
|
|
309
340
|
this.pubPem = publicKeyPem(kp.publicKey);
|
|
310
341
|
if (kp.ephemeral)
|
|
311
|
-
console.error("[fidacy]
|
|
342
|
+
console.error("[fidacy] local firewall active, deny-by-default. Grants are signed with a per-session key (fine for local use); set FIDACY_SIGNING_KEY_B64 for a stable key, or FIDACY_MODE=http to use the hosted core.");
|
|
312
343
|
this.mandate = opts.mandate;
|
|
313
344
|
this.store = new FileAuditStore(opts.auditLogPath ?? "./fidacy-audit.log");
|
|
314
345
|
this.onDecision = opts.onDecision;
|
|
@@ -399,6 +430,7 @@ var HttpFidacyCore = class {
|
|
|
399
430
|
};
|
|
400
431
|
|
|
401
432
|
// src/config.ts
|
|
433
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
402
434
|
import { homedir } from "node:os";
|
|
403
435
|
import { join } from "node:path";
|
|
404
436
|
import {
|
|
@@ -438,6 +470,26 @@ function readConfig() {
|
|
|
438
470
|
return null;
|
|
439
471
|
}
|
|
440
472
|
}
|
|
473
|
+
function writeConfig(cfg) {
|
|
474
|
+
const dir = configDir();
|
|
475
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
476
|
+
writeFileSync(configPath(), JSON.stringify(cfg, null, 2), { mode: 384 });
|
|
477
|
+
}
|
|
478
|
+
function ensureState() {
|
|
479
|
+
const existing = readConfig();
|
|
480
|
+
if (existing) return { config: existing, firstRun: false };
|
|
481
|
+
const config = {
|
|
482
|
+
anon_id: randomUUID3(),
|
|
483
|
+
tier: "free",
|
|
484
|
+
api_key: null,
|
|
485
|
+
mandate: { payees: [], categories: ["*"], currency: "USD", perTxMax: 2500, maxTotal: 1e4 }
|
|
486
|
+
};
|
|
487
|
+
try {
|
|
488
|
+
writeConfig(config);
|
|
489
|
+
} catch {
|
|
490
|
+
}
|
|
491
|
+
return { config, firstRun: true };
|
|
492
|
+
}
|
|
441
493
|
function resolveMandateRules(cfg) {
|
|
442
494
|
const m = cfg?.mandate ?? {};
|
|
443
495
|
const envList = (v) => v === void 0 ? void 0 : v.split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -452,7 +504,11 @@ function resolveMandateRules(cfg) {
|
|
|
452
504
|
}
|
|
453
505
|
|
|
454
506
|
// src/telemetry.ts
|
|
455
|
-
var CLIENT_VERSION = "0.1.
|
|
507
|
+
var CLIENT_VERSION = true ? "0.1.10" : "dev";
|
|
508
|
+
var currentShell = "mcp";
|
|
509
|
+
function setTelemetryShell(shell) {
|
|
510
|
+
currentShell = shell;
|
|
511
|
+
}
|
|
456
512
|
function telemetryEnabled() {
|
|
457
513
|
const v = (process.env.FIDACY_DISABLE_TELEMETRY ?? "").trim().toLowerCase();
|
|
458
514
|
return !(v === "1" || v === "true" || v === "yes");
|
|
@@ -478,7 +534,7 @@ function resultOf(status, violatedRule) {
|
|
|
478
534
|
}
|
|
479
535
|
function record(type, result) {
|
|
480
536
|
if (!telemetryEnabled()) return;
|
|
481
|
-
buffer.push({ type, ts: (/* @__PURE__ */ new Date()).toISOString(), client_version: CLIENT_VERSION, ...result ? { result } : {} });
|
|
537
|
+
buffer.push({ type, ts: (/* @__PURE__ */ new Date()).toISOString(), client_version: CLIENT_VERSION, shell: currentShell, ...result ? { result } : {} });
|
|
482
538
|
if (!flushTimer) {
|
|
483
539
|
flushTimer = setTimeout(() => {
|
|
484
540
|
void flush();
|
|
@@ -486,7 +542,10 @@ function record(type, result) {
|
|
|
486
542
|
if (typeof flushTimer.unref === "function") flushTimer.unref();
|
|
487
543
|
}
|
|
488
544
|
}
|
|
545
|
+
var recordInstall = () => record("install");
|
|
546
|
+
var recordAgentActive = () => record("agent_active");
|
|
489
547
|
var recordDecision = (status, violatedRule) => record("decision", resultOf(status, violatedRule));
|
|
548
|
+
var recordUpgradeIntent = () => record("upgrade_intent");
|
|
490
549
|
async function flush() {
|
|
491
550
|
if (flushTimer) {
|
|
492
551
|
clearTimeout(flushTimer);
|
|
@@ -554,22 +613,79 @@ function makeCore() {
|
|
|
554
613
|
onDecision: (d) => recordDecision(d.status, d.violatedRule)
|
|
555
614
|
});
|
|
556
615
|
}
|
|
616
|
+
|
|
617
|
+
// src/upgrade.ts
|
|
618
|
+
function upgradeUrl() {
|
|
619
|
+
const base = (process.env.FIDACY_APP_URL ?? "https://app.fidacy.com").replace(/\/$/, "");
|
|
620
|
+
const anon = readConfig()?.anon_id ?? "";
|
|
621
|
+
return `${base}/upgrade?anon=${encodeURIComponent(anon)}`;
|
|
622
|
+
}
|
|
623
|
+
function requestUpgrade() {
|
|
624
|
+
recordUpgradeIntent();
|
|
625
|
+
const anonId2 = readConfig()?.anon_id ?? "";
|
|
626
|
+
return { url: upgradeUrl(), anonId: anonId2 };
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// src/provision.ts
|
|
630
|
+
var CLIENT_VERSION2 = true ? "0.1.10" : "dev";
|
|
631
|
+
function provisionEnabled() {
|
|
632
|
+
const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
|
|
633
|
+
return !(v === "1" || v === "true" || v === "yes");
|
|
634
|
+
}
|
|
635
|
+
function endpoint2() {
|
|
636
|
+
const base = (process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com").replace(/\/$/, "");
|
|
637
|
+
return `${base}/v1/provision`;
|
|
638
|
+
}
|
|
639
|
+
async function autoProvision() {
|
|
640
|
+
if (!provisionEnabled()) return "skipped";
|
|
641
|
+
const cfg = readConfig();
|
|
642
|
+
if (!cfg) return "skipped";
|
|
643
|
+
if (cfg.api_key) return "skipped";
|
|
644
|
+
try {
|
|
645
|
+
const res = await fetch(endpoint2(), {
|
|
646
|
+
method: "POST",
|
|
647
|
+
headers: { "content-type": "application/json" },
|
|
648
|
+
body: JSON.stringify({ anon_id: cfg.anon_id, client_version: CLIENT_VERSION2 })
|
|
649
|
+
});
|
|
650
|
+
if (res.status === 429) return "rate_limited";
|
|
651
|
+
if (!res.ok) return "failed";
|
|
652
|
+
const data = await res.json();
|
|
653
|
+
if (typeof data.api_key !== "string" || !data.api_key) return "failed";
|
|
654
|
+
writeConfig({
|
|
655
|
+
...cfg,
|
|
656
|
+
api_key: data.api_key,
|
|
657
|
+
tier: data.tier === "paid" ? "paid" : "free"
|
|
658
|
+
});
|
|
659
|
+
return "provisioned";
|
|
660
|
+
} catch {
|
|
661
|
+
return "failed";
|
|
662
|
+
}
|
|
663
|
+
}
|
|
557
664
|
export {
|
|
558
665
|
DevFidacyCore,
|
|
559
666
|
FileAuditStore,
|
|
560
667
|
GrantEnforcingExecutor,
|
|
561
668
|
HttpFidacyCore,
|
|
562
669
|
ReferenceRail,
|
|
670
|
+
autoProvision,
|
|
563
671
|
canonInvoice,
|
|
672
|
+
ensureState,
|
|
564
673
|
evaluate,
|
|
674
|
+
httpRedeem,
|
|
565
675
|
loadOrGenerateKeyPair,
|
|
566
676
|
lookalikePayee,
|
|
567
677
|
makeCore,
|
|
568
678
|
publicKeyPem,
|
|
679
|
+
readConfig,
|
|
680
|
+
recordAgentActive,
|
|
681
|
+
recordInstall,
|
|
682
|
+
requestUpgrade,
|
|
569
683
|
requireHttpsBase,
|
|
684
|
+
setTelemetryShell,
|
|
570
685
|
sha256,
|
|
571
686
|
sign,
|
|
572
687
|
stableStringify,
|
|
688
|
+
upgradeUrl,
|
|
573
689
|
validateRequest,
|
|
574
690
|
verify,
|
|
575
691
|
verifyGrant
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fidacy/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
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,12 +36,6 @@
|
|
|
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
|
-
},
|
|
45
39
|
"engines": {
|
|
46
40
|
"node": ">=18"
|
|
47
41
|
},
|
|
@@ -50,9 +44,19 @@
|
|
|
50
44
|
"zod": "^3.25.0"
|
|
51
45
|
},
|
|
52
46
|
"devDependencies": {
|
|
53
|
-
"@fidacy/firewall": "workspace:*",
|
|
54
47
|
"@types/node": "^22.10.0",
|
|
55
48
|
"tsx": "^4.19.2",
|
|
56
|
-
"typescript": "^5.7.2"
|
|
49
|
+
"typescript": "^5.7.2",
|
|
50
|
+
"@fidacy/firewall": "0.1.0"
|
|
51
|
+
},
|
|
52
|
+
"mcpName": "com.fidacy/mcp",
|
|
53
|
+
"repository": {
|
|
54
|
+
"type": "git",
|
|
55
|
+
"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"
|
|
57
61
|
}
|
|
58
|
-
}
|
|
62
|
+
}
|