@proofofprotocol/inscribe-mcp 0.3.4 → 0.3.6

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proofofprotocol/inscribe-mcp",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
4
4
  "description": "Verifiable inscription for AI agents - inscribe anything to blockchain with Hedera HCS",
5
5
  "type": "module",
6
6
  "main": "src/server.js",
@@ -317,75 +317,147 @@ export const showCommand = new Command('show')
317
317
  // Debug trace section
318
318
  if (options.debug) {
319
319
  console.log('');
320
- console.log(colors.bold('MCP Sequence Debug Traces'));
320
+ console.log(colors.bold('MCP Sequence Debug Traces (LAN Analyzer View)'));
321
321
  console.log(line);
322
322
 
323
- const traces = getDebugTraces(logsDir, 50);
323
+ const traces = getDebugTraces(logsDir, 100);
324
324
 
325
325
  if (traces.length === 0) {
326
326
  console.log(colors.dim('No debug traces found.'));
327
327
  console.log('');
328
- console.log('To enable debug logging, set environment variable:');
329
- console.log(colors.cyan(' export INSCRIBE_MCP_DEBUG=1'));
328
+ console.log('To enable debug logging:');
329
+ console.log(colors.cyan(' inscribe-mcp debug on'));
330
330
  console.log('');
331
- console.log('Then restart the MCP server (restart Claude Desktop).');
331
+ console.log('Or set environment variable:');
332
+ console.log(colors.cyan(' export INSCRIBE_MCP_DEBUG=1'));
332
333
  } else {
333
334
  const sequences = formatSequenceTrace(traces);
334
335
 
335
336
  if (sequences.length === 0) {
336
337
  console.log(colors.dim('No complete sequences found.'));
337
338
  } else {
338
- // Phase symbols for visual diagram
339
- const phaseSymbols = {
340
- 'agent_to_mcp': '→',
341
- 'mcp_to_chain': '→',
342
- 'chain_to_mcp': '←',
343
- 'mcp_to_agent': '←'
339
+ // LAN Analyzer style header
340
+ console.log('');
341
+ console.log(colors.dim('┌──────────────┬───────┬────────────────────┬───────────────────────────────────────┐'));
342
+ console.log(colors.dim(' Timestamp │ Dir │ Phase │ Details │'));
343
+ console.log(colors.dim('├──────────────┼───────┼────────────────────┼───────────────────────────────────────┤'));
344
+
345
+ // Direction symbols
346
+ const dirSymbols = {
347
+ 'IN': colors.green('←IN '),
348
+ 'OUT': colors.cyan('→OUT'),
349
+ 'ERR': colors.red('✗ERR')
344
350
  };
345
351
 
346
352
  const phaseLabels = {
347
353
  'agent_to_mcp': 'Agent → MCP',
348
354
  'mcp_to_chain': 'MCP → Chain',
349
355
  'chain_to_mcp': 'Chain → MCP',
350
- 'mcp_to_agent': 'MCP → Agent'
356
+ 'mcp_to_agent': 'MCP → Agent',
357
+ 'mcp_error': 'MCP Error'
351
358
  };
352
359
 
353
- console.log('');
354
- console.log(colors.dim(' ┌─────────┐ ┌─────────┐ ┌─────────┐'));
355
- console.log(colors.dim(' │ Agent │ → │ MCP │ → │ Chain │'));
356
- console.log(colors.dim(' │(Claude) │ ← │ Server │ ← │ (Hedera)│'));
357
- console.log(colors.dim(' └─────────┘ └─────────┘ └─────────┘'));
358
- console.log('');
360
+ for (const seq of sequences.slice(-8)) { // Last 8 sequences
361
+ // Sequence header
362
+ const seqTime = new Date(seq.timestamp);
363
+ const timeStr = seqTime.toLocaleTimeString('en-US', { hour12: false });
364
+ const msStr = String(seqTime.getMilliseconds()).padStart(3, '0');
359
365
 
360
- for (const seq of sequences.slice(-5)) { // Last 5 sequences
361
- const time = new Date(seq.timestamp).toLocaleTimeString();
362
- console.log(colors.bold(`[${time}] ${colors.cyan(seq.tool)}`));
366
+ console.log(colors.dim('│') + colors.bold(` ${timeStr}.${msStr}`) + colors.dim(' │ │ ') +
367
+ colors.cyan(colors.bold(`[${seq.tool}]`)).padEnd(28) + colors.dim(' │') +
368
+ colors.dim(' ─────────────────────────────────────── │'));
363
369
 
364
370
  for (const phase of seq.phases) {
365
- const symbol = phaseSymbols[phase.phase] || '?';
366
- const label = phaseLabels[phase.phase] || phase.phase;
367
- let detail = '';
371
+ const phaseTime = new Date(phase.timestamp);
372
+ const pTimeStr = phaseTime.toLocaleTimeString('en-US', { hour12: false });
373
+ const pMsStr = String(phaseTime.getMilliseconds()).padStart(3, '0');
374
+
375
+ const dir = dirSymbols[phase.direction] || colors.dim('─── ');
376
+ const phaseLabel = (phaseLabels[phase.phase] || phase.phase).padEnd(18);
377
+
378
+ // Build details string based on phase type
379
+ let details = [];
380
+
381
+ // Agent → MCP phase: show method, requestId, args
382
+ if (phase.phase === 'agent_to_mcp') {
383
+ if (phase.method) {
384
+ details.push(colors.cyan(phase.method));
385
+ }
386
+ if (phase.requestId) {
387
+ const shortId = String(phase.requestId).slice(-8);
388
+ details.push(`id:${shortId}`);
389
+ }
390
+ if (phase.argsKeys && phase.argsKeys.length > 0) {
391
+ details.push(`[${phase.argsKeys.join(',')}]`);
392
+ }
393
+ }
368
394
 
369
- if (phase.payload) {
370
- const payloadStr = JSON.stringify(phase.payload);
371
- detail = colors.dim(` ${payloadStr.slice(0, 40)}${payloadStr.length > 40 ? '...' : ''}`);
395
+ // MCP → Chain phase: show action
396
+ if (phase.phase === 'mcp_to_chain') {
397
+ if (phase.action) {
398
+ details.push(colors.yellow(phase.action));
399
+ }
400
+ if (phase.requestId) {
401
+ const shortId = String(phase.requestId).slice(-8);
402
+ details.push(`id:${shortId}`);
403
+ }
372
404
  }
373
- if (phase.status) {
374
- detail = phase.status === 'SUCCESS'
375
- ? colors.green(` ${phase.status}`)
376
- : colors.red(` ${phase.status}`);
405
+
406
+ // Chain → MCP phase: show status, duration, txId
407
+ if (phase.phase === 'chain_to_mcp') {
408
+ if (phase.status) {
409
+ const statusColor = phase.status === 'SUCCESS' ? colors.green : colors.red;
410
+ details.push(statusColor(phase.status));
411
+ }
412
+ if (phase.duration !== undefined) {
413
+ details.push(colors.yellow(`${phase.duration}ms`));
414
+ }
415
+ if (phase.resultPreview && phase.resultPreview.txId) {
416
+ const shortTx = String(phase.resultPreview.txId).slice(0, 16);
417
+ details.push(`tx:${shortTx}...`);
418
+ }
419
+ if (phase.error) {
420
+ details.push(colors.red(phase.error.slice(0, 20)));
421
+ }
377
422
  }
378
- if (phase.txId) {
379
- detail += colors.dim(` txId: ${phase.txId}`);
423
+
424
+ // MCP Agent phase: show status, duration, size
425
+ if (phase.phase === 'mcp_to_agent') {
426
+ if (phase.status) {
427
+ const statusColor = phase.status === 'SUCCESS' ? colors.green : colors.red;
428
+ details.push(statusColor(phase.status));
429
+ }
430
+ if (phase.duration !== undefined) {
431
+ details.push(colors.yellow(`${phase.duration}ms`));
432
+ }
433
+ if (phase.responseSize) {
434
+ details.push(`${phase.responseSize}b`);
435
+ }
436
+ if (phase.error) {
437
+ details.push(colors.red(phase.error.slice(0, 20)));
438
+ }
380
439
  }
381
- if (phase.result) {
382
- detail += colors.dim(` result: ${phase.result}`);
440
+
441
+ // MCP Error phase
442
+ if (phase.phase === 'mcp_error') {
443
+ if (phase.error) {
444
+ details.push(colors.red(phase.error));
445
+ }
446
+ if (phase.duration !== undefined) {
447
+ details.push(colors.yellow(`${phase.duration}ms`));
448
+ }
383
449
  }
384
450
 
385
- console.log(` ${symbol} ${label}${detail}`);
451
+ const detailStr = details.join(' ').slice(0, 37).padEnd(37);
452
+
453
+ console.log(colors.dim('│ ') + `${pTimeStr}.${pMsStr}` + colors.dim(' │ ') +
454
+ dir + colors.dim(' │ ') + phaseLabel + colors.dim(' │ ') + detailStr + colors.dim(' │'));
386
455
  }
387
- console.log('');
388
456
  }
457
+
458
+ console.log(colors.dim('└──────────────┴───────┴────────────────────┴───────────────────────────────────────┘'));
459
+ console.log('');
460
+ console.log(colors.dim(`Showing last ${Math.min(8, sequences.length)} of ${sequences.length} sequences`));
389
461
  }
390
462
  }
391
463
  }
package/src/lib/logger.js CHANGED
@@ -5,7 +5,7 @@
5
5
  * Used by MCP Server to record all tool executions.
6
6
  */
7
7
 
8
- import { existsSync, mkdirSync, appendFileSync } from 'fs';
8
+ import { existsSync, mkdirSync, appendFileSync, readFileSync } from 'fs';
9
9
  import { homedir } from 'os';
10
10
  import { join } from 'path';
11
11
 
@@ -80,6 +80,41 @@ export function logToolExecution({
80
80
  });
81
81
  }
82
82
 
83
+ /**
84
+ * Check if debug mode is enabled (config or env)
85
+ */
86
+ function isDebugEnabled() {
87
+ // Environment variable takes precedence
88
+ if (process.env.INSCRIBE_MCP_DEBUG === '1' || process.env.INSCRIBE_MCP_DEBUG === 'true') {
89
+ return true;
90
+ }
91
+
92
+ // Check config file
93
+ const configPath = join(homedir(), '.inscribe-mcp', 'config.json');
94
+ if (!existsSync(configPath)) {
95
+ return false;
96
+ }
97
+
98
+ try {
99
+ const config = JSON.parse(readFileSync(configPath, 'utf-8'));
100
+ if (!config.debug || !config.debug.enabled) {
101
+ return false;
102
+ }
103
+
104
+ // Check expiry
105
+ if (config.debug.expiresAt) {
106
+ const expiry = new Date(config.debug.expiresAt);
107
+ if (Date.now() > expiry.getTime()) {
108
+ return false; // Expired
109
+ }
110
+ }
111
+
112
+ return true;
113
+ } catch {
114
+ return false;
115
+ }
116
+ }
117
+
83
118
  /**
84
119
  * Log a debug sequence trace
85
120
  * @param {Object} options - Debug trace options
@@ -90,10 +125,23 @@ export function logDebugTrace({
90
125
  payload = null,
91
126
  status = null,
92
127
  txId = null,
93
- result = null
128
+ result = null,
129
+ duration = null,
130
+ args = null,
131
+ error = null,
132
+ // LAN analyzer style additional fields
133
+ method = null,
134
+ requestId = null,
135
+ argsKeys = null,
136
+ argsPreview = null,
137
+ action = null,
138
+ direction = null,
139
+ resultPreview = null,
140
+ responseSize = null,
141
+ errorType = null
94
142
  }) {
95
143
  // Only log if debug mode is enabled
96
- if (!process.env.INSCRIBE_MCP_DEBUG) return;
144
+ if (!isDebugEnabled()) return;
97
145
 
98
146
  writeLog({
99
147
  level: 'debug',
@@ -102,7 +150,20 @@ export function logDebugTrace({
102
150
  payload,
103
151
  status,
104
152
  txId,
105
- result
153
+ result,
154
+ duration,
155
+ args,
156
+ error,
157
+ // LAN analyzer style additional fields
158
+ method,
159
+ requestId,
160
+ argsKeys,
161
+ argsPreview,
162
+ action,
163
+ direction,
164
+ resultPreview,
165
+ responseSize,
166
+ errorType
106
167
  });
107
168
  }
108
169
 
package/src/server.js CHANGED
@@ -20,6 +20,9 @@ import { existsSync, readFileSync } from 'fs';
20
20
  import { homedir } from 'os';
21
21
  import { join } from 'path';
22
22
 
23
+ // Logger for file-based debug traces (import early for use in debugLog)
24
+ import { logDebugTrace } from './lib/logger.js';
25
+
23
26
  /**
24
27
  * Check if debug mode is enabled (config or env)
25
28
  */
@@ -56,7 +59,7 @@ function isDebugEnabled() {
56
59
  }
57
60
 
58
61
  /**
59
- * Debug log to stderr (safe for MCP stdio transport)
62
+ * Debug log to stderr AND file (safe for MCP stdio transport)
60
63
  * Checks config on each call for hot-reload support
61
64
  */
62
65
  function debugLog(phase, data = {}) {
@@ -71,6 +74,16 @@ function debugLog(phase, data = {}) {
71
74
 
72
75
  // Output to stderr to avoid interfering with MCP JSON-RPC over stdout
73
76
  console.error(`[DEBUG] ${JSON.stringify(message)}`);
77
+
78
+ // Also write to file for `inscribe-mcp show --debug`
79
+ logDebugTrace({
80
+ phase,
81
+ tool: data.tool,
82
+ args: data.args,
83
+ status: data.status,
84
+ duration: data.duration,
85
+ error: data.error
86
+ });
74
87
  }
75
88
 
76
89
  // Layer 1 Tools (Primary)
@@ -136,19 +149,28 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
136
149
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
137
150
  const { name, arguments: args } = request.params;
138
151
  const startTime = Date.now();
152
+ const requestId = request.id || `req_${Date.now()}`;
139
153
 
140
- // Debug: Agent → MCP (request received)
154
+ // Debug: Agent → MCP (request received) - LAN analyzer style
141
155
  debugLog('agent_to_mcp', {
142
156
  tool: name,
143
- args: args ? Object.keys(args) : []
157
+ method: 'tools/call',
158
+ requestId,
159
+ argsKeys: args ? Object.keys(args) : [],
160
+ argsPreview: args ? JSON.stringify(args).slice(0, 100) : null,
161
+ direction: 'IN'
144
162
  });
145
163
 
146
164
  const tool = allTools.find(t => t.name === name);
147
165
 
148
166
  if (!tool) {
167
+ const duration = Date.now() - startTime;
149
168
  debugLog('mcp_error', {
150
169
  tool: name,
151
- error: 'Unknown tool'
170
+ requestId,
171
+ error: 'Unknown tool',
172
+ duration,
173
+ direction: 'ERR'
152
174
  });
153
175
 
154
176
  return {
@@ -161,25 +183,39 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
161
183
  // Debug: MCP → Chain (processing)
162
184
  debugLog('mcp_to_chain', {
163
185
  tool: name,
164
- processing: true
186
+ requestId,
187
+ action: 'CALL',
188
+ direction: 'OUT'
165
189
  });
166
190
 
167
191
  const result = await tool.handler(args || {});
168
192
  const duration = Date.now() - startTime;
169
193
 
194
+ // Extract key info from result for debug
195
+ const resultPreview = result ? {
196
+ success: result.success,
197
+ txId: result.txId || result.inscription_id || result.topic_id,
198
+ type: result.type
199
+ } : null;
200
+
170
201
  // Debug: Chain → MCP (response)
171
202
  debugLog('chain_to_mcp', {
172
203
  tool: name,
204
+ requestId,
173
205
  status: 'SUCCESS',
174
206
  duration,
175
- hasResult: !!result
207
+ resultPreview,
208
+ direction: 'IN'
176
209
  });
177
210
 
178
211
  // Debug: MCP → Agent (returning result)
179
212
  debugLog('mcp_to_agent', {
180
213
  tool: name,
214
+ requestId,
181
215
  status: 'SUCCESS',
182
- duration
216
+ duration,
217
+ responseSize: JSON.stringify(result).length,
218
+ direction: 'OUT'
183
219
  });
184
220
 
185
221
  return {
@@ -191,17 +227,22 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
191
227
  // Debug: Chain → MCP (error)
192
228
  debugLog('chain_to_mcp', {
193
229
  tool: name,
230
+ requestId,
194
231
  status: 'FAILED',
195
232
  duration,
196
- error: error.message
233
+ error: error.message,
234
+ errorType: error.constructor.name,
235
+ direction: 'IN'
197
236
  });
198
237
 
199
238
  // Debug: MCP → Agent (returning error)
200
239
  debugLog('mcp_to_agent', {
201
240
  tool: name,
241
+ requestId,
202
242
  status: 'ERROR',
203
243
  duration,
204
- error: error.message
244
+ error: error.message,
245
+ direction: 'OUT'
205
246
  });
206
247
 
207
248
  return {
@@ -223,7 +264,7 @@ async function main() {
223
264
  if (isDebugEnabled()) {
224
265
  console.error('Debug mode is currently ON (hot-reloadable)');
225
266
  debugLog('server_start', {
226
- version: '0.3.4',
267
+ version: '0.3.6',
227
268
  tools: allTools.map(t => t.name)
228
269
  });
229
270
  }