@yamo/mcp-server 1.0.5 → 1.3.0

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 +271 -19
  2. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -43,37 +43,167 @@ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
43
43
  const core_1 = require("@yamo/core");
44
44
  const dotenv = __importStar(require("dotenv"));
45
45
  const fs_1 = __importDefault(require("fs"));
46
+ const crypto_1 = __importDefault(require("crypto"));
47
+ const path_1 = __importDefault(require("path"));
48
+ const pkg = JSON.parse(fs_1.default.readFileSync(path_1.default.join(__dirname, '../package.json'), 'utf8'));
46
49
  dotenv.config();
47
50
  const SUBMIT_BLOCK_TOOL = {
48
51
  name: "yamo_submit_block",
49
- description: "Submits a YAMO block to the YAMORegistry smart contract.",
52
+ description: `Submits a YAMO block to the YAMORegistry smart contract.
53
+
54
+ **IMPORTANT FORMAT REQUIREMENTS:**
55
+ - blockId: Must follow YAMO naming convention: {origin}_{workflow}
56
+ • Examples: "claude_chain", "aurora_weave", "document_translation"
57
+ • Do NOT use sequence numbers (001, 002)
58
+ - contentHash: Must be a valid bytes32 hash (32 bytes = 64 hex characters)
59
+ • Format: "0x" followed by exactly 64 hexadecimal characters
60
+ • Do NOT include algorithm prefixes (e.g., "sha256:")
61
+ - previousBlock: For genesis block use: 0x0000000000000000000000000000000000000000000000000000000000000000
62
+
63
+ **Transaction Flow:**
64
+ 1. Content and files are uploaded to IPFS (if provided)
65
+ 2. Block is submitted to smart contract with hash reference
66
+ 3. Returns transaction hash and IPFS CID`,
50
67
  inputSchema: {
51
68
  type: "object",
52
69
  properties: {
53
- blockId: { type: "string" },
54
- previousBlock: { type: "string" },
55
- contentHash: { type: "string" },
56
- consensusType: { type: "string" },
57
- ledger: { type: "string" },
58
- content: { type: "string", description: "Optional: Full YAMO text content for IPFS anchoring." },
59
- encryptionKey: { type: "string", description: "Optional: Key to encrypt the IPFS bundle." },
70
+ blockId: {
71
+ type: "string",
72
+ description: "Unique block identifier following YAMO naming: {origin}_{workflow} (e.g., 'claude_chain')"
73
+ },
74
+ previousBlock: {
75
+ type: "string",
76
+ pattern: "^0x[a-fA-F0-9]{64}$",
77
+ description: "Content hash of parent block. Genesis: 0x0000...0000"
78
+ },
79
+ contentHash: {
80
+ type: "string",
81
+ pattern: "^0x[a-fA-F0-9]{64}$",
82
+ description: "32-byte hash (0x + 64 hex chars). No algorithm prefixes."
83
+ },
84
+ consensusType: {
85
+ type: "string",
86
+ enum: ["agent_vote", "PoW", "PoS", "cli_manual", "mcp_generated"],
87
+ description: "Consensus mechanism"
88
+ },
89
+ ledger: {
90
+ type: "string",
91
+ description: "Distributed storage reference (e.g., 'ipfs', 'arweave')"
92
+ },
93
+ content: {
94
+ type: "string",
95
+ description: "Optional: Full YAMO text content for IPFS anchoring."
96
+ },
97
+ encryptionKey: {
98
+ type: "string",
99
+ description: "Optional: Strong key (12+ chars, mixed types) for IPFS encryption."
100
+ },
60
101
  files: {
61
102
  type: "array",
62
- items: { type: "object", properties: { name: { type: "string" }, content: { type: "string" } } },
63
- description: "Optional: Array of output files to bundle. Each file's 'content' can be either the actual file content (string) or a file path (will be auto-read)."
103
+ items: {
104
+ type: "object",
105
+ properties: {
106
+ name: { type: "string" },
107
+ content: { type: "string" }
108
+ },
109
+ required: ["name", "content"]
110
+ },
111
+ description: "Optional: Output files to bundle. Content can be file path (auto-read) or actual content."
64
112
  }
65
113
  },
66
114
  required: ["blockId", "previousBlock", "contentHash", "consensusType", "ledger"],
67
115
  },
68
116
  };
117
+ const GET_BLOCK_TOOL = {
118
+ name: "yamo_get_block",
119
+ description: `Retrieves full block data from the YAMO blockchain.
120
+
121
+ Returns:
122
+ - blockId: Unique block identifier
123
+ - previousBlock: Content hash of parent block
124
+ - agentAddress: Ethereum address of submitter
125
+ - contentHash: 32-byte hash stored on-chain
126
+ - timestamp: Block submission timestamp (Unix epoch)
127
+ - consensusType: Consensus mechanism used
128
+ - ledger: Distributed storage reference
129
+ - ipfsCID: IPFS CID if content was anchored
130
+
131
+ Use for:
132
+ - Verifying block existence before submission
133
+ - Getting previousBlock hash for chain continuation
134
+ - Auditing block metadata
135
+ - Exploring chain history`,
136
+ inputSchema: {
137
+ type: "object",
138
+ properties: {
139
+ blockId: {
140
+ type: "string",
141
+ description: "Block ID to retrieve (e.g., 'aurora_weave', 'claude_chain')"
142
+ }
143
+ },
144
+ required: ["blockId"],
145
+ },
146
+ };
147
+ const AUDIT_BLOCK_TOOL = {
148
+ name: "yamo_audit_block",
149
+ description: `Performs cryptographic integrity audit of a block.
150
+
151
+ **Audit Process:**
152
+ 1. Fetches block metadata from blockchain
153
+ 2. Downloads content from IPFS (if CID exists)
154
+ 3. Re-computes SHA-256 hash of downloaded content
155
+ 4. Compares computed hash vs on-chain hash
156
+ 5. Returns verification result with details
157
+
158
+ **For Encrypted Blocks:**
159
+ - Provide encryptionKey to decrypt before verification
160
+
161
+ Returns detailed audit report including:
162
+ - verified: true/false integrity check result
163
+ - onChainHash: Hash stored on blockchain
164
+ - computedHash: Hash computed from IPFS content
165
+ - ipfsCID: IPFS content identifier
166
+ - agentAddress: Submitter's Ethereum address
167
+ - contentPreview: First 500 chars of content`,
168
+ inputSchema: {
169
+ type: "object",
170
+ properties: {
171
+ blockId: {
172
+ type: "string",
173
+ description: "Block ID to audit (e.g., 'aurora_weave', 'claude_chain')"
174
+ },
175
+ encryptionKey: {
176
+ type: "string",
177
+ description: "Optional: Decryption key for encrypted bundles"
178
+ }
179
+ },
180
+ required: ["blockId"],
181
+ },
182
+ };
69
183
  const VERIFY_BLOCK_TOOL = {
70
184
  name: "yamo_verify_block",
71
- description: "Verifies a block's content hash.",
185
+ description: `Quick hash verification against on-chain record.
186
+
187
+ **IMPORTANT:** This is a SIMPLE hash comparison, NOT a full content audit.
188
+ It only checks if a provided hash matches what's stored on-chain.
189
+
190
+ For full integrity verification (including IPFS content), use 'yamo_audit_block'.
191
+
192
+ Returns:
193
+ - "VERIFIED" if hash matches on-chain record
194
+ - "FAILED" if hash does not match`,
72
195
  inputSchema: {
73
196
  type: "object",
74
197
  properties: {
75
- blockId: { type: "string" },
76
- contentHash: { type: "string" },
198
+ blockId: {
199
+ type: "string",
200
+ description: "Block ID to verify"
201
+ },
202
+ contentHash: {
203
+ type: "string",
204
+ pattern: "^0x[a-fA-F0-9]{64}$",
205
+ description: "32-byte hash to verify (0x + 64 hex chars)"
206
+ }
77
207
  },
78
208
  required: ["blockId", "contentHash"],
79
209
  },
@@ -83,14 +213,14 @@ class YamoMcpServer {
83
213
  ipfs;
84
214
  chain;
85
215
  constructor() {
86
- this.server = new index_js_1.Server({ name: "yamo", version: "1.0.0" }, { capabilities: { tools: {} } });
216
+ this.server = new index_js_1.Server({ name: "yamo", version: pkg.version }, { capabilities: { tools: {} } });
87
217
  this.ipfs = new core_1.IpfsManager();
88
218
  this.chain = new core_1.YamoChainClient();
89
219
  this.setupHandlers();
90
220
  }
91
221
  setupHandlers() {
92
222
  this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
93
- tools: [SUBMIT_BLOCK_TOOL, VERIFY_BLOCK_TOOL],
223
+ tools: [SUBMIT_BLOCK_TOOL, GET_BLOCK_TOOL, AUDIT_BLOCK_TOOL, VERIFY_BLOCK_TOOL],
94
224
  }));
95
225
  this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
96
226
  const { name, arguments: args } = request.params;
@@ -103,7 +233,7 @@ class YamoMcpServer {
103
233
  processedFiles = files.map((file) => {
104
234
  // Check if content is a file path that exists
105
235
  if (typeof file.content === 'string' && fs_1.default.existsSync(file.content)) {
106
- console.error(`Auto-reading file from path: ${file.content}`);
236
+ console.error(`[DEBUG] Auto-reading file from path: ${file.content}`);
107
237
  return {
108
238
  name: file.name,
109
239
  content: fs_1.default.readFileSync(file.content, 'utf8')
@@ -123,9 +253,126 @@ class YamoMcpServer {
123
253
  }
124
254
  const txHash = await this.chain.submitBlock(blockId, previousBlock, contentHash, consensusType, ledger, ipfsCID);
125
255
  return {
126
- content: [{ type: "text", text: `Block ${blockId} anchored. Tx: ${txHash}. IPFS: ${ipfsCID || "None"}` }],
256
+ content: [{ type: "text", text: JSON.stringify({
257
+ success: true,
258
+ blockId,
259
+ transactionHash: txHash,
260
+ ipfsCID: ipfsCID || null
261
+ }, null, 2) }],
262
+ };
263
+ }
264
+ if (name === "yamo_get_block") {
265
+ const { blockId } = args;
266
+ const block = await this.chain.getBlock(blockId);
267
+ if (!block) {
268
+ return {
269
+ content: [{ type: "text", text: JSON.stringify({
270
+ success: false,
271
+ error: "Block not found on-chain",
272
+ blockId,
273
+ hint: "Verify the blockId or check if the block was submitted"
274
+ }, null, 2) }],
275
+ isError: false,
276
+ };
277
+ }
278
+ return {
279
+ content: [{ type: "text", text: JSON.stringify({
280
+ success: true,
281
+ block: {
282
+ blockId: block.blockId,
283
+ previousBlock: block.previousBlock,
284
+ agentAddress: block.agentAddress,
285
+ contentHash: block.contentHash,
286
+ timestamp: block.timestamp,
287
+ timestampISO: new Date(block.timestamp * 1000).toISOString(),
288
+ consensusType: block.consensusType,
289
+ ledger: block.ledger,
290
+ ipfsCID: block.ipfsCID || null
291
+ }
292
+ }, null, 2) }],
127
293
  };
128
294
  }
295
+ if (name === "yamo_audit_block") {
296
+ const { blockId, encryptionKey } = args;
297
+ // Get block from chain
298
+ const block = await this.chain.getBlock(blockId);
299
+ if (!block) {
300
+ return {
301
+ content: [{ type: "text", text: JSON.stringify({
302
+ verified: false,
303
+ error: "Block not found on-chain",
304
+ blockId,
305
+ hint: "Cannot audit non-existent block"
306
+ }, null, 2) }],
307
+ isError: false,
308
+ };
309
+ }
310
+ // If no IPFS CID, can't audit content
311
+ if (!block.ipfsCID) {
312
+ return {
313
+ content: [{ type: "text", text: JSON.stringify({
314
+ verified: null, // Cannot verify without IPFS
315
+ onChainHash: block.contentHash,
316
+ ipfsCID: null,
317
+ note: "V1 block with no IPFS CID - cannot audit actual content",
318
+ blockId,
319
+ agentAddress: block.agentAddress,
320
+ timestamp: block.timestamp
321
+ }, null, 2) }],
322
+ };
323
+ }
324
+ // Download and verify from IPFS
325
+ try {
326
+ const bundle = await this.ipfs.downloadBundle(block.ipfsCID, encryptionKey);
327
+ const computedHash = "0x" + crypto_1.default.createHash("sha256").update(bundle.block).digest("hex");
328
+ const verified = computedHash === block.contentHash;
329
+ return {
330
+ content: [{ type: "text", text: JSON.stringify({
331
+ verified,
332
+ blockId,
333
+ onChainHash: block.contentHash,
334
+ computedHash,
335
+ ipfsCID: block.ipfsCID,
336
+ agentAddress: block.agentAddress,
337
+ timestamp: block.timestamp,
338
+ timestampISO: new Date(block.timestamp * 1000).toISOString(),
339
+ consensusType: block.consensusType,
340
+ ledger: block.ledger,
341
+ contentPreview: bundle.block.substring(0, 500) + (bundle.block.length > 500 ? "..." : ""),
342
+ contentLength: bundle.block.length,
343
+ artifactFiles: Object.keys(bundle.files),
344
+ wasEncrypted: !!encryptionKey
345
+ }, null, 2) }],
346
+ };
347
+ }
348
+ catch (error) {
349
+ // Enhanced error messages
350
+ let errorType = "unknown";
351
+ let hint = "";
352
+ if (error.message.includes("encrypted") && !encryptionKey) {
353
+ errorType = "missing_key";
354
+ hint = "This bundle is encrypted. Provide encryptionKey to audit.";
355
+ }
356
+ else if (error.message.includes("Decryption failed") || error.message.includes("decrypt")) {
357
+ errorType = "decryption_failed";
358
+ hint = "The provided encryption key may be incorrect.";
359
+ }
360
+ else if (error.message.includes("not found on-chain")) {
361
+ errorType = "block_not_found";
362
+ hint = "Verify the blockId was submitted correctly.";
363
+ }
364
+ return {
365
+ content: [{ type: "text", text: JSON.stringify({
366
+ verified: false,
367
+ error: error.message,
368
+ errorType,
369
+ hint,
370
+ blockId
371
+ }, null, 2) }],
372
+ isError: true,
373
+ };
374
+ }
375
+ }
129
376
  if (name === "yamo_verify_block") {
130
377
  const { blockId, contentHash } = args;
131
378
  const contract = this.chain.getContract(false);
@@ -139,7 +386,12 @@ class YamoMcpServer {
139
386
  }
140
387
  catch (error) {
141
388
  return {
142
- content: [{ type: "text", text: `Error: ${error.message}` }],
389
+ content: [{ type: "text", text: JSON.stringify({
390
+ success: false,
391
+ error: error.message,
392
+ tool: name,
393
+ timestamp: new Date().toISOString()
394
+ }, null, 2) }],
143
395
  isError: true,
144
396
  };
145
397
  }
@@ -148,7 +400,7 @@ class YamoMcpServer {
148
400
  async run() {
149
401
  const transport = new stdio_js_1.StdioServerTransport();
150
402
  await this.server.connect(transport);
151
- console.error("YAMO MCP Server v1.0.0 running on stdio");
403
+ console.error(`YAMO MCP Server v${pkg.version} running on stdio`);
152
404
  }
153
405
  }
154
406
  const server = new YamoMcpServer();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yamo/mcp-server",
3
- "version": "1.0.5",
3
+ "version": "1.3.0",
4
4
  "description": "YAMO Protocol v0.4 - Model Context Protocol server for AI agents",
5
5
  "main": "dist/index.js",
6
6
  "type": "commonjs",
@@ -9,6 +9,7 @@
9
9
  },
10
10
  "files": [
11
11
  "dist/",
12
+ "package.json",
12
13
  "README.md",
13
14
  "LICENSE"
14
15
  ],
@@ -42,7 +43,7 @@
42
43
  },
43
44
  "dependencies": {
44
45
  "@modelcontextprotocol/sdk": "^1.25.1",
45
- "@yamo/core": "^1.0.0",
46
+ "@yamo/core": "^1.1.0",
46
47
  "axios": "^1.13.2",
47
48
  "dotenv": "^17.2.3",
48
49
  "form-data": "^4.0.5"