indelible-mcp 2.4.0 → 2.5.1
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/package.json +1 -1
- package/src/index.js +73 -2
- package/src/tools/load_context.js +14 -0
- package/src/tools/save_file.js +7 -3
- package/src/tools/save_project.js +18 -4
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -220,7 +220,7 @@ Commands:
|
|
|
220
220
|
|
|
221
221
|
function printHelp() {
|
|
222
222
|
console.log(`
|
|
223
|
-
Indelible MCP — Blockchain memory for Claude Code (v2.
|
|
223
|
+
Indelible MCP — Blockchain memory for Claude Code (v2.5.1)
|
|
224
224
|
|
|
225
225
|
Setup:
|
|
226
226
|
indelible-mcp setup --wif=KEY --pin=PIN Import and encrypt your private key
|
|
@@ -316,7 +316,7 @@ function readStdin() {
|
|
|
316
316
|
|
|
317
317
|
const SERVER_INFO = {
|
|
318
318
|
name: 'indelible',
|
|
319
|
-
version: '2.
|
|
319
|
+
version: '2.5.1',
|
|
320
320
|
description: 'Blockchain-backed memory and code storage for Claude Code'
|
|
321
321
|
}
|
|
322
322
|
|
|
@@ -380,6 +380,62 @@ const TOOLS = [
|
|
|
380
380
|
},
|
|
381
381
|
required: []
|
|
382
382
|
}
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
name: 'save_file',
|
|
386
|
+
description: 'Save a single file to the blockchain. Encrypted with your key, permanent and restorable.',
|
|
387
|
+
inputSchema: {
|
|
388
|
+
type: 'object',
|
|
389
|
+
properties: {
|
|
390
|
+
file_path: { type: 'string', description: 'Path to the file to save' }
|
|
391
|
+
},
|
|
392
|
+
required: ['file_path']
|
|
393
|
+
}
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
name: 'load_file',
|
|
397
|
+
description: 'Load a file from the blockchain by transaction ID.',
|
|
398
|
+
inputSchema: {
|
|
399
|
+
type: 'object',
|
|
400
|
+
properties: {
|
|
401
|
+
txid: { type: 'string', description: 'Transaction ID of the saved file' },
|
|
402
|
+
output_path: { type: 'string', description: 'Path to write the restored file (optional)' }
|
|
403
|
+
},
|
|
404
|
+
required: ['txid']
|
|
405
|
+
}
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
name: 'save_project',
|
|
409
|
+
description: 'Save an entire project directory to the blockchain. All files encrypted and bundled.',
|
|
410
|
+
inputSchema: {
|
|
411
|
+
type: 'object',
|
|
412
|
+
properties: {
|
|
413
|
+
dir_path: { type: 'string', description: 'Path to the project directory' },
|
|
414
|
+
name: { type: 'string', description: 'Project name (optional, defaults to directory name)' }
|
|
415
|
+
},
|
|
416
|
+
required: ['dir_path']
|
|
417
|
+
}
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
name: 'load_project',
|
|
421
|
+
description: 'Load a project from the blockchain by transaction ID.',
|
|
422
|
+
inputSchema: {
|
|
423
|
+
type: 'object',
|
|
424
|
+
properties: {
|
|
425
|
+
txid: { type: 'string', description: 'Transaction ID of the saved project' },
|
|
426
|
+
output_dir: { type: 'string', description: 'Directory to restore into (optional)' }
|
|
427
|
+
},
|
|
428
|
+
required: ['txid']
|
|
429
|
+
}
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
name: 'update_vault_index',
|
|
433
|
+
description: 'Update the on-chain vault index with metadata for all saved files and projects.',
|
|
434
|
+
inputSchema: {
|
|
435
|
+
type: 'object',
|
|
436
|
+
properties: {},
|
|
437
|
+
required: []
|
|
438
|
+
}
|
|
383
439
|
}
|
|
384
440
|
]
|
|
385
441
|
|
|
@@ -421,6 +477,21 @@ async function handleMcpRequest(request) {
|
|
|
421
477
|
case 'load_style':
|
|
422
478
|
result = await loadStyle(args?.txid)
|
|
423
479
|
break
|
|
480
|
+
case 'save_file':
|
|
481
|
+
result = await saveFile(args?.file_path)
|
|
482
|
+
break
|
|
483
|
+
case 'load_file':
|
|
484
|
+
result = await loadFile(args?.txid, { outputPath: args?.output_path })
|
|
485
|
+
break
|
|
486
|
+
case 'save_project':
|
|
487
|
+
result = await saveProject(args?.dir_path, { name: args?.name })
|
|
488
|
+
break
|
|
489
|
+
case 'load_project':
|
|
490
|
+
result = await loadProject(args?.txid, { outputDir: args?.output_dir })
|
|
491
|
+
break
|
|
492
|
+
case 'update_vault_index':
|
|
493
|
+
result = await updateVaultIndex()
|
|
494
|
+
break
|
|
424
495
|
default:
|
|
425
496
|
throw new Error(`Unknown tool: ${name}`)
|
|
426
497
|
}
|
|
@@ -12,6 +12,7 @@ import { loadConfig, getWif } from '../lib/config.js'
|
|
|
12
12
|
import { decrypt } from '../lib/crypto.js'
|
|
13
13
|
import { getSessions } from '../lib/api-client.js'
|
|
14
14
|
import * as spv from '../lib/spv.js'
|
|
15
|
+
import { loadStyle } from './load_style.js'
|
|
15
16
|
|
|
16
17
|
const RECENT_MESSAGES_FULL = 25
|
|
17
18
|
const OLDER_MESSAGES_SUMMARIZED = 15
|
|
@@ -226,6 +227,19 @@ export async function loadContext(numSessions = 5) {
|
|
|
226
227
|
}
|
|
227
228
|
}
|
|
228
229
|
|
|
230
|
+
// Auto-inject active style if one exists in config
|
|
231
|
+
try {
|
|
232
|
+
const styleResult = await loadStyle()
|
|
233
|
+
if (styleResult.success && styleResult.rules) {
|
|
234
|
+
contextParts.push('')
|
|
235
|
+
contextParts.push(`# AI Interaction Style: ${styleResult.name || 'default'}`)
|
|
236
|
+
contextParts.push(`Loaded from blockchain tx: ${styleResult.txId}`)
|
|
237
|
+
contextParts.push(`Owner: ${styleResult.owner || config.address} | Created: ${styleResult.createdAt || 'Unknown'}`)
|
|
238
|
+
contextParts.push('')
|
|
239
|
+
contextParts.push(styleResult.rules)
|
|
240
|
+
}
|
|
241
|
+
} catch { /* style injection is best-effort — don't break session restore */ }
|
|
242
|
+
|
|
229
243
|
return {
|
|
230
244
|
success: true,
|
|
231
245
|
context: contextParts.join('\n'),
|
package/src/tools/save_file.js
CHANGED
|
@@ -34,7 +34,7 @@ export async function saveFile(filePath, options = {}) {
|
|
|
34
34
|
const filenameEnc = encrypt(filename, wif)
|
|
35
35
|
const pathEnc = encrypt(relativePath, wif)
|
|
36
36
|
|
|
37
|
-
let utxos = await spv.getUtxos(config.address)
|
|
37
|
+
let utxos = options.utxos || await spv.getUtxos(config.address)
|
|
38
38
|
if (!utxos || utxos.length === 0) {
|
|
39
39
|
return { success: false, error: 'No UTXOs available. Fund your wallet first.' }
|
|
40
40
|
}
|
|
@@ -43,6 +43,7 @@ export async function saveFile(filePath, options = {}) {
|
|
|
43
43
|
|
|
44
44
|
try {
|
|
45
45
|
let masterTxId
|
|
46
|
+
let finalChangeUtxos = null
|
|
46
47
|
|
|
47
48
|
if (encrypted.length <= MAX_CHUNK_SIZE) {
|
|
48
49
|
const payload = {
|
|
@@ -57,11 +58,12 @@ export async function saveFile(filePath, options = {}) {
|
|
|
57
58
|
timestamp
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
const { txHex, txId } = await spv.buildOpReturnTxWithChange(
|
|
61
|
+
const { txHex, txId, changeUtxos } = await spv.buildOpReturnTxWithChange(
|
|
61
62
|
wif, utxos, JSON.stringify(payload)
|
|
62
63
|
)
|
|
63
64
|
const result = await spv.broadcastTx(txHex)
|
|
64
65
|
masterTxId = result.txid || txId
|
|
66
|
+
finalChangeUtxos = changeUtxos
|
|
65
67
|
} else {
|
|
66
68
|
// Chunked upload
|
|
67
69
|
const chunks = []
|
|
@@ -118,11 +120,12 @@ export async function saveFile(filePath, options = {}) {
|
|
|
118
120
|
await new Promise(r => setTimeout(r, 500))
|
|
119
121
|
utxos = await spv.getUtxos(config.address)
|
|
120
122
|
}
|
|
121
|
-
const { txHex, txId } = await spv.buildOpReturnTxWithChange(
|
|
123
|
+
const { txHex, txId, changeUtxos: masterChangeUtxos } = await spv.buildOpReturnTxWithChange(
|
|
122
124
|
wif, utxos, JSON.stringify(masterPayload)
|
|
123
125
|
)
|
|
124
126
|
const result = await spv.broadcastTx(txHex)
|
|
125
127
|
masterTxId = result.txid || txId
|
|
128
|
+
finalChangeUtxos = masterChangeUtxos
|
|
126
129
|
}
|
|
127
130
|
|
|
128
131
|
// Track in config
|
|
@@ -147,6 +150,7 @@ export async function saveFile(filePath, options = {}) {
|
|
|
147
150
|
size: fileSize,
|
|
148
151
|
contentHash: `sha256:${contentHash}`,
|
|
149
152
|
chunks: chunkCount,
|
|
153
|
+
changeUtxos: finalChangeUtxos,
|
|
150
154
|
message: `File "${filename}" saved to blockchain. txId: ${masterTxId}${chunkCount > 1 ? ` (${chunkCount} chunks)` : ''}`
|
|
151
155
|
}
|
|
152
156
|
} catch (error) {
|
|
@@ -97,11 +97,19 @@ export async function saveProject(dirPath, options = {}) {
|
|
|
97
97
|
let totalSize = 0
|
|
98
98
|
|
|
99
99
|
try {
|
|
100
|
+
let lastChangeUtxos = null
|
|
101
|
+
|
|
100
102
|
for (let i = 0; i < allFiles.length; i++) {
|
|
101
103
|
const file = allFiles[i]
|
|
102
|
-
const
|
|
104
|
+
const fileOpts = {
|
|
103
105
|
relativePath: `${projectName}/${file.relativePath}`
|
|
104
|
-
}
|
|
106
|
+
}
|
|
107
|
+
// Chain: pass previous file's change UTXOs so we don't fight over the same UTXO
|
|
108
|
+
if (lastChangeUtxos && lastChangeUtxos.length > 0) {
|
|
109
|
+
fileOpts.utxos = lastChangeUtxos
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const result = await saveFile(file.absolutePath, fileOpts)
|
|
105
113
|
|
|
106
114
|
if (!result.success) {
|
|
107
115
|
return {
|
|
@@ -112,6 +120,9 @@ export async function saveProject(dirPath, options = {}) {
|
|
|
112
120
|
}
|
|
113
121
|
}
|
|
114
122
|
|
|
123
|
+
// Capture change UTXOs for next file
|
|
124
|
+
lastChangeUtxos = result.changeUtxos
|
|
125
|
+
|
|
115
126
|
savedFiles.push({
|
|
116
127
|
path: file.relativePath,
|
|
117
128
|
txid: result.txId,
|
|
@@ -142,7 +153,10 @@ export async function saveProject(dirPath, options = {}) {
|
|
|
142
153
|
timestamp
|
|
143
154
|
}
|
|
144
155
|
|
|
145
|
-
|
|
156
|
+
// Use last file's change UTXOs for manifest (chain continues)
|
|
157
|
+
let utxos = (lastChangeUtxos && lastChangeUtxos.length > 0)
|
|
158
|
+
? lastChangeUtxos
|
|
159
|
+
: await spv.getUtxos(config.address)
|
|
146
160
|
if (!utxos || utxos.length === 0) {
|
|
147
161
|
return {
|
|
148
162
|
success: false,
|
|
@@ -151,7 +165,7 @@ export async function saveProject(dirPath, options = {}) {
|
|
|
151
165
|
}
|
|
152
166
|
}
|
|
153
167
|
|
|
154
|
-
const { txHex, txId } = await spv.
|
|
168
|
+
const { txHex, txId } = await spv.buildOpReturnTxWithChange(
|
|
155
169
|
wif, utxos, JSON.stringify(manifest)
|
|
156
170
|
)
|
|
157
171
|
const result = await spv.broadcastTx(txHex)
|