@mikeargento/bitgraph-mcp 0.5.0 → 0.5.2

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 +1 -1
  2. package/README.md +1 -1
  3. package/dist/__tests__/encoding.test.js +1 -1
  4. package/dist/__tests__/encoding.test.js.map +1 -1
  5. package/dist/__tests__/format.test.js +4 -4
  6. package/dist/__tests__/format.test.js.map +1 -1
  7. package/dist/__tests__/proof-beside.test.d.ts +2 -0
  8. package/dist/__tests__/proof-beside.test.d.ts.map +1 -0
  9. package/dist/__tests__/proof-beside.test.js +25 -0
  10. package/dist/__tests__/proof-beside.test.js.map +1 -0
  11. package/dist/__tests__/scan.test.js +1 -1
  12. package/dist/__tests__/scan.test.js.map +1 -1
  13. package/dist/__tests__/tools.test.js +3 -3
  14. package/dist/__tests__/tools.test.js.map +1 -1
  15. package/dist/api.js +1 -1
  16. package/dist/api.js.map +1 -1
  17. package/dist/encoding.js +1 -1
  18. package/dist/encoding.js.map +1 -1
  19. package/dist/format.js +5 -5
  20. package/dist/format.js.map +1 -1
  21. package/dist/index.js +1 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/scan.js +1 -1
  24. package/dist/scan.js.map +1 -1
  25. package/dist/server.d.ts +1 -1
  26. package/dist/server.d.ts.map +1 -1
  27. package/dist/server.js +31 -12
  28. package/dist/server.js.map +1 -1
  29. package/dist/task.d.ts +11 -0
  30. package/dist/task.d.ts.map +1 -1
  31. package/dist/task.js +29 -5
  32. package/dist/task.js.map +1 -1
  33. package/dist/types.js +1 -1
  34. package/dist/types.js.map +1 -1
  35. package/package.json +9 -1
  36. package/src/__tests__/encoding.test.ts +1 -1
  37. package/src/__tests__/format.test.ts +4 -4
  38. package/src/__tests__/proof-beside.test.ts +25 -0
  39. package/src/__tests__/scan.test.ts +1 -1
  40. package/src/__tests__/tools.test.ts +3 -3
  41. package/src/api.ts +1 -1
  42. package/src/encoding.ts +1 -1
  43. package/src/format.ts +5 -5
  44. package/src/index.ts +1 -1
  45. package/src/scan.ts +1 -1
  46. package/src/server.ts +29 -12
  47. package/src/task.ts +29 -5
  48. package/src/types.ts +1 -1
package/src/server.ts CHANGED
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2024-2026 Mike Argento. Licensed under the MIT License. See LICENSE.
1
+ // Copyright (c) 2024-2026 Argento Computing Inc. Licensed under the MIT License. See LICENSE.
2
2
 
3
3
  /**
4
4
  * @mikeargento/bitgraph-mcp: tool definitions.
@@ -49,10 +49,10 @@ import {
49
49
  type SetOutcome,
50
50
  } from "./format.js";
51
51
  import { expandPaths, fusedDigestFor, scanFile, type ScannedFile } from "./scan.js";
52
- import { SLOT_TTL_SECONDS, TASK_INSTRUCTIONS, beginTask, decodeTaskToken, sealTask } from "./task.js";
52
+ import { SLOT_TTL_SECONDS, TASK_INSTRUCTIONS, beginTask, decodeTaskToken, sealTask, writeProofBeside } from "./task.js";
53
53
  import type { BitGraphProof } from "./types.js";
54
54
 
55
- export const SERVER_VERSION = "0.5.0";
55
+ export const SERVER_VERSION = "0.5.2";
56
56
 
57
57
  const SCAN_CONCURRENCY = 4;
58
58
  /** Paths per call; a directory counts once and expands to its files. */
@@ -332,7 +332,7 @@ export function buildServer(deps: ServerDeps = {}): McpServer {
332
332
  },
333
333
  {
334
334
  instructions:
335
- "BitGraph gives a file's bytes a causal position in a public ledger bracketed by Ethereum anchors. bitgraph_record makes ONE BitGraph of everything in a call, files and folders alike: a single file is fused on its own; two or more become one set under one slot, one position, every file's new fused bytes listed by digest in the committed artifact. " +
335
+ "BitGraph gives a file's bytes a causal position in a public sequence bracketed by Ethereum anchors. bitgraph_record makes ONE BitGraph of everything in a call, files and folders alike: a single file is fused on its own; two or more become one set under one slot, one position, every file's new fused bytes listed by digest in the committed artifact. " +
336
336
  "Files are read on this machine and never uploaded or modified; the new bytes are virtual and never written. Recordings are permanent: only make BitGraphs of files the user asked for, and never generate content just to record it. bitgraph_check and bitgraph_get_proof are read-only. " +
337
337
  "To do work INSIDE a BitGraph, call bitgraph_open BEFORE starting: it returns a position and its commitment; put the commitment string into the task, seal the task with bitgraph_commit within 120 seconds, then record the outputs with bitgraph_record. The task then could not have existed before the position's floor block, and the outputs sit after it.",
338
338
  }
@@ -343,11 +343,11 @@ export function buildServer(deps: ServerDeps = {}): McpServer {
343
343
  {
344
344
  title: "Make a BitGraph",
345
345
  description:
346
- "Make a BitGraph of files or folders. Everything in one call becomes ONE BitGraph, the way a drop on the site works: a single file is fused on its own slot; two or more files become a set under a single slot in the BitGraph ledger (bitgraph.ing), one position for all of them. " +
346
+ "Make a BitGraph of files or folders. Everything in one call becomes ONE BitGraph, the way a drop on the site works: a single file is fused on its own slot; two or more files become a set under a single slot on BitGraph (bitgraph.ing), one position for all of them. " +
347
347
  "On this machine each file is read once for its SHA-256 (the origin) and a hasher state; an unused slot is allocated before any new file exists; every file's new fused bytes (the original plus a registered placement carrying the slot's commitment: a 48-byte trailer for JPEG, PNG, GIF, TIFF and TIFF-based raws, BMP, WebP, WAV and AVI, a small tar container with the original first for everything else) are hashed from that state without being written or held; and for a set the canonical list of those digests (above 2,000 files, a Merkle root over it) is committed under the same slot. " +
348
348
  "Files are never modified and never uploaded: only digests, the committed artifact and slot records leave the machine. " +
349
349
  "Give file paths, directory paths, or both (absolute paths preferred): a directory is every regular file under it, recursively, with hidden entries and symbolic links left out. " +
350
- "Files BitGraph still indexes (recorded before 2026-09-08) are NOT made again by default; they come back as 'on record' with their earliest position. BitGraph does not index new proofs, so a file may already have a BitGraph its holder keeps. Pass again=true to make a new BitGraph regardless. " +
350
+ "Files already on record are NOT made again by default; they come back as 'on record' with their earliest position. A file can also hold a BitGraph its holder keeps, which no lookup sees. Pass again=true to make a new BitGraph regardless. " +
351
351
  "Positions are permanent and the proof comes back to you to keep, so only BitGraph files the user asked to, and never generate content just to record it. " +
352
352
  "Returns one outcome per file: 'fused' (for a set, its row, one of N, and the set's position and proof page; for a single file, its own position and Frame), 'on record', or 'not fused' (with the reason). Keep the proof beside the files; BitGraph does not index it. " +
353
353
  "Use bitgraph_check instead when the user only wants to know whether files are on record.",
@@ -361,7 +361,7 @@ export function buildServer(deps: ServerDeps = {}): McpServer {
361
361
  .boolean()
362
362
  .default(false)
363
363
  .describe(
364
- "false (default): files BitGraph still indexes are returned as-is, nothing made. true: put every file in the set regardless. Outcomes are per unique file content: two paths with identical bytes are one member."
364
+ "false (default): files already on record are returned as-is, nothing made. true: put every file in the set regardless. Outcomes are per unique file content: two paths with identical bytes are one member."
365
365
  ),
366
366
  response_format: responseFormatSchema,
367
367
  },
@@ -413,7 +413,7 @@ export function buildServer(deps: ServerDeps = {}): McpServer {
413
413
  const unique = [...byDigest.keys()];
414
414
 
415
415
  // 4. What is on record already.
416
- report(0, 1, "checking the ledger");
416
+ report(0, 1, "checking BitGraph's copy");
417
417
  const checked = await batchCheck(config, unique.map(toUrlSafeB64));
418
418
  const existing = new Map<string, Array<{ proof: BitGraphProof }>>();
419
419
  for (const d of unique) {
@@ -651,12 +651,29 @@ export function buildServer(deps: ServerDeps = {}): McpServer {
651
651
  const counter = sealed.proof.commit?.counter ?? null;
652
652
  const epoch = sealed.proof.commit?.epochId ?? null;
653
653
  const floor = sealed.proof.commit?.slotAnchor?.blockNumber ?? null;
654
- const structured = { outcome: "sealed", slot_counter: state.slot.counter, counter, epoch: epoch ? toUrlSafeB64(epoch) : null, floor_block: floor, artifact_digest: toUrlSafeB64(sealed.artifactDigestB64), commitment_offsets: sealed.offsets, proof: sealed.proof };
654
+ /* The proof goes beside the sealed file, written here, whole. Given
655
+ * only a digest there is no file to put it beside, so it is handed
656
+ * back with the one instruction that matters. */
657
+ let proofPath: string | null = null;
658
+ let proofNote = "";
659
+ if (path !== undefined) {
660
+ try {
661
+ proofPath = await writeProofBeside(path, sealed.proof);
662
+ proofNote = `The proof is written at ${proofPath}, whole; leave it as written. `;
663
+ } catch (err) {
664
+ proofNote = `The proof could not be written beside the file (${errorText(err)}); save the JSON below whole and unedited, every field. `;
665
+ }
666
+ } else {
667
+ proofNote = "Save the JSON below as a file beside the task bytes, whole and unedited, every field, including environment.attestation.reportB64, the long base64 string, copied exactly: a proof missing slotAllocation, environment, or the attestation cannot be verified. ";
668
+ }
669
+ const structured = { outcome: "sealed", slot_counter: state.slot.counter, counter, epoch: epoch ? toUrlSafeB64(epoch) : null, floor_block: floor, artifact_digest: toUrlSafeB64(sealed.artifactDigestB64), commitment_offsets: sealed.offsets, proof_path: proofPath, instructions: proofNote.trim(), proof: sealed.proof };
655
670
  if (response_format === "json") return ok(JSON.stringify(structured, null, 2), structured);
656
671
  return ok(
657
672
  `Sealed the task at position ${counter ?? "?"} (slot ${state.slot.counter}).${floor !== null ? ` Not before block ${floor}.` : ""} ` +
658
- (sealed.offsets !== null ? `The commitment string was found in the task bytes at offset ${sealed.offsets[0]}. ` : "The task bytes were not read here; a verifier looks for the commitment string in them. ") +
659
- "Keep the proof beside the task bytes, and record the outputs with bitgraph_record when they exist.\n\n```json\n" + JSON.stringify(sealed.proof, null, 2) + "\n```",
673
+ (sealed.offsets !== null ? `The commitment string was found in the sealed bytes at offset ${sealed.offsets[0]}. ` : "The bytes were not read here; a verifier looks for the commitment string in them. ") +
674
+ proofNote +
675
+ "Record the outputs with bitgraph_record when they exist." +
676
+ (proofPath === null ? "\n\n```json\n" + JSON.stringify(sealed.proof, null, 2) + "\n```" : ""),
660
677
  structured
661
678
  );
662
679
  } catch (err) {
@@ -670,7 +687,7 @@ export function buildServer(deps: ServerDeps = {}): McpServer {
670
687
  {
671
688
  title: "Check for BitGraphs",
672
689
  description:
673
- "Check whether files or digests are on record in the BitGraph ledger, without recording anything. " +
690
+ "Check whether files or digests are on record in BitGraph's copy, without recording anything. " +
674
691
  `Accepts file paths and directory paths (every regular file under them, up to ${MAX_CHECK_FILES} in all; hashed locally, only digests are sent) and/or raw SHA-256 digests in standard or URL-safe base64. ` +
675
692
  "Returns, per item: on_record (the bytes are on record, as an exact recording, as the original a new file was made from, or as a member of a set), every position by counter, and the proof page URL. " +
676
693
  "Read-only. Use bitgraph_record to BitGraph files that turn out not to be on record.",
package/src/task.ts CHANGED
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2024-2026 Mike Argento. Licensed under the MIT License. See LICENSE.
1
+ // Copyright (c) 2024-2026 Argento Computing Inc. Licensed under the MIT License. See LICENSE.
2
2
 
3
3
  /**
4
4
  * A position BEFORE the work: the task form of the stdio server.
@@ -157,7 +157,31 @@ async function recover(config: ApiConfig, artifactDigestB64: string, slot: SlotA
157
157
  }
158
158
 
159
159
  export const TASK_INSTRUCTIONS =
160
- "You hold a position and its commitment, and no work exists yet. Put the commitment string into the task before you run it: in the prompt or request you are about to send, as a seed, as a line in the document, as text that must appear in the output. " +
161
- `Then call bitgraph_commit with this fuse_token and the path of the task file (or the SHA-256 of its bytes), within ${SLOT_TTL_SECONDS} seconds of opening: that seals the task under the position before its output exists. ` +
162
- "Keep those exact bytes: a verifier recomputes the commitment from the proof and looks for the string inside them. " +
163
- "When the output exists, record it with bitgraph_record; it will sit at a later position. What a stranger can then check: the task could not have existed before the position's floor block, and the output was recorded after it.";
160
+ "You hold a position and its commitment, and no work exists yet. Put the commitment string INSIDE THE OUTPUT you are about to make whenever its format can hold text (SVG, HTML, code, JSON, Markdown, CSV, a document): as a comment, a field, a line that stays in the file. " +
161
+ `When the output exists, call bitgraph_commit with this fuse_token and the output file's path, within ${SLOT_TTL_SECONDS} seconds of opening: the output itself is sealed under the position, and the proof is written beside it. ` +
162
+ "If the output cannot hold text (a PNG, audio, video) or will take longer than that to make, put the commitment into a task file instead (the exact prompt or request you send), seal that file the same way, and when the output exists record it with bitgraph_record: the task is sealed before the output existed, and the output is recorded after. " +
163
+ "Keep the exact bytes that were sealed unchanged: a verifier recomputes the commitment from the proof and looks for the string inside them. " +
164
+ "Leave the proof file as written, whole, every field, environment.attestation.reportB64 included: a proof missing slotAllocation, environment, or the attestation cannot be verified.";
165
+
166
+ /**
167
+ * Write a sealed task's proof beside the file it is about, whole, and say
168
+ * where. `make_task.txt` gets `make_task.proof.json`; a name already taken
169
+ * gets the position's counter in it rather than being overwritten.
170
+ *
171
+ * ⚠️ THE SERVER WRITES IT, NOT THE AGENT. Handed the JSON to save, an agent
172
+ * tidied it and dropped slotAllocation and environment, and the signature
173
+ * had no body left to be checked against (Grok, 2026-09-13). A file this
174
+ * process writes is the proof exactly as the boundary returned it.
175
+ */
176
+ export async function writeProofBeside(sealedPath: string, proof: BitGraphProof): Promise<string> {
177
+ const { writeFile, access } = await import("node:fs/promises");
178
+ const { basename, dirname, extname, join } = await import("node:path");
179
+ const dir = dirname(sealedPath);
180
+ const base = basename(sealedPath);
181
+ const stem = extname(base) === "" ? base : base.slice(0, -extname(base).length);
182
+ const counter = proof.commit?.counter ?? "0";
183
+ let target = join(dir, `${stem}.proof.json`);
184
+ if (await access(target).then(() => true).catch(() => false)) target = join(dir, `${stem}.proof.${counter}.json`);
185
+ await writeFile(target, JSON.stringify(proof, null, 2) + "\n", { flag: "wx" });
186
+ return target;
187
+ }
package/src/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- // Copyright (c) 2024-2026 Mike Argento. Licensed under the MIT License. See LICENSE.
1
+ // Copyright (c) 2024-2026 Argento Computing Inc. Licensed under the MIT License. See LICENSE.
2
2
 
3
3
  /**
4
4
  * @mikeargento/bitgraph-mcp: wire types.