@otskit/mcp 0.1.5 → 0.1.6
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/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="docs/header.png" alt="OTSkit.ts MCP" width="480" />
|
|
2
|
+
<img src="https://raw.githubusercontent.com/OTSkit/OTSkit-MCP/master/docs/header.png" alt="OTSkit.ts MCP" width="480" />
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
# @otskit/mcp
|
|
@@ -8,6 +8,8 @@ OpenTimestamps MCP server — stamp, upgrade, and verify Bitcoin timestamps via
|
|
|
8
8
|
|
|
9
9
|
Exposes a set of tools to any MCP-compatible agent so it can timestamp documents, monitor confirmation status, and verify proofs against the Bitcoin blockchain — all from a conversation.
|
|
10
10
|
|
|
11
|
+
> **Note on confirmation times:** After stamping, a proof is `pending` until Bitcoin confirms it. This typically takes **10–60 minutes** but can take **several hours** during network congestion. Use `ots-mcp watch` or `upgrade_timestamp` to monitor. A pending status is not an error.
|
|
12
|
+
|
|
11
13
|
## Install
|
|
12
14
|
|
|
13
15
|
```bash
|
|
@@ -38,7 +40,7 @@ Each command writes the MCP entry into the agent's config file, makes a `.bak` b
|
|
|
38
40
|
| `ots-mcp backup [dest]` | Backup the SQLite database |
|
|
39
41
|
| `ots-mcp setup <claude\|codex>` | Configure MCP for an agent |
|
|
40
42
|
|
|
41
|
-
## MCP tools exposed to
|
|
43
|
+
## MCP tools exposed to agents
|
|
42
44
|
|
|
43
45
|
| Tool | Description |
|
|
44
46
|
|---|---|
|
|
@@ -67,10 +69,6 @@ Create `~/.ots-mcp/config.json` to override defaults:
|
|
|
67
69
|
```json
|
|
68
70
|
{
|
|
69
71
|
"stamp_enabled": true,
|
|
70
|
-
"preserve_enabled": true,
|
|
71
|
-
"preserve_whitelist": ["/path/to/allowed/dir"],
|
|
72
|
-
"preserve_max_bytes": 104857600,
|
|
73
|
-
"preserve_max_files": 10000,
|
|
74
72
|
"scheduler_interval_minutes": 30,
|
|
75
73
|
"retry_max_attempts": 20,
|
|
76
74
|
"calendar_timeout_ms": 10000,
|
|
@@ -54,6 +54,10 @@ function listStamps(db, params) {
|
|
|
54
54
|
conds.push("created_at < ?");
|
|
55
55
|
vals.push(cutoff);
|
|
56
56
|
}
|
|
57
|
+
if (params.due_now) {
|
|
58
|
+
conds.push("(next_retry_at IS NULL OR next_retry_at <= ?)");
|
|
59
|
+
vals.push((/* @__PURE__ */ new Date()).toISOString());
|
|
60
|
+
}
|
|
57
61
|
const where = conds.length ? `WHERE ${conds.join(" AND ")}` : "";
|
|
58
62
|
const total = db.prepare(`SELECT COUNT(*) as n FROM stamps ${where}`).get(...vals).n;
|
|
59
63
|
const items = db.prepare(
|
|
@@ -121,17 +125,23 @@ async function createTimestamp(input, db, config) {
|
|
|
121
125
|
// src/tools/upgrade-timestamp.ts
|
|
122
126
|
import { readFileSync } from "fs";
|
|
123
127
|
import { OpenTimestampsClient as OpenTimestampsClient2, UpgradeError } from "@otskit/client";
|
|
124
|
-
import { DetachedTimestampFile } from "@otskit/core";
|
|
128
|
+
import { DetachedTimestampFile, StreamDeserializationContext } from "@otskit/core";
|
|
129
|
+
function collectAttestations(ts) {
|
|
130
|
+
const atts = [...ts.attestations];
|
|
131
|
+
for (const branch of ts.branches) {
|
|
132
|
+
atts.push(...collectAttestations(branch.stamp));
|
|
133
|
+
}
|
|
134
|
+
return atts;
|
|
135
|
+
}
|
|
125
136
|
function checkBitcoinConfirmation(bytes) {
|
|
126
137
|
try {
|
|
127
|
-
const
|
|
128
|
-
const
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
return { confirmed: false };
|
|
138
|
+
const ctx = new StreamDeserializationContext(new Uint8Array(bytes));
|
|
139
|
+
const dtf = DetachedTimestampFile.deserialize(ctx);
|
|
140
|
+
const attestations = collectAttestations(dtf.timestamp);
|
|
141
|
+
const bitcoinAtts = attestations.filter((a) => a.kind === "bitcoin");
|
|
142
|
+
if (bitcoinAtts.length === 0) return { confirmed: false };
|
|
143
|
+
const block = Math.min(...bitcoinAtts.map((a) => a.height));
|
|
144
|
+
return { confirmed: true, block };
|
|
135
145
|
} catch {
|
|
136
146
|
return { confirmed: false };
|
|
137
147
|
}
|
|
@@ -237,7 +247,8 @@ function listPending(input, db, _config) {
|
|
|
237
247
|
status: input.status ?? "pending",
|
|
238
248
|
limit: Math.min(input.limit ?? 50, 200),
|
|
239
249
|
offset: input.offset ?? 0,
|
|
240
|
-
older_than_hours: input.older_than_hours
|
|
250
|
+
older_than_hours: input.older_than_hours,
|
|
251
|
+
due_now: input.due_now
|
|
241
252
|
});
|
|
242
253
|
}
|
|
243
254
|
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
listPending,
|
|
4
4
|
upgradeTimestamp,
|
|
5
5
|
verifyTimestamp
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-VHQD7HZD.js";
|
|
7
7
|
import {
|
|
8
8
|
backupDb,
|
|
9
9
|
getDb,
|
|
@@ -68,7 +68,7 @@ async function runCli(command, args) {
|
|
|
68
68
|
break;
|
|
69
69
|
}
|
|
70
70
|
case "check-pending": {
|
|
71
|
-
const { items } = listPending({ status: "pending", limit: 200 }, db, config);
|
|
71
|
+
const { items } = listPending({ status: "pending", limit: 200, due_now: true }, db, config);
|
|
72
72
|
process.stderr.write(`Processing ${items.length} pending stamps...
|
|
73
73
|
`);
|
|
74
74
|
for (const record of items) {
|
package/dist/index.js
CHANGED
|
@@ -20,7 +20,7 @@ Commands:
|
|
|
20
20
|
}
|
|
21
21
|
switch (command) {
|
|
22
22
|
case "serve": {
|
|
23
|
-
const { runServer } = await import("./server-
|
|
23
|
+
const { runServer } = await import("./server-D5YXBC55.js");
|
|
24
24
|
await runServer();
|
|
25
25
|
break;
|
|
26
26
|
}
|
|
@@ -62,7 +62,7 @@ switch (command) {
|
|
|
62
62
|
case "check-pending":
|
|
63
63
|
case "backup":
|
|
64
64
|
case "scheduler": {
|
|
65
|
-
const { runCli } = await import("./cli-
|
|
65
|
+
const { runCli } = await import("./cli-TF5PYS3T.js");
|
|
66
66
|
await runCli(command, args);
|
|
67
67
|
break;
|
|
68
68
|
}
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
listPending,
|
|
5
5
|
upgradeTimestamp,
|
|
6
6
|
verifyTimestamp
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-VHQD7HZD.js";
|
|
8
8
|
import {
|
|
9
9
|
getDb,
|
|
10
10
|
loadConfig
|
|
@@ -31,15 +31,15 @@ function inspectTimestamp(input, db, _config) {
|
|
|
31
31
|
} catch {
|
|
32
32
|
return { error: "proof_missing", details: `Cannot read proof: ${record.proof_path}` };
|
|
33
33
|
}
|
|
34
|
-
let
|
|
35
|
-
let
|
|
34
|
+
let calendarAttestations = 0;
|
|
35
|
+
let bitcoinAttestations = 0;
|
|
36
36
|
let bitcoinBlock = null;
|
|
37
37
|
try {
|
|
38
38
|
const proof = DetachedTimestampFile.deserialize(new Uint8Array(proofBytes));
|
|
39
39
|
const attestations = proof.timestamp.getAttestations();
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (
|
|
40
|
+
bitcoinAttestations = attestations.filter((a) => a.kind === "bitcoin").length;
|
|
41
|
+
calendarAttestations = attestations.filter((a) => a.kind !== "bitcoin").length;
|
|
42
|
+
if (bitcoinAttestations > 0) {
|
|
43
43
|
const blocks = attestations.filter((a) => a.kind === "bitcoin").map((a) => a.height);
|
|
44
44
|
bitcoinBlock = blocks.length > 0 ? Math.min(...blocks) : null;
|
|
45
45
|
}
|
|
@@ -52,8 +52,9 @@ function inspectTimestamp(input, db, _config) {
|
|
|
52
52
|
created_at: record.created_at,
|
|
53
53
|
proof_path: record.proof_path,
|
|
54
54
|
proof_size_bytes: proofSize,
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
calendar_attestations: calendarAttestations,
|
|
56
|
+
bitcoin_attestations: bitcoinAttestations,
|
|
57
|
+
bitcoin_confirmed: bitcoinAttestations > 0,
|
|
57
58
|
bitcoin_block: bitcoinBlock
|
|
58
59
|
};
|
|
59
60
|
}
|
|
@@ -119,7 +120,7 @@ var TOOL_DEFINITIONS = [
|
|
|
119
120
|
},
|
|
120
121
|
{
|
|
121
122
|
name: "inspect_timestamp",
|
|
122
|
-
description: "
|
|
123
|
+
description: "Inspects a stored proof file without network calls. Returns calendar_attestations (promises from OTS servers, NOT Bitcoin confirmation) and bitcoin_attestations (actual Bitcoin blocks). A stamp is only truly confirmed when bitcoin_attestations > 0 and bitcoin_confirmed is true.",
|
|
123
124
|
inputSchema: {
|
|
124
125
|
type: "object",
|
|
125
126
|
properties: { id: { type: "string", description: "UUID from the stamp record" } },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@otskit/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "OpenTimestamps MCP server — stamp, upgrade, verify via AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
"test:watch": "vitest"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@otskit/core": "^0.1.0",
|
|
24
|
-
"@otskit/client": "^0.1.1",
|
|
25
23
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
24
|
+
"@otskit/client": "^0.1.1",
|
|
25
|
+
"@otskit/core": "^0.1.0",
|
|
26
26
|
"better-sqlite3": "^12.10.0"
|
|
27
27
|
},
|
|
28
28
|
"pnpm": {
|
|
@@ -32,8 +32,15 @@
|
|
|
32
32
|
]
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
+
"@semantic-release/changelog": "^6",
|
|
36
|
+
"@semantic-release/commit-analyzer": "^13.0.0",
|
|
37
|
+
"@semantic-release/git": "^10",
|
|
38
|
+
"@semantic-release/github": "^12.0.8",
|
|
39
|
+
"@semantic-release/npm": "^13.1.5",
|
|
40
|
+
"@semantic-release/release-notes-generator": "^14.0.0",
|
|
35
41
|
"@types/better-sqlite3": "^7.6.13",
|
|
36
42
|
"@types/node": "^22.0.0",
|
|
43
|
+
"semantic-release": "^25.0.3",
|
|
37
44
|
"tsup": "^8.3.5",
|
|
38
45
|
"typescript": "^5.6.3",
|
|
39
46
|
"vitest": "^2.1.4"
|