@fidacy/mcp 0.1.14 → 0.1.16

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
@@ -3,6 +3,18 @@
3
3
  All notable changes to `@fidacy/mcp` are documented here. This project follows
4
4
  semantic versioning.
5
5
 
6
+ ## 0.1.16 — 2026-07-03
7
+
8
+ - `anchor_artifact`/`check_artifact`: new kind `conversation` — anchor chatbot
9
+ session digests produced by `@fidacy/session` (conversation receipts).
10
+
11
+ ## 0.1.15 — 2026-07-03
12
+
13
+ - New tools: `anchor_artifact` and `check_artifact` — Bitcoin-anchored integrity
14
+ proof for any artifact (contracts, invoices, prescriptions, claims, images,
15
+ audio, video). The file is hashed locally and never uploaded; only the SHA-256
16
+ joins the tamper-evident audit chain. Signed JWS receipt verifies offline.
17
+
6
18
  ## 0.1.4
7
19
 
8
20
  ### Fixed
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.14" : "dev";
426
+ var CLIENT_VERSION = true ? "0.1.16" : "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.14" : "dev";
455
+ var CLIENT_VERSION = true ? "0.1.16" : "dev";
456
456
  var currentShell = "mcp";
457
457
  function telemetryEnabled() {
458
458
  const v = (process.env.FIDACY_DISABLE_TELEMETRY ?? "").trim().toLowerCase();
@@ -680,8 +680,73 @@ 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
+ "conversation",
697
+ "custom"
698
+ ];
699
+ async function hashFile(path) {
700
+ const hash = createHash("sha256");
701
+ await pipeline(createReadStream(path), async function* (source) {
702
+ for await (const chunk of source) {
703
+ hash.update(chunk);
704
+ yield;
705
+ }
706
+ });
707
+ return hash.digest("hex");
708
+ }
709
+ async function engineFetch(method, pathAndQuery, cfg, body) {
710
+ const base = requireSafeBaseUrl(cfg.engineUrl);
711
+ const controller = new AbortController();
712
+ const timer = setTimeout(() => controller.abort(), 1e4);
713
+ let res;
714
+ try {
715
+ res = await fetch(`${base}${pathAndQuery}`, {
716
+ method,
717
+ headers: {
718
+ authorization: `Bearer ${cfg.apiKey}`,
719
+ ...body !== void 0 ? { "content-type": "application/json" } : {}
720
+ },
721
+ body: body !== void 0 ? JSON.stringify(body) : void 0,
722
+ signal: controller.signal
723
+ });
724
+ } catch {
725
+ throw new AssessError({ type: "network_error", status: 0 });
726
+ } finally {
727
+ clearTimeout(timer);
728
+ }
729
+ let json;
730
+ try {
731
+ json = await res.json();
732
+ } catch {
733
+ throw new AssessError({ type: "bad_response", status: res.status });
734
+ }
735
+ if (!res.ok) {
736
+ const type = typeof json?.error === "string" ? String(json.error) : "engine_error";
737
+ throw new AssessError({ type, status: res.status });
738
+ }
739
+ return json;
740
+ }
741
+ async function anchorArtifact(params, cfg) {
742
+ return await engineFetch("POST", "/v1/artifacts", cfg, params);
743
+ }
744
+ async function findArtifacts(sha2562, cfg) {
745
+ return await engineFetch("GET", `/v1/artifacts?sha256=${sha2562}`, cfg);
746
+ }
747
+
683
748
  // src/provision.ts
684
- var CLIENT_VERSION2 = true ? "0.1.14" : "dev";
749
+ var CLIENT_VERSION2 = true ? "0.1.16" : "dev";
685
750
  function provisionEnabled() {
686
751
  const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
687
752
  return !(v === "1" || v === "true" || v === "yes");
@@ -781,7 +846,7 @@ function weekOneBootNudge(upgradeToolName) {
781
846
  var state = ensureState();
782
847
  var core = makeCore();
783
848
  var subject = process.env.FIDACY_SUBJECT ?? "agent:demo";
784
- var SERVER_VERSION = true ? "0.1.14" : "dev";
849
+ var SERVER_VERSION = true ? "0.1.16" : "dev";
785
850
  var server = new McpServer({ name: "fidacy", version: SERVER_VERSION });
786
851
  server.registerTool(
787
852
  "request_payment",
@@ -901,6 +966,112 @@ server.registerTool(
901
966
  }
902
967
  }
903
968
  );
969
+ server.registerTool(
970
+ "anchor_artifact",
971
+ {
972
+ title: "Anchor Artifact (Bitcoin-anchored integrity proof)",
973
+ 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).",
974
+ inputSchema: {
975
+ path: z.string().optional(),
976
+ sha256: z.string().regex(/^[0-9a-f]{64}$/).optional(),
977
+ kind: z.enum(ARTIFACT_KINDS).optional(),
978
+ label: z.string().max(120).optional(),
979
+ subject: z.string().max(120).optional()
980
+ },
981
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }
982
+ },
983
+ async ({ path, sha256: sha2562, kind, label, subject: subj }) => {
984
+ const engineUrl = process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
985
+ const apiKey = (process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
986
+ if (!apiKey) {
987
+ return {
988
+ 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." }],
989
+ isError: true
990
+ };
991
+ }
992
+ if (!path && !sha2562) {
993
+ return { content: [{ type: "text", text: "Give a file `path` (hashed locally) or a precomputed `sha256`." }], isError: true };
994
+ }
995
+ let hash = sha2562 ?? "";
996
+ if (!hash && path) {
997
+ try {
998
+ hash = await hashFile(path);
999
+ } catch {
1000
+ return { content: [{ type: "text", text: "Could not read that file locally (check the path and permissions). Nothing was sent anywhere." }], isError: true };
1001
+ }
1002
+ }
1003
+ try {
1004
+ const r = await anchorArtifact(
1005
+ { sha256: hash, kind: kind ?? "document", ...label ? { label } : {}, ...subj ? { subject: subj } : {} },
1006
+ { engineUrl, apiKey }
1007
+ );
1008
+ return {
1009
+ content: [{
1010
+ type: "text",
1011
+ 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}.`
1012
+ }],
1013
+ structuredContent: r
1014
+ };
1015
+ } catch (e) {
1016
+ if (e instanceof AssessError) {
1017
+ return { content: [{ type: "text", text: `ANCHOR ${e.status}: ${e.type}` }], isError: true };
1018
+ }
1019
+ return { content: [{ type: "text", text: "ANCHOR failed: unexpected error" }], isError: true };
1020
+ }
1021
+ }
1022
+ );
1023
+ server.registerTool(
1024
+ "check_artifact",
1025
+ {
1026
+ title: "Check Artifact (was this hash anchored?)",
1027
+ 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.",
1028
+ inputSchema: {
1029
+ path: z.string().optional(),
1030
+ sha256: z.string().regex(/^[0-9a-f]{64}$/).optional()
1031
+ },
1032
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }
1033
+ },
1034
+ async ({ path, sha256: sha2562 }) => {
1035
+ const engineUrl = process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com";
1036
+ const apiKey = (process.env.FIDACY_ENGINE_API_KEY ?? "").trim();
1037
+ if (!apiKey) {
1038
+ return {
1039
+ 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." }],
1040
+ isError: true
1041
+ };
1042
+ }
1043
+ if (!path && !sha2562) {
1044
+ return { content: [{ type: "text", text: "Give a file `path` (hashed locally) or a `sha256`." }], isError: true };
1045
+ }
1046
+ let hash = sha2562 ?? "";
1047
+ if (!hash && path) {
1048
+ try {
1049
+ hash = await hashFile(path);
1050
+ } catch {
1051
+ return { content: [{ type: "text", text: "Could not read that file locally (check the path and permissions). Nothing was sent anywhere." }], isError: true };
1052
+ }
1053
+ }
1054
+ try {
1055
+ const r = await findArtifacts(hash, { engineUrl, apiKey });
1056
+ if (!r.artifacts.length) {
1057
+ return {
1058
+ 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.` }],
1059
+ structuredContent: { sha256: hash, artifacts: [] }
1060
+ };
1061
+ }
1062
+ const first = r.artifacts[0];
1063
+ return {
1064
+ 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.` }],
1065
+ structuredContent: { sha256: hash, ...r }
1066
+ };
1067
+ } catch (e) {
1068
+ if (e instanceof AssessError) {
1069
+ return { content: [{ type: "text", text: `CHECK ${e.status}: ${e.type}` }], isError: true };
1070
+ }
1071
+ return { content: [{ type: "text", text: "CHECK failed: unexpected error" }], isError: true };
1072
+ }
1073
+ }
1074
+ );
904
1075
  server.registerTool(
905
1076
  "upgrade",
906
1077
  {
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.14" : "dev";
574
+ var CLIENT_VERSION = true ? "0.1.16" : "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.14" : "dev";
702
+ var CLIENT_VERSION2 = true ? "0.1.16" : "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.14",
3
+ "version": "0.1.16",
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
  }