@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.
Files changed (2) hide show
  1. package/dist/index.js +100 -9
  2. 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: For genesis block use: 0x0000000000000000000000000000000000000000000000000000000000000000
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. Content and files are uploaded to IPFS (if provided)
73
- 2. Block is submitted to smart contract with hash reference
74
- 3. Returns transaction hash and IPFS CID`,
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. Genesis: 0x0000...0000"
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", "previousBlock", "contentHash", "consensusType", "ledger"],
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
- validateBytes32(previousBlock, "previousBlock");
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, previousBlock, contentHash, consensusType, ledger, ipfsCID);
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.2",
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.1.0",
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"