@yamo/mcp-server 1.3.2 → 1.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +100 -9
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -66,12 +66,15 @@ const SUBMIT_BLOCK_TOOL = {
|
|
|
66
66
|
- contentHash: Must be a valid bytes32 hash (32 bytes = 64 hex characters)
|
|
67
67
|
• Format: "0x" followed by exactly 64 hexadecimal characters
|
|
68
68
|
• Do NOT include algorithm prefixes (e.g., "sha256:")
|
|
69
|
-
- previousBlock:
|
|
69
|
+
- previousBlock: Content hash of parent block.
|
|
70
|
+
• If omitted, automatically fetches the latest block's contentHash
|
|
71
|
+
• For genesis block: 0x0000000000000000000000000000000000000000000000000000000000000000
|
|
70
72
|
|
|
71
73
|
**Transaction Flow:**
|
|
72
|
-
1.
|
|
73
|
-
2.
|
|
74
|
-
3.
|
|
74
|
+
1. If previousBlock omitted, fetches latest block from chain automatically
|
|
75
|
+
2. Content and files are uploaded to IPFS (if provided)
|
|
76
|
+
3. Block is submitted to smart contract with hash reference
|
|
77
|
+
4. Returns transaction hash and IPFS CID`,
|
|
75
78
|
inputSchema: {
|
|
76
79
|
type: "object",
|
|
77
80
|
properties: {
|
|
@@ -82,7 +85,7 @@ const SUBMIT_BLOCK_TOOL = {
|
|
|
82
85
|
previousBlock: {
|
|
83
86
|
type: "string",
|
|
84
87
|
pattern: "^0x[a-fA-F0-9]{64}$",
|
|
85
|
-
description: "Content hash of parent block.
|
|
88
|
+
description: "Content hash of parent block. If omitted, auto-fetched from latest block. Use 0x0000...0000 for genesis."
|
|
86
89
|
},
|
|
87
90
|
contentHash: {
|
|
88
91
|
type: "string",
|
|
@@ -119,7 +122,7 @@ const SUBMIT_BLOCK_TOOL = {
|
|
|
119
122
|
description: "Optional: Output files to bundle. Content can be file path (auto-read) or actual content."
|
|
120
123
|
}
|
|
121
124
|
},
|
|
122
|
-
required: ["blockId", "
|
|
125
|
+
required: ["blockId", "contentHash", "consensusType", "ledger"],
|
|
123
126
|
},
|
|
124
127
|
};
|
|
125
128
|
const GET_BLOCK_TOOL = {
|
|
@@ -152,6 +155,33 @@ Use for:
|
|
|
152
155
|
required: ["blockId"],
|
|
153
156
|
},
|
|
154
157
|
};
|
|
158
|
+
const GET_LATEST_BLOCK_TOOL = {
|
|
159
|
+
name: "yamo_get_latest_block",
|
|
160
|
+
description: `Retrieves the most recently submitted YAMO block from the blockchain.
|
|
161
|
+
|
|
162
|
+
Queries BlockSubmitted events to find the block with the highest timestamp,
|
|
163
|
+
then fetches its full details including the contentHash that should be used
|
|
164
|
+
as previousBlock for the next submission.
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
- blockId: Unique block identifier
|
|
168
|
+
- previousBlock: Content hash of parent block
|
|
169
|
+
- agentAddress: Ethereum address of submitter
|
|
170
|
+
- contentHash: 32-byte hash stored on-chain (use as previousBlock for next submission)
|
|
171
|
+
- timestamp: Block submission timestamp (Unix epoch)
|
|
172
|
+
- consensusType: Consensus mechanism used
|
|
173
|
+
- ledger: Distributed storage reference
|
|
174
|
+
- ipfsCID: IPFS CID if content was anchored
|
|
175
|
+
|
|
176
|
+
Use for:
|
|
177
|
+
- Automatically getting the chain tip for extending the chain
|
|
178
|
+
- Fetching the contentHash to use as previousBlock in submitBlock
|
|
179
|
+
- Discovering the latest block without knowing its ID`,
|
|
180
|
+
inputSchema: {
|
|
181
|
+
type: "object",
|
|
182
|
+
properties: {},
|
|
183
|
+
},
|
|
184
|
+
};
|
|
155
185
|
const AUDIT_BLOCK_TOOL = {
|
|
156
186
|
name: "yamo_audit_block",
|
|
157
187
|
description: `Performs cryptographic integrity audit of a block.
|
|
@@ -220,6 +250,8 @@ class YamoMcpServer {
|
|
|
220
250
|
server;
|
|
221
251
|
ipfs;
|
|
222
252
|
chain;
|
|
253
|
+
// Cache for chain continuation: latest submitted block's contentHash
|
|
254
|
+
latestContentHash = null;
|
|
223
255
|
constructor() {
|
|
224
256
|
this.server = new index_js_1.Server({ name: "yamo", version: pkg.version }, { capabilities: { tools: {} } });
|
|
225
257
|
this.ipfs = new core_1.IpfsManager();
|
|
@@ -228,7 +260,7 @@ class YamoMcpServer {
|
|
|
228
260
|
}
|
|
229
261
|
setupHandlers() {
|
|
230
262
|
this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
|
|
231
|
-
tools: [SUBMIT_BLOCK_TOOL, GET_BLOCK_TOOL, AUDIT_BLOCK_TOOL, VERIFY_BLOCK_TOOL],
|
|
263
|
+
tools: [SUBMIT_BLOCK_TOOL, GET_BLOCK_TOOL, GET_LATEST_BLOCK_TOOL, AUDIT_BLOCK_TOOL, VERIFY_BLOCK_TOOL],
|
|
232
264
|
}));
|
|
233
265
|
this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
234
266
|
const { name, arguments: args } = request.params;
|
|
@@ -259,7 +291,33 @@ class YamoMcpServer {
|
|
|
259
291
|
}
|
|
260
292
|
// Input validation (Part 3: Security Fixes)
|
|
261
293
|
validateBytes32(contentHash, "contentHash");
|
|
262
|
-
|
|
294
|
+
// Auto-fetch previousBlock if not provided
|
|
295
|
+
let resolvedPreviousBlock = previousBlock;
|
|
296
|
+
if (!resolvedPreviousBlock) {
|
|
297
|
+
console.error(`[INFO] No previousBlock provided, fetching latest block from chain...`);
|
|
298
|
+
// First, try the cache (most reliable for chain continuation)
|
|
299
|
+
if (this.latestContentHash) {
|
|
300
|
+
resolvedPreviousBlock = this.latestContentHash;
|
|
301
|
+
console.error(`[INFO] Using cached latest block's contentHash: ${resolvedPreviousBlock}`);
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
// Fallback to direct contract state read (reliable)
|
|
305
|
+
const latestHash = await this.chain.getLatestBlockHash();
|
|
306
|
+
if (latestHash && latestHash !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
|
|
307
|
+
resolvedPreviousBlock = latestHash;
|
|
308
|
+
this.latestContentHash = latestHash; // Update cache
|
|
309
|
+
console.error(`[INFO] Using latest block's contentHash from contract: ${resolvedPreviousBlock}`);
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
// No blocks exist yet, use genesis
|
|
313
|
+
resolvedPreviousBlock = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
314
|
+
console.error(`[INFO] No existing blocks found, using genesis`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
validateBytes32(previousBlock, "previousBlock");
|
|
320
|
+
}
|
|
263
321
|
let ipfsCID = undefined;
|
|
264
322
|
if (content) {
|
|
265
323
|
ipfsCID = await this.ipfs.upload({
|
|
@@ -268,8 +326,11 @@ class YamoMcpServer {
|
|
|
268
326
|
encryptionKey
|
|
269
327
|
});
|
|
270
328
|
}
|
|
271
|
-
const tx = await this.chain.submitBlock(blockId,
|
|
329
|
+
const tx = await this.chain.submitBlock(blockId, resolvedPreviousBlock, contentHash, consensusType, ledger, ipfsCID);
|
|
272
330
|
const receipt = await tx.wait();
|
|
331
|
+
// Update cache with the new block's contentHash for chain continuation
|
|
332
|
+
this.latestContentHash = contentHash;
|
|
333
|
+
console.error(`[INFO] Updated latestContentHash cache: ${contentHash}`);
|
|
273
334
|
return {
|
|
274
335
|
content: [{ type: "text", text: JSON.stringify({
|
|
275
336
|
success: true,
|
|
@@ -279,6 +340,7 @@ class YamoMcpServer {
|
|
|
279
340
|
gasUsed: receipt.gasUsed.toString(),
|
|
280
341
|
effectiveGasPrice: receipt.effectiveGasPrice?.toString(),
|
|
281
342
|
ipfsCID: ipfsCID || null,
|
|
343
|
+
previousBlock: resolvedPreviousBlock,
|
|
282
344
|
contractAddress: this.chain.getContractAddress(),
|
|
283
345
|
timestamp: new Date().toISOString()
|
|
284
346
|
}, null, 2) }],
|
|
@@ -315,6 +377,35 @@ class YamoMcpServer {
|
|
|
315
377
|
}, null, 2) }],
|
|
316
378
|
};
|
|
317
379
|
}
|
|
380
|
+
if (name === "yamo_get_latest_block") {
|
|
381
|
+
const latestBlock = await this.chain.getLatestBlock();
|
|
382
|
+
if (!latestBlock) {
|
|
383
|
+
return {
|
|
384
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
385
|
+
success: false,
|
|
386
|
+
error: "No blocks found on-chain",
|
|
387
|
+
hint: "The chain may be empty. Try submitting a genesis block first."
|
|
388
|
+
}, null, 2) }],
|
|
389
|
+
isError: false,
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
return {
|
|
393
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
394
|
+
success: true,
|
|
395
|
+
block: {
|
|
396
|
+
blockId: latestBlock.blockId,
|
|
397
|
+
previousBlock: latestBlock.previousBlock,
|
|
398
|
+
agentAddress: latestBlock.agentAddress,
|
|
399
|
+
contentHash: latestBlock.contentHash,
|
|
400
|
+
timestamp: latestBlock.timestamp,
|
|
401
|
+
timestampISO: new Date(latestBlock.timestamp * 1000).toISOString(),
|
|
402
|
+
consensusType: latestBlock.consensusType,
|
|
403
|
+
ledger: latestBlock.ledger,
|
|
404
|
+
ipfsCID: latestBlock.ipfsCID || null
|
|
405
|
+
}
|
|
406
|
+
}, null, 2) }],
|
|
407
|
+
};
|
|
408
|
+
}
|
|
318
409
|
if (name === "yamo_audit_block") {
|
|
319
410
|
const { blockId, encryptionKey } = args;
|
|
320
411
|
// Get block from chain
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yamo/mcp-server",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.9",
|
|
4
4
|
"description": "YAMO Protocol v0.4 - Model Context Protocol server for AI agents",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
48
|
-
"@yamo/core": "^1.
|
|
48
|
+
"@yamo/core": "^1.2.12",
|
|
49
49
|
"axios": "^1.13.2",
|
|
50
50
|
"dotenv": "^17.2.3",
|
|
51
51
|
"form-data": "^4.0.5"
|