dero-mcp-server 0.1.1 → 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 +79 -6
- package/data/docs-index.json +5702 -0
- 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/docs-parse.d.ts +30 -0
- package/dist/docs-parse.d.ts.map +1 -0
- package/dist/docs-parse.js +147 -0
- package/dist/docs-parse.js.map +1 -0
- package/dist/docs.d.ts +101 -0
- package/dist/docs.d.ts.map +1 -0
- package/dist/docs.js +172 -0
- package/dist/docs.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +417 -100
- 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 +15 -3
- package/.github/workflows/ci.yml +0 -62
- package/docs/example-agent-flows.md +0 -236
- package/docs/mcp-agent-ready-evidence.md +0 -108
- package/glama.json +0 -6
- package/scripts/doctor.sh +0 -85
- package/scripts/flow-test.ts +0 -257
- package/scripts/mcp-smoke-probes.ts +0 -168
- package/server.json +0 -23
- package/src/index.ts +0 -30
- package/src/rpc.ts +0 -60
- package/src/server.ts +0 -636
- package/tsconfig.json +0 -16
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env npx tsx
|
|
2
|
-
/**
|
|
3
|
-
* Lightweight MCP smoke probes for local stdio server contract checks.
|
|
4
|
-
*
|
|
5
|
-
* Verifies:
|
|
6
|
-
* - tools/list count + name parity
|
|
7
|
-
* - resources/list count + URI parity
|
|
8
|
-
* - prompts/list count + name parity
|
|
9
|
-
* - prompts/get returns usable messages
|
|
10
|
-
* - structured tool error payload shape on execution failure
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
|
|
14
|
-
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'
|
|
15
|
-
|
|
16
|
-
const DEFAULT_DAEMON_URL = 'http://82.65.143.182:10102'
|
|
17
|
-
const NAME_REGISTRY_SCID = '0000000000000000000000000000000000000000000000000000000000000001'
|
|
18
|
-
|
|
19
|
-
const EXPECTED_TOOLS = [
|
|
20
|
-
'dero_daemon_ping',
|
|
21
|
-
'dero_daemon_echo',
|
|
22
|
-
'dero_get_info',
|
|
23
|
-
'dero_get_height',
|
|
24
|
-
'dero_get_block_count',
|
|
25
|
-
'dero_get_last_block_header',
|
|
26
|
-
'dero_get_block',
|
|
27
|
-
'dero_get_block_header_by_topo_height',
|
|
28
|
-
'dero_get_block_header_by_hash',
|
|
29
|
-
'dero_get_tx_pool',
|
|
30
|
-
'dero_get_random_address',
|
|
31
|
-
'dero_get_transaction',
|
|
32
|
-
'dero_get_encrypted_balance',
|
|
33
|
-
'dero_get_sc',
|
|
34
|
-
'dero_get_gas_estimate',
|
|
35
|
-
'dero_name_to_address',
|
|
36
|
-
'dero_get_block_template',
|
|
37
|
-
] as const
|
|
38
|
-
|
|
39
|
-
const EXPECTED_RESOURCES = [
|
|
40
|
-
'dero://mcp/server-info',
|
|
41
|
-
'dero://mcp/safety-boundary',
|
|
42
|
-
'dero://mcp/example-flows',
|
|
43
|
-
] as const
|
|
44
|
-
|
|
45
|
-
const EXPECTED_PROMPTS = [
|
|
46
|
-
'network_health_check',
|
|
47
|
-
'inspect_smart_contract',
|
|
48
|
-
'trace_transaction',
|
|
49
|
-
] as const
|
|
50
|
-
|
|
51
|
-
function parseArgs(argv: string[]) {
|
|
52
|
-
let daemonUrl = process.env.DERO_DAEMON_URL ?? DEFAULT_DAEMON_URL
|
|
53
|
-
for (let i = 0; i < argv.length; i++) {
|
|
54
|
-
const arg = argv[i]
|
|
55
|
-
if ((arg === '--daemon-url' || arg === '--url') && argv[i + 1]) {
|
|
56
|
-
daemonUrl = argv[++i]
|
|
57
|
-
} else if (arg.startsWith('--daemon-url=')) {
|
|
58
|
-
daemonUrl = arg.slice('--daemon-url='.length)
|
|
59
|
-
} else if (arg.startsWith('--url=')) {
|
|
60
|
-
daemonUrl = arg.slice('--url='.length)
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return daemonUrl.replace(/\/$/, '')
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function assertSortedEqual(actual: string[], expected: readonly string[], label: string) {
|
|
67
|
-
const a = [...actual].sort()
|
|
68
|
-
const e = [...expected].sort()
|
|
69
|
-
if (a.length !== e.length) {
|
|
70
|
-
throw new Error(`${label}: expected ${e.length}, got ${a.length}`)
|
|
71
|
-
}
|
|
72
|
-
for (let i = 0; i < e.length; i++) {
|
|
73
|
-
if (a[i] !== e[i]) {
|
|
74
|
-
throw new Error(`${label} mismatch at ${i}: expected ${e[i]}, got ${a[i]}`)
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function parseFirstTextJson(result: { content: Array<{ type: string; text?: string }> }): unknown {
|
|
80
|
-
const textEntry = result.content.find((c) => c.type === 'text' && typeof c.text === 'string')
|
|
81
|
-
if (!textEntry?.text) {
|
|
82
|
-
throw new Error('Tool result missing text content')
|
|
83
|
-
}
|
|
84
|
-
try {
|
|
85
|
-
return JSON.parse(textEntry.text)
|
|
86
|
-
} catch {
|
|
87
|
-
throw new Error('Tool text content is not valid JSON')
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
async function main() {
|
|
92
|
-
const daemonUrl = parseArgs(process.argv.slice(2))
|
|
93
|
-
console.log(`[smoke:mcp] daemon=${daemonUrl}`)
|
|
94
|
-
console.log('================================')
|
|
95
|
-
|
|
96
|
-
const transport = new StdioClientTransport({
|
|
97
|
-
command: 'node',
|
|
98
|
-
args: ['dist/index.js'],
|
|
99
|
-
env: {
|
|
100
|
-
...process.env,
|
|
101
|
-
DERO_DAEMON_URL: daemonUrl,
|
|
102
|
-
} as Record<string, string>,
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
const client = new Client({
|
|
106
|
-
name: 'dero-mcp-smoke-probes',
|
|
107
|
-
version: '1.0.0',
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
try {
|
|
111
|
-
await client.connect(transport)
|
|
112
|
-
|
|
113
|
-
const tools = await client.listTools()
|
|
114
|
-
const toolNames = tools.tools.map((t) => t.name)
|
|
115
|
-
assertSortedEqual(toolNames, EXPECTED_TOOLS, 'tools/list')
|
|
116
|
-
console.log(`OK tools/list ${toolNames.length} tools`)
|
|
117
|
-
|
|
118
|
-
const resources = await client.listResources()
|
|
119
|
-
const resourceUris = resources.resources.map((r) => r.uri)
|
|
120
|
-
assertSortedEqual(resourceUris, EXPECTED_RESOURCES, 'resources/list')
|
|
121
|
-
console.log(`OK resources/list ${resourceUris.length} resources`)
|
|
122
|
-
|
|
123
|
-
const prompts = await client.listPrompts()
|
|
124
|
-
const promptNames = prompts.prompts.map((p) => p.name)
|
|
125
|
-
assertSortedEqual(promptNames, EXPECTED_PROMPTS, 'prompts/list')
|
|
126
|
-
console.log(`OK prompts/list ${promptNames.length} prompts`)
|
|
127
|
-
|
|
128
|
-
const prompt = await client.getPrompt({
|
|
129
|
-
name: 'inspect_smart_contract',
|
|
130
|
-
arguments: { scid: NAME_REGISTRY_SCID },
|
|
131
|
-
})
|
|
132
|
-
if (!prompt.messages?.length) {
|
|
133
|
-
throw new Error('prompts/get returned zero messages')
|
|
134
|
-
}
|
|
135
|
-
console.log('OK prompts/get inspect_smart_contract')
|
|
136
|
-
|
|
137
|
-
const structuredErrorProbe = await client.callTool({
|
|
138
|
-
name: 'dero_get_block',
|
|
139
|
-
arguments: {},
|
|
140
|
-
})
|
|
141
|
-
const errorPayload = parseFirstTextJson(structuredErrorProbe as { content: Array<{ type: string; text?: string }> }) as {
|
|
142
|
-
ok?: boolean
|
|
143
|
-
_meta?: { error?: { code?: string; hint?: string; retryable?: boolean } }
|
|
144
|
-
}
|
|
145
|
-
if (
|
|
146
|
-
errorPayload.ok !== false ||
|
|
147
|
-
!errorPayload._meta?.error?.code ||
|
|
148
|
-
typeof errorPayload._meta.error.hint !== 'string' ||
|
|
149
|
-
typeof errorPayload._meta.error.retryable !== 'boolean'
|
|
150
|
-
) {
|
|
151
|
-
throw new Error('structured error probe did not return expected _meta.error shape')
|
|
152
|
-
}
|
|
153
|
-
console.log('OK tools/call structured _meta.error probe')
|
|
154
|
-
|
|
155
|
-
console.log('')
|
|
156
|
-
console.log('All MCP smoke probes passed.')
|
|
157
|
-
process.exit(0)
|
|
158
|
-
} catch (error) {
|
|
159
|
-
console.error('')
|
|
160
|
-
console.error('[smoke:mcp] FAIL:', error instanceof Error ? error.message : error)
|
|
161
|
-
process.exit(1)
|
|
162
|
-
} finally {
|
|
163
|
-
await client.close()
|
|
164
|
-
await transport.close()
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
main()
|
package/server.json
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
-
"name": "io.github.DHEBP/dero-mcp-server",
|
|
4
|
-
"title": "DERO MCP Server",
|
|
5
|
-
"description": "Read-only DERO daemon MCP tools for chain inspection and analysis.",
|
|
6
|
-
"version": "0.1.1",
|
|
7
|
-
"websiteUrl": "https://derod.org",
|
|
8
|
-
"repository": {
|
|
9
|
-
"url": "https://github.com/DHEBP/dero-mcp-server",
|
|
10
|
-
"source": "github"
|
|
11
|
-
},
|
|
12
|
-
"packages": [
|
|
13
|
-
{
|
|
14
|
-
"registryType": "npm",
|
|
15
|
-
"registryBaseUrl": "https://registry.npmjs.org",
|
|
16
|
-
"identifier": "dero-mcp-server",
|
|
17
|
-
"version": "0.1.1",
|
|
18
|
-
"transport": {
|
|
19
|
-
"type": "stdio"
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
]
|
|
23
|
-
}
|
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
|
-
}
|