dero-mcp-server 0.1.0 → 0.1.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/src/index.ts DELETED
@@ -1,30 +0,0 @@
1
- #!/usr/bin/env node
2
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
3
- import { createDeroMcpServer } from './server.js'
4
-
5
- /** Default public mainnet JSON-RPC (override with DERO_DAEMON_URL). */
6
- const DEFAULT_DAEMON_BASE = 'http://82.65.143.182:10102'
7
-
8
- function daemonUrlFromEnv(): string {
9
- const fromEnv = process.env.DERO_DAEMON_URL?.trim()
10
- if (fromEnv) return fromEnv.replace(/\/json_rpc\/?$/, '')
11
- return DEFAULT_DAEMON_BASE
12
- }
13
-
14
- async function main() {
15
- const base = daemonUrlFromEnv()
16
- const server = createDeroMcpServer(base)
17
-
18
- const transport = new StdioServerTransport()
19
-
20
- process.stderr.write(
21
- `[dero-mcp-server] DERO_DAEMON_URL base: ${base} (JSON-RPC at ${base.replace(/\/$/, '')}/json_rpc)\n`,
22
- )
23
-
24
- await server.connect(transport)
25
- }
26
-
27
- main().catch((err) => {
28
- process.stderr.write(`[dero-mcp-server] fatal: ${err instanceof Error ? err.message : String(err)}\n`)
29
- process.exit(1)
30
- })
package/src/rpc.ts DELETED
@@ -1,60 +0,0 @@
1
- const DEFAULT_TIMEOUT_MS = 45_000
2
-
3
- export type JsonRpcResponse<T = unknown> = {
4
- jsonrpc: '2.0'
5
- id: string | number
6
- result?: T
7
- error?: { code: number; message: string; data?: unknown }
8
- }
9
-
10
- /**
11
- * POST JSON-RPC 2.0 to a DERO daemon or wallet endpoint (…/json_rpc).
12
- */
13
- export async function deroJsonRpc<T = unknown>(
14
- jsonRpcUrl: string,
15
- method: string,
16
- params?: unknown,
17
- options?: { timeoutMs?: number },
18
- ): Promise<T> {
19
- const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS
20
- const body: Record<string, unknown> = {
21
- jsonrpc: '2.0',
22
- id: 'dero-mcp',
23
- method,
24
- }
25
- if (params !== undefined) body.params = params
26
-
27
- const controller = new AbortController()
28
- const timer = setTimeout(() => controller.abort(), timeoutMs)
29
- try {
30
- const res = await fetch(jsonRpcUrl, {
31
- method: 'POST',
32
- headers: { 'content-type': 'application/json' },
33
- body: JSON.stringify(body),
34
- signal: controller.signal,
35
- })
36
- const text = await res.text()
37
- if (!res.ok) {
38
- throw new Error(`HTTP ${res.status}: ${text.slice(0, 500)}`)
39
- }
40
- let json: JsonRpcResponse<T>
41
- try {
42
- json = JSON.parse(text) as JsonRpcResponse<T>
43
- } catch {
44
- throw new Error(`Invalid JSON from node: ${text.slice(0, 200)}`)
45
- }
46
- if (json.error) {
47
- throw new Error(
48
- `RPC error ${json.error.code}: ${json.error.message}${json.error.data != null ? ` ${JSON.stringify(json.error.data)}` : ''}`,
49
- )
50
- }
51
- return json.result as T
52
- } finally {
53
- clearTimeout(timer)
54
- }
55
- }
56
-
57
- export function jsonRpcEndpoint(baseUrl: string): string {
58
- const trimmed = baseUrl.replace(/\/$/, '')
59
- return trimmed.endsWith('/json_rpc') ? trimmed : `${trimmed}/json_rpc`
60
- }
package/src/server.ts DELETED
@@ -1,317 +0,0 @@
1
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
2
- import { z } from 'zod'
3
- import { deroJsonRpc, jsonRpcEndpoint } from './rpc.js'
4
-
5
- const scRpcArgSchema = z.object({
6
- name: z.string(),
7
- datatype: z.enum(['S', 'U', 'H']),
8
- value: z.union([z.string(), z.number()]),
9
- })
10
-
11
- function toolText(data: unknown) {
12
- return {
13
- content: [
14
- {
15
- type: 'text' as const,
16
- text: typeof data === 'string' ? data : JSON.stringify(data, null, 2),
17
- },
18
- ],
19
- }
20
- }
21
-
22
- export function createDeroMcpServer(daemonBaseUrl: string): McpServer {
23
- const endpoint = jsonRpcEndpoint(daemonBaseUrl)
24
- const rpc = async <T>(method: string, params?: unknown) =>
25
- deroJsonRpc<T>(endpoint, method, params)
26
-
27
- const server = new McpServer({
28
- name: 'dero-daemon-mcp',
29
- version: '0.1.0',
30
- })
31
-
32
- server.registerTool(
33
- 'dero_daemon_ping',
34
- {
35
- description:
36
- 'DERO daemon connectivity check. Calls DERO.Ping. No parameters.',
37
- },
38
- async () => toolText(await rpc<string>('DERO.Ping')),
39
- )
40
-
41
- server.registerTool(
42
- 'dero_daemon_echo',
43
- {
44
- description: 'Echo strings through the daemon (DERO.Echo).',
45
- inputSchema: {
46
- words: z.array(z.string()).describe('Strings to echo back'),
47
- },
48
- },
49
- async ({ words }) => toolText(await rpc<string>('DERO.Echo', words)),
50
- )
51
-
52
- server.registerTool(
53
- 'dero_get_info',
54
- {
55
- description:
56
- 'Get daemon / chain info: height, difficulty, version, mempool size, etc. (DERO.GetInfo).',
57
- },
58
- async () => toolText(await rpc('DERO.GetInfo')),
59
- )
60
-
61
- server.registerTool(
62
- 'dero_get_height',
63
- {
64
- description: 'Get top block height and stable/topo heights (DERO.GetHeight).',
65
- },
66
- async () => toolText(await rpc('DERO.GetHeight')),
67
- )
68
-
69
- server.registerTool(
70
- 'dero_get_block_count',
71
- {
72
- description: 'Total block count (DERO.GetBlockCount).',
73
- },
74
- async () => toolText(await rpc('DERO.GetBlockCount')),
75
- )
76
-
77
- server.registerTool(
78
- 'dero_get_last_block_header',
79
- {
80
- description: 'Header of the tip block (DERO.GetLastBlockHeader).',
81
- },
82
- async () => toolText(await rpc('DERO.GetLastBlockHeader')),
83
- )
84
-
85
- server.registerTool(
86
- 'dero_get_block',
87
- {
88
- description: 'Fetch a full block by height or hash (DERO.GetBlock). Provide one of hash or height.',
89
- inputSchema: {
90
- hash: z
91
- .string()
92
- .optional()
93
- .describe('64-char hex block hash'),
94
- height: z
95
- .number()
96
- .int()
97
- .nonnegative()
98
- .optional()
99
- .describe('Block height'),
100
- },
101
- },
102
- async (args) => {
103
- if (!args.hash && args.height === undefined) {
104
- throw new Error('Provide either hash or height')
105
- }
106
- const params: Record<string, unknown> = {}
107
- if (args.hash) params.hash = args.hash
108
- if (args.height !== undefined) params.height = args.height
109
- return toolText(await rpc('DERO.GetBlock', params))
110
- },
111
- )
112
-
113
- server.registerTool(
114
- 'dero_get_block_header_by_topo_height',
115
- {
116
- description: 'Block header by topological height (DERO.GetBlockHeaderByTopoHeight).',
117
- inputSchema: {
118
- topoheight: z
119
- .number()
120
- .int()
121
- .nonnegative()
122
- .describe('Topological height'),
123
- },
124
- },
125
- async ({ topoheight }) =>
126
- toolText(await rpc('DERO.GetBlockHeaderByTopoHeight', { topoheight })),
127
- )
128
-
129
- server.registerTool(
130
- 'dero_get_block_header_by_hash',
131
- {
132
- description: 'Block header by hash (DERO.GetBlockHeaderByHash).',
133
- inputSchema: {
134
- hash: z.string().describe('Block top hash (hex)'),
135
- },
136
- },
137
- async ({ hash }) =>
138
- toolText(await rpc('DERO.GetBlockHeaderByHash', { hash })),
139
- )
140
-
141
- server.registerTool(
142
- 'dero_get_tx_pool',
143
- {
144
- description: 'Pending mempool transaction hashes (DERO.GetTxPool).',
145
- },
146
- async () => toolText(await rpc('DERO.GetTxPool')),
147
- )
148
-
149
- server.registerTool(
150
- 'dero_get_random_address',
151
- {
152
- description:
153
- 'Random registered addresses from chain (for ring construction); optional asset scid (DERO.GetRandomAddress).',
154
- inputSchema: {
155
- scid: z
156
- .string()
157
- .optional()
158
- .describe('Optional asset smart-contract id (hex)'),
159
- },
160
- },
161
- async (args) =>
162
- toolText(
163
- await rpc(
164
- 'DERO.GetRandomAddress',
165
- args.scid != null ? { scid: args.scid } : undefined,
166
- ),
167
- ),
168
- )
169
-
170
- server.registerTool(
171
- 'dero_get_transaction',
172
- {
173
- description: 'Fetch transactions by tx hashes (DERO.GetTransaction).',
174
- inputSchema: {
175
- txs_hashes: z
176
- .array(z.string())
177
- .min(1)
178
- .describe('List of transaction hashes (hex)'),
179
- decode_as_json: z
180
- .number()
181
- .int()
182
- .optional()
183
- .describe('Optional: decode each tx as JSON when non-zero'),
184
- },
185
- },
186
- async ({ txs_hashes, decode_as_json }) => {
187
- const params: Record<string, unknown> = { txs_hashes }
188
- if (decode_as_json !== undefined) params.decode_as_json = decode_as_json
189
- return toolText(await rpc('DERO.GetTransaction', params))
190
- },
191
- )
192
-
193
- server.registerTool(
194
- 'dero_get_encrypted_balance',
195
- {
196
- description:
197
- 'Encrypted balance blob for an address at a topo height (DERO.GetEncryptedBalance). Not cleartext balance.',
198
- inputSchema: {
199
- address: z.string().describe('DERO address (deto1…)'),
200
- topoheight: z
201
- .number()
202
- .int()
203
- .describe('Use -1 for latest chain tip'),
204
- scid: z.string().optional().describe('Asset SCID hex; omit for native DERO'),
205
- },
206
- },
207
- async ({ address, topoheight, scid }) => {
208
- const params: Record<string, unknown> = { address, topoheight }
209
- if (scid) params.scid = scid
210
- return toolText(await rpc('DERO.GetEncryptedBalance', params))
211
- },
212
- )
213
-
214
- server.registerTool(
215
- 'dero_get_sc',
216
- {
217
- description:
218
- 'Read smart contract code and/or variables by SCID (DERO.GetSC).',
219
- inputSchema: {
220
- scid: z.string().describe('64-char hex Smart Contract ID'),
221
- code: z
222
- .boolean()
223
- .optional()
224
- .describe('Include contract source (default true)'),
225
- variables: z
226
- .boolean()
227
- .optional()
228
- .describe('Include stored variables (default true)'),
229
- topoheight: z
230
- .number()
231
- .int()
232
- .optional()
233
- .describe('Topo height; omit or use -1 for latest'),
234
- },
235
- },
236
- async ({ scid, code, variables, topoheight }) => {
237
- const params: Record<string, unknown> = {
238
- scid,
239
- code: code ?? true,
240
- variables: variables ?? true,
241
- }
242
- if (topoheight !== undefined) params.topoheight = topoheight
243
- return toolText(await rpc('DERO.GetSC', params))
244
- },
245
- )
246
-
247
- server.registerTool(
248
- 'dero_get_gas_estimate',
249
- {
250
- description:
251
- 'Estimate gas (compute + storage) for transfers, deploy, or SC call (DERO.GetGasEstimate).',
252
- inputSchema: {
253
- transfers: z
254
- .array(z.record(z.unknown()))
255
- .optional()
256
- .describe('Optional transfer list'),
257
- sc: z.string().optional().describe('SC source to deploy'),
258
- sc_rpc: z
259
- .array(scRpcArgSchema)
260
- .optional()
261
- .describe('SC invocation arguments (entrypoint, SC_ID, etc.)'),
262
- signer: z
263
- .string()
264
- .optional()
265
- .describe('Signer address used for estimation'),
266
- },
267
- },
268
- async (args) => {
269
- const params: Record<string, unknown> = {}
270
- if (args.transfers) params.transfers = args.transfers
271
- if (args.sc) params.sc = args.sc
272
- if (args.sc_rpc) params.sc_rpc = args.sc_rpc
273
- if (args.signer) params.signer = args.signer
274
- return toolText(await rpc('DERO.GetGasEstimate', params))
275
- },
276
- )
277
-
278
- server.registerTool(
279
- 'dero_name_to_address',
280
- {
281
- description: 'Resolve a DERO on-chain name to address (DERO.NameToAddress).',
282
- inputSchema: {
283
- name: z.string().describe('Registered name'),
284
- topoheight: z
285
- .number()
286
- .int()
287
- .describe('Use -1 for latest'),
288
- },
289
- },
290
- async ({ name, topoheight }) =>
291
- toolText(await rpc('DERO.NameToAddress', { name, topoheight })),
292
- )
293
-
294
- server.registerTool(
295
- 'dero_get_block_template',
296
- {
297
- description:
298
- 'Mining: get block template for a miner address (DERO.GetBlockTemplate).',
299
- inputSchema: {
300
- wallet_address: z.string().describe('Miner payout DERO address'),
301
- block: z
302
- .boolean()
303
- .optional()
304
- .describe('Include block blob'),
305
- miner: z.string().optional().describe('Optional miner id / label'),
306
- },
307
- },
308
- async ({ wallet_address, block, miner }) => {
309
- const params: Record<string, unknown> = { wallet_address }
310
- if (block !== undefined) params.block = block
311
- if (miner) params.miner = miner
312
- return toolText(await rpc('DERO.GetBlockTemplate', params))
313
- },
314
- )
315
-
316
- return server
317
- }
package/tsconfig.json DELETED
@@ -1,16 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "outDir": "dist",
7
- "rootDir": "src",
8
- "strict": true,
9
- "skipLibCheck": true,
10
- "declaration": true,
11
- "declarationMap": true,
12
- "sourceMap": true,
13
- "esModuleInterop": true
14
- },
15
- "include": ["src/**/*.ts"]
16
- }