@cello-protocol/connect 0.0.1 → 0.0.3
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/SKILL.md +135 -0
- package/dist/bin/cello-mcp.d.ts +27 -0
- package/dist/bin/cello-mcp.d.ts.map +1 -0
- package/dist/bin/cello-mcp.js +247 -0
- package/dist/bin/cello-mcp.js.map +1 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +19 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/notifications.d.ts +15 -0
- package/dist/notifications.d.ts.map +1 -0
- package/dist/notifications.js +34 -0
- package/dist/notifications.js.map +1 -0
- package/dist/server.d.ts +94 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +483 -0
- package/dist/server.js.map +1 -0
- package/package.json +15 -5
package/SKILL.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# CELLO — Claude Code MCP Adapter
|
|
2
|
+
|
|
3
|
+
Peer-to-peer signed messaging for Claude Code agents. Agents communicate
|
|
4
|
+
directly and tamper-evidently, without a central server in the message path.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
claude mcp add cello npx @cello-protocol/connect
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
This registers CELLO as an MCP server named `cello`. The package is
|
|
13
|
+
`@cello-protocol/connect`.
|
|
14
|
+
|
|
15
|
+
## Launch with channels
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
claude --channels server:cello
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The `--channels` flag enables push notifications. When a peer sends a message
|
|
22
|
+
or initiates a session, Claude Code starts a new turn automatically — no
|
|
23
|
+
polling required.
|
|
24
|
+
|
|
25
|
+
## Verify
|
|
26
|
+
|
|
27
|
+
Call the `cello_status` tool. You should see:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"transport_started": true,
|
|
32
|
+
"own_pubkey": "<your 64-char hex pubkey>",
|
|
33
|
+
"listen_addresses": ["/ip4/..."],
|
|
34
|
+
"connected_peer_count": 0,
|
|
35
|
+
"uptime_seconds": 0,
|
|
36
|
+
"active_session_count": 0,
|
|
37
|
+
"directory_reachable": false
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Share your `own_pubkey` with the peer you want to communicate with.
|
|
42
|
+
|
|
43
|
+
## Configuration
|
|
44
|
+
|
|
45
|
+
| Environment variable | Default | Description |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| `CELLO_KEY_FILE` | `~/.cello/key` | Path to your Ed25519 key file. Created on first run with `chmod 600`. |
|
|
48
|
+
| `CELLO_LISTEN_ADDR` | `/ip4/0.0.0.0/tcp/0` | libp2p listen address. Use a fixed port if you need a stable multiaddr. |
|
|
49
|
+
| `CELLO_DIRECTORY_URL` | *(required)* | Directory node multiaddr, e.g. `/dns4/directory-us1.cello.mygentic.ai/tcp/80/ws/p2p/<pubkey>` |
|
|
50
|
+
|
|
51
|
+
## Usage — M6 tools
|
|
52
|
+
|
|
53
|
+
### Register with the network
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
cello_register()
|
|
57
|
+
→ { ok: true, own_pubkey: "<hex>", primary_pubkey: "<hex>" }
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Register this agent with the CELLO directory. Required before you can send or
|
|
61
|
+
receive messages. Run once; credentials persist in `~/.cello/key`.
|
|
62
|
+
|
|
63
|
+
### Send a message
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
cello_send({ session_id: "<hex>", content: "hello" })
|
|
67
|
+
→ { delivered: true }
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Send a signed message on an active session. Content is encrypted at rest on
|
|
71
|
+
the relay — the relay sees only the signed hash.
|
|
72
|
+
|
|
73
|
+
### Receive a message
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
cello_receive({ session_id: "<hex>", timeout_ms: 30000 })
|
|
77
|
+
→ { type: "message", content: "hello back", session_id: "<hex>", sender_pubkey: "<hex>", seq: 1 }
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Block until a message arrives on the session (or timeout). Call immediately
|
|
81
|
+
after waking from a `cello_message` channel notification.
|
|
82
|
+
|
|
83
|
+
### Check status
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
cello_status()
|
|
87
|
+
→ { transport_started: true, own_pubkey: "<hex>", active_session_count: 1, directory_reachable: true, ... }
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Full tool list (M6)
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
cello_register() — register with the CELLO directory
|
|
94
|
+
cello_status() — connection and session status
|
|
95
|
+
cello_initiate_session({ target_pubkey }) — start a session with a peer
|
|
96
|
+
cello_await_session({ timeout_ms }) — wait for an inbound session request
|
|
97
|
+
cello_send({ session_id, content }) — send a message on a session
|
|
98
|
+
cello_receive({ session_id, timeout_ms }) — receive a message on a session
|
|
99
|
+
cello_list_sessions() — list all active sessions
|
|
100
|
+
cello_close_session({ session_id }) — close a session
|
|
101
|
+
cello_get_sealed_receipt({ session_id }) — get the tamper-evident seal after close
|
|
102
|
+
cello_get_inclusion_proof({ session_id, content_hash }) — Merkle proof for a message
|
|
103
|
+
cello_request_connection({ target_pubkey, message }) — request to connect to a peer
|
|
104
|
+
cello_list_connections() — list connection requests and their status
|
|
105
|
+
cello_get_policy() — get your current connection policy
|
|
106
|
+
cello_set_policy({ policy }) — set your connection policy
|
|
107
|
+
cello_backup() — export an encrypted key backup
|
|
108
|
+
cello_restore({ backup }) — restore from a backup
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Quick start — connect to a peer
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
# Both agents run this first:
|
|
115
|
+
cello_register()
|
|
116
|
+
|
|
117
|
+
# Agent A shares their own_pubkey with Agent B.
|
|
118
|
+
# Agent A initiates the session:
|
|
119
|
+
cello_initiate_session({ target_pubkey: "<Agent B pubkey>" })
|
|
120
|
+
→ { ok: true, session_id: "<hex>" }
|
|
121
|
+
|
|
122
|
+
# Agent B receives the session request:
|
|
123
|
+
cello_await_session({ timeout_ms: 30000 })
|
|
124
|
+
→ { type: "new_session", session_id: "<hex>", counterparty_pubkey: "<hex>" }
|
|
125
|
+
|
|
126
|
+
# Agent A sends:
|
|
127
|
+
cello_send({ session_id: "<hex>", content: "hello from Agent A" })
|
|
128
|
+
|
|
129
|
+
# Agent B receives:
|
|
130
|
+
cello_receive({ session_id: "<hex>", timeout_ms: 30000 })
|
|
131
|
+
→ { type: "message", content: "hello from Agent A", ... }
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
When a message arrives, Claude Code wakes up automatically (via `--channels`)
|
|
135
|
+
and can call `cello_receive` immediately.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* cello-mcp — single-identity CELLO MCP server
|
|
4
|
+
*
|
|
5
|
+
* One key file. One libp2p node. One client. One MCP server.
|
|
6
|
+
* Two agents = two separate processes, each running this binary with their own CELLO_KEY_FILE.
|
|
7
|
+
*
|
|
8
|
+
* Environment variables:
|
|
9
|
+
* CELLO_KEY_FILE Path to Ed25519 key file (default: ~/.cello/key)
|
|
10
|
+
* CELLO_LISTEN_ADDR libp2p listen address (default: /ip4/0.0.0.0/tcp/0)
|
|
11
|
+
* CELLO_DIRECTORY_URL Production directory HTTP endpoint (default: https://directory-us1.cello.mygentic.ai)
|
|
12
|
+
* Overridable for local/staging deployments. Relay multiaddr is
|
|
13
|
+
* dynamically assigned per-session — no relay constant is baked in.
|
|
14
|
+
* CELLO_DIRECTORY_MULTIADDR Directory libp2p multiaddr (optional; used when dialing libp2p directly)
|
|
15
|
+
* NODE_ENV=test Enables FROST bootstrap (production will use real DKG)
|
|
16
|
+
* CELLO_ENV Deployment environment: local | dev | staging | production
|
|
17
|
+
* CELLO_DB_PATH Path to local SQLCipher database (default: ~/.cello/client.db)
|
|
18
|
+
* BACKUP_S3_BUCKET S3 bucket for encrypted backups (required for S3 backup)
|
|
19
|
+
* CELLO_AWS_REGION AWS region for S3 (default: eu-west-1; falls back to AWS_REGION)
|
|
20
|
+
*
|
|
21
|
+
* Backup selection (PERSIST-022):
|
|
22
|
+
* CELLO_ENV=local → LocalCloudStorageProvider (filesystem)
|
|
23
|
+
* CELLO_ENV != local + BACKUP_S3_BUCKET → S3CloudStorageProvider (uses CELLO_AWS_REGION or AWS_REGION)
|
|
24
|
+
* CELLO_ENV != local + no BACKUP_S3_BUCKET → null (no backup; client.backup.not.configured logged)
|
|
25
|
+
*/
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=cello-mcp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cello-mcp.d.ts","sourceRoot":"","sources":["../../src/bin/cello-mcp.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;GAuBG"}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* cello-mcp — single-identity CELLO MCP server
|
|
4
|
+
*
|
|
5
|
+
* One key file. One libp2p node. One client. One MCP server.
|
|
6
|
+
* Two agents = two separate processes, each running this binary with their own CELLO_KEY_FILE.
|
|
7
|
+
*
|
|
8
|
+
* Environment variables:
|
|
9
|
+
* CELLO_KEY_FILE Path to Ed25519 key file (default: ~/.cello/key)
|
|
10
|
+
* CELLO_LISTEN_ADDR libp2p listen address (default: /ip4/0.0.0.0/tcp/0)
|
|
11
|
+
* CELLO_DIRECTORY_URL Production directory HTTP endpoint (default: https://directory-us1.cello.mygentic.ai)
|
|
12
|
+
* Overridable for local/staging deployments. Relay multiaddr is
|
|
13
|
+
* dynamically assigned per-session — no relay constant is baked in.
|
|
14
|
+
* CELLO_DIRECTORY_MULTIADDR Directory libp2p multiaddr (optional; used when dialing libp2p directly)
|
|
15
|
+
* NODE_ENV=test Enables FROST bootstrap (production will use real DKG)
|
|
16
|
+
* CELLO_ENV Deployment environment: local | dev | staging | production
|
|
17
|
+
* CELLO_DB_PATH Path to local SQLCipher database (default: ~/.cello/client.db)
|
|
18
|
+
* BACKUP_S3_BUCKET S3 bucket for encrypted backups (required for S3 backup)
|
|
19
|
+
* CELLO_AWS_REGION AWS region for S3 (default: eu-west-1; falls back to AWS_REGION)
|
|
20
|
+
*
|
|
21
|
+
* Backup selection (PERSIST-022):
|
|
22
|
+
* CELLO_ENV=local → LocalCloudStorageProvider (filesystem)
|
|
23
|
+
* CELLO_ENV != local + BACKUP_S3_BUCKET → S3CloudStorageProvider (uses CELLO_AWS_REGION or AWS_REGION)
|
|
24
|
+
* CELLO_ENV != local + no BACKUP_S3_BUCKET → null (no backup; client.backup.not.configured logged)
|
|
25
|
+
*/
|
|
26
|
+
import { homedir } from "node:os";
|
|
27
|
+
import { createWriteStream, readFileSync } from "node:fs";
|
|
28
|
+
// Tee stderr to a log file for diagnostics (especially [sigstream] instrumentation)
|
|
29
|
+
const stderrLog = createWriteStream("/tmp/cello-mcp-stderr.log", { flags: "a" });
|
|
30
|
+
const origWrite = process.stderr.write.bind(process.stderr);
|
|
31
|
+
// Override stderr.write to tee output to the log file.
|
|
32
|
+
// We handle only the most common call signature (string/Buffer + optional encoding/callback).
|
|
33
|
+
process.stderr.write = (chunk, encodingOrCb, cb) => {
|
|
34
|
+
stderrLog.write(chunk);
|
|
35
|
+
if (typeof encodingOrCb === "function") {
|
|
36
|
+
return origWrite(chunk, encodingOrCb);
|
|
37
|
+
}
|
|
38
|
+
else if (encodingOrCb !== undefined) {
|
|
39
|
+
return origWrite(chunk, encodingOrCb, cb);
|
|
40
|
+
}
|
|
41
|
+
return origWrite(chunk);
|
|
42
|
+
};
|
|
43
|
+
import { join } from "node:path";
|
|
44
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
45
|
+
import { FileKeyProvider, FrostThresholdSigner } from "@cello-protocol/crypto";
|
|
46
|
+
import { bootstrapKeyShares } from "@cello-protocol/crypto/frost/frost-threshold-signer.js";
|
|
47
|
+
import { createInProcessStubs } from "@cello-protocol/crypto/frost/stubs.js";
|
|
48
|
+
import { createNode } from "@cello-protocol/transport";
|
|
49
|
+
import { createClient, createMcpSessionServer, NetworkDirectoryNode, bootstrapNetworkKeyShares, ClientBackup, S3CloudStorageProvider } from "@cello-protocol/client";
|
|
50
|
+
import { LocalCloudStorageProvider } from "@cello-protocol/interfaces/stubs";
|
|
51
|
+
import { pushChannelNotification } from "../notifications.js";
|
|
52
|
+
import { resolveDirectoryUrl } from "../config.js";
|
|
53
|
+
const keyPath = process.env["CELLO_KEY_FILE"] ?? join(homedir(), ".cello", "key");
|
|
54
|
+
const listenAddr = process.env["CELLO_LISTEN_ADDR"] ?? "/ip4/0.0.0.0/tcp/0";
|
|
55
|
+
// AC-003 (REPOSPLIT-002): production directory endpoint baked in as default; overridable by env.
|
|
56
|
+
// Relay multiaddr is dynamically assigned per-session — no relay constant is baked in.
|
|
57
|
+
const directoryUrl = resolveDirectoryUrl(process.env);
|
|
58
|
+
const directoryMultiaddr = process.env["CELLO_DIRECTORY_MULTIADDR"];
|
|
59
|
+
const celloEnv = process.env["CELLO_ENV"] ?? "local";
|
|
60
|
+
const dbPath = process.env["CELLO_DB_PATH"] ?? join(homedir(), ".cello", "client.db");
|
|
61
|
+
const backupS3Bucket = process.env["BACKUP_S3_BUCKET"];
|
|
62
|
+
// CELLO_AWS_REGION is the operator-settable variable (AWS_REGION is reserved by ECS/Lambda)
|
|
63
|
+
const awsRegion = process.env["CELLO_AWS_REGION"] ?? process.env["AWS_REGION"] ?? "eu-west-1";
|
|
64
|
+
// Load key
|
|
65
|
+
let kp;
|
|
66
|
+
try {
|
|
67
|
+
kp = await FileKeyProvider.load(keyPath);
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
const msg = typeof err === "object" && err !== null && "message" in err
|
|
71
|
+
? err.message
|
|
72
|
+
: String(err);
|
|
73
|
+
process.stderr.write(`cello-mcp: key file error: ${msg}\n`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
// PERSIST-022: Read identity key (Ed25519 seed) from the key file for backup derivation.
|
|
77
|
+
// Key file format (from packages/crypto/src/ed25519.ts):
|
|
78
|
+
// Magic[0..3] = [0xce, 0x11, 0x0e, 0x01] ("CELLO\x01" marker)
|
|
79
|
+
// version[4] = 0x01
|
|
80
|
+
// seed[5..36] = 32-byte Ed25519 seed
|
|
81
|
+
// Total: 37 bytes (KEY_FILE_SIZE)
|
|
82
|
+
// The seed is used only for HKDF derivation (backup_key and db_key).
|
|
83
|
+
// It is never stored, never logged, and is zeroed after ClientBackup construction.
|
|
84
|
+
const KEY_FILE_MAGIC = new Uint8Array([0xce, 0x11, 0x0e, 0x01]);
|
|
85
|
+
const KEY_FILE_VERSION = 0x01;
|
|
86
|
+
const KEY_FILE_SIZE = 37; // Magic(4) + version(1) + seed(32)
|
|
87
|
+
const SEED_OFFSET = 5; // KEY_FILE_MAGIC.length + 1
|
|
88
|
+
const SEED_LENGTH = 32;
|
|
89
|
+
let identityKeyBytes = null;
|
|
90
|
+
try {
|
|
91
|
+
const rawKeyFile = readFileSync(keyPath);
|
|
92
|
+
// Validate exact file size
|
|
93
|
+
if (rawKeyFile.length === KEY_FILE_SIZE) {
|
|
94
|
+
// Validate magic bytes to ensure this is a valid CELLO key file
|
|
95
|
+
const magicOk = KEY_FILE_MAGIC.every((b, i) => rawKeyFile[i] === b);
|
|
96
|
+
// Validate version byte
|
|
97
|
+
const versionOk = rawKeyFile[KEY_FILE_MAGIC.length] === KEY_FILE_VERSION;
|
|
98
|
+
if (magicOk && versionOk) {
|
|
99
|
+
identityKeyBytes = new Uint8Array(rawKeyFile.slice(SEED_OFFSET, SEED_OFFSET + SEED_LENGTH));
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
process.stderr.write(`cello-mcp: key file has invalid magic bytes or version — backup disabled\n`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
process.stderr.write(`cello-mcp: key file has unexpected size (${rawKeyFile.length} bytes, expected ${KEY_FILE_SIZE}) — backup disabled\n`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Non-fatal: if the key file can't be read for backup derivation, backup is disabled
|
|
111
|
+
process.stderr.write(`cello-mcp: could not read identity key for backup derivation — backup disabled\n`);
|
|
112
|
+
}
|
|
113
|
+
// PERSIST-022: Derive agentId from the public key
|
|
114
|
+
const ownPubkeyForBackup = await kp.getPublicKey();
|
|
115
|
+
const agentId = Buffer.from(ownPubkeyForBackup).toString("hex");
|
|
116
|
+
// PERSIST-022: Select CloudStorageProvider based on CELLO_ENV and BACKUP_S3_BUCKET
|
|
117
|
+
let cloudStorageForBackup = null;
|
|
118
|
+
if (celloEnv === "local") {
|
|
119
|
+
// Local: use filesystem-backed provider in ~/.cello/backups
|
|
120
|
+
const localBackupDir = join(homedir(), ".cello", "backups");
|
|
121
|
+
cloudStorageForBackup = new LocalCloudStorageProvider(localBackupDir);
|
|
122
|
+
process.stderr.write(`cello-mcp: backup: using LocalCloudStorageProvider at ${localBackupDir}\n`);
|
|
123
|
+
}
|
|
124
|
+
else if (backupS3Bucket) {
|
|
125
|
+
// Non-local with bucket configured: use S3
|
|
126
|
+
cloudStorageForBackup = new S3CloudStorageProvider({ bucket: backupS3Bucket, region: awsRegion });
|
|
127
|
+
process.stderr.write(`cello-mcp: backup: using S3CloudStorageProvider, bucket=${backupS3Bucket}, region=${awsRegion}\n`);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// Non-local without bucket: no backup configured — ClientBackup will log client.backup.not.configured
|
|
131
|
+
process.stderr.write(`cello-mcp: backup: BACKUP_S3_BUCKET not set — backup not configured\n`);
|
|
132
|
+
}
|
|
133
|
+
// PERSIST-022: Construct ClientBackup (only if identity key is available)
|
|
134
|
+
// A minimal logger for the composition root backup instance that writes to stderr.
|
|
135
|
+
// In production deployments the full structured logger is wired in via server.ts.
|
|
136
|
+
const backupLogger = {
|
|
137
|
+
debug: (event, ctx) => process.stderr.write(`cello-mcp: [debug] ${event} ${JSON.stringify(ctx ?? {})}\n`),
|
|
138
|
+
info: (event, ctx) => process.stderr.write(`cello-mcp: [info] ${event} ${JSON.stringify(ctx ?? {})}\n`),
|
|
139
|
+
warn: (event, ctx) => process.stderr.write(`cello-mcp: [warn] ${event} ${JSON.stringify(ctx ?? {})}\n`),
|
|
140
|
+
error: (event, ctx) => process.stderr.write(`cello-mcp: [error] ${event} ${JSON.stringify(ctx ?? {})}\n`),
|
|
141
|
+
};
|
|
142
|
+
let clientBackupInstance;
|
|
143
|
+
if (identityKeyBytes) {
|
|
144
|
+
clientBackupInstance = new ClientBackup({
|
|
145
|
+
agentId,
|
|
146
|
+
identityKey: identityKeyBytes,
|
|
147
|
+
dbPath,
|
|
148
|
+
cloudStorage: cloudStorageForBackup,
|
|
149
|
+
logger: backupLogger,
|
|
150
|
+
destinationType: celloEnv === "local" ? "local" : (backupS3Bucket ? "s3" : "local"),
|
|
151
|
+
});
|
|
152
|
+
// Zero the identity key bytes immediately after construction — it must not linger in memory (SI-001)
|
|
153
|
+
identityKeyBytes.fill(0);
|
|
154
|
+
identityKeyBytes = null;
|
|
155
|
+
}
|
|
156
|
+
// Parse directory endpoint from CELLO_DIRECTORY_MULTIADDR (if set)
|
|
157
|
+
let directoryEndpoint = undefined;
|
|
158
|
+
if (directoryMultiaddr) {
|
|
159
|
+
const parts = directoryMultiaddr.split("/");
|
|
160
|
+
const p2pIndex = parts.findIndex((p) => p === "p2p");
|
|
161
|
+
const peerId = p2pIndex !== -1 ? parts[p2pIndex + 1] : null;
|
|
162
|
+
if (peerId) {
|
|
163
|
+
directoryEndpoint = { peer_id: peerId, multiaddrs: [directoryMultiaddr] };
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
process.stderr.write("cello-mcp: CELLO_DIRECTORY_MULTIADDR must include /p2p/<peer-id>\n");
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// Create and start single node
|
|
170
|
+
const node = await createNode({ keyProvider: kp, listenAddresses: [listenAddr] });
|
|
171
|
+
await node.start();
|
|
172
|
+
// Bootstrap FROST key shares (test-only shortcut)
|
|
173
|
+
// In production (M3+), real DKG ceremony replaces this.
|
|
174
|
+
let thresholdSigner;
|
|
175
|
+
let primaryPubkey;
|
|
176
|
+
process.stderr.write(`cello-mcp: NODE_ENV=${process.env.NODE_ENV ?? "(unset)"} CELLO_DIRECTORY_URL=${directoryUrl} CELLO_DIRECTORY_MULTIADDR=${directoryMultiaddr ?? "(unset)"}\n`);
|
|
177
|
+
if (process.env.NODE_ENV === "test") {
|
|
178
|
+
const ownPubkey = await kp.getPublicKey();
|
|
179
|
+
if (directoryMultiaddr && directoryEndpoint) {
|
|
180
|
+
process.stderr.write(`cello-mcp: bootstrapping FROST via network directory...\n`);
|
|
181
|
+
try {
|
|
182
|
+
await node.dial(directoryEndpoint.multiaddrs[0]);
|
|
183
|
+
process.stderr.write(`cello-mcp: node dialed directory OK\n`);
|
|
184
|
+
const networkNodes = [new NetworkDirectoryNode({
|
|
185
|
+
id: `cello-test-node-0000`,
|
|
186
|
+
node,
|
|
187
|
+
directoryPeerId: directoryEndpoint.peer_id,
|
|
188
|
+
directoryMultiaddrs: directoryEndpoint.multiaddrs,
|
|
189
|
+
})];
|
|
190
|
+
const bootstrap = await bootstrapNetworkKeyShares(ownPubkey, {
|
|
191
|
+
threshold: 2,
|
|
192
|
+
participants: 1,
|
|
193
|
+
directoryNodes: networkNodes,
|
|
194
|
+
});
|
|
195
|
+
thresholdSigner = bootstrap.signer;
|
|
196
|
+
primaryPubkey = bootstrap.primaryPubkey;
|
|
197
|
+
process.stderr.write(`cello-mcp: FROST bootstrap OK, primaryPubkey=${Buffer.from(primaryPubkey).toString("hex").slice(0, 16)}...\n`);
|
|
198
|
+
}
|
|
199
|
+
catch (err) {
|
|
200
|
+
const msg = err instanceof Error ? `${err.name}: ${err.message}\n${err.stack}` : JSON.stringify(err);
|
|
201
|
+
process.stderr.write(`cello-mcp: FROST bootstrap FAILED: ${msg}\n`);
|
|
202
|
+
process.stderr.write(`cello-mcp: falling back to in-process stubs\n`);
|
|
203
|
+
// Fall back to in-process stubs so the server starts but without directory FROST
|
|
204
|
+
const stubs = createInProcessStubs(3);
|
|
205
|
+
const ownPubkeyFresh = await kp.getPublicKey();
|
|
206
|
+
const bootstrapResult = await bootstrapKeyShares(ownPubkeyFresh, { threshold: 2, participants: 3, directoryNodeStubs: stubs });
|
|
207
|
+
thresholdSigner = new FrostThresholdSigner({ threshold: 2, participants: 3, directoryNodeStubs: stubs }, ownPubkeyFresh);
|
|
208
|
+
primaryPubkey = bootstrapResult.primaryPubkey;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
// Fallback: in-process stubs (no directory reachable)
|
|
213
|
+
const stubs = createInProcessStubs(3);
|
|
214
|
+
const ownPubkeyFresh = await kp.getPublicKey();
|
|
215
|
+
const bootstrapResult = await bootstrapKeyShares(ownPubkeyFresh, { threshold: 2, participants: 3, directoryNodeStubs: stubs });
|
|
216
|
+
thresholdSigner = new FrostThresholdSigner({ threshold: 2, participants: 3, directoryNodeStubs: stubs }, ownPubkeyFresh);
|
|
217
|
+
primaryPubkey = bootstrapResult.primaryPubkey;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// Late-bound server reference — set after createMcpServer returns.
|
|
221
|
+
// The closure captures the box; notifications fired before server is assigned are dropped.
|
|
222
|
+
let mcpServer;
|
|
223
|
+
// Create single client
|
|
224
|
+
const client = createClient(node, kp, {
|
|
225
|
+
thresholdSigner,
|
|
226
|
+
directoryEndpoint,
|
|
227
|
+
onMessageQueued: (senderHex) => {
|
|
228
|
+
if (mcpServer)
|
|
229
|
+
void pushChannelNotification(mcpServer, senderHex);
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
if (primaryPubkey) {
|
|
233
|
+
client.setPrimaryPubkey(primaryPubkey);
|
|
234
|
+
}
|
|
235
|
+
// Create server with single identity.
|
|
236
|
+
// PERSIST-017: checkpointStatusProvider is not available in the cello-mcp binary
|
|
237
|
+
// (the client binary has no access to the directory's MmrStore). The provider is
|
|
238
|
+
// wired in directory-facing deployments via the server.ts composition root.
|
|
239
|
+
// Passing undefined is a safe fallback — the tools return M1 stub responses.
|
|
240
|
+
// PERSIST-022: clientBackupInstance passed so cello_backup/cello_restore are registered
|
|
241
|
+
// inside createMcpSessionServer (single canonical registration path).
|
|
242
|
+
const server = createMcpSessionServer(node, client, kp, { clientBackup: clientBackupInstance });
|
|
243
|
+
mcpServer = server;
|
|
244
|
+
// Connect stdio transport and register handler
|
|
245
|
+
await server.connect(new StdioServerTransport());
|
|
246
|
+
await client.registerHandler();
|
|
247
|
+
//# sourceMappingURL=cello-mcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cello-mcp.js","sourceRoot":"","sources":["../../src/bin/cello-mcp.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CELLO adapter runtime configuration.
|
|
3
|
+
*
|
|
4
|
+
* AC-003 (REPOSPLIT-002): CELLO_DIRECTORY_URL defaults to the production directory
|
|
5
|
+
* ALB endpoint. No CELLO_RELAY_MULTIADDR constant exists — relay multiaddr is
|
|
6
|
+
* dynamically assigned per-session by the directory.
|
|
7
|
+
*/
|
|
8
|
+
/** Production directory HTTPS endpoint (ALB / Route53). */
|
|
9
|
+
export declare const PRODUCTION_DIRECTORY_URL = "https://directory-us1.cello.mygentic.ai";
|
|
10
|
+
/**
|
|
11
|
+
* Resolve the directory URL from the environment, falling back to the
|
|
12
|
+
* production endpoint when CELLO_DIRECTORY_URL is not set.
|
|
13
|
+
*
|
|
14
|
+
* @param env - process.env or a test substitute
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveDirectoryUrl(env?: Record<string, string | undefined>): string;
|
|
17
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,2DAA2D;AAC3D,eAAO,MAAM,wBAAwB,4CAA4C,CAAC;AAElF;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GAAG,MAAM,CAEjG"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CELLO adapter runtime configuration.
|
|
3
|
+
*
|
|
4
|
+
* AC-003 (REPOSPLIT-002): CELLO_DIRECTORY_URL defaults to the production directory
|
|
5
|
+
* ALB endpoint. No CELLO_RELAY_MULTIADDR constant exists — relay multiaddr is
|
|
6
|
+
* dynamically assigned per-session by the directory.
|
|
7
|
+
*/
|
|
8
|
+
/** Production directory HTTPS endpoint (ALB / Route53). */
|
|
9
|
+
export const PRODUCTION_DIRECTORY_URL = "https://directory-us1.cello.mygentic.ai";
|
|
10
|
+
/**
|
|
11
|
+
* Resolve the directory URL from the environment, falling back to the
|
|
12
|
+
* production endpoint when CELLO_DIRECTORY_URL is not set.
|
|
13
|
+
*
|
|
14
|
+
* @param env - process.env or a test substitute
|
|
15
|
+
*/
|
|
16
|
+
export function resolveDirectoryUrl(env = process.env) {
|
|
17
|
+
return env["CELLO_DIRECTORY_URL"] ?? PRODUCTION_DIRECTORY_URL;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,2DAA2D;AAC3D,MAAM,CAAC,MAAM,wBAAwB,GAAG,yCAAyC,CAAC;AAElF;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAA0C,OAAO,CAAC,GAAG;IACvF,OAAO,GAAG,CAAC,qBAAqB,CAAC,IAAI,wBAAwB,CAAC;AAChE,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
export {};
|
|
1
|
+
export { createMcpServer } from "./server.js";
|
|
2
|
+
export { pushChannelNotification, pushSessionRequestNotification } from "./notifications.js";
|
|
3
|
+
export { resolveDirectoryUrl, PRODUCTION_DIRECTORY_URL } from "./config.js";
|
|
2
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AAC7F,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export {};
|
|
2
|
-
|
|
1
|
+
export { createMcpServer } from "./server.js";
|
|
2
|
+
export { pushChannelNotification, pushSessionRequestNotification } from "./notifications.js";
|
|
3
|
+
export { resolveDirectoryUrl, PRODUCTION_DIRECTORY_URL } from "./config.js";
|
|
3
4
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AAC7F,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
/**
|
|
3
|
+
* Push a claude/channel wake-up notification to the connected Claude Code session.
|
|
4
|
+
* Content is never included — the agent calls cello_receive to retrieve the message.
|
|
5
|
+
* SI-001 (ADAPTER-001): notification payload contains only type and sender pubkey.
|
|
6
|
+
*/
|
|
7
|
+
export declare function pushChannelNotification(server: McpServer, from: string): Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* Push a cello_session_request claude/channel notification to the connected Claude Code session.
|
|
10
|
+
* Carries only counterparty pubkey and session_id — no content, no multiaddrs, no trust data.
|
|
11
|
+
* SI-001 (ADAPTER-002): notification payload contains exactly type, from, and session_id.
|
|
12
|
+
* The agent calls cello_await_session to retrieve the full session details.
|
|
13
|
+
*/
|
|
14
|
+
export declare function pushSessionRequestNotification(server: McpServer, from: string, sessionId: string): Promise<void>;
|
|
15
|
+
//# sourceMappingURL=notifications.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../src/notifications.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAS5F;AAED;;;;;GAKG;AACH,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CASf"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Push a claude/channel wake-up notification to the connected Claude Code session.
|
|
3
|
+
* Content is never included — the agent calls cello_receive to retrieve the message.
|
|
4
|
+
* SI-001 (ADAPTER-001): notification payload contains only type and sender pubkey.
|
|
5
|
+
*/
|
|
6
|
+
export async function pushChannelNotification(server, from) {
|
|
7
|
+
try {
|
|
8
|
+
await server.server.notification({
|
|
9
|
+
method: "notifications/claude/channel",
|
|
10
|
+
params: { type: "cello_message", from },
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
// Transport may not be connected or may have closed — silently swallow
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Push a cello_session_request claude/channel notification to the connected Claude Code session.
|
|
19
|
+
* Carries only counterparty pubkey and session_id — no content, no multiaddrs, no trust data.
|
|
20
|
+
* SI-001 (ADAPTER-002): notification payload contains exactly type, from, and session_id.
|
|
21
|
+
* The agent calls cello_await_session to retrieve the full session details.
|
|
22
|
+
*/
|
|
23
|
+
export async function pushSessionRequestNotification(server, from, sessionId) {
|
|
24
|
+
try {
|
|
25
|
+
await server.server.notification({
|
|
26
|
+
method: "notifications/claude/channel",
|
|
27
|
+
params: { type: "cello_session_request", from, session_id: sessionId },
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Transport may not be connected or may have closed — silently swallow
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=notifications.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notifications.js","sourceRoot":"","sources":["../src/notifications.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,MAAiB,EAAE,IAAY;IAC3E,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC;YAC/B,MAAM,EAAE,8BAA8B;YACtC,MAAM,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE;SACxC,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,uEAAuE;IACzE,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAClD,MAAiB,EACjB,IAAY,EACZ,SAAiB;IAEjB,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC;YAC/B,MAAM,EAAE,8BAA8B;YACtC,MAAM,EAAE,EAAE,IAAI,EAAE,uBAAuB,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE;SACvE,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,uEAAuE;IACzE,CAAC;AACH,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CELLO Adapter — server.ts (ADAPTER-002 / M1)
|
|
3
|
+
*
|
|
4
|
+
* createMcpServer(node, client, keyProvider): McpServer
|
|
5
|
+
* Registers the M1 tool set against a CelloClient.
|
|
6
|
+
* Transport-agnostic: identical tool names, schemas, and wiring under
|
|
7
|
+
* InMemoryTransport (tests) and stdio (production). AC-007.
|
|
8
|
+
*
|
|
9
|
+
* PSEUDOCODE (Phase P — ADAPTER-002):
|
|
10
|
+
*
|
|
11
|
+
* State held by the MCP server instance:
|
|
12
|
+
* sessionEventQueue: Array<InboundSessionEvent> FIFO; populated by onSessionAssignment handler
|
|
13
|
+
* sessionEventResolvers: Array<(event: InboundSessionEvent) => void> blocked cello_await_session waiters
|
|
14
|
+
* startedAt: number = Date.now()
|
|
15
|
+
*
|
|
16
|
+
* Session event enqueue (onSessionAssignment handler):
|
|
17
|
+
* Receives: SessionAssignmentEvent { sessionIdHex, counterpartyPubkeyHex, genesisPrevRootHex }
|
|
18
|
+
* (from CelloClient.onSessionAssignment per CELLO-MCP-002; fields are pre-encoded as hex)
|
|
19
|
+
* 1. Push cello_session_request notification via claude/channel (SI-001: only type, from, session_id)
|
|
20
|
+
* 2. If sessionEventResolvers.length > 0:
|
|
21
|
+
* - shift the first resolver and call it with the event (wake up blocked cello_await_session)
|
|
22
|
+
* 3. Else:
|
|
23
|
+
* - Push event to sessionEventQueue
|
|
24
|
+
*
|
|
25
|
+
* tool: cello_initiate_session({ target_pubkey: string })
|
|
26
|
+
* Delegates to CelloClient.initiateSession (added by MCP-002).
|
|
27
|
+
* Returns { ok: true, session_id: hex } or { ok: false, reason: string }.
|
|
28
|
+
* Stubbed if method not present on client.
|
|
29
|
+
*
|
|
30
|
+
* tool: cello_await_session({ timeout_ms: number })
|
|
31
|
+
* 1. If sessionEventQueue.length > 0:
|
|
32
|
+
* - shift first event
|
|
33
|
+
* - return { type: 'new_session', session_id, counterparty_pubkey, genesis_prev_root }
|
|
34
|
+
* 2. Else: block until event arrives or timeout expires:
|
|
35
|
+
* - Create a Promise that resolves when an event arrives or deadline fires
|
|
36
|
+
* - Push a resolver into sessionEventResolvers
|
|
37
|
+
* - Race: event arrival vs setTimeout(timeout_ms)
|
|
38
|
+
* - On arrival: return new_session
|
|
39
|
+
* - On timeout: remove resolver from array; return { type: 'timeout' }
|
|
40
|
+
*
|
|
41
|
+
* tool: cello_send({ session_id: string, content: string })
|
|
42
|
+
* 1. Guard: transport_not_started
|
|
43
|
+
* 2. UTF-8 encode content → contentBytes
|
|
44
|
+
* 3. client.sendMessage(session_id, contentBytes) → SendMessageResult
|
|
45
|
+
* 4. Map to MCP output:
|
|
46
|
+
* ok:true → { delivered: true }
|
|
47
|
+
* ok:false → { delivered: false, reason: result.reason }
|
|
48
|
+
*
|
|
49
|
+
* tool: cello_receive_session({ session_id: string, timeout_ms: number })
|
|
50
|
+
* 1. Guard: transport_not_started
|
|
51
|
+
* 2. deadline = Date.now() + timeout_ms
|
|
52
|
+
* 3. Poll every 20ms until deadline:
|
|
53
|
+
* a. msg = client.receiveMessage(session_id)
|
|
54
|
+
* b. if msg: return formatted message result
|
|
55
|
+
* c. await sleep(Math.min(20, remaining))
|
|
56
|
+
* 4. return { type: 'timeout' }
|
|
57
|
+
*
|
|
58
|
+
* tool: cello_close_session({ session_id: string })
|
|
59
|
+
* client.closeSession(session_id)
|
|
60
|
+
* return { closed: true }
|
|
61
|
+
*
|
|
62
|
+
* tool: cello_list_sessions()
|
|
63
|
+
* return client.listSessions() mapped to wire shape
|
|
64
|
+
*
|
|
65
|
+
* tool: cello_get_sealed_receipt({ session_id: string })
|
|
66
|
+
* Stubbed in M1: return { available: false, reason: 'not_yet_sealed' }
|
|
67
|
+
* (SESSION-003 implements real seals)
|
|
68
|
+
*
|
|
69
|
+
* tool: cello_get_inclusion_proof({ session_id: string, content_hash: string })
|
|
70
|
+
* Stubbed in M1: return { available: false, reason: 'not_yet_sealed' }
|
|
71
|
+
*
|
|
72
|
+
* tool: cello_status()
|
|
73
|
+
* 1. ownPubkey = Buffer.from(await keyProvider.getPublicKey()).toString('hex')
|
|
74
|
+
* 2. sessions = client.listSessions()
|
|
75
|
+
* 3. return {
|
|
76
|
+
* transport_started: transportStarted(),
|
|
77
|
+
* own_pubkey: ownPubkey,
|
|
78
|
+
* listen_addresses: node.listenAddresses(),
|
|
79
|
+
* connected_peer_count: node.getConnections().length,
|
|
80
|
+
* uptime_seconds: Math.floor((Date.now() - startedAt) / 1000),
|
|
81
|
+
* active_session_count: sessions.filter(s => s.status === 'active').length,
|
|
82
|
+
* directory_reachable: false // M1 stub — directory reachability check deferred
|
|
83
|
+
* }
|
|
84
|
+
*/
|
|
85
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
86
|
+
import type { CelloClient, ClientBackup } from "@cello-protocol/client";
|
|
87
|
+
import type { CelloNode } from "@cello-protocol/transport";
|
|
88
|
+
import type { KeyProvider } from "@cello-protocol/crypto";
|
|
89
|
+
import type { CheckpointStatusProvider } from "@cello-protocol/interfaces";
|
|
90
|
+
export declare function createMcpServer(node: CelloNode, client: CelloClient, keyProvider: KeyProvider, opts?: {
|
|
91
|
+
checkpointStatusProvider?: CheckpointStatusProvider;
|
|
92
|
+
clientBackup?: ClientBackup;
|
|
93
|
+
}): McpServer;
|
|
94
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmFG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,KAAK,EAAE,WAAW,EAA0B,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAChG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAoB3E,wBAAgB,eAAe,CAC7B,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,WAAW,EACnB,WAAW,EAAE,WAAW,EACxB,IAAI,CAAC,EAAE;IAAE,wBAAwB,CAAC,EAAE,wBAAwB,CAAC;IAAC,YAAY,CAAC,EAAE,YAAY,CAAA;CAAE,GAC1F,SAAS,CA0cX"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CELLO Adapter — server.ts (ADAPTER-002 / M1)
|
|
3
|
+
*
|
|
4
|
+
* createMcpServer(node, client, keyProvider): McpServer
|
|
5
|
+
* Registers the M1 tool set against a CelloClient.
|
|
6
|
+
* Transport-agnostic: identical tool names, schemas, and wiring under
|
|
7
|
+
* InMemoryTransport (tests) and stdio (production). AC-007.
|
|
8
|
+
*
|
|
9
|
+
* PSEUDOCODE (Phase P — ADAPTER-002):
|
|
10
|
+
*
|
|
11
|
+
* State held by the MCP server instance:
|
|
12
|
+
* sessionEventQueue: Array<InboundSessionEvent> FIFO; populated by onSessionAssignment handler
|
|
13
|
+
* sessionEventResolvers: Array<(event: InboundSessionEvent) => void> blocked cello_await_session waiters
|
|
14
|
+
* startedAt: number = Date.now()
|
|
15
|
+
*
|
|
16
|
+
* Session event enqueue (onSessionAssignment handler):
|
|
17
|
+
* Receives: SessionAssignmentEvent { sessionIdHex, counterpartyPubkeyHex, genesisPrevRootHex }
|
|
18
|
+
* (from CelloClient.onSessionAssignment per CELLO-MCP-002; fields are pre-encoded as hex)
|
|
19
|
+
* 1. Push cello_session_request notification via claude/channel (SI-001: only type, from, session_id)
|
|
20
|
+
* 2. If sessionEventResolvers.length > 0:
|
|
21
|
+
* - shift the first resolver and call it with the event (wake up blocked cello_await_session)
|
|
22
|
+
* 3. Else:
|
|
23
|
+
* - Push event to sessionEventQueue
|
|
24
|
+
*
|
|
25
|
+
* tool: cello_initiate_session({ target_pubkey: string })
|
|
26
|
+
* Delegates to CelloClient.initiateSession (added by MCP-002).
|
|
27
|
+
* Returns { ok: true, session_id: hex } or { ok: false, reason: string }.
|
|
28
|
+
* Stubbed if method not present on client.
|
|
29
|
+
*
|
|
30
|
+
* tool: cello_await_session({ timeout_ms: number })
|
|
31
|
+
* 1. If sessionEventQueue.length > 0:
|
|
32
|
+
* - shift first event
|
|
33
|
+
* - return { type: 'new_session', session_id, counterparty_pubkey, genesis_prev_root }
|
|
34
|
+
* 2. Else: block until event arrives or timeout expires:
|
|
35
|
+
* - Create a Promise that resolves when an event arrives or deadline fires
|
|
36
|
+
* - Push a resolver into sessionEventResolvers
|
|
37
|
+
* - Race: event arrival vs setTimeout(timeout_ms)
|
|
38
|
+
* - On arrival: return new_session
|
|
39
|
+
* - On timeout: remove resolver from array; return { type: 'timeout' }
|
|
40
|
+
*
|
|
41
|
+
* tool: cello_send({ session_id: string, content: string })
|
|
42
|
+
* 1. Guard: transport_not_started
|
|
43
|
+
* 2. UTF-8 encode content → contentBytes
|
|
44
|
+
* 3. client.sendMessage(session_id, contentBytes) → SendMessageResult
|
|
45
|
+
* 4. Map to MCP output:
|
|
46
|
+
* ok:true → { delivered: true }
|
|
47
|
+
* ok:false → { delivered: false, reason: result.reason }
|
|
48
|
+
*
|
|
49
|
+
* tool: cello_receive_session({ session_id: string, timeout_ms: number })
|
|
50
|
+
* 1. Guard: transport_not_started
|
|
51
|
+
* 2. deadline = Date.now() + timeout_ms
|
|
52
|
+
* 3. Poll every 20ms until deadline:
|
|
53
|
+
* a. msg = client.receiveMessage(session_id)
|
|
54
|
+
* b. if msg: return formatted message result
|
|
55
|
+
* c. await sleep(Math.min(20, remaining))
|
|
56
|
+
* 4. return { type: 'timeout' }
|
|
57
|
+
*
|
|
58
|
+
* tool: cello_close_session({ session_id: string })
|
|
59
|
+
* client.closeSession(session_id)
|
|
60
|
+
* return { closed: true }
|
|
61
|
+
*
|
|
62
|
+
* tool: cello_list_sessions()
|
|
63
|
+
* return client.listSessions() mapped to wire shape
|
|
64
|
+
*
|
|
65
|
+
* tool: cello_get_sealed_receipt({ session_id: string })
|
|
66
|
+
* Stubbed in M1: return { available: false, reason: 'not_yet_sealed' }
|
|
67
|
+
* (SESSION-003 implements real seals)
|
|
68
|
+
*
|
|
69
|
+
* tool: cello_get_inclusion_proof({ session_id: string, content_hash: string })
|
|
70
|
+
* Stubbed in M1: return { available: false, reason: 'not_yet_sealed' }
|
|
71
|
+
*
|
|
72
|
+
* tool: cello_status()
|
|
73
|
+
* 1. ownPubkey = Buffer.from(await keyProvider.getPublicKey()).toString('hex')
|
|
74
|
+
* 2. sessions = client.listSessions()
|
|
75
|
+
* 3. return {
|
|
76
|
+
* transport_started: transportStarted(),
|
|
77
|
+
* own_pubkey: ownPubkey,
|
|
78
|
+
* listen_addresses: node.listenAddresses(),
|
|
79
|
+
* connected_peer_count: node.getConnections().length,
|
|
80
|
+
* uptime_seconds: Math.floor((Date.now() - startedAt) / 1000),
|
|
81
|
+
* active_session_count: sessions.filter(s => s.status === 'active').length,
|
|
82
|
+
* directory_reachable: false // M1 stub — directory reachability check deferred
|
|
83
|
+
* }
|
|
84
|
+
*/
|
|
85
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
86
|
+
import { z } from "zod";
|
|
87
|
+
import { pushSessionRequestNotification } from "./notifications.js";
|
|
88
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
89
|
+
function jsonText(value) {
|
|
90
|
+
return { content: [{ type: "text", text: JSON.stringify(value) }] };
|
|
91
|
+
}
|
|
92
|
+
const TRANSPORT_NOT_STARTED = jsonText({ error: { reason: "transport_not_started" } });
|
|
93
|
+
const CLIENT_NOT_INITIALIZED = jsonText({ error: { reason: "client_not_initialized" } });
|
|
94
|
+
// ─── createMcpServer ─────────────────────────────────────────────────────────
|
|
95
|
+
export function createMcpServer(node, client, keyProvider, opts) {
|
|
96
|
+
const checkpointStatusProvider = opts?.checkpointStatusProvider;
|
|
97
|
+
const clientBackup = opts?.clientBackup;
|
|
98
|
+
const startedAt = Date.now();
|
|
99
|
+
// ─── Session event queue ─────────────────────────────────────────────────
|
|
100
|
+
const sessionEventQueue = [];
|
|
101
|
+
const sessionEventResolvers = [];
|
|
102
|
+
function transportStarted() {
|
|
103
|
+
return node.listenAddresses().length > 0;
|
|
104
|
+
}
|
|
105
|
+
const server = new McpServer({ name: "cello", version: "0.1.0" }, { capabilities: { experimental: { "claude/channel": {} } } });
|
|
106
|
+
// ─── Register onSessionAssignment handler ───────────────────────────────
|
|
107
|
+
if (client != null && typeof client.onSessionAssignment === "function") {
|
|
108
|
+
client.onSessionAssignment((event) => {
|
|
109
|
+
void pushSessionRequestNotification(server, event.counterpartyPubkeyHex, event.sessionIdHex);
|
|
110
|
+
if (sessionEventResolvers.length > 0) {
|
|
111
|
+
const resolve = sessionEventResolvers.shift();
|
|
112
|
+
resolve(event);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
sessionEventQueue.push(event);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
// ── cello_initiate_session ────────────────────────────────────────────────
|
|
120
|
+
server.registerTool("cello_initiate_session", {
|
|
121
|
+
description: "Initiate a CELLO session with a target agent identified by their public key.",
|
|
122
|
+
inputSchema: {
|
|
123
|
+
target_pubkey: z.string().describe("Target agent public key hex (K_local pubkey of the peer)"),
|
|
124
|
+
},
|
|
125
|
+
}, async ({ target_pubkey }) => {
|
|
126
|
+
if (!transportStarted())
|
|
127
|
+
return TRANSPORT_NOT_STARTED;
|
|
128
|
+
const result = await client.initiateSession(target_pubkey);
|
|
129
|
+
if (result.ok) {
|
|
130
|
+
return jsonText({
|
|
131
|
+
ok: true,
|
|
132
|
+
session_id: Buffer.from(result.sessionId).toString("hex"),
|
|
133
|
+
genesis_prev_root: Buffer.from(result.genesisPrevRoot).toString("hex"),
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
return jsonText({ ok: false, reason: result.reason });
|
|
137
|
+
});
|
|
138
|
+
// ── cello_await_session ───────────────────────────────────────────────────
|
|
139
|
+
server.registerTool("cello_await_session", {
|
|
140
|
+
description: "Wait for an inbound session request. Returns immediately if one is already queued, or blocks until one arrives or the timeout expires.",
|
|
141
|
+
inputSchema: {
|
|
142
|
+
timeout_ms: z.number().int().min(0).describe("Maximum wait time in milliseconds"),
|
|
143
|
+
},
|
|
144
|
+
}, async ({ timeout_ms }) => {
|
|
145
|
+
// If a queued event exists, return it immediately (FIFO)
|
|
146
|
+
if (sessionEventQueue.length > 0) {
|
|
147
|
+
const event = sessionEventQueue.shift();
|
|
148
|
+
return jsonText({
|
|
149
|
+
type: "new_session",
|
|
150
|
+
session_id: event.sessionIdHex,
|
|
151
|
+
counterparty_pubkey: event.counterpartyPubkeyHex,
|
|
152
|
+
genesis_prev_root: event.genesisPrevRootHex,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
// Block until an event arrives or timeout fires
|
|
156
|
+
const result = await new Promise((resolve) => {
|
|
157
|
+
let timerId;
|
|
158
|
+
function resolveEvent(event) {
|
|
159
|
+
clearTimeout(timerId);
|
|
160
|
+
resolve(event);
|
|
161
|
+
}
|
|
162
|
+
sessionEventResolvers.push(resolveEvent);
|
|
163
|
+
timerId = setTimeout(() => {
|
|
164
|
+
const idx = sessionEventResolvers.indexOf(resolveEvent);
|
|
165
|
+
if (idx !== -1)
|
|
166
|
+
sessionEventResolvers.splice(idx, 1);
|
|
167
|
+
resolve(null);
|
|
168
|
+
}, timeout_ms);
|
|
169
|
+
});
|
|
170
|
+
if (result === null) {
|
|
171
|
+
return jsonText({ type: "timeout" });
|
|
172
|
+
}
|
|
173
|
+
return jsonText({
|
|
174
|
+
type: "new_session",
|
|
175
|
+
session_id: result.sessionIdHex,
|
|
176
|
+
counterparty_pubkey: result.counterpartyPubkeyHex,
|
|
177
|
+
genesis_prev_root: result.genesisPrevRootHex,
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
// ── cello_send (session-keyed, M1) ────────────────────────────────────────
|
|
181
|
+
server.registerTool("cello_send", {
|
|
182
|
+
description: "Send a UTF-8 message on an active CELLO session.",
|
|
183
|
+
inputSchema: {
|
|
184
|
+
session_id: z.string().describe("Session ID hex (from cello_initiate_session or cello_await_session)"),
|
|
185
|
+
content: z.string().describe("UTF-8 message content"),
|
|
186
|
+
},
|
|
187
|
+
}, async ({ session_id, content }) => {
|
|
188
|
+
if (client == null)
|
|
189
|
+
return CLIENT_NOT_INITIALIZED;
|
|
190
|
+
if (!transportStarted())
|
|
191
|
+
return TRANSPORT_NOT_STARTED;
|
|
192
|
+
const contentBytes = new TextEncoder().encode(content);
|
|
193
|
+
const result = await client.sendMessage(session_id, contentBytes);
|
|
194
|
+
if (result.ok) {
|
|
195
|
+
return jsonText({ delivered: true });
|
|
196
|
+
}
|
|
197
|
+
return jsonText({ delivered: false, reason: result.reason });
|
|
198
|
+
});
|
|
199
|
+
// ── cello_receive_session (session-keyed, M1) ────────────────────────────
|
|
200
|
+
server.registerTool("cello_receive_session", {
|
|
201
|
+
description: "Wait for a message on a specific CELLO session (session-locked). Blocks until a message arrives or the timeout expires.",
|
|
202
|
+
inputSchema: {
|
|
203
|
+
session_id: z
|
|
204
|
+
.string()
|
|
205
|
+
.describe("Session ID hex (from cello_initiate_session or cello_await_session)"),
|
|
206
|
+
timeout_ms: z.number().int().min(0).describe("Maximum wait time in milliseconds"),
|
|
207
|
+
},
|
|
208
|
+
}, async ({ session_id, timeout_ms }) => {
|
|
209
|
+
if (client == null)
|
|
210
|
+
return CLIENT_NOT_INITIALIZED;
|
|
211
|
+
if (!transportStarted())
|
|
212
|
+
return TRANSPORT_NOT_STARTED;
|
|
213
|
+
const deadline = Date.now() + timeout_ms;
|
|
214
|
+
while (Date.now() < deadline) {
|
|
215
|
+
const msg = client.receiveMessage(session_id);
|
|
216
|
+
if (msg) {
|
|
217
|
+
if (msg.type === "session_sealed") {
|
|
218
|
+
return jsonText({
|
|
219
|
+
type: "session_sealed",
|
|
220
|
+
session_id: msg.sessionIdHex,
|
|
221
|
+
sealed_root: Buffer.from(msg.sealedRoot).toString("hex"),
|
|
222
|
+
close_timestamp: msg.closeTimestamp,
|
|
223
|
+
checkpoint_status: msg.checkpointStatus,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
return jsonText(formatSessionMessage(msg, session_id));
|
|
227
|
+
}
|
|
228
|
+
const remaining = deadline - Date.now();
|
|
229
|
+
if (remaining <= 0)
|
|
230
|
+
break;
|
|
231
|
+
await sleep(Math.min(20, remaining));
|
|
232
|
+
}
|
|
233
|
+
return jsonText({ type: "timeout" });
|
|
234
|
+
});
|
|
235
|
+
// ── cello_receive (any-session, default) ──────────────────────────────────
|
|
236
|
+
server.registerTool("cello_receive", {
|
|
237
|
+
description: "Wait for a message from any active CELLO session (default receive). Returns session_id so caller knows which session the message came from. Blocks until a message arrives or the timeout expires.",
|
|
238
|
+
inputSchema: {
|
|
239
|
+
timeout_ms: z.number().int().min(0).describe("Maximum wait time in milliseconds"),
|
|
240
|
+
},
|
|
241
|
+
}, async ({ timeout_ms }) => {
|
|
242
|
+
if (client == null)
|
|
243
|
+
return CLIENT_NOT_INITIALIZED;
|
|
244
|
+
if (!transportStarted())
|
|
245
|
+
return TRANSPORT_NOT_STARTED;
|
|
246
|
+
const result = await client.receiveMessageAsync(timeout_ms);
|
|
247
|
+
if (result.type === "timeout") {
|
|
248
|
+
return jsonText({ type: "timeout" });
|
|
249
|
+
}
|
|
250
|
+
if (result.type === "session_sealed") {
|
|
251
|
+
return jsonText({
|
|
252
|
+
type: "session_sealed",
|
|
253
|
+
session_id: result.sessionIdHex,
|
|
254
|
+
sealed_root: Buffer.from(result.sealedRoot).toString("hex"),
|
|
255
|
+
close_timestamp: result.closeTimestamp,
|
|
256
|
+
checkpoint_status: result.checkpointStatus,
|
|
257
|
+
...(result.otherSessionsPending && result.otherSessionsPending.length > 0
|
|
258
|
+
? { other_sessions_pending: result.otherSessionsPending }
|
|
259
|
+
: {}),
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
if (result.type === "message") {
|
|
263
|
+
let content;
|
|
264
|
+
try {
|
|
265
|
+
content = new TextDecoder("utf-8", { fatal: true }).decode(result.content);
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
content = Buffer.from(result.content).toString("hex");
|
|
269
|
+
}
|
|
270
|
+
return jsonText({
|
|
271
|
+
type: "message",
|
|
272
|
+
session_id: result.sessionIdHex,
|
|
273
|
+
content,
|
|
274
|
+
sender_pubkey: Buffer.from(result.senderPubkey).toString("hex"),
|
|
275
|
+
sequence_number: result.sequenceNumber,
|
|
276
|
+
leaf_hash: Buffer.from(result.leafHash).toString("hex"),
|
|
277
|
+
...(result.otherSessionsPending && result.otherSessionsPending.length > 0
|
|
278
|
+
? { other_sessions_pending: result.otherSessionsPending }
|
|
279
|
+
: {}),
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
return jsonText({ type: "unknown", raw: JSON.stringify(result) });
|
|
283
|
+
});
|
|
284
|
+
// ── cello_close_session ───────────────────────────────────────────────────
|
|
285
|
+
server.registerTool("cello_close_session", {
|
|
286
|
+
description: "Close and remove a CELLO session. Idempotent.",
|
|
287
|
+
inputSchema: {
|
|
288
|
+
session_id: z.string().describe("Session ID hex to close"),
|
|
289
|
+
},
|
|
290
|
+
}, async ({ session_id }) => {
|
|
291
|
+
if (client == null)
|
|
292
|
+
return CLIENT_NOT_INITIALIZED;
|
|
293
|
+
client.closeSession(session_id);
|
|
294
|
+
// PERSIST-017: include checkpoint_status if CheckpointStatusProvider is wired in.
|
|
295
|
+
// When not provided (M1 stubs, tests without DB), return closed: true only.
|
|
296
|
+
if (checkpointStatusProvider) {
|
|
297
|
+
const stagingStatus = await checkpointStatusProvider.getSealStagingStatus(session_id);
|
|
298
|
+
if (stagingStatus.status === "pending") {
|
|
299
|
+
return jsonText({ closed: true, checkpoint_status: "pending", staged_at: stagingStatus.staged_at });
|
|
300
|
+
}
|
|
301
|
+
if (stagingStatus.status === "confirmed") {
|
|
302
|
+
return jsonText({
|
|
303
|
+
closed: true,
|
|
304
|
+
checkpoint_status: "confirmed",
|
|
305
|
+
session_mmr_peak: stagingStatus.checkpoint_peak_hash,
|
|
306
|
+
leaf_index: stagingStatus.leaf_index,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return jsonText({ closed: true });
|
|
311
|
+
});
|
|
312
|
+
// ── cello_list_sessions ───────────────────────────────────────────────────
|
|
313
|
+
server.registerTool("cello_list_sessions", {
|
|
314
|
+
description: "List all current CELLO sessions and their status.",
|
|
315
|
+
inputSchema: {},
|
|
316
|
+
}, async () => {
|
|
317
|
+
if (client == null)
|
|
318
|
+
return CLIENT_NOT_INITIALIZED;
|
|
319
|
+
const sessions = client.listSessions().map((s) => ({
|
|
320
|
+
session_id: Buffer.from(s.session_id).toString("hex"),
|
|
321
|
+
counterparty_pubkey: Buffer.from(s.counterparty_pubkey).toString("hex"),
|
|
322
|
+
status: s.status,
|
|
323
|
+
}));
|
|
324
|
+
return jsonText(sessions);
|
|
325
|
+
});
|
|
326
|
+
// ── cello_get_sealed_receipt ──────────────────────────────────────────────
|
|
327
|
+
// PERSIST-017: returns checkpoint_status when CheckpointStatusProvider is wired.
|
|
328
|
+
// Falls back to the M1 stub response when no provider is configured.
|
|
329
|
+
server.registerTool("cello_get_sealed_receipt", {
|
|
330
|
+
description: "Retrieve the sealed receipt and MMR checkpoint status for a closed session.",
|
|
331
|
+
inputSchema: {
|
|
332
|
+
session_id: z.string().describe("Session ID hex"),
|
|
333
|
+
},
|
|
334
|
+
}, async ({ session_id }) => {
|
|
335
|
+
if (checkpointStatusProvider) {
|
|
336
|
+
const stagingStatus = await checkpointStatusProvider.getSealStagingStatus(session_id);
|
|
337
|
+
if (stagingStatus.status === "pending") {
|
|
338
|
+
return jsonText({
|
|
339
|
+
available: true,
|
|
340
|
+
checkpoint_status: "pending",
|
|
341
|
+
staged_at: stagingStatus.staged_at,
|
|
342
|
+
attestation_self: "PENDING",
|
|
343
|
+
attestation_self_reason: "MMR checkpoint not yet written",
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
if (stagingStatus.status === "confirmed") {
|
|
347
|
+
return jsonText({
|
|
348
|
+
available: true,
|
|
349
|
+
checkpoint_status: "confirmed",
|
|
350
|
+
session_mmr_peak: stagingStatus.checkpoint_peak_hash,
|
|
351
|
+
leaf_index: stagingStatus.leaf_index,
|
|
352
|
+
checkpoint_id: stagingStatus.checkpoint_id,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
// not_staged: session was never sealed
|
|
356
|
+
return jsonText({ available: false, reason: "not_yet_sealed" });
|
|
357
|
+
}
|
|
358
|
+
return jsonText({ available: false, reason: "not_yet_sealed" });
|
|
359
|
+
});
|
|
360
|
+
// ── cello_get_inclusion_proof ─────────────────────────────────────────────
|
|
361
|
+
// PERSIST-017: returns pending/confirmed status when CheckpointStatusProvider is wired.
|
|
362
|
+
// Falls back to the M1 stub response when no provider is configured.
|
|
363
|
+
server.registerTool("cello_get_inclusion_proof", {
|
|
364
|
+
description: "Retrieve the MMR inclusion proof status for a sealed session.",
|
|
365
|
+
inputSchema: {
|
|
366
|
+
session_id: z.string().describe("Session ID hex"),
|
|
367
|
+
content_hash: z.string().describe("Content hash hex of the message"),
|
|
368
|
+
},
|
|
369
|
+
}, async ({ session_id }) => {
|
|
370
|
+
if (checkpointStatusProvider) {
|
|
371
|
+
const stagingStatus = await checkpointStatusProvider.getSealStagingStatus(session_id);
|
|
372
|
+
if (stagingStatus.status === "pending") {
|
|
373
|
+
return jsonText({
|
|
374
|
+
status: "pending",
|
|
375
|
+
staged_at: stagingStatus.staged_at,
|
|
376
|
+
message: "sealed_root staged; inclusion proof available after next MMR checkpoint",
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
if (stagingStatus.status === "confirmed") {
|
|
380
|
+
return jsonText({
|
|
381
|
+
status: "confirmed",
|
|
382
|
+
leaf_index: stagingStatus.leaf_index,
|
|
383
|
+
checkpoint_peak_hash: stagingStatus.checkpoint_peak_hash,
|
|
384
|
+
checkpoint_id: stagingStatus.checkpoint_id,
|
|
385
|
+
sibling_hashes: stagingStatus.sibling_hashes,
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
// not_staged: session was never sealed — error distinguishes from 'pending'
|
|
389
|
+
return jsonText({ status: "error", reason: "not_yet_sealed" });
|
|
390
|
+
}
|
|
391
|
+
return jsonText({ available: false, reason: "not_yet_sealed" });
|
|
392
|
+
});
|
|
393
|
+
// ── cello_status (extended with M1 fields) ────────────────────────────────
|
|
394
|
+
server.registerTool("cello_status", {
|
|
395
|
+
description: "Return transport status, own pubkey, connection info, and M1 session summary.",
|
|
396
|
+
inputSchema: {},
|
|
397
|
+
}, async () => {
|
|
398
|
+
const ownPubkey = Buffer.from(await keyProvider.getPublicKey()).toString("hex");
|
|
399
|
+
const sessions = client.listSessions();
|
|
400
|
+
const activeSessionCount = sessions.filter((s) => s.status === "active").length;
|
|
401
|
+
// directoryReachable check: any session with a directory_endpoint indicates reachability
|
|
402
|
+
const directoryReachable = sessions.some((s) => s.directory_endpoint && s.directory_endpoint.peer_id !== "");
|
|
403
|
+
return jsonText({
|
|
404
|
+
transport_started: transportStarted(),
|
|
405
|
+
own_pubkey: ownPubkey,
|
|
406
|
+
listen_addresses: node.listenAddresses(),
|
|
407
|
+
connected_peer_count: node.getConnections().length,
|
|
408
|
+
uptime_seconds: Math.floor((Date.now() - startedAt) / 1000),
|
|
409
|
+
active_session_count: activeSessionCount,
|
|
410
|
+
directory_reachable: directoryReachable,
|
|
411
|
+
});
|
|
412
|
+
});
|
|
413
|
+
// ── cello_backup ─────────────────────────────────────────────────────────────
|
|
414
|
+
// PERSIST-022: Trigger an immediate encrypted backup of the local database.
|
|
415
|
+
// If no backup is configured (cloudStorage=null), ClientBackup logs
|
|
416
|
+
// client.backup.not.configured at WARN and returns without error.
|
|
417
|
+
server.registerTool("cello_backup", {
|
|
418
|
+
description: "Trigger an immediate encrypted backup of the local CELLO database to cloud storage. " +
|
|
419
|
+
"Returns ok:true on success. Returns ok:false with reason if backup fails or is not configured.",
|
|
420
|
+
inputSchema: {},
|
|
421
|
+
}, async () => {
|
|
422
|
+
if (!clientBackup) {
|
|
423
|
+
return jsonText({ ok: false, reason: "not_configured" });
|
|
424
|
+
}
|
|
425
|
+
try {
|
|
426
|
+
const result = await clientBackup.backup();
|
|
427
|
+
return jsonText(result);
|
|
428
|
+
}
|
|
429
|
+
catch (err) {
|
|
430
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
431
|
+
return jsonText({ ok: false, reason });
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
// ── cello_restore ─────────────────────────────────────────────────────────────
|
|
435
|
+
// PERSIST-022: Download and decrypt the backup, replacing the local database file.
|
|
436
|
+
// On checksum mismatch or decrypt failure the local file is NOT overwritten and restore throws.
|
|
437
|
+
server.registerTool("cello_restore", {
|
|
438
|
+
description: "Restore the local CELLO database from the most recent cloud backup. " +
|
|
439
|
+
"Replaces the local database file only after checksum verification passes. " +
|
|
440
|
+
"Returns ok:true on success. If cloud storage is not configured, returns ok:false.",
|
|
441
|
+
inputSchema: {},
|
|
442
|
+
}, async () => {
|
|
443
|
+
if (!clientBackup) {
|
|
444
|
+
return jsonText({ ok: false, reason: "not_configured" });
|
|
445
|
+
}
|
|
446
|
+
try {
|
|
447
|
+
await clientBackup.restore();
|
|
448
|
+
return jsonText({ ok: true });
|
|
449
|
+
}
|
|
450
|
+
catch (err) {
|
|
451
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
452
|
+
return jsonText({ ok: false, reason });
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
return server;
|
|
456
|
+
}
|
|
457
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
458
|
+
function formatSessionMessage(msg, sessionIdHex) {
|
|
459
|
+
try {
|
|
460
|
+
const content = new TextDecoder("utf-8", { fatal: true }).decode(msg.content);
|
|
461
|
+
return {
|
|
462
|
+
type: "message",
|
|
463
|
+
content,
|
|
464
|
+
session_id: sessionIdHex,
|
|
465
|
+
sender_pubkey: Buffer.from(msg.senderPubkey).toString("hex"),
|
|
466
|
+
sequence_number: msg.sequenceNumber,
|
|
467
|
+
leaf_hash: Buffer.from(msg.leafHash).toString("hex"),
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
catch {
|
|
471
|
+
return {
|
|
472
|
+
type: "decode_error",
|
|
473
|
+
session_id: sessionIdHex,
|
|
474
|
+
sender_pubkey: Buffer.from(msg.senderPubkey).toString("hex"),
|
|
475
|
+
sequence_number: msg.sequenceNumber,
|
|
476
|
+
leaf_hash: Buffer.from(msg.leafHash).toString("hex"),
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
function sleep(ms) {
|
|
481
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
482
|
+
}
|
|
483
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cello-protocol/connect",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
},
|
|
12
12
|
"main": "./dist/index.js",
|
|
13
13
|
"types": "./dist/index.d.ts",
|
|
14
|
+
"bin": {
|
|
15
|
+
"cello-mcp": "./dist/bin/cello-mcp.js"
|
|
16
|
+
},
|
|
14
17
|
"exports": {
|
|
15
18
|
".": {
|
|
16
19
|
"import": "./dist/index.js",
|
|
@@ -23,12 +26,19 @@
|
|
|
23
26
|
"SKILL.md"
|
|
24
27
|
],
|
|
25
28
|
"dependencies": {
|
|
26
|
-
"@cello-protocol/
|
|
27
|
-
"@
|
|
28
|
-
"@
|
|
29
|
+
"@cello-protocol/interfaces": "0.0.3",
|
|
30
|
+
"@libp2p/peer-id": "^6.0.8",
|
|
31
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
32
|
+
"zod": "^4.4.2",
|
|
33
|
+
"@cello-protocol/client": "0.0.3",
|
|
34
|
+
"@cello-protocol/crypto": "0.0.3",
|
|
35
|
+
"@cello-protocol/transport": "0.0.3"
|
|
29
36
|
},
|
|
30
37
|
"devDependencies": {
|
|
31
|
-
"@
|
|
38
|
+
"@claude-flow/testing": "3.0.0-alpha.6",
|
|
39
|
+
"@types/node": "^25.6.2",
|
|
40
|
+
"cbor-x": "^1.6.0",
|
|
41
|
+
"it-length-prefixed": "^10.0.1"
|
|
32
42
|
},
|
|
33
43
|
"scripts": {
|
|
34
44
|
"typecheck": "tsc --build",
|