@sheafhq/mcp-tools 0.1.0 → 0.1.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/dist/cli.js CHANGED
@@ -3,13 +3,21 @@
3
3
  // (Claude Desktop, Cline, custom runtime) and speaks JSON-RPC over stdio.
4
4
  //
5
5
  // Environment variables (all optional):
6
- // SHEAF_RECORDER_URL where to query (default: production recorder)
6
+ // SHEAF_RECORDER_URL explicit recorder override. If unset, tries
7
+ // https://api.sheafhq.io and falls back to
8
+ // https://sheaf-recorder.goldencalf.workers.dev.
7
9
  // SHEAF_BASE_RPC Base mainnet RPC for anchor verification
8
10
  // (default: https://mainnet.base.org)
9
11
  import { runServer } from "./server.js";
12
+ const DEFAULT_URLS = [
13
+ "https://api.sheafhq.io",
14
+ "https://sheaf-recorder.goldencalf.workers.dev",
15
+ ];
16
+ const recorderUrls = process.env.SHEAF_RECORDER_URL
17
+ ? [process.env.SHEAF_RECORDER_URL]
18
+ : DEFAULT_URLS;
10
19
  const ctx = {
11
- recorderUrl: process.env.SHEAF_RECORDER_URL ??
12
- "https://sheaf-recorder.goldencalf.workers.dev",
20
+ recorderUrls,
13
21
  baseRpc: process.env.SHEAF_BASE_RPC ?? "https://mainnet.base.org",
14
22
  };
15
23
  runServer(ctx);
package/dist/tools.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export type ToolContext = {
2
- recorderUrl: string;
2
+ /** Primary + fallback recorder base URLs. First entry is reported as
3
+ * `source_url` in responses. Fallback only kicks in on fetch error. */
4
+ recorderUrls: string[];
3
5
  baseRpc: string;
4
6
  };
5
7
  export declare function chainHead(ctx: ToolContext, args: {
package/dist/tools.js CHANGED
@@ -5,12 +5,24 @@
5
5
  // All three tools are READ-only against the Sheaf recorder. No wallet, no
6
6
  // settlement, no mutation. Safe to expose to any agent.
7
7
  import { verify } from "@sheafhq/verify";
8
+ async function fetchWithFallback(ctx, path, init) {
9
+ let lastErr;
10
+ for (const base of ctx.recorderUrls) {
11
+ try {
12
+ const response = await fetch(base + path, init);
13
+ return { response, sourceUrl: base + path };
14
+ }
15
+ catch (err) {
16
+ lastErr = err;
17
+ }
18
+ }
19
+ throw lastErr ?? new Error("no recorder URLs configured");
20
+ }
8
21
  export async function chainHead(ctx, args) {
9
22
  if (!args.chain_id || typeof args.chain_id !== "string") {
10
23
  throw new Error("chain_id (string) is required");
11
24
  }
12
- const url = `${ctx.recorderUrl}/chains/${encodeURIComponent(args.chain_id)}/head`;
13
- const r = await fetch(url);
25
+ const { response: r, sourceUrl } = await fetchWithFallback(ctx, `/chains/${encodeURIComponent(args.chain_id)}/head`);
14
26
  if (r.status === 404) {
15
27
  return {
16
28
  chain_id: args.chain_id,
@@ -29,7 +41,7 @@ export async function chainHead(ctx, args) {
29
41
  event_count: body.event_count,
30
42
  created_at_unix: body.created_at,
31
43
  updated_at_unix: body.updated_at,
32
- source_url: url,
44
+ source_url: sourceUrl,
33
45
  };
34
46
  }
35
47
  export async function getProofFor(ctx, args) {
@@ -40,8 +52,7 @@ export async function getProofFor(ctx, args) {
40
52
  throw new Error("seq (non-negative integer) is required");
41
53
  }
42
54
  // First find out which epoch the event is in.
43
- const evUrl = `${ctx.recorderUrl}/events/${encodeURIComponent(args.chain_id)}/${args.seq}`;
44
- const evRes = await fetch(evUrl);
55
+ const { response: evRes } = await fetchWithFallback(ctx, `/events/${encodeURIComponent(args.chain_id)}/${args.seq}`);
45
56
  if (evRes.status === 404) {
46
57
  throw new Error(`no event at ${args.chain_id}/${args.seq}`);
47
58
  }
@@ -58,8 +69,7 @@ export async function getProofFor(ctx, args) {
58
69
  note: "Event is signed and chained but not yet included in an on-chain anchor. Call again after the next epoch anchor to get a Merkle inclusion proof.",
59
70
  };
60
71
  }
61
- const proofUrl = `${ctx.recorderUrl}/epoch/${ev.epoch_id}/proof/${encodeURIComponent(args.chain_id)}/${args.seq}`;
62
- const proofRes = await fetch(proofUrl);
72
+ const { response: proofRes, sourceUrl } = await fetchWithFallback(ctx, `/epoch/${ev.epoch_id}/proof/${encodeURIComponent(args.chain_id)}/${args.seq}`);
63
73
  if (!proofRes.ok) {
64
74
  throw new Error(`recorder returned HTTP ${proofRes.status} on proof fetch`);
65
75
  }
@@ -75,7 +85,7 @@ export async function getProofFor(ctx, args) {
75
85
  siblings: proof.siblings,
76
86
  anchor: proof.anchor,
77
87
  verification_note: proof.verification_note,
78
- source_url: proofUrl,
88
+ source_url: sourceUrl,
79
89
  };
80
90
  }
81
91
  export async function verifyBundle(ctx, args) {
@@ -111,7 +121,7 @@ export async function verifyBundle(ctx, args) {
111
121
  // verification is self-contained; fall back to /.well-known if missing.
112
122
  let recorderKey = b.issuer?.public_key_base64 ?? "";
113
123
  if (!recorderKey) {
114
- const r = await fetch(`${ctx.recorderUrl}/.well-known/sheaf-public-key`);
124
+ const { response: r } = await fetchWithFallback(ctx, `/.well-known/sheaf-public-key`);
115
125
  if (!r.ok) {
116
126
  throw new Error(`could not fetch recorder public key: HTTP ${r.status}`);
117
127
  }
@@ -136,7 +146,7 @@ export async function verifyBundle(ctx, args) {
136
146
  checks: result.checks,
137
147
  reason: result.reason,
138
148
  anchor_block: result.anchor_block,
139
- recorder: ctx.recorderUrl,
149
+ recorder: ctx.recorderUrls[0],
140
150
  anchorer_checked: anchorer,
141
151
  };
142
152
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sheafhq/mcp-tools",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "MCP server exposing Sheaf read tools (chain_head, get_proof_for, verify_bundle). Lets any MCP-compatible agent query the Sheaf recorder and verify receipts offline. Pairs with @sheafhq/mcp-recorder (transparent event capture) but ships as a separate, opt-in tool surface so the recorder stays purely transparent.",
5
5
  "type": "module",
6
6
  "main": "./dist/cli.js",