@otskit/mcp 0.1.2 → 0.1.4

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.
@@ -0,0 +1,115 @@
1
+ import {
2
+ getDataDir,
3
+ getStamp,
4
+ insertStamp,
5
+ listStamps,
6
+ logOperation
7
+ } from "./chunk-ASPLCXMC.js";
8
+ import {
9
+ writeAtomic
10
+ } from "./chunk-YFSUDT24.js";
11
+
12
+ // src/tools/create-timestamp.ts
13
+ import { mkdirSync } from "fs";
14
+ import { join } from "path";
15
+ import { randomUUID } from "crypto";
16
+ import { OpenTimestampsClient } from "@otskit/client";
17
+ var HEX64 = /^[0-9a-f]{64}$/i;
18
+ async function createTimestamp(input, db, config) {
19
+ if (!HEX64.test(input.hash)) {
20
+ return { error: "invalid_hash", details: "hash must be 64 hex characters (SHA-256)" };
21
+ }
22
+ const normalizedHash = input.hash.toLowerCase();
23
+ const client = new OpenTimestampsClient({
24
+ calendars: config.calendars,
25
+ resilience: { timeout: config.calendar_timeout_ms }
26
+ });
27
+ const t0 = Date.now();
28
+ let proofBuffer;
29
+ try {
30
+ proofBuffer = await client.stamp(normalizedHash);
31
+ } catch (e) {
32
+ return { error: "calendar_error", details: String(e) };
33
+ }
34
+ const responseTimeMs = Date.now() - t0;
35
+ const id = randomUUID();
36
+ const proofDir = join(getDataDir(), "proofs");
37
+ mkdirSync(proofDir, { recursive: true });
38
+ const proofPath = join(proofDir, `${id}.ots`);
39
+ try {
40
+ writeAtomic(proofPath, proofBuffer);
41
+ } catch (e) {
42
+ return { error: "storage_error", details: String(e) };
43
+ }
44
+ const record = insertStamp(db, { id, hash: normalizedHash, proof_path: proofPath });
45
+ logOperation(db, { stamp_id: id, action: "stamp", result: "success", response_time_ms: responseTimeMs });
46
+ return {
47
+ id: record.id,
48
+ hash: record.hash,
49
+ status: "pending",
50
+ calendars: config.calendars,
51
+ created_at: record.created_at,
52
+ proof_path: proofPath
53
+ };
54
+ }
55
+
56
+ // src/tools/verify-timestamp.ts
57
+ import { readFileSync } from "fs";
58
+ import { OpenTimestampsClient as OpenTimestampsClient2 } from "@otskit/client";
59
+ async function verifyTimestamp(input, db, config) {
60
+ const record = getStamp(db, input.id);
61
+ if (!record) return { error: "not_found", details: `No stamp with id ${input.id}` };
62
+ if (!record.proof_path) return { error: "storage_error", details: "No proof_path on record" };
63
+ let proofBytes;
64
+ try {
65
+ proofBytes = readFileSync(record.proof_path);
66
+ } catch (e) {
67
+ return { error: "storage_error", details: String(e) };
68
+ }
69
+ const client = new OpenTimestampsClient2({
70
+ calendars: config.calendars,
71
+ resilience: { timeout: config.calendar_timeout_ms }
72
+ });
73
+ let result;
74
+ try {
75
+ result = await client.verify(proofBytes, record.hash);
76
+ } catch (e) {
77
+ logOperation(db, { stamp_id: input.id, action: "verify", result: "failed", error_msg: String(e) });
78
+ return { status: "network_error", hash: record.hash, details: String(e) };
79
+ }
80
+ if (!result.valid) {
81
+ if (result.error?.includes("No Bitcoin attestation")) {
82
+ logOperation(db, { stamp_id: input.id, action: "verify", result: "pending" });
83
+ return { status: "pending", hash: record.hash, calendars: config.calendars };
84
+ }
85
+ if (result.error?.toLowerCase().includes("invalid") || result.error?.toLowerCase().includes("corrupt")) {
86
+ logOperation(db, { stamp_id: input.id, action: "verify", result: "failed", error_msg: result.error });
87
+ return { status: "invalid", hash: record.hash, reason: result.error ?? "unknown" };
88
+ }
89
+ logOperation(db, { stamp_id: input.id, action: "verify", result: "failed", error_msg: result.error });
90
+ return { status: "unknown", hash: record.hash };
91
+ }
92
+ logOperation(db, { stamp_id: input.id, action: "verify", result: "success" });
93
+ return {
94
+ status: "confirmed",
95
+ hash: record.hash,
96
+ bitcoin_block: result.blockHeight,
97
+ bitcoin_time: new Date(result.timestamp * 1e3).toISOString()
98
+ };
99
+ }
100
+
101
+ // src/tools/list-pending.ts
102
+ function listPending(input, db, _config) {
103
+ return listStamps(db, {
104
+ status: input.status ?? "pending",
105
+ limit: Math.min(input.limit ?? 50, 200),
106
+ offset: input.offset ?? 0,
107
+ older_than_hours: input.older_than_hours
108
+ });
109
+ }
110
+
111
+ export {
112
+ createTimestamp,
113
+ verifyTimestamp,
114
+ listPending
115
+ };
@@ -119,11 +119,10 @@ function reconcileOrphans(db) {
119
119
  }
120
120
  }
121
121
 
122
- // src/tools/create-timestamp.ts
123
- import { mkdirSync as mkdirSync3 } from "fs";
124
- import { join as join3 } from "path";
125
- import { randomUUID } from "crypto";
126
- import { OpenTimestampsClient } from "@otskit/client";
122
+ // src/tools/upgrade-timestamp.ts
123
+ import { readFileSync as readFileSync2 } from "fs";
124
+ import { OpenTimestampsClient, UpgradeError } from "@otskit/client";
125
+ import { DetachedTimestampFile } from "@otskit/core";
127
126
 
128
127
  // src/db/stamps.ts
129
128
  function insertStamp(db, params) {
@@ -192,50 +191,7 @@ function logOperation(db, params) {
192
191
  );
193
192
  }
194
193
 
195
- // src/tools/create-timestamp.ts
196
- var HEX64 = /^[0-9a-f]{64}$/i;
197
- async function createTimestamp(input, db, config) {
198
- if (!HEX64.test(input.hash)) {
199
- return { error: "invalid_hash", details: "hash must be 64 hex characters (SHA-256)" };
200
- }
201
- const normalizedHash = input.hash.toLowerCase();
202
- const client = new OpenTimestampsClient({
203
- calendars: config.calendars,
204
- resilience: { timeout: config.calendar_timeout_ms }
205
- });
206
- const t0 = Date.now();
207
- let proofBuffer;
208
- try {
209
- proofBuffer = await client.stamp(normalizedHash);
210
- } catch (e) {
211
- return { error: "calendar_error", details: String(e) };
212
- }
213
- const responseTimeMs = Date.now() - t0;
214
- const id = randomUUID();
215
- const proofDir = join3(getDataDir(), "proofs");
216
- mkdirSync3(proofDir, { recursive: true });
217
- const proofPath = join3(proofDir, `${id}.ots`);
218
- try {
219
- writeAtomic(proofPath, proofBuffer);
220
- } catch (e) {
221
- return { error: "storage_error", details: String(e) };
222
- }
223
- const record = insertStamp(db, { id, hash: normalizedHash, proof_path: proofPath });
224
- logOperation(db, { stamp_id: id, action: "stamp", result: "success", response_time_ms: responseTimeMs });
225
- return {
226
- id: record.id,
227
- hash: record.hash,
228
- status: "pending",
229
- calendars: config.calendars,
230
- created_at: record.created_at,
231
- proof_path: proofPath
232
- };
233
- }
234
-
235
194
  // src/tools/upgrade-timestamp.ts
236
- import { readFileSync as readFileSync2 } from "fs";
237
- import { OpenTimestampsClient as OpenTimestampsClient2, UpgradeError } from "@otskit/client";
238
- import { DetachedTimestampFile } from "@otskit/core";
239
195
  function checkBitcoinConfirmation(bytes) {
240
196
  try {
241
197
  const proof = DetachedTimestampFile.deserialize(new Uint8Array(bytes));
@@ -260,7 +216,7 @@ async function upgradeTimestamp(input, db, config) {
260
216
  if (!record) return { error: "not_found", details: `No stamp with id ${input.id}` };
261
217
  if (!record.proof_path) return { error: "storage_error", details: "No proof_path on record" };
262
218
  const proofBefore = readFileSync2(record.proof_path);
263
- const client = new OpenTimestampsClient2({
219
+ const client = new OpenTimestampsClient({
264
220
  calendars: config.calendars,
265
221
  resilience: { timeout: config.calendar_timeout_ms }
266
222
  });
@@ -300,69 +256,14 @@ async function upgradeTimestamp(input, db, config) {
300
256
  return { id: input.id, status: "pending", attempt_count: newAttemptCount, last_attempt_at: now, next_retry_at: next };
301
257
  }
302
258
 
303
- // src/tools/verify-timestamp.ts
304
- import { readFileSync as readFileSync3 } from "fs";
305
- import { OpenTimestampsClient as OpenTimestampsClient3 } from "@otskit/client";
306
- async function verifyTimestamp(input, db, config) {
307
- const record = getStamp(db, input.id);
308
- if (!record) return { error: "not_found", details: `No stamp with id ${input.id}` };
309
- if (!record.proof_path) return { error: "storage_error", details: "No proof_path on record" };
310
- let proofBytes;
311
- try {
312
- proofBytes = readFileSync3(record.proof_path);
313
- } catch (e) {
314
- return { error: "storage_error", details: String(e) };
315
- }
316
- const client = new OpenTimestampsClient3({
317
- calendars: config.calendars,
318
- resilience: { timeout: config.calendar_timeout_ms }
319
- });
320
- let result;
321
- try {
322
- result = await client.verify(proofBytes, record.hash);
323
- } catch (e) {
324
- logOperation(db, { stamp_id: input.id, action: "verify", result: "failed", error_msg: String(e) });
325
- return { status: "network_error", hash: record.hash, details: String(e) };
326
- }
327
- if (!result.valid) {
328
- if (result.error?.includes("No Bitcoin attestation")) {
329
- logOperation(db, { stamp_id: input.id, action: "verify", result: "pending" });
330
- return { status: "pending", hash: record.hash, calendars: config.calendars };
331
- }
332
- if (result.error?.toLowerCase().includes("invalid") || result.error?.toLowerCase().includes("corrupt")) {
333
- logOperation(db, { stamp_id: input.id, action: "verify", result: "failed", error_msg: result.error });
334
- return { status: "invalid", hash: record.hash, reason: result.error ?? "unknown" };
335
- }
336
- logOperation(db, { stamp_id: input.id, action: "verify", result: "failed", error_msg: result.error });
337
- return { status: "unknown", hash: record.hash };
338
- }
339
- logOperation(db, { stamp_id: input.id, action: "verify", result: "success" });
340
- return {
341
- status: "confirmed",
342
- hash: record.hash,
343
- bitcoin_block: result.blockHeight,
344
- bitcoin_time: new Date(result.timestamp * 1e3).toISOString()
345
- };
346
- }
347
-
348
- // src/tools/list-pending.ts
349
- function listPending(input, db, _config) {
350
- return listStamps(db, {
351
- status: input.status ?? "pending",
352
- limit: Math.min(input.limit ?? 50, 200),
353
- offset: input.offset ?? 0,
354
- older_than_hours: input.older_than_hours
355
- });
356
- }
357
-
358
259
  export {
359
260
  getDataDir,
360
261
  loadConfig,
361
262
  getDb,
362
263
  backupDb,
264
+ insertStamp,
363
265
  getStamp,
364
- createTimestamp,
365
- upgradeTimestamp,
366
- verifyTimestamp,
367
- listPending
266
+ listStamps,
267
+ logOperation,
268
+ upgradeTimestamp
368
269
  };
@@ -1,12 +1,14 @@
1
1
  import {
2
- backupDb,
3
2
  createTimestamp,
4
- getDb,
5
3
  listPending,
6
- loadConfig,
7
- upgradeTimestamp,
8
4
  verifyTimestamp
9
- } from "./chunk-6IPLIM6I.js";
5
+ } from "./chunk-45GR42GV.js";
6
+ import {
7
+ backupDb,
8
+ getDb,
9
+ loadConfig,
10
+ upgradeTimestamp
11
+ } from "./chunk-ASPLCXMC.js";
10
12
  import "./chunk-YFSUDT24.js";
11
13
 
12
14
  // src/cli.ts
package/dist/index.js CHANGED
@@ -5,23 +5,36 @@ var [, , command, ...args] = process.argv;
5
5
  if (!command || command === "--help" || command === "help") {
6
6
  process.stderr.write(`Usage: ots-mcp <command>
7
7
  Commands:
8
- serve Start MCP server (stdio transport)
9
- stamp <hash> Stamp a SHA-256 hash
10
- upgrade <id> Upgrade a pending stamp
11
- verify <id> Verify a stamp
12
- list [status] List stamps (default: pending)
13
- check-pending Run pending upgrades (for scheduler)
14
- backup [dest] Backup the SQLite database
15
- scheduler Manage OS scheduler (install|remove|status)
8
+ serve Start MCP server (stdio transport)
9
+ install-claude Install MCP in Claude Desktop config (auto-setup)
10
+ watch [interval] Watch pending stamps in real-time (default: 5 min)
11
+ stamp <hash> Stamp a SHA-256 hash
12
+ upgrade <id> Upgrade a pending stamp
13
+ verify <id> Verify a stamp
14
+ list [status] List stamps (default: pending)
15
+ check-pending Run pending upgrades (for scheduler)
16
+ backup [dest] Backup the SQLite database
17
+ scheduler Manage OS scheduler (install|remove|status)
16
18
  `);
17
19
  process.exit(command ? 0 : 1);
18
20
  }
19
21
  switch (command) {
20
22
  case "serve": {
21
- const { runServer } = await import("./server-NCO7JIUI.js");
23
+ const { runServer } = await import("./server-2YEI77IT.js");
22
24
  await runServer();
23
25
  break;
24
26
  }
27
+ case "install-claude": {
28
+ const { installClaude } = await import("./install-claude-UKSS65BW.js");
29
+ installClaude();
30
+ break;
31
+ }
32
+ case "watch": {
33
+ const { watchPending } = await import("./watch-JWDMAZWS.js");
34
+ const interval = args[0] ? parseInt(args[0]) : 5;
35
+ await watchPending(interval);
36
+ break;
37
+ }
25
38
  case "stamp":
26
39
  case "upgrade":
27
40
  case "verify":
@@ -29,7 +42,7 @@ switch (command) {
29
42
  case "check-pending":
30
43
  case "backup":
31
44
  case "scheduler": {
32
- const { runCli } = await import("./cli-GQMURR5I.js");
45
+ const { runCli } = await import("./cli-67APZKT6.js");
33
46
  await runCli(command, args);
34
47
  break;
35
48
  }
@@ -0,0 +1,44 @@
1
+ // src/install-claude.ts
2
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
3
+ import { join } from "path";
4
+ function getConfigPath() {
5
+ if (process.platform === "win32") {
6
+ return join(process.env.APPDATA ?? "", "Claude", "claude_desktop_config.json");
7
+ } else if (process.platform === "darwin") {
8
+ return join(process.env.HOME ?? "", "Library", "Application Support", "Claude", "claude_desktop_config.json");
9
+ } else {
10
+ return join(process.env.HOME ?? "", ".config", "Claude", "claude_desktop_config.json");
11
+ }
12
+ }
13
+ function installClaude() {
14
+ const configPath = getConfigPath();
15
+ const configDir = join(configPath, "..");
16
+ if (!existsSync(configDir)) {
17
+ mkdirSync(configDir, { recursive: true });
18
+ }
19
+ let config = {};
20
+ if (existsSync(configPath)) {
21
+ try {
22
+ config = JSON.parse(readFileSync(configPath, "utf8"));
23
+ } catch {
24
+ process.stderr.write(`Warning: could not parse existing config, creating new one
25
+ `);
26
+ }
27
+ }
28
+ const mcpServers = config.mcpServers ?? {};
29
+ mcpServers["otskit"] = {
30
+ command: "npx",
31
+ args: ["-y", "@otskit/mcp", "serve"]
32
+ };
33
+ config.mcpServers = mcpServers;
34
+ writeFileSync(configPath, JSON.stringify(config, null, 2), "utf8");
35
+ process.stdout.write(`\u2713 OTSkit MCP installed in Claude Desktop
36
+ `);
37
+ process.stdout.write(` Config: ${configPath}
38
+ `);
39
+ process.stdout.write(` Restart Claude Desktop to apply changes.
40
+ `);
41
+ }
42
+ export {
43
+ installClaude
44
+ };
@@ -1,13 +1,15 @@
1
1
  import {
2
2
  createTimestamp,
3
+ listPending,
4
+ verifyTimestamp
5
+ } from "./chunk-45GR42GV.js";
6
+ import {
3
7
  getDataDir,
4
8
  getDb,
5
9
  getStamp,
6
- listPending,
7
10
  loadConfig,
8
- upgradeTimestamp,
9
- verifyTimestamp
10
- } from "./chunk-6IPLIM6I.js";
11
+ upgradeTimestamp
12
+ } from "./chunk-ASPLCXMC.js";
11
13
  import "./chunk-YFSUDT24.js";
12
14
 
13
15
  // src/server.ts
@@ -0,0 +1,47 @@
1
+ import {
2
+ getDb,
3
+ loadConfig,
4
+ upgradeTimestamp
5
+ } from "./chunk-ASPLCXMC.js";
6
+ import "./chunk-YFSUDT24.js";
7
+
8
+ // src/tools/watch.ts
9
+ async function watchPending(intervalMinutes = 5) {
10
+ const config = loadConfig();
11
+ const db = getDb(config);
12
+ process.stdout.write(`Watching pending stamps every ${intervalMinutes} min. Ctrl+C to stop.
13
+
14
+ `);
15
+ async function tick() {
16
+ const rows = db.prepare(`SELECT id, hash, status FROM stamps WHERE status = 'pending'`).all();
17
+ if (rows.length === 0) {
18
+ process.stdout.write(`${now()} All stamps confirmed.
19
+ `);
20
+ process.exit(0);
21
+ }
22
+ process.stdout.write(`${now()} Checking ${rows.length} pending stamps...
23
+ `);
24
+ for (const row of rows) {
25
+ const result = await upgradeTimestamp({ id: row.id }, db, config);
26
+ if ("error" in result) {
27
+ process.stdout.write(` ${row.id.slice(0, 8)}... ERROR: ${result.error}
28
+ `);
29
+ } else if (result.status === "confirmed") {
30
+ process.stdout.write(` ${row.id.slice(0, 8)}... CONFIRMED block #${result.bitcoin_block}
31
+ `);
32
+ } else {
33
+ process.stdout.write(` ${row.id.slice(0, 8)}... pending (attempt ${result.attempt_count})
34
+ `);
35
+ }
36
+ }
37
+ process.stdout.write("\n");
38
+ }
39
+ function now() {
40
+ return (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
41
+ }
42
+ await tick();
43
+ setInterval(tick, intervalMinutes * 60 * 1e3);
44
+ }
45
+ export {
46
+ watchPending
47
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@otskit/mcp",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "OpenTimestamps MCP server — stamp, upgrade, verify via AI agents",
5
5
  "type": "module",
6
6
  "engines": { "node": ">=18" },