@fidacy/mcp 0.1.14 → 0.1.15
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/dist/core.js +1 -1
- package/dist/index.js +173 -3
- package/dist/lib.js +2 -2
- package/package.json +9 -8
package/dist/core.js
CHANGED
|
@@ -423,7 +423,7 @@ function resolveMandateRules(cfg) {
|
|
|
423
423
|
}
|
|
424
424
|
|
|
425
425
|
// src/telemetry.ts
|
|
426
|
-
var CLIENT_VERSION = true ? "0.1.
|
|
426
|
+
var CLIENT_VERSION = true ? "0.1.15" : "dev";
|
|
427
427
|
var currentShell = "mcp";
|
|
428
428
|
function telemetryEnabled() {
|
|
429
429
|
const v = (process.env.FIDACY_DISABLE_TELEMETRY ?? "").trim().toLowerCase();
|
package/dist/index.js
CHANGED
|
@@ -452,7 +452,7 @@ function resolveMandateRules(cfg) {
|
|
|
452
452
|
}
|
|
453
453
|
|
|
454
454
|
// src/telemetry.ts
|
|
455
|
-
var CLIENT_VERSION = true ? "0.1.
|
|
455
|
+
var CLIENT_VERSION = true ? "0.1.15" : "dev";
|
|
456
456
|
var currentShell = "mcp";
|
|
457
457
|
function telemetryEnabled() {
|
|
458
458
|
const v = (process.env.FIDACY_DISABLE_TELEMETRY ?? "").trim().toLowerCase();
|
|
@@ -680,8 +680,72 @@ async function postOnce(fetchImpl, url, headers, payload, timeoutMs) {
|
|
|
680
680
|
return parsed;
|
|
681
681
|
}
|
|
682
682
|
|
|
683
|
+
// src/artifacts.ts
|
|
684
|
+
import { createHash } from "node:crypto";
|
|
685
|
+
import { createReadStream } from "node:fs";
|
|
686
|
+
import { pipeline } from "node:stream/promises";
|
|
687
|
+
var ARTIFACT_KINDS = [
|
|
688
|
+
"contract",
|
|
689
|
+
"invoice",
|
|
690
|
+
"prescription",
|
|
691
|
+
"claim",
|
|
692
|
+
"document",
|
|
693
|
+
"image",
|
|
694
|
+
"audio",
|
|
695
|
+
"video",
|
|
696
|
+
"custom"
|
|
697
|
+
];
|
|
698
|
+
async function hashFile(path) {
|
|
699
|
+
const hash = createHash("sha256");
|
|
700
|
+
await pipeline(createReadStream(path), async function* (source) {
|
|
701
|
+
for await (const chunk of source) {
|
|
702
|
+
hash.update(chunk);
|
|
703
|
+
yield;
|
|
704
|
+
}
|
|
705
|
+
});
|
|
706
|
+
return hash.digest("hex");
|
|
707
|
+
}
|
|
708
|
+
async function engineFetch(method, pathAndQuery, cfg, body) {
|
|
709
|
+
const base = requireSafeBaseUrl(cfg.engineUrl);
|
|
710
|
+
const controller = new AbortController();
|
|
711
|
+
const timer = setTimeout(() => controller.abort(), 1e4);
|
|
712
|
+
let res;
|
|
713
|
+
try {
|
|
714
|
+
res = await fetch(`${base}${pathAndQuery}`, {
|
|
715
|
+
method,
|
|
716
|
+
headers: {
|
|
717
|
+
authorization: `Bearer ${cfg.apiKey}`,
|
|
718
|
+
...body !== void 0 ? { "content-type": "application/json" } : {}
|
|
719
|
+
},
|
|
720
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0,
|
|
721
|
+
signal: controller.signal
|
|
722
|
+
});
|
|
723
|
+
} catch {
|
|
724
|
+
throw new AssessError({ type: "network_error", status: 0 });
|
|
725
|
+
} finally {
|
|
726
|
+
clearTimeout(timer);
|
|
727
|
+
}
|
|
728
|
+
let json;
|
|
729
|
+
try {
|
|
730
|
+
json = await res.json();
|
|
731
|
+
} catch {
|
|
732
|
+
throw new AssessError({ type: "bad_response", status: res.status });
|
|
733
|
+
}
|
|
734
|
+
if (!res.ok) {
|
|
735
|
+
const type = typeof json?.error === "string" ? String(json.error) : "engine_error";
|
|
736
|
+
throw new AssessError({ type, status: res.status });
|
|
737
|
+
}
|
|
738
|
+
return json;
|
|
739
|
+
}
|
|
740
|
+
async function anchorArtifact(params, cfg) {
|
|
741
|
+
return await engineFetch("POST", "/v1/artifacts", cfg, params);
|
|
742
|
+
}
|
|
743
|
+
async function findArtifacts(sha2562, cfg) {
|
|
744
|
+
return await engineFetch("GET", `/v1/artifacts?sha256=${sha2562}`, cfg);
|
|
745
|
+
}
|
|
746
|
+
|
|
683
747
|
// src/provision.ts
|
|
684
|
-
var CLIENT_VERSION2 = true ? "0.1.
|
|
748
|
+
var CLIENT_VERSION2 = true ? "0.1.15" : "dev";
|
|
685
749
|
function provisionEnabled() {
|
|
686
750
|
const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
|
|
687
751
|
return !(v === "1" || v === "true" || v === "yes");
|
|
@@ -781,7 +845,7 @@ function weekOneBootNudge(upgradeToolName) {
|
|
|
781
845
|
var state = ensureState();
|
|
782
846
|
var core = makeCore();
|
|
783
847
|
var subject = process.env.FIDACY_SUBJECT ?? "agent:demo";
|
|
784
|
-
var SERVER_VERSION = true ? "0.1.
|
|
848
|
+
var SERVER_VERSION = true ? "0.1.15" : "dev";
|
|
785
849
|
var server = new McpServer({ name: "fidacy", version: SERVER_VERSION });
|
|
786
850
|
server.registerTool(
|
|
787
851
|
"request_payment",
|
|
@@ -901,6 +965,112 @@ server.registerTool(
|
|
|
901
965
|
}
|
|
902
966
|
}
|
|
903
967
|
);
|
|
968
|
+
server.registerTool(
|
|
969
|
+
"anchor_artifact",
|
|
970
|
+
{
|
|
971
|
+
title: "Anchor Artifact (Bitcoin-anchored integrity proof)",
|
|
972
|
+
description: "Prove an artifact existed exactly as-is at this moment, and make any later tampering detectable. Give a file `path` (hashed locally with SHA-256 \u2014 the file itself is NEVER uploaded) or a precomputed `sha256`. The hash is registered on Fidacy's tamper-evident audit chain, which is checkpointed to the Bitcoin blockchain, and you get a signed receipt (JWS) verifiable offline against the engine JWKS. Use it for contracts, invoices, medical prescriptions, insurance claims, images, audio, video. `kind` defaults to document; optional `label` is a short reference (an invoice number, a case id \u2014 no PII).",
|
|
973
|
+
inputSchema: {
|
|
974
|
+
path: z.string().optional(),
|
|
975
|
+
sha256: z.string().regex(/^[0-9a-f]{64}$/).optional(),
|
|
976
|
+
kind: z.enum(ARTIFACT_KINDS).optional(),
|
|
977
|
+
label: z.string().max(120).optional(),
|
|
978
|
+
subject: z.string().max(120).optional()
|
|
979
|
+
},
|
|
980
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }
|
|
981
|
+
},
|
|
982
|
+
async ({ path, sha256: sha2562, kind, label, subject: subj }) => {
|
|
983
|
+
const engineUrl = process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
|
|
984
|
+
const apiKey = (process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
|
|
985
|
+
if (!apiKey) {
|
|
986
|
+
return {
|
|
987
|
+
content: [{ type: "text", text: "anchor_artifact needs an engine key \u2014 free in about a minute: call the upgrade tool, or set FIDACY_ENGINE_API_KEY if you already have one." }],
|
|
988
|
+
isError: true
|
|
989
|
+
};
|
|
990
|
+
}
|
|
991
|
+
if (!path && !sha2562) {
|
|
992
|
+
return { content: [{ type: "text", text: "Give a file `path` (hashed locally) or a precomputed `sha256`." }], isError: true };
|
|
993
|
+
}
|
|
994
|
+
let hash = sha2562 ?? "";
|
|
995
|
+
if (!hash && path) {
|
|
996
|
+
try {
|
|
997
|
+
hash = await hashFile(path);
|
|
998
|
+
} catch {
|
|
999
|
+
return { content: [{ type: "text", text: "Could not read that file locally (check the path and permissions). Nothing was sent anywhere." }], isError: true };
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
try {
|
|
1003
|
+
const r = await anchorArtifact(
|
|
1004
|
+
{ sha256: hash, kind: kind ?? "document", ...label ? { label } : {}, ...subj ? { subject: subj } : {} },
|
|
1005
|
+
{ engineUrl, apiKey }
|
|
1006
|
+
);
|
|
1007
|
+
return {
|
|
1008
|
+
content: [{
|
|
1009
|
+
type: "text",
|
|
1010
|
+
text: `ANCHORED ${r.kind} \xB7 sha256 ${hash.slice(0, 16)}\u2026 \xB7 audit seq ${r.audit.seq} \xB7 Bitcoin checkpoint: ${r.anchor.status}. The file never left this machine; only its hash was registered. Keep the receipt (structured output) \u2014 it verifies offline and proves this artifact existed exactly as-is, signed, at ${r.ts}.`
|
|
1011
|
+
}],
|
|
1012
|
+
structuredContent: r
|
|
1013
|
+
};
|
|
1014
|
+
} catch (e) {
|
|
1015
|
+
if (e instanceof AssessError) {
|
|
1016
|
+
return { content: [{ type: "text", text: `ANCHOR ${e.status}: ${e.type}` }], isError: true };
|
|
1017
|
+
}
|
|
1018
|
+
return { content: [{ type: "text", text: "ANCHOR failed: unexpected error" }], isError: true };
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
);
|
|
1022
|
+
server.registerTool(
|
|
1023
|
+
"check_artifact",
|
|
1024
|
+
{
|
|
1025
|
+
title: "Check Artifact (was this hash anchored?)",
|
|
1026
|
+
description: "Check whether an artifact was anchored by this account and the state of its Bitcoin checkpoint. Give a file `path` (hashed locally, never uploaded) or a `sha256`. If the current hash of a file does NOT match any anchored record that you expected to match, the file changed since anchoring \u2014 that is exactly the tampering signal.",
|
|
1027
|
+
inputSchema: {
|
|
1028
|
+
path: z.string().optional(),
|
|
1029
|
+
sha256: z.string().regex(/^[0-9a-f]{64}$/).optional()
|
|
1030
|
+
},
|
|
1031
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }
|
|
1032
|
+
},
|
|
1033
|
+
async ({ path, sha256: sha2562 }) => {
|
|
1034
|
+
const engineUrl = process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
|
|
1035
|
+
const apiKey = (process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
|
|
1036
|
+
if (!apiKey) {
|
|
1037
|
+
return {
|
|
1038
|
+
content: [{ type: "text", text: "check_artifact needs an engine key \u2014 free in about a minute: call the upgrade tool, or set FIDACY_ENGINE_API_KEY if you already have one." }],
|
|
1039
|
+
isError: true
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
if (!path && !sha2562) {
|
|
1043
|
+
return { content: [{ type: "text", text: "Give a file `path` (hashed locally) or a `sha256`." }], isError: true };
|
|
1044
|
+
}
|
|
1045
|
+
let hash = sha2562 ?? "";
|
|
1046
|
+
if (!hash && path) {
|
|
1047
|
+
try {
|
|
1048
|
+
hash = await hashFile(path);
|
|
1049
|
+
} catch {
|
|
1050
|
+
return { content: [{ type: "text", text: "Could not read that file locally (check the path and permissions). Nothing was sent anywhere." }], isError: true };
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
try {
|
|
1054
|
+
const r = await findArtifacts(hash, { engineUrl, apiKey });
|
|
1055
|
+
if (!r.artifacts.length) {
|
|
1056
|
+
return {
|
|
1057
|
+
content: [{ type: "text", text: `NOT FOUND \xB7 sha256 ${hash.slice(0, 16)}\u2026 has no anchored record in this account. If you expected a match, the file has changed since it was anchored.` }],
|
|
1058
|
+
structuredContent: { sha256: hash, artifacts: [] }
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
const first = r.artifacts[0];
|
|
1062
|
+
return {
|
|
1063
|
+
content: [{ type: "text", text: `FOUND ${r.artifacts.length} record(s) \xB7 newest: ${String(first.kind)} anchored ${String(first.createdAt)} (audit seq ${String(first.auditSeq)}). The current content matches the anchored hash byte for byte.` }],
|
|
1064
|
+
structuredContent: { sha256: hash, ...r }
|
|
1065
|
+
};
|
|
1066
|
+
} catch (e) {
|
|
1067
|
+
if (e instanceof AssessError) {
|
|
1068
|
+
return { content: [{ type: "text", text: `CHECK ${e.status}: ${e.type}` }], isError: true };
|
|
1069
|
+
}
|
|
1070
|
+
return { content: [{ type: "text", text: "CHECK failed: unexpected error" }], isError: true };
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
);
|
|
904
1074
|
server.registerTool(
|
|
905
1075
|
"upgrade",
|
|
906
1076
|
{
|
package/dist/lib.js
CHANGED
|
@@ -571,7 +571,7 @@ function resolveMandateRules(cfg) {
|
|
|
571
571
|
}
|
|
572
572
|
|
|
573
573
|
// src/telemetry.ts
|
|
574
|
-
var CLIENT_VERSION = true ? "0.1.
|
|
574
|
+
var CLIENT_VERSION = true ? "0.1.15" : "dev";
|
|
575
575
|
var currentShell = "mcp";
|
|
576
576
|
function setTelemetryShell(shell) {
|
|
577
577
|
currentShell = shell;
|
|
@@ -699,7 +699,7 @@ function requestUpgrade() {
|
|
|
699
699
|
}
|
|
700
700
|
|
|
701
701
|
// src/provision.ts
|
|
702
|
-
var CLIENT_VERSION2 = true ? "0.1.
|
|
702
|
+
var CLIENT_VERSION2 = true ? "0.1.15" : "dev";
|
|
703
703
|
function provisionEnabled() {
|
|
704
704
|
const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
|
|
705
705
|
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.
|
|
3
|
+
"version": "0.1.15",
|
|
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.0"
|
|
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
|
}
|