@proofofprotocol/inscribe-mcp 0.3.1 → 0.3.3
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/README.md +67 -1
- package/package.json +1 -1
- package/src/cli/commands/debug.js +116 -0
- package/src/cli/commands/setup-mcp.js +1 -1
- package/src/cli/commands/show.js +147 -0
- package/src/cli/index.js +2 -0
- package/src/cli/lib/config.js +61 -0
- package/src/server.js +133 -1
package/README.md
CHANGED
|
@@ -59,7 +59,7 @@ inscribe-mcp setup-mcp --show-json
|
|
|
59
59
|
"mcpServers": {
|
|
60
60
|
"inscribe": {
|
|
61
61
|
"command": "npx",
|
|
62
|
-
"args": ["-y", "@proofofprotocol/inscribe-mcp", "inscribe-mcp-server"]
|
|
62
|
+
"args": ["-y", "-p", "@proofofprotocol/inscribe-mcp", "inscribe-mcp-server"]
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
}
|
|
@@ -236,6 +236,72 @@ Account: https://hashscan.io/testnet/account/0.0.7455134
|
|
|
236
236
|
Topic: https://hashscan.io/testnet/topic/0.0.7503789
|
|
237
237
|
```
|
|
238
238
|
|
|
239
|
+
## Debug Mode (Learning Material)
|
|
240
|
+
|
|
241
|
+
MCPのシーケンスを詳細に追跡できるデバッグモードを搭載。学習教材として活用できます。
|
|
242
|
+
|
|
243
|
+
### デバッグモードの有効化(CLI推奨)
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
# デバッグモードをON(1時間後に自動OFF)
|
|
247
|
+
inscribe-mcp debug on
|
|
248
|
+
|
|
249
|
+
# 30分間だけON
|
|
250
|
+
inscribe-mcp debug on --time 30
|
|
251
|
+
|
|
252
|
+
# 状態確認
|
|
253
|
+
inscribe-mcp debug
|
|
254
|
+
|
|
255
|
+
# 手動でOFF
|
|
256
|
+
inscribe-mcp debug off
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
デバッグ設定後、Claude Desktopを再起動してください。
|
|
260
|
+
|
|
261
|
+
### 環境変数での有効化(上級者向け)
|
|
262
|
+
|
|
263
|
+
```json
|
|
264
|
+
{
|
|
265
|
+
"mcpServers": {
|
|
266
|
+
"inscribe": {
|
|
267
|
+
"command": "npx",
|
|
268
|
+
"args": ["-y", "-p", "@proofofprotocol/inscribe-mcp", "inscribe-mcp-server"],
|
|
269
|
+
"env": {
|
|
270
|
+
"INSCRIBE_MCP_DEBUG": "1"
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### デバッグ出力の確認
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
# デバッグトレースを表示
|
|
281
|
+
inscribe-mcp show --debug
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### MCPシーケンス図
|
|
285
|
+
|
|
286
|
+
```
|
|
287
|
+
┌─────────┐ ┌─────────┐ ┌─────────┐
|
|
288
|
+
│ Agent │ → │ MCP │ → │ Chain │
|
|
289
|
+
│(Claude) │ ← │ Server │ ← │ (Hedera)│
|
|
290
|
+
└─────────┘ └─────────┘ └─────────┘
|
|
291
|
+
|
|
292
|
+
[12:34:56] inscribe
|
|
293
|
+
→ Agent → MCP {"content":"Hello World"}
|
|
294
|
+
→ MCP → Chain processing...
|
|
295
|
+
← Chain → MCP SUCCESS txId: 0.0.xxx-123...
|
|
296
|
+
← MCP → Agent result: success
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
デバッグモードでは以下の情報が記録されます:
|
|
300
|
+
- `agent_to_mcp`: AIエージェントからMCPサーバーへのリクエスト
|
|
301
|
+
- `mcp_to_chain`: MCPサーバーからHederaへの送信
|
|
302
|
+
- `chain_to_mcp`: Hederaからの応答
|
|
303
|
+
- `mcp_to_agent`: AIエージェントへの結果返却
|
|
304
|
+
|
|
239
305
|
## Configuration
|
|
240
306
|
|
|
241
307
|
設定は `~/.inscribe-mcp/config.json` に保存されます:
|
package/package.json
CHANGED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* debug command
|
|
3
|
+
*
|
|
4
|
+
* Toggle debug mode on/off with auto-expiry.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Command } from 'commander';
|
|
8
|
+
import { getDebugSettings, setDebugEnabled, configExists } from '../lib/config.js';
|
|
9
|
+
import { EXIT_CODES } from '../lib/exit-codes.js';
|
|
10
|
+
|
|
11
|
+
// ANSI colors
|
|
12
|
+
const colors = {
|
|
13
|
+
green: (s) => `\x1b[32m${s}\x1b[0m`,
|
|
14
|
+
yellow: (s) => `\x1b[33m${s}\x1b[0m`,
|
|
15
|
+
red: (s) => `\x1b[31m${s}\x1b[0m`,
|
|
16
|
+
cyan: (s) => `\x1b[36m${s}\x1b[0m`,
|
|
17
|
+
bold: (s) => `\x1b[1m${s}\x1b[0m`,
|
|
18
|
+
dim: (s) => `\x1b[2m${s}\x1b[0m`
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Format remaining time
|
|
23
|
+
*/
|
|
24
|
+
function formatRemainingTime(expiresAt) {
|
|
25
|
+
if (!expiresAt) return '';
|
|
26
|
+
|
|
27
|
+
const remaining = new Date(expiresAt).getTime() - Date.now();
|
|
28
|
+
if (remaining <= 0) return 'expired';
|
|
29
|
+
|
|
30
|
+
const minutes = Math.floor(remaining / 60000);
|
|
31
|
+
const hours = Math.floor(minutes / 60);
|
|
32
|
+
|
|
33
|
+
if (hours > 0) {
|
|
34
|
+
const remainingMins = minutes % 60;
|
|
35
|
+
return `${hours}h ${remainingMins}m remaining`;
|
|
36
|
+
}
|
|
37
|
+
return `${minutes}m remaining`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const debugCommand = new Command('debug')
|
|
41
|
+
.description('Toggle debug mode on/off')
|
|
42
|
+
.argument('[action]', 'on, off, or status (default: status)')
|
|
43
|
+
.option('-t, --time <minutes>', 'Auto-off after N minutes (default: 60)', '60')
|
|
44
|
+
.action(async (action, options) => {
|
|
45
|
+
// Check config
|
|
46
|
+
if (!configExists()) {
|
|
47
|
+
console.log('');
|
|
48
|
+
console.log(colors.red('Config not found.'));
|
|
49
|
+
console.log('');
|
|
50
|
+
console.log('Run ' + colors.cyan('inscribe-mcp init') + ' to configure.');
|
|
51
|
+
console.log('');
|
|
52
|
+
process.exit(EXIT_CODES.CONFIG_ERROR);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const durationMinutes = parseInt(options.time, 10) || 60;
|
|
56
|
+
|
|
57
|
+
// Default to status if no action provided
|
|
58
|
+
if (!action || action === 'status') {
|
|
59
|
+
const settings = getDebugSettings();
|
|
60
|
+
console.log('');
|
|
61
|
+
console.log(colors.bold('Debug Mode Status'));
|
|
62
|
+
console.log('─'.repeat(40));
|
|
63
|
+
|
|
64
|
+
if (settings.enabled) {
|
|
65
|
+
console.log(`Status: ${colors.green('ON')}`);
|
|
66
|
+
console.log(`Expires: ${formatRemainingTime(settings.expiresAt)}`);
|
|
67
|
+
console.log('');
|
|
68
|
+
console.log(colors.dim('Debug logs will be recorded by MCP server.'));
|
|
69
|
+
console.log(colors.dim('Use ') + colors.cyan('inscribe-mcp show --debug') + colors.dim(' to view.'));
|
|
70
|
+
} else {
|
|
71
|
+
console.log(`Status: ${colors.dim('OFF')}`);
|
|
72
|
+
console.log('');
|
|
73
|
+
console.log('To enable: ' + colors.cyan('inscribe-mcp debug on'));
|
|
74
|
+
console.log(' ' + colors.cyan('inscribe-mcp debug on --time 30') + colors.dim(' (30 min)'));
|
|
75
|
+
}
|
|
76
|
+
console.log('');
|
|
77
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (action === 'on') {
|
|
81
|
+
setDebugEnabled(true, durationMinutes);
|
|
82
|
+
console.log('');
|
|
83
|
+
console.log(colors.green('✓') + ' Debug mode ' + colors.green('ON'));
|
|
84
|
+
console.log('');
|
|
85
|
+
console.log(`Auto-off: in ${durationMinutes} minutes`);
|
|
86
|
+
console.log('');
|
|
87
|
+
console.log(colors.bold('Next steps:'));
|
|
88
|
+
console.log(' 1. ' + colors.cyan('Restart Claude Desktop') + ' to apply');
|
|
89
|
+
console.log(' 2. Use inscribe tools to generate debug traces');
|
|
90
|
+
console.log(' 3. ' + colors.cyan('inscribe-mcp show --debug') + ' to view traces');
|
|
91
|
+
console.log('');
|
|
92
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (action === 'off') {
|
|
96
|
+
setDebugEnabled(false);
|
|
97
|
+
console.log('');
|
|
98
|
+
console.log(colors.green('✓') + ' Debug mode ' + colors.dim('OFF'));
|
|
99
|
+
console.log('');
|
|
100
|
+
console.log('Restart Claude Desktop to apply.');
|
|
101
|
+
console.log('');
|
|
102
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Unknown action
|
|
106
|
+
console.log('');
|
|
107
|
+
console.log(colors.red('Unknown action: ' + action));
|
|
108
|
+
console.log('');
|
|
109
|
+
console.log('Usage:');
|
|
110
|
+
console.log(' ' + colors.cyan('inscribe-mcp debug') + ' Show status');
|
|
111
|
+
console.log(' ' + colors.cyan('inscribe-mcp debug on') + ' Enable (auto-off in 1h)');
|
|
112
|
+
console.log(' ' + colors.cyan('inscribe-mcp debug on -t 30') + ' Enable for 30 minutes');
|
|
113
|
+
console.log(' ' + colors.cyan('inscribe-mcp debug off') + ' Disable');
|
|
114
|
+
console.log('');
|
|
115
|
+
process.exit(EXIT_CODES.ERROR);
|
|
116
|
+
});
|
|
@@ -53,7 +53,7 @@ function getClaudeDesktopConfigPath() {
|
|
|
53
53
|
// MCP server config for inscribe
|
|
54
54
|
const INSCRIBE_MCP_CONFIG = {
|
|
55
55
|
command: 'npx',
|
|
56
|
-
args: ['-y', '@proofofprotocol/inscribe-mcp', 'inscribe-mcp-server']
|
|
56
|
+
args: ['-y', '-p', '@proofofprotocol/inscribe-mcp', 'inscribe-mcp-server']
|
|
57
57
|
};
|
|
58
58
|
|
|
59
59
|
// Generate JSON snippet for display
|
package/src/cli/commands/show.js
CHANGED
|
@@ -135,9 +135,80 @@ function computeStats(logsDir) {
|
|
|
135
135
|
return stats;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Read debug trace entries from logs
|
|
140
|
+
*/
|
|
141
|
+
function getDebugTraces(logsDir, limit = 20) {
|
|
142
|
+
const traces = [];
|
|
143
|
+
|
|
144
|
+
if (!existsSync(logsDir)) {
|
|
145
|
+
return traces;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Get log files sorted by date (newest first)
|
|
149
|
+
const files = readdirSync(logsDir)
|
|
150
|
+
.filter(f => f.endsWith('.jsonl'))
|
|
151
|
+
.sort()
|
|
152
|
+
.reverse();
|
|
153
|
+
|
|
154
|
+
for (const file of files) {
|
|
155
|
+
if (traces.length >= limit) break;
|
|
156
|
+
|
|
157
|
+
const filePath = join(logsDir, file);
|
|
158
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
159
|
+
const lines = content.trim().split('\n').filter(Boolean).reverse();
|
|
160
|
+
|
|
161
|
+
for (const line of lines) {
|
|
162
|
+
if (traces.length >= limit) break;
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
const entry = JSON.parse(line);
|
|
166
|
+
if (entry.level === 'debug') {
|
|
167
|
+
traces.push(entry);
|
|
168
|
+
}
|
|
169
|
+
} catch {
|
|
170
|
+
// Skip invalid lines
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return traces.reverse(); // Chronological order
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Format MCP sequence trace for display
|
|
180
|
+
*/
|
|
181
|
+
function formatSequenceTrace(traces) {
|
|
182
|
+
const sequences = [];
|
|
183
|
+
let currentSequence = null;
|
|
184
|
+
|
|
185
|
+
for (const trace of traces) {
|
|
186
|
+
if (trace.phase === 'agent_to_mcp') {
|
|
187
|
+
// Start new sequence
|
|
188
|
+
if (currentSequence) {
|
|
189
|
+
sequences.push(currentSequence);
|
|
190
|
+
}
|
|
191
|
+
currentSequence = {
|
|
192
|
+
tool: trace.tool,
|
|
193
|
+
timestamp: trace.timestamp,
|
|
194
|
+
phases: [trace]
|
|
195
|
+
};
|
|
196
|
+
} else if (currentSequence) {
|
|
197
|
+
currentSequence.phases.push(trace);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (currentSequence) {
|
|
202
|
+
sequences.push(currentSequence);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return sequences;
|
|
206
|
+
}
|
|
207
|
+
|
|
138
208
|
export const showCommand = new Command('show')
|
|
139
209
|
.description('Display inscribe-mcp status dashboard')
|
|
140
210
|
.option('--json', 'Output as JSON')
|
|
211
|
+
.option('--debug', 'Show MCP sequence debug traces')
|
|
141
212
|
.action(async (options) => {
|
|
142
213
|
// Check config
|
|
143
214
|
if (!configExists()) {
|
|
@@ -243,6 +314,82 @@ export const showCommand = new Command('show')
|
|
|
243
314
|
console.log(`Topic: ${hashscanBase}/topic/${defaultTopicId}`);
|
|
244
315
|
}
|
|
245
316
|
|
|
317
|
+
// Debug trace section
|
|
318
|
+
if (options.debug) {
|
|
319
|
+
console.log('');
|
|
320
|
+
console.log(colors.bold('MCP Sequence Debug Traces'));
|
|
321
|
+
console.log(line);
|
|
322
|
+
|
|
323
|
+
const traces = getDebugTraces(logsDir, 50);
|
|
324
|
+
|
|
325
|
+
if (traces.length === 0) {
|
|
326
|
+
console.log(colors.dim('No debug traces found.'));
|
|
327
|
+
console.log('');
|
|
328
|
+
console.log('To enable debug logging, set environment variable:');
|
|
329
|
+
console.log(colors.cyan(' export INSCRIBE_MCP_DEBUG=1'));
|
|
330
|
+
console.log('');
|
|
331
|
+
console.log('Then restart the MCP server (restart Claude Desktop).');
|
|
332
|
+
} else {
|
|
333
|
+
const sequences = formatSequenceTrace(traces);
|
|
334
|
+
|
|
335
|
+
if (sequences.length === 0) {
|
|
336
|
+
console.log(colors.dim('No complete sequences found.'));
|
|
337
|
+
} 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': '←'
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
const phaseLabels = {
|
|
347
|
+
'agent_to_mcp': 'Agent → MCP',
|
|
348
|
+
'mcp_to_chain': 'MCP → Chain',
|
|
349
|
+
'chain_to_mcp': 'Chain → MCP',
|
|
350
|
+
'mcp_to_agent': 'MCP → Agent'
|
|
351
|
+
};
|
|
352
|
+
|
|
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('');
|
|
359
|
+
|
|
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)}`));
|
|
363
|
+
|
|
364
|
+
for (const phase of seq.phases) {
|
|
365
|
+
const symbol = phaseSymbols[phase.phase] || '?';
|
|
366
|
+
const label = phaseLabels[phase.phase] || phase.phase;
|
|
367
|
+
let detail = '';
|
|
368
|
+
|
|
369
|
+
if (phase.payload) {
|
|
370
|
+
const payloadStr = JSON.stringify(phase.payload);
|
|
371
|
+
detail = colors.dim(` ${payloadStr.slice(0, 40)}${payloadStr.length > 40 ? '...' : ''}`);
|
|
372
|
+
}
|
|
373
|
+
if (phase.status) {
|
|
374
|
+
detail = phase.status === 'SUCCESS'
|
|
375
|
+
? colors.green(` ${phase.status}`)
|
|
376
|
+
: colors.red(` ${phase.status}`);
|
|
377
|
+
}
|
|
378
|
+
if (phase.txId) {
|
|
379
|
+
detail += colors.dim(` txId: ${phase.txId}`);
|
|
380
|
+
}
|
|
381
|
+
if (phase.result) {
|
|
382
|
+
detail += colors.dim(` result: ${phase.result}`);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
console.log(` ${symbol} ${label}${detail}`);
|
|
386
|
+
}
|
|
387
|
+
console.log('');
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
246
393
|
console.log('');
|
|
247
394
|
|
|
248
395
|
process.exit(EXIT_CODES.SUCCESS);
|
package/src/cli/index.js
CHANGED
|
@@ -18,6 +18,7 @@ import { balanceCommand } from './commands/balance.js';
|
|
|
18
18
|
import { logCommand } from './commands/log.js';
|
|
19
19
|
import { showCommand } from './commands/show.js';
|
|
20
20
|
import { setupMcpCommand } from './commands/setup-mcp.js';
|
|
21
|
+
import { debugCommand } from './commands/debug.js';
|
|
21
22
|
|
|
22
23
|
const __filename = fileURLToPath(import.meta.url);
|
|
23
24
|
const __dirname = dirname(__filename);
|
|
@@ -41,6 +42,7 @@ program.addCommand(configCommand);
|
|
|
41
42
|
program.addCommand(balanceCommand);
|
|
42
43
|
program.addCommand(logCommand);
|
|
43
44
|
program.addCommand(showCommand);
|
|
45
|
+
program.addCommand(debugCommand);
|
|
44
46
|
|
|
45
47
|
// Parse and execute
|
|
46
48
|
program.parse();
|
package/src/cli/lib/config.js
CHANGED
|
@@ -124,3 +124,64 @@ export function getDisplayConfig() {
|
|
|
124
124
|
operatorPrivateKey: maskPrivateKey(config.operatorPrivateKey)
|
|
125
125
|
};
|
|
126
126
|
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Update a specific field in config
|
|
130
|
+
* @param {string} key - Config key to update
|
|
131
|
+
* @param {any} value - New value
|
|
132
|
+
*/
|
|
133
|
+
export function updateConfig(key, value) {
|
|
134
|
+
const config = readConfig() || {};
|
|
135
|
+
config[key] = value;
|
|
136
|
+
writeConfig(config);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get debug settings
|
|
141
|
+
* @returns {Object} { enabled: boolean, expiresAt: string|null }
|
|
142
|
+
*/
|
|
143
|
+
export function getDebugSettings() {
|
|
144
|
+
const config = readConfig();
|
|
145
|
+
if (!config || !config.debug) {
|
|
146
|
+
return { enabled: false, expiresAt: null };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const { enabled, expiresAt } = config.debug;
|
|
150
|
+
|
|
151
|
+
// Check if expired
|
|
152
|
+
if (enabled && expiresAt) {
|
|
153
|
+
const expiry = new Date(expiresAt);
|
|
154
|
+
if (Date.now() > expiry.getTime()) {
|
|
155
|
+
// Expired - auto disable
|
|
156
|
+
setDebugEnabled(false);
|
|
157
|
+
return { enabled: false, expiresAt: null };
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return { enabled: !!enabled, expiresAt: expiresAt || null };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Set debug enabled/disabled
|
|
166
|
+
* @param {boolean} enabled - Enable debug mode
|
|
167
|
+
* @param {number} durationMinutes - Auto-off duration in minutes (default: 60)
|
|
168
|
+
*/
|
|
169
|
+
export function setDebugEnabled(enabled, durationMinutes = 60) {
|
|
170
|
+
const config = readConfig() || {};
|
|
171
|
+
|
|
172
|
+
if (enabled) {
|
|
173
|
+
const expiresAt = new Date(Date.now() + durationMinutes * 60 * 1000);
|
|
174
|
+
config.debug = {
|
|
175
|
+
enabled: true,
|
|
176
|
+
expiresAt: expiresAt.toISOString(),
|
|
177
|
+
durationMinutes
|
|
178
|
+
};
|
|
179
|
+
} else {
|
|
180
|
+
config.debug = {
|
|
181
|
+
enabled: false,
|
|
182
|
+
expiresAt: null
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
writeConfig(config);
|
|
187
|
+
}
|
package/src/server.js
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
* inscribe-mcp Server (stdio transport)
|
|
4
4
|
*
|
|
5
5
|
* MCP Server for verifiable inscriptions on Hedera blockchain.
|
|
6
|
+
*
|
|
7
|
+
* Debug mode:
|
|
8
|
+
* - CLI: inscribe-mcp debug on
|
|
9
|
+
* - Environment: INSCRIBE_MCP_DEBUG=1
|
|
6
10
|
*/
|
|
7
11
|
|
|
8
12
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
@@ -12,6 +16,64 @@ import {
|
|
|
12
16
|
ListToolsRequestSchema,
|
|
13
17
|
} from '@modelcontextprotocol/sdk/types.js';
|
|
14
18
|
import dotenv from 'dotenv';
|
|
19
|
+
import { existsSync, readFileSync } from 'fs';
|
|
20
|
+
import { homedir } from 'os';
|
|
21
|
+
import { join } from 'path';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Check if debug mode is enabled (config or env)
|
|
25
|
+
*/
|
|
26
|
+
function isDebugEnabled() {
|
|
27
|
+
// Environment variable takes precedence
|
|
28
|
+
if (process.env.INSCRIBE_MCP_DEBUG === '1' || process.env.INSCRIBE_MCP_DEBUG === 'true') {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Check config file
|
|
33
|
+
const configPath = join(homedir(), '.inscribe-mcp', 'config.json');
|
|
34
|
+
if (!existsSync(configPath)) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const config = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
40
|
+
if (!config.debug || !config.debug.enabled) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Check expiry
|
|
45
|
+
if (config.debug.expiresAt) {
|
|
46
|
+
const expiry = new Date(config.debug.expiresAt);
|
|
47
|
+
if (Date.now() > expiry.getTime()) {
|
|
48
|
+
return false; // Expired
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return true;
|
|
53
|
+
} catch {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Debug mode check (evaluated at startup)
|
|
59
|
+
const DEBUG = isDebugEnabled();
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Debug log to stderr (safe for MCP stdio transport)
|
|
63
|
+
*/
|
|
64
|
+
function debugLog(phase, data = {}) {
|
|
65
|
+
if (!DEBUG) return;
|
|
66
|
+
|
|
67
|
+
const timestamp = new Date().toISOString();
|
|
68
|
+
const message = {
|
|
69
|
+
timestamp,
|
|
70
|
+
phase,
|
|
71
|
+
...data
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Output to stderr to avoid interfering with MCP JSON-RPC over stdout
|
|
75
|
+
console.error(`[DEBUG] ${JSON.stringify(message)}`);
|
|
76
|
+
}
|
|
15
77
|
|
|
16
78
|
// Layer 1 Tools (Primary)
|
|
17
79
|
import { inscribeUrlTool } from './tools/layer1/inscribe_url.js';
|
|
@@ -58,6 +120,11 @@ const server = new Server(
|
|
|
58
120
|
|
|
59
121
|
// List Tools Handler
|
|
60
122
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
123
|
+
debugLog('list_tools', {
|
|
124
|
+
toolCount: allTools.length,
|
|
125
|
+
tools: allTools.map(t => t.name)
|
|
126
|
+
});
|
|
127
|
+
|
|
61
128
|
return {
|
|
62
129
|
tools: allTools.map(t => ({
|
|
63
130
|
name: t.name,
|
|
@@ -70,9 +137,22 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
70
137
|
// Call Tool Handler
|
|
71
138
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
72
139
|
const { name, arguments: args } = request.params;
|
|
140
|
+
const startTime = Date.now();
|
|
141
|
+
|
|
142
|
+
// Debug: Agent → MCP (request received)
|
|
143
|
+
debugLog('agent_to_mcp', {
|
|
144
|
+
tool: name,
|
|
145
|
+
args: args ? Object.keys(args) : []
|
|
146
|
+
});
|
|
147
|
+
|
|
73
148
|
const tool = allTools.find(t => t.name === name);
|
|
74
149
|
|
|
75
150
|
if (!tool) {
|
|
151
|
+
debugLog('mcp_error', {
|
|
152
|
+
tool: name,
|
|
153
|
+
error: 'Unknown tool'
|
|
154
|
+
});
|
|
155
|
+
|
|
76
156
|
return {
|
|
77
157
|
content: [{ type: 'text', text: `Unknown tool: ${name}` }],
|
|
78
158
|
isError: true
|
|
@@ -80,11 +160,52 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
80
160
|
}
|
|
81
161
|
|
|
82
162
|
try {
|
|
163
|
+
// Debug: MCP → Chain (processing)
|
|
164
|
+
debugLog('mcp_to_chain', {
|
|
165
|
+
tool: name,
|
|
166
|
+
processing: true
|
|
167
|
+
});
|
|
168
|
+
|
|
83
169
|
const result = await tool.handler(args || {});
|
|
170
|
+
const duration = Date.now() - startTime;
|
|
171
|
+
|
|
172
|
+
// Debug: Chain → MCP (response)
|
|
173
|
+
debugLog('chain_to_mcp', {
|
|
174
|
+
tool: name,
|
|
175
|
+
status: 'SUCCESS',
|
|
176
|
+
duration,
|
|
177
|
+
hasResult: !!result
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// Debug: MCP → Agent (returning result)
|
|
181
|
+
debugLog('mcp_to_agent', {
|
|
182
|
+
tool: name,
|
|
183
|
+
status: 'SUCCESS',
|
|
184
|
+
duration
|
|
185
|
+
});
|
|
186
|
+
|
|
84
187
|
return {
|
|
85
188
|
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
|
|
86
189
|
};
|
|
87
190
|
} catch (error) {
|
|
191
|
+
const duration = Date.now() - startTime;
|
|
192
|
+
|
|
193
|
+
// Debug: Chain → MCP (error)
|
|
194
|
+
debugLog('chain_to_mcp', {
|
|
195
|
+
tool: name,
|
|
196
|
+
status: 'FAILED',
|
|
197
|
+
duration,
|
|
198
|
+
error: error.message
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Debug: MCP → Agent (returning error)
|
|
202
|
+
debugLog('mcp_to_agent', {
|
|
203
|
+
tool: name,
|
|
204
|
+
status: 'ERROR',
|
|
205
|
+
duration,
|
|
206
|
+
error: error.message
|
|
207
|
+
});
|
|
208
|
+
|
|
88
209
|
return {
|
|
89
210
|
content: [{ type: 'text', text: `Error: ${error.message}` }],
|
|
90
211
|
isError: true
|
|
@@ -96,7 +217,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
96
217
|
async function main() {
|
|
97
218
|
const transport = new StdioServerTransport();
|
|
98
219
|
await server.connect(transport);
|
|
99
|
-
|
|
220
|
+
|
|
221
|
+
if (DEBUG) {
|
|
222
|
+
console.error('inscribe-mcp server started (stdio) [DEBUG MODE]');
|
|
223
|
+
console.error('Debug logs will be written to stderr');
|
|
224
|
+
debugLog('server_start', {
|
|
225
|
+
version: '0.3.3',
|
|
226
|
+
debug: true,
|
|
227
|
+
tools: allTools.map(t => t.name)
|
|
228
|
+
});
|
|
229
|
+
} else {
|
|
230
|
+
console.error('inscribe-mcp server started (stdio)');
|
|
231
|
+
}
|
|
100
232
|
}
|
|
101
233
|
|
|
102
234
|
main().catch(console.error);
|