@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.
- package/LICENSE +1 -1
- package/README.md +1 -1
- package/dist/__tests__/encoding.test.js +1 -1
- package/dist/__tests__/encoding.test.js.map +1 -1
- package/dist/__tests__/format.test.js +4 -4
- package/dist/__tests__/format.test.js.map +1 -1
- package/dist/__tests__/proof-beside.test.d.ts +2 -0
- package/dist/__tests__/proof-beside.test.d.ts.map +1 -0
- package/dist/__tests__/proof-beside.test.js +25 -0
- package/dist/__tests__/proof-beside.test.js.map +1 -0
- package/dist/__tests__/scan.test.js +1 -1
- package/dist/__tests__/scan.test.js.map +1 -1
- package/dist/__tests__/tools.test.js +3 -3
- package/dist/__tests__/tools.test.js.map +1 -1
- package/dist/api.js +1 -1
- package/dist/api.js.map +1 -1
- package/dist/encoding.js +1 -1
- package/dist/encoding.js.map +1 -1
- package/dist/format.js +5 -5
- package/dist/format.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/scan.js +1 -1
- package/dist/scan.js.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +31 -12
- package/dist/server.js.map +1 -1
- package/dist/task.d.ts +11 -0
- package/dist/task.d.ts.map +1 -1
- package/dist/task.js +29 -5
- package/dist/task.js.map +1 -1
- package/dist/types.js +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +9 -1
- package/src/__tests__/encoding.test.ts +1 -1
- package/src/__tests__/format.test.ts +4 -4
- package/src/__tests__/proof-beside.test.ts +25 -0
- package/src/__tests__/scan.test.ts +1 -1
- package/src/__tests__/tools.test.ts +3 -3
- package/src/api.ts +1 -1
- package/src/encoding.ts +1 -1
- package/src/format.ts +5 -5
- package/src/index.ts +1 -1
- package/src/scan.ts +1 -1
- package/src/server.ts +29 -12
- package/src/task.ts +29 -5
- package/src/types.ts +1 -1
package/src/server.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Copyright (c) 2024-2026
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
659
|
-
|
|
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
|
|
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
|
|
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
|
|
161
|
-
`
|
|
162
|
-
"
|
|
163
|
-
"
|
|
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