dero-mcp-server 0.1.2 → 0.2.2
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 +8 -8
- package/data/docs-index.json +17 -9
- package/dist/citations.d.ts +70 -0
- package/dist/citations.d.ts.map +1 -0
- package/dist/citations.js +162 -0
- package/dist/citations.js.map +1 -0
- package/dist/composites/_shared.d.ts +119 -0
- package/dist/composites/_shared.d.ts.map +1 -0
- package/dist/composites/_shared.js +152 -0
- package/dist/composites/_shared.js.map +1 -0
- package/dist/composites/diagnose-chain-health.d.ts +64 -0
- package/dist/composites/diagnose-chain-health.d.ts.map +1 -0
- package/dist/composites/diagnose-chain-health.js +144 -0
- package/dist/composites/diagnose-chain-health.js.map +1 -0
- package/dist/composites/estimate-deploy-cost.d.ts +83 -0
- package/dist/composites/estimate-deploy-cost.d.ts.map +1 -0
- package/dist/composites/estimate-deploy-cost.js +116 -0
- package/dist/composites/estimate-deploy-cost.js.map +1 -0
- package/dist/composites/explain-smart-contract.d.ts +64 -0
- package/dist/composites/explain-smart-contract.d.ts.map +1 -0
- package/dist/composites/explain-smart-contract.js +149 -0
- package/dist/composites/explain-smart-contract.js.map +1 -0
- package/dist/composites/recommend-docs-path.d.ts +97 -0
- package/dist/composites/recommend-docs-path.d.ts.map +1 -0
- package/dist/composites/recommend-docs-path.js +149 -0
- package/dist/composites/recommend-docs-path.js.map +1 -0
- package/dist/composites/trace-transaction-with-context.d.ts +107 -0
- package/dist/composites/trace-transaction-with-context.d.ts.map +1 -0
- package/dist/composites/trace-transaction-with-context.js +217 -0
- package/dist/composites/trace-transaction-with-context.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +334 -112
- package/dist/server.js.map +1 -1
- package/dist/tool-descriptions.d.ts +50 -0
- package/dist/tool-descriptions.d.ts.map +1 -0
- package/dist/tool-descriptions.js +246 -0
- package/dist/tool-descriptions.js.map +1 -0
- package/package.json +5 -1
package/dist/server.js
CHANGED
|
@@ -2,6 +2,13 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { deroJsonRpc, jsonRpcEndpoint } from './rpc.js';
|
|
4
4
|
import { DERO_DOC_PRODUCTS, getDeroDocPage, listDeroDocs, searchDeroDocs, } from './docs.js';
|
|
5
|
+
import { DERO_TOOL_NAMES, TOOL_DESCRIPTIONS } from './tool-descriptions.js';
|
|
6
|
+
import { relatedDocsFor } from './citations.js';
|
|
7
|
+
import { diagnoseChainHealth, diagnoseChainHealthInputSchema, } from './composites/diagnose-chain-health.js';
|
|
8
|
+
import { explainSmartContract, explainSmartContractInputSchema, } from './composites/explain-smart-contract.js';
|
|
9
|
+
import { recommendDocsPath, recommendDocsPathInputSchema, } from './composites/recommend-docs-path.js';
|
|
10
|
+
import { estimateDeployCost, estimateDeployCostInputSchema, } from './composites/estimate-deploy-cost.js';
|
|
11
|
+
import { traceTransactionWithContext, traceTransactionWithContextInputSchema, } from './composites/trace-transaction-with-context.js';
|
|
5
12
|
const scRpcArgSchema = z.object({
|
|
6
13
|
name: z.string(),
|
|
7
14
|
datatype: z.enum(['S', 'U', 'H']),
|
|
@@ -14,37 +21,18 @@ const deroAddressSchema = z
|
|
|
14
21
|
.string()
|
|
15
22
|
.regex(/^(dero1|deto1)[0-9a-z]+$/i, 'Expected DERO address starting with dero1 or deto1');
|
|
16
23
|
const NAME_REGISTRY_SCID = '0000000000000000000000000000000000000000000000000000000000000001';
|
|
17
|
-
const DERO_TOOL_NAMES = [
|
|
18
|
-
'dero_daemon_ping',
|
|
19
|
-
'dero_daemon_echo',
|
|
20
|
-
'dero_get_info',
|
|
21
|
-
'dero_get_height',
|
|
22
|
-
'dero_get_block_count',
|
|
23
|
-
'dero_get_last_block_header',
|
|
24
|
-
'dero_get_block',
|
|
25
|
-
'dero_get_block_header_by_topo_height',
|
|
26
|
-
'dero_get_block_header_by_hash',
|
|
27
|
-
'dero_get_tx_pool',
|
|
28
|
-
'dero_get_random_address',
|
|
29
|
-
'dero_get_transaction',
|
|
30
|
-
'dero_get_encrypted_balance',
|
|
31
|
-
'dero_get_sc',
|
|
32
|
-
'dero_get_gas_estimate',
|
|
33
|
-
'dero_name_to_address',
|
|
34
|
-
'dero_get_block_template',
|
|
35
|
-
'dero_docs_search',
|
|
36
|
-
'dero_docs_get_page',
|
|
37
|
-
'dero_docs_list',
|
|
38
|
-
];
|
|
39
24
|
const DERO_RESOURCE_URIS = [
|
|
40
25
|
'dero://mcp/server-info',
|
|
41
26
|
'dero://mcp/safety-boundary',
|
|
42
27
|
'dero://mcp/example-flows',
|
|
28
|
+
'dero://mcp/composites',
|
|
43
29
|
];
|
|
44
30
|
const DERO_PROMPT_NAMES = [
|
|
45
31
|
'network_health_check',
|
|
46
32
|
'inspect_smart_contract',
|
|
47
33
|
'trace_transaction',
|
|
34
|
+
'find_dero_docs_for_intent',
|
|
35
|
+
'estimate_deploy_for_contract',
|
|
48
36
|
];
|
|
49
37
|
const deroDocProductSchema = z.enum(DERO_DOC_PRODUCTS);
|
|
50
38
|
function toolText(data) {
|
|
@@ -95,6 +83,20 @@ function classifyToolError(error) {
|
|
|
95
83
|
retryable: false,
|
|
96
84
|
};
|
|
97
85
|
}
|
|
86
|
+
if (message.includes('No DERO docs matched intent')) {
|
|
87
|
+
return {
|
|
88
|
+
code: 'NO_DOCS_MATCH',
|
|
89
|
+
hint: 'Rephrase the intent (drop verbs, use product nouns like "TELA app" or "DVM contract"), then retry. You can also pass product_hint to bias the search.',
|
|
90
|
+
retryable: false,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
if (message.includes('DERO transaction not found')) {
|
|
94
|
+
return {
|
|
95
|
+
code: 'TX_NOT_FOUND',
|
|
96
|
+
hint: 'The daemon has no record of that tx hash on this chain. Verify the hash is correct (64 hex chars), check whether you queried the right network (mainnet vs testnet), and if the tx is freshly broadcast wait a few seconds for mempool propagation and retry.',
|
|
97
|
+
retryable: true,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
98
100
|
if (message.includes('RPC error -32601')) {
|
|
99
101
|
return {
|
|
100
102
|
code: 'RPC_METHOD_NOT_FOUND',
|
|
@@ -109,6 +111,13 @@ function classifyToolError(error) {
|
|
|
109
111
|
retryable: false,
|
|
110
112
|
};
|
|
111
113
|
}
|
|
114
|
+
if (message.includes('RPC error -32098')) {
|
|
115
|
+
return {
|
|
116
|
+
code: 'INVALID_INPUT',
|
|
117
|
+
hint: 'The DVM compiler rejected the contract source. Inspect _meta.error.raw for the exact compile error (often points at a line, symbol, or missing keyword). Common causes: missing `End Function`, missing return type (`Uint64`/`String`), unbalanced parens, or sending a function body instead of a full contract.',
|
|
118
|
+
retryable: false,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
112
121
|
const httpMatch = message.match(/HTTP (\d{3})/);
|
|
113
122
|
if (httpMatch) {
|
|
114
123
|
const status = Number(httpMatch[1]);
|
|
@@ -167,36 +176,69 @@ function withStructuredErrors(tool, handler) {
|
|
|
167
176
|
}
|
|
168
177
|
};
|
|
169
178
|
}
|
|
179
|
+
/**
|
|
180
|
+
* MCP tool annotation hint block applied to every tool in this server.
|
|
181
|
+
*
|
|
182
|
+
* - `readOnlyHint: true` lets MCP hosts (Cursor, Claude Desktop, OpenCode)
|
|
183
|
+
* auto-approve calls without per-invocation confirmation.
|
|
184
|
+
* - `destructiveHint: false` makes the read-only promise explicit so hosts
|
|
185
|
+
* render a safe-call badge.
|
|
186
|
+
* - `idempotentHint: false` because chain state advances between calls —
|
|
187
|
+
* identical inputs may return different blocks/heights/tx pools.
|
|
188
|
+
* - `openWorldHint: false` because we hit a configured daemon endpoint only,
|
|
189
|
+
* not arbitrary external services.
|
|
190
|
+
*
|
|
191
|
+
* Any future wallet/write tools MUST use a different annotation block
|
|
192
|
+
* (`readOnlyHint: false`, `destructiveHint: true`) and remain require-approval.
|
|
193
|
+
*/
|
|
194
|
+
const READ_ONLY_ANNOTATIONS = {
|
|
195
|
+
readOnlyHint: true,
|
|
196
|
+
destructiveHint: false,
|
|
197
|
+
idempotentHint: false,
|
|
198
|
+
openWorldHint: false,
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* Helper that tags a tool config with the read-only annotation block.
|
|
202
|
+
* Use for every primitive in this v0.1 server. Composites built on these
|
|
203
|
+
* primitives are also read-only and should use this same helper.
|
|
204
|
+
*/
|
|
205
|
+
function readOnly(config) {
|
|
206
|
+
return { ...config, annotations: READ_ONLY_ANNOTATIONS };
|
|
207
|
+
}
|
|
170
208
|
export function createDeroMcpServer(daemonBaseUrl) {
|
|
171
209
|
const endpoint = jsonRpcEndpoint(daemonBaseUrl);
|
|
172
210
|
const rpc = async (method, params) => deroJsonRpc(endpoint, method, params);
|
|
173
211
|
const server = new McpServer({
|
|
174
212
|
name: 'dero-daemon-mcp',
|
|
175
|
-
version: '0.
|
|
213
|
+
version: '0.2.2',
|
|
176
214
|
});
|
|
177
|
-
server.registerTool('dero_daemon_ping', {
|
|
178
|
-
description:
|
|
179
|
-
}, withStructuredErrors('dero_daemon_ping', async () => rpc('DERO.Ping')));
|
|
180
|
-
server.registerTool('dero_daemon_echo', {
|
|
181
|
-
description:
|
|
215
|
+
server.registerTool('dero_daemon_ping', readOnly({
|
|
216
|
+
description: TOOL_DESCRIPTIONS.dero_daemon_ping,
|
|
217
|
+
}), withStructuredErrors('dero_daemon_ping', async () => rpc('DERO.Ping')));
|
|
218
|
+
server.registerTool('dero_daemon_echo', readOnly({
|
|
219
|
+
description: TOOL_DESCRIPTIONS.dero_daemon_echo,
|
|
182
220
|
inputSchema: {
|
|
183
221
|
words: z.array(z.string()).describe('Strings to echo back'),
|
|
184
222
|
},
|
|
185
|
-
}, withStructuredErrors('dero_daemon_echo', async ({ words }) => rpc('DERO.Echo', words)));
|
|
186
|
-
server.registerTool('dero_get_info', {
|
|
187
|
-
description:
|
|
188
|
-
}, withStructuredErrors('dero_get_info', async () =>
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
223
|
+
}), withStructuredErrors('dero_daemon_echo', async ({ words }) => rpc('DERO.Echo', words)));
|
|
224
|
+
server.registerTool('dero_get_info', readOnly({
|
|
225
|
+
description: TOOL_DESCRIPTIONS.dero_get_info,
|
|
226
|
+
}), withStructuredErrors('dero_get_info', async () => {
|
|
227
|
+
const result = (await rpc('DERO.GetInfo')) ?? {};
|
|
228
|
+
const related_docs = relatedDocsFor('dero_get_info');
|
|
229
|
+
return { ...result, ...(related_docs ? { related_docs } : {}) };
|
|
230
|
+
}));
|
|
231
|
+
server.registerTool('dero_get_height', readOnly({
|
|
232
|
+
description: TOOL_DESCRIPTIONS.dero_get_height,
|
|
233
|
+
}), withStructuredErrors('dero_get_height', async () => rpc('DERO.GetHeight')));
|
|
234
|
+
server.registerTool('dero_get_block_count', readOnly({
|
|
235
|
+
description: TOOL_DESCRIPTIONS.dero_get_block_count,
|
|
236
|
+
}), withStructuredErrors('dero_get_block_count', async () => rpc('DERO.GetBlockCount')));
|
|
237
|
+
server.registerTool('dero_get_last_block_header', readOnly({
|
|
238
|
+
description: TOOL_DESCRIPTIONS.dero_get_last_block_header,
|
|
239
|
+
}), withStructuredErrors('dero_get_last_block_header', async () => rpc('DERO.GetLastBlockHeader')));
|
|
240
|
+
server.registerTool('dero_get_block', readOnly({
|
|
241
|
+
description: TOOL_DESCRIPTIONS.dero_get_block,
|
|
200
242
|
inputSchema: {
|
|
201
243
|
hash: hex64Schema
|
|
202
244
|
.optional()
|
|
@@ -208,7 +250,7 @@ export function createDeroMcpServer(daemonBaseUrl) {
|
|
|
208
250
|
.optional()
|
|
209
251
|
.describe('Block height'),
|
|
210
252
|
},
|
|
211
|
-
}, withStructuredErrors('dero_get_block', async (args) => {
|
|
253
|
+
}), withStructuredErrors('dero_get_block', async (args) => {
|
|
212
254
|
if (!args.hash && args.height === undefined) {
|
|
213
255
|
throw new Error('Provide either hash or height');
|
|
214
256
|
}
|
|
@@ -219,8 +261,8 @@ export function createDeroMcpServer(daemonBaseUrl) {
|
|
|
219
261
|
params.height = args.height;
|
|
220
262
|
return rpc('DERO.GetBlock', params);
|
|
221
263
|
}));
|
|
222
|
-
server.registerTool('dero_get_block_header_by_topo_height', {
|
|
223
|
-
description:
|
|
264
|
+
server.registerTool('dero_get_block_header_by_topo_height', readOnly({
|
|
265
|
+
description: TOOL_DESCRIPTIONS.dero_get_block_header_by_topo_height,
|
|
224
266
|
inputSchema: {
|
|
225
267
|
topoheight: z
|
|
226
268
|
.number()
|
|
@@ -228,26 +270,26 @@ export function createDeroMcpServer(daemonBaseUrl) {
|
|
|
228
270
|
.nonnegative()
|
|
229
271
|
.describe('Topological height'),
|
|
230
272
|
},
|
|
231
|
-
}, withStructuredErrors('dero_get_block_header_by_topo_height', async ({ topoheight }) => rpc('DERO.GetBlockHeaderByTopoHeight', { topoheight })));
|
|
232
|
-
server.registerTool('dero_get_block_header_by_hash', {
|
|
233
|
-
description:
|
|
273
|
+
}), withStructuredErrors('dero_get_block_header_by_topo_height', async ({ topoheight }) => rpc('DERO.GetBlockHeaderByTopoHeight', { topoheight })));
|
|
274
|
+
server.registerTool('dero_get_block_header_by_hash', readOnly({
|
|
275
|
+
description: TOOL_DESCRIPTIONS.dero_get_block_header_by_hash,
|
|
234
276
|
inputSchema: {
|
|
235
277
|
hash: hex64Schema.describe('Block top hash (hex)'),
|
|
236
278
|
},
|
|
237
|
-
}, withStructuredErrors('dero_get_block_header_by_hash', async ({ hash }) => rpc('DERO.GetBlockHeaderByHash', { hash })));
|
|
238
|
-
server.registerTool('dero_get_tx_pool', {
|
|
239
|
-
description:
|
|
240
|
-
}, withStructuredErrors('dero_get_tx_pool', async () => rpc('DERO.GetTxPool')));
|
|
241
|
-
server.registerTool('dero_get_random_address', {
|
|
242
|
-
description:
|
|
279
|
+
}), withStructuredErrors('dero_get_block_header_by_hash', async ({ hash }) => rpc('DERO.GetBlockHeaderByHash', { hash })));
|
|
280
|
+
server.registerTool('dero_get_tx_pool', readOnly({
|
|
281
|
+
description: TOOL_DESCRIPTIONS.dero_get_tx_pool,
|
|
282
|
+
}), withStructuredErrors('dero_get_tx_pool', async () => rpc('DERO.GetTxPool')));
|
|
283
|
+
server.registerTool('dero_get_random_address', readOnly({
|
|
284
|
+
description: TOOL_DESCRIPTIONS.dero_get_random_address,
|
|
243
285
|
inputSchema: {
|
|
244
286
|
scid: hex64Schema
|
|
245
287
|
.optional()
|
|
246
288
|
.describe('Optional asset smart-contract id (hex)'),
|
|
247
289
|
},
|
|
248
|
-
}, withStructuredErrors('dero_get_random_address', async (args) => rpc('DERO.GetRandomAddress', args.scid != null ? { scid: args.scid } : undefined)));
|
|
249
|
-
server.registerTool('dero_get_transaction', {
|
|
250
|
-
description:
|
|
290
|
+
}), withStructuredErrors('dero_get_random_address', async (args) => rpc('DERO.GetRandomAddress', args.scid != null ? { scid: args.scid } : undefined)));
|
|
291
|
+
server.registerTool('dero_get_transaction', readOnly({
|
|
292
|
+
description: TOOL_DESCRIPTIONS.dero_get_transaction,
|
|
251
293
|
inputSchema: {
|
|
252
294
|
txs_hashes: z
|
|
253
295
|
.array(hex64Schema)
|
|
@@ -259,14 +301,14 @@ export function createDeroMcpServer(daemonBaseUrl) {
|
|
|
259
301
|
.optional()
|
|
260
302
|
.describe('Optional: decode each tx as JSON when non-zero'),
|
|
261
303
|
},
|
|
262
|
-
}, withStructuredErrors('dero_get_transaction', async ({ txs_hashes, decode_as_json }) => {
|
|
304
|
+
}), withStructuredErrors('dero_get_transaction', async ({ txs_hashes, decode_as_json }) => {
|
|
263
305
|
const params = { txs_hashes };
|
|
264
306
|
if (decode_as_json !== undefined)
|
|
265
307
|
params.decode_as_json = decode_as_json;
|
|
266
308
|
return rpc('DERO.GetTransaction', params);
|
|
267
309
|
}));
|
|
268
|
-
server.registerTool('dero_get_encrypted_balance', {
|
|
269
|
-
description:
|
|
310
|
+
server.registerTool('dero_get_encrypted_balance', readOnly({
|
|
311
|
+
description: TOOL_DESCRIPTIONS.dero_get_encrypted_balance,
|
|
270
312
|
inputSchema: {
|
|
271
313
|
address: deroAddressSchema.describe('DERO address (dero1… or deto1…)'),
|
|
272
314
|
topoheight: z
|
|
@@ -275,14 +317,14 @@ export function createDeroMcpServer(daemonBaseUrl) {
|
|
|
275
317
|
.describe('Use -1 for latest chain tip'),
|
|
276
318
|
scid: hex64Schema.optional().describe('Asset SCID hex; omit for native DERO'),
|
|
277
319
|
},
|
|
278
|
-
}, withStructuredErrors('dero_get_encrypted_balance', async ({ address, topoheight, scid }) => {
|
|
320
|
+
}), withStructuredErrors('dero_get_encrypted_balance', async ({ address, topoheight, scid }) => {
|
|
279
321
|
const params = { address, topoheight };
|
|
280
322
|
if (scid)
|
|
281
323
|
params.scid = scid;
|
|
282
324
|
return rpc('DERO.GetEncryptedBalance', params);
|
|
283
325
|
}));
|
|
284
|
-
server.registerTool('dero_get_sc', {
|
|
285
|
-
description:
|
|
326
|
+
server.registerTool('dero_get_sc', readOnly({
|
|
327
|
+
description: TOOL_DESCRIPTIONS.dero_get_sc,
|
|
286
328
|
inputSchema: {
|
|
287
329
|
scid: hex64Schema.describe('64-char hex Smart Contract ID'),
|
|
288
330
|
code: z
|
|
@@ -299,7 +341,7 @@ export function createDeroMcpServer(daemonBaseUrl) {
|
|
|
299
341
|
.optional()
|
|
300
342
|
.describe('Topo height; omit or use -1 for latest'),
|
|
301
343
|
},
|
|
302
|
-
}, withStructuredErrors('dero_get_sc', async ({ scid, code, variables, topoheight }) => {
|
|
344
|
+
}), withStructuredErrors('dero_get_sc', async ({ scid, code, variables, topoheight }) => {
|
|
303
345
|
const params = {
|
|
304
346
|
scid,
|
|
305
347
|
code: code ?? true,
|
|
@@ -307,10 +349,12 @@ export function createDeroMcpServer(daemonBaseUrl) {
|
|
|
307
349
|
};
|
|
308
350
|
if (topoheight !== undefined)
|
|
309
351
|
params.topoheight = topoheight;
|
|
310
|
-
|
|
352
|
+
const result = (await rpc('DERO.GetSC', params)) ?? {};
|
|
353
|
+
const related_docs = relatedDocsFor('dero_get_sc');
|
|
354
|
+
return { ...result, ...(related_docs ? { related_docs } : {}) };
|
|
311
355
|
}));
|
|
312
|
-
server.registerTool('dero_get_gas_estimate', {
|
|
313
|
-
description:
|
|
356
|
+
server.registerTool('dero_get_gas_estimate', readOnly({
|
|
357
|
+
description: TOOL_DESCRIPTIONS.dero_get_gas_estimate,
|
|
314
358
|
inputSchema: {
|
|
315
359
|
transfers: z
|
|
316
360
|
.array(z.record(z.unknown()))
|
|
@@ -326,7 +370,7 @@ export function createDeroMcpServer(daemonBaseUrl) {
|
|
|
326
370
|
.optional()
|
|
327
371
|
.describe('Signer address used for estimation'),
|
|
328
372
|
},
|
|
329
|
-
}, withStructuredErrors('dero_get_gas_estimate', async (args) => {
|
|
373
|
+
}), withStructuredErrors('dero_get_gas_estimate', async (args) => {
|
|
330
374
|
const params = {};
|
|
331
375
|
if (args.transfers)
|
|
332
376
|
params.transfers = args.transfers;
|
|
@@ -336,10 +380,12 @@ export function createDeroMcpServer(daemonBaseUrl) {
|
|
|
336
380
|
params.sc_rpc = args.sc_rpc;
|
|
337
381
|
if (args.signer)
|
|
338
382
|
params.signer = args.signer;
|
|
339
|
-
|
|
383
|
+
const result = (await rpc('DERO.GetGasEstimate', params)) ?? {};
|
|
384
|
+
const related_docs = relatedDocsFor('dero_get_gas_estimate');
|
|
385
|
+
return { ...result, ...(related_docs ? { related_docs } : {}) };
|
|
340
386
|
}));
|
|
341
|
-
server.registerTool('dero_name_to_address', {
|
|
342
|
-
description:
|
|
387
|
+
server.registerTool('dero_name_to_address', readOnly({
|
|
388
|
+
description: TOOL_DESCRIPTIONS.dero_name_to_address,
|
|
343
389
|
inputSchema: {
|
|
344
390
|
name: z.string().min(1).describe('Registered name'),
|
|
345
391
|
topoheight: z
|
|
@@ -347,9 +393,9 @@ export function createDeroMcpServer(daemonBaseUrl) {
|
|
|
347
393
|
.int()
|
|
348
394
|
.describe('Use -1 for latest'),
|
|
349
395
|
},
|
|
350
|
-
}, withStructuredErrors('dero_name_to_address', async ({ name, topoheight }) => rpc('DERO.NameToAddress', { name, topoheight })));
|
|
351
|
-
server.registerTool('dero_get_block_template', {
|
|
352
|
-
description:
|
|
396
|
+
}), withStructuredErrors('dero_name_to_address', async ({ name, topoheight }) => rpc('DERO.NameToAddress', { name, topoheight })));
|
|
397
|
+
server.registerTool('dero_get_block_template', readOnly({
|
|
398
|
+
description: TOOL_DESCRIPTIONS.dero_get_block_template,
|
|
353
399
|
inputSchema: {
|
|
354
400
|
wallet_address: deroAddressSchema.describe('Miner payout DERO address'),
|
|
355
401
|
block: z
|
|
@@ -358,7 +404,7 @@ export function createDeroMcpServer(daemonBaseUrl) {
|
|
|
358
404
|
.describe('Include block blob'),
|
|
359
405
|
miner: z.string().optional().describe('Optional miner id / label'),
|
|
360
406
|
},
|
|
361
|
-
}, withStructuredErrors('dero_get_block_template', async ({ wallet_address, block, miner }) => {
|
|
407
|
+
}), withStructuredErrors('dero_get_block_template', async ({ wallet_address, block, miner }) => {
|
|
362
408
|
const params = { wallet_address };
|
|
363
409
|
if (block !== undefined)
|
|
364
410
|
params.block = block;
|
|
@@ -366,8 +412,8 @@ export function createDeroMcpServer(daemonBaseUrl) {
|
|
|
366
412
|
params.miner = miner;
|
|
367
413
|
return rpc('DERO.GetBlockTemplate', params);
|
|
368
414
|
}));
|
|
369
|
-
server.registerTool('dero_docs_search', {
|
|
370
|
-
description:
|
|
415
|
+
server.registerTool('dero_docs_search', readOnly({
|
|
416
|
+
description: TOOL_DESCRIPTIONS.dero_docs_search,
|
|
371
417
|
inputSchema: {
|
|
372
418
|
query: z
|
|
373
419
|
.string()
|
|
@@ -388,9 +434,9 @@ export function createDeroMcpServer(daemonBaseUrl) {
|
|
|
388
434
|
.optional()
|
|
389
435
|
.describe('Max matches (default 8, max 25)'),
|
|
390
436
|
},
|
|
391
|
-
}, withStructuredErrors('dero_docs_search', async ({ query, product, section, limit }) => searchDeroDocs({ query, product, section, limit })));
|
|
392
|
-
server.registerTool('dero_docs_get_page', {
|
|
393
|
-
description:
|
|
437
|
+
}), withStructuredErrors('dero_docs_search', async ({ query, product, section, limit }) => searchDeroDocs({ query, product, section, limit })));
|
|
438
|
+
server.registerTool('dero_docs_get_page', readOnly({
|
|
439
|
+
description: TOOL_DESCRIPTIONS.dero_docs_get_page,
|
|
394
440
|
inputSchema: {
|
|
395
441
|
slug: z
|
|
396
442
|
.string()
|
|
@@ -400,9 +446,9 @@ export function createDeroMcpServer(daemonBaseUrl) {
|
|
|
400
446
|
.optional()
|
|
401
447
|
.describe('Optional product scope to disambiguate duplicate slugs'),
|
|
402
448
|
},
|
|
403
|
-
}, withStructuredErrors('dero_docs_get_page', async ({ slug, product }) => getDeroDocPage({ slug, product })));
|
|
404
|
-
server.registerTool('dero_docs_list', {
|
|
405
|
-
description:
|
|
449
|
+
}), withStructuredErrors('dero_docs_get_page', async ({ slug, product }) => getDeroDocPage({ slug, product })));
|
|
450
|
+
server.registerTool('dero_docs_list', readOnly({
|
|
451
|
+
description: TOOL_DESCRIPTIONS.dero_docs_list,
|
|
406
452
|
inputSchema: {
|
|
407
453
|
product: deroDocProductSchema
|
|
408
454
|
.optional()
|
|
@@ -415,7 +461,7 @@ export function createDeroMcpServer(daemonBaseUrl) {
|
|
|
415
461
|
.optional()
|
|
416
462
|
.describe('Max pages returned (default 120, max 500)'),
|
|
417
463
|
},
|
|
418
|
-
}, withStructuredErrors('dero_docs_list', async ({ product, limit }) => {
|
|
464
|
+
}), withStructuredErrors('dero_docs_list', async ({ product, limit }) => {
|
|
419
465
|
const docsIndex = await listDeroDocs(product);
|
|
420
466
|
const capped = Math.max(1, Math.min(limit ?? 120, 500));
|
|
421
467
|
return {
|
|
@@ -424,6 +470,31 @@ export function createDeroMcpServer(daemonBaseUrl) {
|
|
|
424
470
|
pages: docsIndex.pages.slice(0, capped),
|
|
425
471
|
};
|
|
426
472
|
}));
|
|
473
|
+
// ---------- Composite tools (Phase C) ----------
|
|
474
|
+
// Composites chain read-only primitives and bundled docs into
|
|
475
|
+
// intent-shaped responses. Each composite has a design entry in
|
|
476
|
+
// `docs/composites.md` that pins its input schema, internal chain,
|
|
477
|
+
// response shape, failure modes, and flow test ID.
|
|
478
|
+
server.registerTool('diagnose_chain_health', readOnly({
|
|
479
|
+
description: TOOL_DESCRIPTIONS.diagnose_chain_health,
|
|
480
|
+
inputSchema: diagnoseChainHealthInputSchema,
|
|
481
|
+
}), withStructuredErrors('diagnose_chain_health', async (args) => diagnoseChainHealth(rpc, args ?? {})));
|
|
482
|
+
server.registerTool('explain_smart_contract', readOnly({
|
|
483
|
+
description: TOOL_DESCRIPTIONS.explain_smart_contract,
|
|
484
|
+
inputSchema: explainSmartContractInputSchema,
|
|
485
|
+
}), withStructuredErrors('explain_smart_contract', async (args) => explainSmartContract(rpc, args)));
|
|
486
|
+
server.registerTool('recommend_docs_path', readOnly({
|
|
487
|
+
description: TOOL_DESCRIPTIONS.recommend_docs_path,
|
|
488
|
+
inputSchema: recommendDocsPathInputSchema,
|
|
489
|
+
}), withStructuredErrors('recommend_docs_path', async (args) => recommendDocsPath(args)));
|
|
490
|
+
server.registerTool('estimate_deploy_cost', readOnly({
|
|
491
|
+
description: TOOL_DESCRIPTIONS.estimate_deploy_cost,
|
|
492
|
+
inputSchema: estimateDeployCostInputSchema,
|
|
493
|
+
}), withStructuredErrors('estimate_deploy_cost', async (args) => estimateDeployCost(rpc, args)));
|
|
494
|
+
server.registerTool('trace_transaction_with_context', readOnly({
|
|
495
|
+
description: TOOL_DESCRIPTIONS.trace_transaction_with_context,
|
|
496
|
+
inputSchema: traceTransactionWithContextInputSchema,
|
|
497
|
+
}), withStructuredErrors('trace_transaction_with_context', async (args) => traceTransactionWithContext(rpc, args)));
|
|
427
498
|
server.registerResource('dero_mcp_server_info', 'dero://mcp/server-info', {
|
|
428
499
|
description: 'Server metadata, tool list, resource list, and prompt names.',
|
|
429
500
|
mimeType: 'application/json',
|
|
@@ -434,7 +505,7 @@ export function createDeroMcpServer(daemonBaseUrl) {
|
|
|
434
505
|
mimeType: 'application/json',
|
|
435
506
|
text: JSON.stringify({
|
|
436
507
|
name: 'dero-daemon-mcp',
|
|
437
|
-
version: '0.
|
|
508
|
+
version: '0.2.2',
|
|
438
509
|
mode: 'read-only',
|
|
439
510
|
endpoint: endpoint,
|
|
440
511
|
docs_products: DERO_DOC_PRODUCTS,
|
|
@@ -473,7 +544,7 @@ export function createDeroMcpServer(daemonBaseUrl) {
|
|
|
473
544
|
],
|
|
474
545
|
}));
|
|
475
546
|
server.registerResource('dero_mcp_example_flows', 'dero://mcp/example-flows', {
|
|
476
|
-
description: 'Compact agent flow recipes for common DERO investigations.',
|
|
547
|
+
description: 'Compact agent flow recipes for common DERO investigations. Composites are listed FIRST; primitives are the fallback path.',
|
|
477
548
|
mimeType: 'text/markdown',
|
|
478
549
|
}, async (uri) => ({
|
|
479
550
|
contents: [
|
|
@@ -483,16 +554,98 @@ export function createDeroMcpServer(daemonBaseUrl) {
|
|
|
483
554
|
text: [
|
|
484
555
|
'# DERO MCP Example Flows',
|
|
485
556
|
'',
|
|
486
|
-
'
|
|
487
|
-
|
|
488
|
-
'
|
|
489
|
-
'
|
|
557
|
+
'Prefer composites — each is one call replacing a primitive chain, and each returns a narrative + curated docs citations.',
|
|
558
|
+
'',
|
|
559
|
+
'## Composites (preferred)',
|
|
560
|
+
'',
|
|
561
|
+
'- **Network health**: call `diagnose_chain_health` (no args). Returns narrative + signals + citations in one shot.',
|
|
562
|
+
`- **Inspect a contract**: call \`explain_smart_contract\` with the SCID. For example, the name registry: \`${NAME_REGISTRY_SCID}\`.`,
|
|
563
|
+
'- **Trace a transaction**: call `trace_transaction_with_context` with the tx_hash. Handles SC install surface extraction inline.',
|
|
564
|
+
'- **Find the right docs**: call `recommend_docs_path` with a natural-language intent (e.g. "deploy a TELA app"). Optional `product_hint` biases the score 1.5x toward that product.',
|
|
565
|
+
'- **Pre-flight a deploy**: call `estimate_deploy_cost` with the DVM-BASIC source. Returns gas estimate + plain-text breakdown + parsed surface.',
|
|
566
|
+
'',
|
|
567
|
+
'## Primitive fallback paths (only when a composite is unavailable or returns _meta.error)',
|
|
568
|
+
'',
|
|
569
|
+
'- Network: `dero_daemon_ping` → `dero_get_info` → `dero_get_height` → `dero_get_tx_pool`',
|
|
570
|
+
'- Contract: `dero_get_sc` (code=true, variables=true) then optionally `dero_docs_get_page`',
|
|
571
|
+
'- Transaction: `dero_get_transaction` (decode_as_json=1) — does NOT decode SC invocation args',
|
|
572
|
+
'- Docs: `dero_docs_search` (then `dero_docs_get_page` for full text)',
|
|
573
|
+
'- Deploy estimate: `dero_get_gas_estimate`',
|
|
574
|
+
'',
|
|
575
|
+
'## Structured error codes (`_meta.error.code`) the agent should react to',
|
|
576
|
+
'',
|
|
577
|
+
'- `NO_DOCS_MATCH` (recommend_docs_path): rephrase the intent, retry. Not a hard failure.',
|
|
578
|
+
'- `INVALID_INPUT` (estimate_deploy_cost): the daemon\'s raw -32098 compile message is in `_meta.error.raw`; surface it to the user.',
|
|
579
|
+
'- `TX_NOT_FOUND` (trace_transaction_with_context): the daemon returned an empty record. Retryable=true (mempool propagation), but only after verifying the hash and network.',
|
|
580
|
+
'',
|
|
581
|
+
'## Read-only boundary',
|
|
582
|
+
'',
|
|
583
|
+
'No wallet writes. No raw tx submission. No contract invocation. See `dero://mcp/safety-boundary` and `dero://mcp/composites` for the full posture.',
|
|
490
584
|
].join('\n'),
|
|
491
585
|
},
|
|
492
586
|
],
|
|
493
587
|
}));
|
|
588
|
+
server.registerResource('dero_mcp_composites', 'dero://mcp/composites', {
|
|
589
|
+
description: 'Catalog of the 5 composite tools — what each replaces, when to call it, what it returns, and which structured _meta.error codes it can emit. Read this when picking between a composite and a primitive.',
|
|
590
|
+
mimeType: 'application/json',
|
|
591
|
+
}, async (uri) => ({
|
|
592
|
+
contents: [
|
|
593
|
+
{
|
|
594
|
+
uri: uri.toString(),
|
|
595
|
+
mimeType: 'application/json',
|
|
596
|
+
text: JSON.stringify({
|
|
597
|
+
version: 1,
|
|
598
|
+
note: 'Composites fuse one or more daemon-read primitives with bundled-docs lookups and emit a single narrative + curated related_docs. Always prefer the composite when its intent matches the user request.',
|
|
599
|
+
composites: [
|
|
600
|
+
{
|
|
601
|
+
name: 'diagnose_chain_health',
|
|
602
|
+
replaces: ['dero_daemon_ping', 'dero_get_info', 'dero_get_height', 'dero_get_tx_pool'],
|
|
603
|
+
when_to_call: 'User asks "is the chain healthy", "are we synced", "what is the network state", or any general daemon-status question.',
|
|
604
|
+
inputs: { include_tx_pool: 'optional boolean, default true' },
|
|
605
|
+
output_highlights: ['status (healthy | degraded | unreachable)', 'signals (e.g. healthy, stale-tip, lagging)', 'tip metadata', 'narrative', 'related_docs'],
|
|
606
|
+
error_codes: ['RPC_UNREACHABLE'],
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
name: 'explain_smart_contract',
|
|
610
|
+
replaces: ['dero_get_sc + manual parsing + dero_docs_search'],
|
|
611
|
+
when_to_call: 'User wants to UNDERSTAND a contract (functions, state shape, what DVM concept to read about). NOT for raw variable inspection — use dero_get_sc for that.',
|
|
612
|
+
inputs: { scid: '64-char hex SCID', topoheight: 'optional number' },
|
|
613
|
+
output_highlights: ['kind (token | registry | minimal | generic)', 'surface (functions, stringkeys, uint64keys, balances)', 'narrative', '1-4 curated DVM docs citations re-ranked for the contract pattern'],
|
|
614
|
+
error_codes: ['RPC_UNREACHABLE', 'RPC_INVALID_PARAMS'],
|
|
615
|
+
},
|
|
616
|
+
{
|
|
617
|
+
name: 'recommend_docs_path',
|
|
618
|
+
replaces: ['4x parallel dero_docs_search calls + manual ranking'],
|
|
619
|
+
when_to_call: 'User has a natural-language intent ("deploy a TELA app", "estimate gas") and needs to know which doc page to read. Bias-not-filter on product_hint.',
|
|
620
|
+
inputs: { intent: 'short natural-language string', product_hint: 'optional derod | tela | hologram | deropay', limit_per_product: 'optional number, default 2' },
|
|
621
|
+
output_highlights: ['recommended[] with score/boosted_score/rationale', 'summary_by_product', 'related_docs'],
|
|
622
|
+
error_codes: ['NO_DOCS_MATCH'],
|
|
623
|
+
},
|
|
624
|
+
{
|
|
625
|
+
name: 'estimate_deploy_cost',
|
|
626
|
+
replaces: ['dero_get_gas_estimate + manual surface extraction + manual interpretation of gascompute/gasstorage'],
|
|
627
|
+
when_to_call: 'User wants to deploy a contract and needs to know what it will cost. Read-only; nothing is submitted.',
|
|
628
|
+
inputs: { sc: 'DVM-BASIC source string', include_breakdown: 'optional boolean, default true' },
|
|
629
|
+
output_highlights: ['estimate (gascompute, gasstorage, total, status)', 'breakdown (compute_note, storage_note) | null', 'surface (functions, stringkeys, uint64keys)'],
|
|
630
|
+
error_codes: ['INVALID_INPUT (wraps daemon -32098 DVM compile errors; raw message in _meta.error.raw)', 'RPC_UNREACHABLE'],
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
name: 'trace_transaction_with_context',
|
|
634
|
+
replaces: ['dero_get_transaction + (for SC installs) dero_get_sc + manual classification'],
|
|
635
|
+
when_to_call: 'User asks "what is this tx", "is this confirmed", "what contract did this deploy", "what does this tx do".',
|
|
636
|
+
inputs: { tx_hash: '64-char hex', decode: 'optional boolean, default true', include_sc_context: 'optional boolean, default true' },
|
|
637
|
+
output_highlights: ['confirmation (status, block_height, valid_block, in_pool)', 'kind (sc_install | transfer_or_invocation | coinbase | unknown)', 'ring (groups, first_group_size)', 'sc_install (scid + parsed surface) | null', 'raw_tx_hex_length', 'narrative', 'related_docs'],
|
|
638
|
+
scope_note: 'SC invocation arg decoding is NOT performed (would require the binary tx codec). SC INSTALL surface extraction IS performed inline because the source is embedded in the tx record.',
|
|
639
|
+
error_codes: ['TX_NOT_FOUND (retryable=true; daemon returns empty record on unknown hashes)', 'RPC_UNREACHABLE'],
|
|
640
|
+
},
|
|
641
|
+
],
|
|
642
|
+
design_contract_doc: 'docs/composites.md in the dero-mcp-server repo',
|
|
643
|
+
}, null, 2),
|
|
644
|
+
},
|
|
645
|
+
],
|
|
646
|
+
}));
|
|
494
647
|
server.registerPrompt('network_health_check', {
|
|
495
|
-
description: 'Guide the model through a DERO daemon sync and health check
|
|
648
|
+
description: 'Guide the model through a DERO daemon sync and health check using the diagnose_chain_health composite.',
|
|
496
649
|
argsSchema: {
|
|
497
650
|
reference_topoheight: z
|
|
498
651
|
.number()
|
|
@@ -501,65 +654,134 @@ export function createDeroMcpServer(daemonBaseUrl) {
|
|
|
501
654
|
.optional(),
|
|
502
655
|
},
|
|
503
656
|
}, async ({ reference_topoheight }) => ({
|
|
504
|
-
description: 'Prompt for sync health investigation.',
|
|
657
|
+
description: 'Prompt for sync health investigation (composite-first).',
|
|
505
658
|
messages: [
|
|
506
659
|
{
|
|
507
660
|
role: 'user',
|
|
508
661
|
content: {
|
|
509
662
|
type: 'text',
|
|
510
663
|
text: [
|
|
511
|
-
'Check DERO daemon health using MCP tools.',
|
|
512
|
-
'
|
|
513
|
-
'
|
|
514
|
-
'
|
|
664
|
+
'Check DERO daemon health using the MCP composite tools (one call replaces the old four-step chain).',
|
|
665
|
+
'',
|
|
666
|
+
'1) Call diagnose_chain_health with no arguments (or include_tx_pool=true if you specifically want mempool counts).',
|
|
667
|
+
'2) Read the returned narrative aloud; it already summarizes ping latency, topoheight, stableheight, version, network, and mempool state.',
|
|
668
|
+
'3) Inspect signals (e.g. "healthy", "stale-tip", "lagging") and surface any that are not "healthy".',
|
|
669
|
+
'4) Quote the related_docs citations so the user knows where to read further.',
|
|
515
670
|
reference_topoheight
|
|
516
|
-
? `
|
|
517
|
-
: '
|
|
671
|
+
? `5) Compare the returned topoheight against reference_topoheight=${reference_topoheight} and report the delta.`
|
|
672
|
+
: '5) If no reference topoheight was provided, state that external comparison is still needed for final sync confidence.',
|
|
673
|
+
'',
|
|
674
|
+
'Fallback: only chain primitives manually (dero_daemon_ping → dero_get_info → dero_get_height → dero_get_tx_pool) if diagnose_chain_health is unavailable or returns _meta.error.',
|
|
518
675
|
].join('\n'),
|
|
519
676
|
},
|
|
520
677
|
},
|
|
521
678
|
],
|
|
522
679
|
}));
|
|
523
680
|
server.registerPrompt('inspect_smart_contract', {
|
|
524
|
-
description: 'Inspect contract
|
|
681
|
+
description: 'Inspect a DERO contract via the explain_smart_contract composite (function surface + classification + curated DVM docs).',
|
|
525
682
|
argsSchema: {
|
|
526
683
|
scid: hex64Schema,
|
|
527
684
|
},
|
|
528
685
|
}, async ({ scid }) => ({
|
|
529
|
-
description: 'Prompt for smart contract inspection.',
|
|
686
|
+
description: 'Prompt for smart contract inspection (composite-first).',
|
|
530
687
|
messages: [
|
|
531
688
|
{
|
|
532
689
|
role: 'user',
|
|
533
690
|
content: {
|
|
534
691
|
type: 'text',
|
|
535
692
|
text: [
|
|
536
|
-
`
|
|
537
|
-
'
|
|
538
|
-
|
|
539
|
-
'
|
|
540
|
-
'
|
|
693
|
+
`Investigate DERO smart contract ${scid} using the MCP composite tools.`,
|
|
694
|
+
'',
|
|
695
|
+
`1) Call explain_smart_contract with scid="${scid}". This single call returns the parsed function surface, a contract kind classification (token | registry | minimal | generic), a plain-language narrative, and 1-4 DVM docs citations ranked for the contract pattern.`,
|
|
696
|
+
'2) Quote the narrative as-is — it already explains the likely data model, state keys, and where to read next.',
|
|
697
|
+
'3) If the user wants raw state (variable values, balances), THEN call dero_get_sc with variables=true and code=true as a follow-up; explain why you needed the second call.',
|
|
698
|
+
'4) If you want documentation for a DVM concept the contract uses, call dero_docs_get_page with the slug from one of the related_docs entries.',
|
|
699
|
+
'',
|
|
700
|
+
'Fallback: call dero_get_sc manually only if explain_smart_contract is unavailable or returns _meta.error.',
|
|
541
701
|
].join('\n'),
|
|
542
702
|
},
|
|
543
703
|
},
|
|
544
704
|
],
|
|
545
705
|
}));
|
|
546
706
|
server.registerPrompt('trace_transaction', {
|
|
547
|
-
description: 'Trace one transaction
|
|
707
|
+
description: 'Trace one transaction via the trace_transaction_with_context composite (confirmation + kind classification + SC install surface).',
|
|
548
708
|
argsSchema: {
|
|
549
709
|
tx_hash: hex64Schema,
|
|
550
710
|
},
|
|
551
711
|
}, async ({ tx_hash }) => ({
|
|
552
|
-
description: 'Prompt for transaction tracing.',
|
|
712
|
+
description: 'Prompt for transaction tracing (composite-first).',
|
|
713
|
+
messages: [
|
|
714
|
+
{
|
|
715
|
+
role: 'user',
|
|
716
|
+
content: {
|
|
717
|
+
type: 'text',
|
|
718
|
+
text: [
|
|
719
|
+
`Trace DERO transaction ${tx_hash} using the MCP composite tools.`,
|
|
720
|
+
'',
|
|
721
|
+
`1) Call trace_transaction_with_context with tx_hash="${tx_hash}". The response gives you confirmation status (confirmed | mempool | unknown), block height + valid_block, kind classification (sc_install | transfer_or_invocation | coinbase | unknown), ring stats, and — if the tx is a contract install — the parsed function surface inline (no second call needed).`,
|
|
722
|
+
'2) Read the returned narrative aloud. Quote the related_docs citations.',
|
|
723
|
+
'3) If confirmation is "mempool", explicitly note that the result is provisional and tell the user when to retry.',
|
|
724
|
+
'4) If _meta.error.code is TX_NOT_FOUND, do NOT retry blindly — verify the hash, confirm the network (mainnet vs testnet), and only retry if the tx was just broadcast.',
|
|
725
|
+
'5) Note: SC invocation arg decoding is NOT performed by the composite (would require the binary tx codec). If the tx is a non-install SC call and the user needs the entrypoint + args, surface that limitation and suggest a wallet-side decoder.',
|
|
726
|
+
'',
|
|
727
|
+
'Fallback: call dero_get_transaction manually only if trace_transaction_with_context is unavailable or returns an unhandled _meta.error.',
|
|
728
|
+
].join('\n'),
|
|
729
|
+
},
|
|
730
|
+
},
|
|
731
|
+
],
|
|
732
|
+
}));
|
|
733
|
+
server.registerPrompt('find_dero_docs_for_intent', {
|
|
734
|
+
description: 'Find the right DERO documentation page(s) for a natural-language intent via the recommend_docs_path composite.',
|
|
735
|
+
argsSchema: {
|
|
736
|
+
intent: z.string().min(3, 'Provide a short intent like "deploy a TELA app" or "estimate gas"'),
|
|
737
|
+
product_hint: z.enum(DERO_DOC_PRODUCTS).optional(),
|
|
738
|
+
},
|
|
739
|
+
}, async ({ intent, product_hint }) => ({
|
|
740
|
+
description: 'Prompt for routing an agent intent to the right DERO docs.',
|
|
741
|
+
messages: [
|
|
742
|
+
{
|
|
743
|
+
role: 'user',
|
|
744
|
+
content: {
|
|
745
|
+
type: 'text',
|
|
746
|
+
text: [
|
|
747
|
+
`Find the best DERO documentation page(s) for the intent: "${intent}".`,
|
|
748
|
+
'',
|
|
749
|
+
`1) Call recommend_docs_path with intent="${intent}"${product_hint ? `, product_hint="${product_hint}" (this biases the score 1.5x toward that product; it does NOT filter the other three out)` : ' (no product_hint — all four DERO products will be searched in parallel)'}.`,
|
|
750
|
+
'2) Read the top 2-3 recommendations to the user with their rationale strings; quote the canonical URLs.',
|
|
751
|
+
'3) If the user wants the full content of any page, call dero_docs_get_page with the slug + product from the recommendation.',
|
|
752
|
+
'4) If _meta.error.code is NO_DOCS_MATCH, rephrase the intent (drop verbs, use product nouns like "TELA app" or "DVM contract") and call again. Do NOT just give up.',
|
|
753
|
+
'',
|
|
754
|
+
'Prefer this composite over chaining dero_docs_search yourself across four products.',
|
|
755
|
+
].join('\n'),
|
|
756
|
+
},
|
|
757
|
+
},
|
|
758
|
+
],
|
|
759
|
+
}));
|
|
760
|
+
server.registerPrompt('estimate_deploy_for_contract', {
|
|
761
|
+
description: 'Run gas pre-flight for a DVM-BASIC contract source via the estimate_deploy_cost composite (numeric estimate + plain-text breakdown + parsed surface).',
|
|
762
|
+
argsSchema: {
|
|
763
|
+
sc_source: z.string().min(20, 'Provide DVM-BASIC contract source (at minimum: a Function/End Function block)'),
|
|
764
|
+
include_breakdown: z.boolean().optional(),
|
|
765
|
+
},
|
|
766
|
+
}, async ({ sc_source, include_breakdown }) => ({
|
|
767
|
+
description: 'Prompt for DVM deploy pre-flight (composite-first).',
|
|
553
768
|
messages: [
|
|
554
769
|
{
|
|
555
770
|
role: 'user',
|
|
556
771
|
content: {
|
|
557
772
|
type: 'text',
|
|
558
773
|
text: [
|
|
559
|
-
|
|
560
|
-
'
|
|
561
|
-
|
|
562
|
-
'
|
|
774
|
+
'Run a deploy pre-flight (gas estimate) for the DVM-BASIC source the user supplied. This is read-only; nothing is submitted to chain.',
|
|
775
|
+
'',
|
|
776
|
+
`1) Call estimate_deploy_cost with the contract source as sc${include_breakdown === false ? ' and include_breakdown=false (caller does NOT want the plain-text gas notes)' : ' (include_breakdown defaults to true)'}.`,
|
|
777
|
+
'2) Quote estimate.gascompute, estimate.gasstorage, estimate.total, and the daemon\'s status string.',
|
|
778
|
+
'3) If include_breakdown is true, read the breakdown.compute_note and breakdown.storage_note as plain-language explanations.',
|
|
779
|
+
'4) Quote the parsed function surface (functions[].name) so the user can sanity-check the contract.',
|
|
780
|
+
'5) If _meta.error.code is INVALID_INPUT, read the hint and the raw -32098 compile message from _meta.error.raw verbatim — that tells the user what to fix in the source.',
|
|
781
|
+
'',
|
|
782
|
+
`Source (${sc_source.length} chars) starts with: ${sc_source.slice(0, 120)}${sc_source.length > 120 ? '...' : ''}`,
|
|
783
|
+
'',
|
|
784
|
+
'Fallback: call dero_get_gas_estimate manually only if estimate_deploy_cost is unavailable.',
|
|
563
785
|
].join('\n'),
|
|
564
786
|
},
|
|
565
787
|
},
|