@mikeargento/bitgraph-mcp 0.1.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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +25 -0
  3. package/dist/__tests__/encoding.test.d.ts +2 -0
  4. package/dist/__tests__/encoding.test.d.ts.map +1 -0
  5. package/dist/__tests__/encoding.test.js +51 -0
  6. package/dist/__tests__/encoding.test.js.map +1 -0
  7. package/dist/__tests__/format.test.d.ts +2 -0
  8. package/dist/__tests__/format.test.d.ts.map +1 -0
  9. package/dist/__tests__/format.test.js +59 -0
  10. package/dist/__tests__/format.test.js.map +1 -0
  11. package/dist/__tests__/tools.test.d.ts +2 -0
  12. package/dist/__tests__/tools.test.d.ts.map +1 -0
  13. package/dist/__tests__/tools.test.js +304 -0
  14. package/dist/__tests__/tools.test.js.map +1 -0
  15. package/dist/api.d.ts +54 -0
  16. package/dist/api.d.ts.map +1 -0
  17. package/dist/api.js +144 -0
  18. package/dist/api.js.map +1 -0
  19. package/dist/encoding.d.ts +19 -0
  20. package/dist/encoding.d.ts.map +1 -0
  21. package/dist/encoding.js +64 -0
  22. package/dist/encoding.js.map +1 -0
  23. package/dist/format.d.ts +44 -0
  24. package/dist/format.d.ts.map +1 -0
  25. package/dist/format.js +164 -0
  26. package/dist/format.js.map +1 -0
  27. package/dist/index.d.ts +3 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +25 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/server.d.ts +11 -0
  32. package/dist/server.d.ts.map +1 -0
  33. package/dist/server.js +375 -0
  34. package/dist/server.js.map +1 -0
  35. package/dist/types.d.ts +88 -0
  36. package/dist/types.d.ts.map +1 -0
  37. package/dist/types.js +3 -0
  38. package/dist/types.js.map +1 -0
  39. package/package.json +47 -0
  40. package/src/__tests__/encoding.test.ts +64 -0
  41. package/src/__tests__/format.test.ts +69 -0
  42. package/src/__tests__/tools.test.ts +334 -0
  43. package/src/api.ts +211 -0
  44. package/src/encoding.ts +72 -0
  45. package/src/format.ts +201 -0
  46. package/src/index.ts +30 -0
  47. package/src/server.ts +463 -0
  48. package/src/types.ts +74 -0
package/dist/server.js ADDED
@@ -0,0 +1,375 @@
1
+ // Copyright (c) 2024-2026 Mike Argento. Licensed under the MIT License. See LICENSE.
2
+ /**
3
+ * @mikeargento/bitgraph-mcp: tool definitions.
4
+ *
5
+ * Three gestures, the same three the website has: record a file (take a
6
+ * BitGraph), check whether bytes are on record, fetch a proof. Only SHA-256
7
+ * digests ever leave the machine; file contents are never uploaded.
8
+ */
9
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10
+ import { z } from "zod";
11
+ import { ApiError, PartialCommitError, batchCheck, commitDigests, configFromEnv, getProofDetail, search, } from "./api.js";
12
+ import { fromUrlSafeB64, looksLikeDigest, mapConcurrent, sha256FileB64, toUrlSafeB64, } from "./encoding.js";
13
+ import { capJson, positionOf, proofUrl, renderCheckMarkdown, renderProofMarkdown, renderRecordMarkdown, } from "./format.js";
14
+ export const SERVER_VERSION = "0.1.1";
15
+ const HASH_CONCURRENCY = 4;
16
+ const MAX_FILES = 500;
17
+ const responseFormatSchema = z
18
+ .enum(["markdown", "json"])
19
+ .default("markdown")
20
+ .describe("markdown (default): human-readable summary. json: complete structured data.");
21
+ function ok(text, structured) {
22
+ const result = { content: [{ type: "text", text }] };
23
+ if (structured !== undefined)
24
+ result.structuredContent = structured;
25
+ return result;
26
+ }
27
+ function fail(message) {
28
+ return { isError: true, content: [{ type: "text", text: message }] };
29
+ }
30
+ function errorText(err) {
31
+ if (err instanceof ApiError) {
32
+ const retry = err.retryAfterSec !== null ? ` Retry after ${err.retryAfterSec} seconds.` : "";
33
+ return `Error: BitGraph API responded ${err.status}: ${err.message}.${retry}`;
34
+ }
35
+ return `Error: ${err instanceof Error ? err.message : String(err)}`;
36
+ }
37
+ /** Hash the given paths (bounded concurrency). Throws before any network call. */
38
+ async function hashPaths(paths) {
39
+ const failures = [];
40
+ const digests = await mapConcurrent(paths, HASH_CONCURRENCY, async (p) => {
41
+ try {
42
+ return await sha256FileB64(p);
43
+ }
44
+ catch (err) {
45
+ failures.push(`${p}: ${err instanceof Error ? err.message : String(err)}`);
46
+ return "";
47
+ }
48
+ });
49
+ if (failures.length > 0) {
50
+ throw new Error(`Could not read ${failures.length} file(s); nothing was recorded.\n${failures.join("\n")}\nUse absolute paths to existing regular files.`);
51
+ }
52
+ return digests;
53
+ }
54
+ export function buildServer() {
55
+ const server = new McpServer({
56
+ name: "bitgraph-mcp-server",
57
+ version: SERVER_VERSION,
58
+ });
59
+ server.registerTool("bitgraph_record", {
60
+ title: "Take a BitGraph",
61
+ description: "Take a BitGraph of one or more files: record each file's SHA-256 digest at a new causal position in the BitGraph ledger (bitgraph.ing). " +
62
+ "Only the digest leaves the machine; file contents are never uploaded. " +
63
+ "Files whose bytes are already on record are NOT re-recorded by default; they come back as 'on record' with their existing proof. " +
64
+ "Pass again=true to deliberately record already-recorded bytes at a new causal position (BitGraph Again). " +
65
+ "Recordings are permanent: the ledger has 10-year retention and no deletes, so only record files the user asked to record. " +
66
+ "Returns one outcome per file: 'recorded' (newly minted) or 'on record' (was already there), with its position number and proof page URL. " +
67
+ "Use bitgraph_check instead when the user only wants to know whether a file is on record.",
68
+ inputSchema: {
69
+ paths: z
70
+ .array(z.string().min(1))
71
+ .min(1)
72
+ .max(MAX_FILES)
73
+ .describe(`File paths to record (absolute paths preferred), up to ${MAX_FILES}.`),
74
+ attribution: z
75
+ .object({
76
+ name: z.string().max(200).optional().describe("Submitter's name (self-attributed)."),
77
+ title: z.string().max(200).optional(),
78
+ message: z.string().max(2000).optional(),
79
+ })
80
+ .optional()
81
+ .describe("Optional self-attributed submitter's note, stored in the signed proof. Rendered as a note, never as verified identity."),
82
+ again: z
83
+ .boolean()
84
+ .default(false)
85
+ .describe("false (default): files already on record are returned as-is, nothing minted. true: record every file at a new causal position even if already on record. Positions are per unique file content: two paths with identical bytes yield one position."),
86
+ response_format: responseFormatSchema,
87
+ },
88
+ annotations: {
89
+ readOnlyHint: false,
90
+ destructiveHint: false,
91
+ idempotentHint: false,
92
+ openWorldHint: true,
93
+ },
94
+ }, async ({ paths, attribution, again, response_format }) => {
95
+ const config = configFromEnv();
96
+ try {
97
+ const digests = await hashPaths(paths);
98
+ // Unique digests, first path wins for display; extra paths listed too.
99
+ const byDigest = new Map();
100
+ digests.forEach((d, i) => {
101
+ const list = byDigest.get(d) ?? [];
102
+ list.push(paths[i]);
103
+ byDigest.set(d, list);
104
+ });
105
+ const unique = [...byDigest.keys()];
106
+ const checked = await batchCheck(config, unique.map(toUrlSafeB64));
107
+ const existing = new Map();
108
+ for (const d of unique) {
109
+ const entry = checked.results[toUrlSafeB64(d)];
110
+ if (entry && entry.proofs.length > 0)
111
+ existing.set(d, entry.proofs);
112
+ }
113
+ const toMint = again ? unique : unique.filter((d) => !existing.has(d));
114
+ let minted = [];
115
+ let partial = null;
116
+ if (toMint.length > 0) {
117
+ try {
118
+ minted = await commitDigests(config, toMint, attribution);
119
+ // A 200 with fewer proofs than digests is still a partial failure;
120
+ // never let it reach the success path looking complete.
121
+ if (minted.length < toMint.length) {
122
+ partial = new PartialCommitError(minted, toMint.length, new Error("commit returned fewer proofs than digests sent"));
123
+ }
124
+ }
125
+ catch (err) {
126
+ if (err instanceof PartialCommitError) {
127
+ minted = err.minted;
128
+ partial = err;
129
+ }
130
+ else {
131
+ throw err;
132
+ }
133
+ }
134
+ }
135
+ const mintedByDigest = new Map();
136
+ for (const p of minted) {
137
+ const d = p.artifact?.digestB64;
138
+ if (d !== undefined)
139
+ mintedByDigest.set(d, p);
140
+ }
141
+ const outcomes = [];
142
+ for (const [digest, pathList] of byDigest) {
143
+ const mintedProof = mintedByDigest.get(digest);
144
+ const prior = existing.get(digest);
145
+ for (const path of pathList) {
146
+ if (mintedProof) {
147
+ const { counter, epoch } = positionOf(mintedProof);
148
+ outcomes.push({
149
+ path,
150
+ digest: toUrlSafeB64(digest),
151
+ outcome: "recorded",
152
+ counter,
153
+ epoch,
154
+ total_positions: (prior?.length ?? 0) + 1,
155
+ proof_url: proofUrl(config.baseUrl, digest, counter ?? undefined, mintedProof.commit?.epochId),
156
+ });
157
+ }
158
+ else if (prior) {
159
+ const first = prior[0]?.proof;
160
+ const { counter, epoch } = first ? positionOf(first) : { counter: null, epoch: null };
161
+ outcomes.push({
162
+ path,
163
+ digest: toUrlSafeB64(digest),
164
+ outcome: "on record",
165
+ counter,
166
+ epoch,
167
+ total_positions: prior.length,
168
+ proof_url: proofUrl(config.baseUrl, digest),
169
+ });
170
+ }
171
+ else {
172
+ // Neither minted nor previously on record: lost to a partial
173
+ // failure. The honest outcome is "not recorded", never a claim.
174
+ outcomes.push({
175
+ path,
176
+ digest: toUrlSafeB64(digest),
177
+ outcome: "not recorded",
178
+ counter: null,
179
+ epoch: null,
180
+ total_positions: 0,
181
+ proof_url: null,
182
+ });
183
+ }
184
+ }
185
+ }
186
+ const structured = {
187
+ results: outcomes,
188
+ summary: {
189
+ recorded: outcomes.filter((o) => o.outcome === "recorded").length,
190
+ on_record: outcomes.filter((o) => o.outcome === "on record").length,
191
+ not_recorded: outcomes.filter((o) => o.outcome === "not recorded").length,
192
+ },
193
+ };
194
+ if (partial) {
195
+ const unrecorded = toMint.length - minted.length;
196
+ const retryGuidance = again
197
+ ? `Some 'not recorded' files MAY still have been recorded server-side if the failure was a timeout. Run bitgraph_check on the 'not recorded' paths first, then re-run bitgraph_record with again=true for only the paths still missing.`
198
+ : `Re-run bitgraph_record with the same paths: already-recorded files will come back as 'on record' and only the missing ones will mint.`;
199
+ return {
200
+ isError: true,
201
+ content: [
202
+ {
203
+ type: "text",
204
+ text: `${errorText(partial.cause2)}\n` +
205
+ `${minted.length} of ${toMint.length} digests were recorded before the failure (those recordings are permanent); ${unrecorded} were not. ` +
206
+ `${retryGuidance}\n\n` +
207
+ renderRecordMarkdown(outcomes),
208
+ },
209
+ ],
210
+ structuredContent: structured,
211
+ };
212
+ }
213
+ const text = response_format === "json"
214
+ ? capJson(structured).text
215
+ : renderRecordMarkdown(outcomes);
216
+ return ok(text, structured);
217
+ }
218
+ catch (err) {
219
+ return fail(errorText(err));
220
+ }
221
+ });
222
+ server.registerTool("bitgraph_check", {
223
+ title: "Check for BitGraphs",
224
+ description: "Check whether files or digests are on record in the BitGraph ledger, without recording anything. " +
225
+ "Accepts file paths (hashed locally; only digests are sent) and/or raw SHA-256 digests in standard or URL-safe base64. " +
226
+ "Returns, per item: on record or not, every causal position (a file recorded more than once has several), and the proof page URL. " +
227
+ "Read-only. Use bitgraph_record to record files that turn out not to be on record.",
228
+ inputSchema: {
229
+ paths: z
230
+ .array(z.string().min(1))
231
+ .max(MAX_FILES)
232
+ .optional()
233
+ .describe("File paths to check."),
234
+ digests: z
235
+ .array(z.string().min(1).max(100))
236
+ .max(MAX_FILES)
237
+ .optional()
238
+ .describe("SHA-256 digests, base64 (standard or URL-safe form)."),
239
+ response_format: responseFormatSchema,
240
+ },
241
+ annotations: {
242
+ readOnlyHint: true,
243
+ destructiveHint: false,
244
+ idempotentHint: true,
245
+ openWorldHint: true,
246
+ },
247
+ }, async ({ paths, digests, response_format }) => {
248
+ const config = configFromEnv();
249
+ try {
250
+ const inputs = [];
251
+ if (paths && paths.length > 0) {
252
+ const hashed = await hashPaths(paths);
253
+ hashed.forEach((d, i) => inputs.push({ label: paths[i], standardDigest: d }));
254
+ }
255
+ for (const d of digests ?? []) {
256
+ const trimmed = d.trim();
257
+ if (!looksLikeDigest(trimmed)) {
258
+ return fail(`Error: "${d}" is not a base64 SHA-256 digest. Pass 32-byte digests in standard or URL-safe base64, or use paths to hash files locally.`);
259
+ }
260
+ inputs.push({ label: d, standardDigest: fromUrlSafeB64(trimmed) });
261
+ }
262
+ if (inputs.length === 0) {
263
+ return fail("Error: provide at least one of paths or digests.");
264
+ }
265
+ const checked = await batchCheck(config, inputs.map((i) => toUrlSafeB64(i.standardDigest)));
266
+ const outcomes = inputs.map((input) => {
267
+ const entry = checked.results[toUrlSafeB64(input.standardDigest)];
268
+ const proofs = entry?.proofs ?? [];
269
+ const positions = proofs.map((p) => positionOf(p.proof));
270
+ return {
271
+ input: input.label,
272
+ digest: toUrlSafeB64(input.standardDigest),
273
+ on_record: proofs.length > 0,
274
+ positions,
275
+ proof_url: proofs.length > 0 ? proofUrl(config.baseUrl, input.standardDigest) : null,
276
+ };
277
+ });
278
+ const structured = {
279
+ results: outcomes,
280
+ summary: {
281
+ on_record: outcomes.filter((o) => o.on_record).length,
282
+ not_on_record: outcomes.filter((o) => !o.on_record).length,
283
+ },
284
+ };
285
+ const text = response_format === "json" ? capJson(structured).text : renderCheckMarkdown(outcomes);
286
+ return ok(text, structured);
287
+ }
288
+ catch (err) {
289
+ return fail(errorText(err));
290
+ }
291
+ });
292
+ server.registerTool("bitgraph_get_proof", {
293
+ title: "Get a BitGraph proof",
294
+ description: "Fetch a BitGraph proof and its context: causal position, every position the same bytes occupy, and the two-sided Ethereum anchor window " +
295
+ "('BitGraphed between X and Y'). Look up by digest (base64, either form), by BitGraph number (e.g. '4523' or '#4,523', current epoch), or by file path (hashed locally). " +
296
+ "Exactly one of digest, number, or path is required. Read-only. " +
297
+ "markdown returns a summary; json returns the full proof object with positions and anchor window.",
298
+ inputSchema: {
299
+ digest: z
300
+ .string()
301
+ .min(1)
302
+ .max(100)
303
+ .optional()
304
+ .describe("SHA-256 digest, standard or URL-safe base64."),
305
+ number: z
306
+ .string()
307
+ .min(1)
308
+ .max(30)
309
+ .optional()
310
+ .describe("BitGraph counter number in the current epoch, e.g. '4523' or '#4,523'."),
311
+ path: z.string().min(1).optional().describe("File path; hashed locally."),
312
+ counter: z
313
+ .string()
314
+ .optional()
315
+ .describe("Select a specific causal position by commit counter (with epoch)."),
316
+ epoch: z
317
+ .string()
318
+ .optional()
319
+ .describe("URL-safe epoch id qualifying the counter."),
320
+ response_format: responseFormatSchema,
321
+ },
322
+ annotations: {
323
+ readOnlyHint: true,
324
+ destructiveHint: false,
325
+ idempotentHint: true,
326
+ openWorldHint: true,
327
+ },
328
+ }, async ({ digest, number, path, counter, epoch, response_format }) => {
329
+ const config = configFromEnv();
330
+ try {
331
+ const given = [digest, number, path].filter((v) => v !== undefined);
332
+ if (given.length !== 1) {
333
+ return fail("Error: pass exactly one of digest, number, or path.");
334
+ }
335
+ let urlSafeDigest;
336
+ let selCounter = counter;
337
+ if (number !== undefined) {
338
+ const result = await search(config, number);
339
+ if (!result.found || result.digest === undefined) {
340
+ return fail(`Error: no BitGraph found for number "${number}" in the current epoch. Numbers reset each epoch; look up older recordings by digest or path instead.`);
341
+ }
342
+ urlSafeDigest = result.digest;
343
+ if (selCounter === undefined && result.counter != null)
344
+ selCounter = result.counter;
345
+ }
346
+ else if (path !== undefined) {
347
+ const hashed = await hashPaths([path]);
348
+ urlSafeDigest = toUrlSafeB64(hashed[0]);
349
+ }
350
+ else {
351
+ const trimmed = digest.trim();
352
+ if (!looksLikeDigest(trimmed)) {
353
+ return fail(`Error: "${digest}" is not a base64 SHA-256 digest. Pass a 32-byte digest in standard or URL-safe base64.`);
354
+ }
355
+ urlSafeDigest = toUrlSafeB64(fromUrlSafeB64(trimmed));
356
+ }
357
+ // The route compares epochs in URL-safe form; accept either form here.
358
+ const selEpoch = epoch !== undefined ? toUrlSafeB64(fromUrlSafeB64(epoch)) : undefined;
359
+ const detail = await getProofDetail(config, urlSafeDigest, selCounter, selEpoch);
360
+ if (detail.proofs.length === 0) {
361
+ return fail(`Not on record: no proof exists for digest ${urlSafeDigest}. Use bitgraph_record to record the file.`);
362
+ }
363
+ if (response_format === "json") {
364
+ const capped = capJson(detail);
365
+ return ok(capped.text, detail);
366
+ }
367
+ return ok(renderProofMarkdown(detail, config.baseUrl), detail);
368
+ }
369
+ catch (err) {
370
+ return fail(errorText(err));
371
+ }
372
+ });
373
+ return server;
374
+ }
375
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * @mikeargento/bitgraph-mcp: wire types.
3
+ *
4
+ * Minimal views of the bitgraph/1 schema and the bitgraph.ing API responses.
5
+ * The canonical, fully documented schema lives in @mikeargento/bitgraph-verify.
6
+ * Fields not needed for display are kept as unknown; proofs are passed through
7
+ * verbatim when the caller asks for JSON.
8
+ */
9
+ export interface ProofCommit {
10
+ counter?: string;
11
+ epochId?: string;
12
+ time?: number;
13
+ slotCounter?: string;
14
+ [k: string]: unknown;
15
+ }
16
+ export interface BitGraphProof {
17
+ version?: string;
18
+ artifact?: {
19
+ hashAlg?: string;
20
+ digestB64?: string;
21
+ };
22
+ commit?: ProofCommit;
23
+ signer?: {
24
+ publicKeyB64?: string;
25
+ signatureB64?: string;
26
+ };
27
+ environment?: {
28
+ enforcement?: string;
29
+ measurement?: string;
30
+ attestation?: {
31
+ format?: string;
32
+ reportB64?: string;
33
+ };
34
+ };
35
+ attribution?: {
36
+ name?: string;
37
+ title?: string;
38
+ message?: string;
39
+ };
40
+ proofHash?: string;
41
+ [k: string]: unknown;
42
+ }
43
+ /** POST /api/proofs/batch response. Keyed by the digest exactly as sent (URL-safe). */
44
+ export interface BatchCheckResponse {
45
+ results: Record<string, {
46
+ proofs: Array<{
47
+ proof: BitGraphProof;
48
+ }>;
49
+ }>;
50
+ }
51
+ export interface AnchorView {
52
+ counter?: string;
53
+ attrName?: string;
54
+ blockNumber?: number | null;
55
+ blockHash?: string | null;
56
+ etherscanUrl?: string | null;
57
+ blockTime?: string | null;
58
+ }
59
+ export interface PositionView {
60
+ counter: string | null;
61
+ epoch: string | null;
62
+ lowerTime: string | null;
63
+ upperTime: string | null;
64
+ }
65
+ /**
66
+ * GET /api/proofs/digest/{digest} response.
67
+ * causalWindow naming note: anchorBefore is the EARLIER Ethereum block (the
68
+ * proof was BitGraphed after it, the lower time bound); anchorAfter is the
69
+ * LATER block (the upper bound). Present time as "between lower and upper".
70
+ */
71
+ export interface ProofDetailResponse {
72
+ proofs: Array<{
73
+ proof: BitGraphProof;
74
+ }>;
75
+ positions?: PositionView[];
76
+ causalWindow?: {
77
+ anchorBefore: AnchorView | null;
78
+ anchorAfter: AnchorView | null;
79
+ } | null;
80
+ anchorBlock?: AnchorView | null;
81
+ }
82
+ /** GET /api/search response. */
83
+ export interface SearchResponse {
84
+ found: boolean;
85
+ digest?: string;
86
+ counter?: string | null;
87
+ }
88
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACpD,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,MAAM,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1D,WAAW,CAAC,EAAE;QACZ,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;KACvD,CAAC;IACF,WAAW,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAClE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB;AAED,uFAAuF;AACvF,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,MAAM,EAAE,KAAK,CAAC;YAAE,KAAK,EAAE,aAAa,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAC;CACtE;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,aAAa,CAAA;KAAE,CAAC,CAAC;IACxC,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;IAC3B,YAAY,CAAC,EAAE;QAAE,YAAY,EAAE,UAAU,GAAG,IAAI,CAAC;QAAC,WAAW,EAAE,UAAU,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;IAC1F,WAAW,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;CACjC;AAED,gCAAgC;AAChC,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB"}
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ // Copyright (c) 2024-2026 Mike Argento. Licensed under the MIT License. See LICENSE.
2
+ export {};
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,qFAAqF"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@mikeargento/bitgraph-mcp",
3
+ "version": "0.1.1",
4
+ "description": "MCP server for BitGraph. Take a BitGraph of a file, check whether bytes are on record, and fetch proofs from any MCP client.",
5
+ "license": "MIT",
6
+ "homepage": "https://bitgraph.ing",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/mikeargento/bitgraph.git",
10
+ "directory": "packages/mcp"
11
+ },
12
+ "type": "module",
13
+ "main": "./dist/server.js",
14
+ "types": "./dist/server.d.ts",
15
+ "bin": {
16
+ "bitgraph-mcp": "dist/index.js"
17
+ },
18
+ "exports": {
19
+ ".": {
20
+ "import": "./dist/server.js",
21
+ "types": "./dist/server.d.ts"
22
+ }
23
+ },
24
+ "files": [
25
+ "dist",
26
+ "src",
27
+ "README.md",
28
+ "LICENSE"
29
+ ],
30
+ "scripts": {
31
+ "build": "tsc",
32
+ "prepack": "tsc",
33
+ "typecheck": "tsc --noEmit",
34
+ "test": "tsc && node --test 'dist/__tests__/*.test.js'"
35
+ },
36
+ "engines": {
37
+ "node": ">=20"
38
+ },
39
+ "dependencies": {
40
+ "@modelcontextprotocol/sdk": "^1.29.0",
41
+ "zod": "^3.25.0"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^20.0.0",
45
+ "typescript": "^5.4.0"
46
+ }
47
+ }
@@ -0,0 +1,64 @@
1
+ // Copyright (c) 2024-2026 Mike Argento. Licensed under the MIT License. See LICENSE.
2
+
3
+ import { test } from "node:test";
4
+ import { strict as assert } from "node:assert";
5
+ import { createHash } from "node:crypto";
6
+ import { writeFile, mkdtemp } from "node:fs/promises";
7
+ import { tmpdir } from "node:os";
8
+ import { join } from "node:path";
9
+ import {
10
+ fromUrlSafeB64,
11
+ looksLikeDigest,
12
+ mapConcurrent,
13
+ sha256FileB64,
14
+ toUrlSafeB64,
15
+ } from "../encoding.js";
16
+
17
+ // A real 32-byte digest with + and / to exercise both substitutions.
18
+ const STANDARD = createHash("sha256").update("bitgraph").digest("base64");
19
+
20
+ test("url-safe round trip preserves the digest", () => {
21
+ const safe = toUrlSafeB64(STANDARD);
22
+ assert.ok(!safe.includes("+") && !safe.includes("/") && !safe.includes("="));
23
+ assert.equal(fromUrlSafeB64(safe), STANDARD);
24
+ });
25
+
26
+ test("fromUrlSafeB64 is a no-op on standard base64", () => {
27
+ assert.equal(fromUrlSafeB64(STANDARD), STANDARD);
28
+ });
29
+
30
+ test("looksLikeDigest accepts both forms, rejects junk", () => {
31
+ assert.ok(looksLikeDigest(STANDARD));
32
+ assert.ok(looksLikeDigest(toUrlSafeB64(STANDARD)));
33
+ assert.ok(!looksLikeDigest("not-a-digest"));
34
+ assert.ok(!looksLikeDigest(""));
35
+ assert.ok(!looksLikeDigest(Buffer.alloc(16).toString("base64"))); // 16 bytes, not 32
36
+ });
37
+
38
+ test("sha256FileB64 matches crypto over the same bytes", async () => {
39
+ const dir = await mkdtemp(join(tmpdir(), "bitgraph-mcp-"));
40
+ const file = join(dir, "sample.bin");
41
+ const bytes = Buffer.from("BitGraph proves causal ordering.\n");
42
+ await writeFile(file, bytes);
43
+ const expected = createHash("sha256").update(bytes).digest("base64");
44
+ assert.equal(await sha256FileB64(file), expected);
45
+ });
46
+
47
+ test("sha256FileB64 rejects directories and missing files", async () => {
48
+ await assert.rejects(() => sha256FileB64(tmpdir()), /Not a regular file/);
49
+ await assert.rejects(() => sha256FileB64(join(tmpdir(), "definitely-missing-9x7")));
50
+ });
51
+
52
+ test("mapConcurrent preserves order and honors the limit", async () => {
53
+ let inFlight = 0;
54
+ let peak = 0;
55
+ const out = await mapConcurrent([1, 2, 3, 4, 5, 6, 7, 8], 3, async (n) => {
56
+ inFlight += 1;
57
+ peak = Math.max(peak, inFlight);
58
+ await new Promise((r) => setTimeout(r, 5));
59
+ inFlight -= 1;
60
+ return n * 2;
61
+ });
62
+ assert.deepEqual(out, [2, 4, 6, 8, 10, 12, 14, 16]);
63
+ assert.ok(peak <= 3, `peak concurrency ${peak} exceeded 3`);
64
+ });