@oracle-agent/oracle 0.3.3 → 0.3.5
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 +5 -12
- package/SETUP.md +1 -35
- package/artifacts/specialist-packs/oracle-full-crypto.json +9 -31
- package/bin/oracle-data-mcp.mjs +74 -247
- package/bin/oracle-init.mjs +27 -100
- package/docs/profiles.md +7 -27
- package/package.json +1 -1
- package/profiles/_template/SOUL.md +1 -8
- package/profiles/oracle/SOUL.md +1 -8
- package/profiles/oracle/profile.json +2 -5
- package/profiles/protocol-builder/SOUL.md +6 -13
- package/profiles/protocol-builder/profile.json +1 -3
- package/src/cards.mjs +369 -0
- package/src/data/catalog.mjs +3 -27
- package/src/data/desk-data.mjs +4 -34
- package/src/data/providers/magiceden-sol.mjs +2 -21
- package/src/data/providers/opensea-nft.mjs +0 -272
- package/src/data/providers/satflow.mjs +0 -1
- package/src/data/providers/uniswap-v3.mjs +17 -1
- package/src/exec-policy.mjs +0 -5
- package/src/gmx-attestation.mjs +0 -1
- package/src/router/prepare-route.mjs +23 -0
- package/src/router/route-sources.mjs +37 -0
- package/src/scanner/chains.config.mjs +48 -1
- package/src/vault-attestation.mjs +0 -1
- package/skills/balance/SKILL.md +0 -176
- package/skills/oracle-multichain-nft-launch/SKILL.md +0 -338
- package/skills/oracle-multichain-token-launch/SKILL.md +0 -300
- package/src/data/providers/nft-gallery.mjs +0 -163
- package/src/data/providers/nft-portfolio.mjs +0 -494
- package/src/data/providers/portfolio-history.mjs +0 -394
- package/src/data/providers/portfolio.mjs +0 -594
package/README.md
CHANGED
|
@@ -199,14 +199,10 @@ Oracle's default pack is deliberately broad but disarmed:
|
|
|
199
199
|
|
|
200
200
|
- **Trader** — best-execution route comparison, quote/prepare, simulation, and
|
|
201
201
|
receipt checks; no set-and-forget custody.
|
|
202
|
-
- **Builder**
|
|
203
|
-
|
|
204
|
-
Unsupported chain adapters fail closed instead of pretending one deploy fits all.
|
|
202
|
+
- **Builder** — protocol, NFT, gacha, DEX, and launchpad scaffolds with unsigned
|
|
203
|
+
deploy/admin transactions.
|
|
205
204
|
- **Analyzer** — token, contract, venue, portfolio, market, and risk research with
|
|
206
|
-
evidence labels.
|
|
207
|
-
across every configured EVM chain plus Solana, Bitcoin, Hyperliquid, and
|
|
208
|
-
discoverable NFTs. Profile-local observations power history and SVG value graphs;
|
|
209
|
-
unavailable values stay null instead of becoming fake zeroes.
|
|
205
|
+
evidence labels.
|
|
210
206
|
- **On-chain scanner** — chain-config scanners for tokens, pools, launches,
|
|
211
207
|
risk, exits, and smart-wallet boards.
|
|
212
208
|
- **Meme-token sniper** — fast launch/liquidity monitoring across configured
|
|
@@ -277,8 +273,7 @@ Oracle is a library. Drive it with Claude, GPT, Gemini, Grok, a local model, or
|
|
|
277
273
|
a plain script — the tools are ordinary functions plus an MCP server:
|
|
278
274
|
|
|
279
275
|
```bash
|
|
280
|
-
npx oracle-data
|
|
281
|
-
npx oracle-data-mcp # MCP stdio server (any MCP client); needs oracle-data up
|
|
276
|
+
npx oracle-data-mcp # works with any MCP client
|
|
282
277
|
```
|
|
283
278
|
|
|
284
279
|
It is better under [Hermes](https://claude-code.nousresearch.com/docs), because
|
|
@@ -299,9 +294,7 @@ Oracle ships an installable 8-lane mesh for Hermes:
|
|
|
299
294
|
|
|
300
295
|
```bash
|
|
301
296
|
oracle-init # dry run -- shows exactly what it would do
|
|
302
|
-
oracle-init --apply # create profiles, install SOULs + skills, wire MCP
|
|
303
|
-
npx oracle-data # keep running — MCP tools call the local read plane on :8787
|
|
304
|
-
hermes -p oracle chat # router lane; inherits whatever model Hermes already uses
|
|
297
|
+
oracle-init --apply # create profiles, install SOULs + skills, wire MCP
|
|
305
298
|
```
|
|
306
299
|
|
|
307
300
|
Lanes: `oracle` (router), `polymarket-agent`, `hyperliquid-agent`,
|
package/SETUP.md
CHANGED
|
@@ -103,39 +103,6 @@ export BASE_RPC_URL=https://...
|
|
|
103
103
|
export SOLANA_RPC_URL=https://...
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
-
`/balance` and plain `balance` need only public addresses. Set any families the
|
|
107
|
-
wallet uses, then start `oracle-data` from the same environment:
|
|
108
|
-
|
|
109
|
-
```bash
|
|
110
|
-
export ORACLE_EVM_ADDRESS=0x...
|
|
111
|
-
export ORACLE_SOLANA_ADDRESS=...
|
|
112
|
-
export ORACLE_BITCOIN_ADDRESS=bc1...
|
|
113
|
-
export ORACLE_HYPERLIQUID_ADDRESS=0x... # optional, defaults to EVM address
|
|
114
|
-
npx oracle-data
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
Missing address families are reported as `not-configured`, failed providers as
|
|
118
|
-
`unavailable`, and unsupported chain families as `unsupported`. They are never
|
|
119
|
-
reported as zero. EVM native balances cover every configured EVM chain. Solana
|
|
120
|
-
adds SPL and Token-2022 accounts, Bitcoin adds Runes and inscriptions when an
|
|
121
|
-
address indexer is configured, Hyperliquid adds spot and perp account state, and
|
|
122
|
-
NFT inventory adds OpenSea-supported EVM/Solana collections plus Bitcoin
|
|
123
|
-
inscriptions where owner indexers are available.
|
|
124
|
-
|
|
125
|
-
Every `portfolio_snapshot` call appends one compact observation to
|
|
126
|
-
`$HERMES_HOME/state/oracle/portfolio-history.jsonl` with mode `0600`. It stores a
|
|
127
|
-
public-address fingerprint, coverage, value breakdown, and warnings, not keys,
|
|
128
|
-
prepared transactions, or raw NFT metadata. Override the internal location only
|
|
129
|
-
when needed:
|
|
130
|
-
|
|
131
|
-
```bash
|
|
132
|
-
export ORACLE_PORTFOLIO_HISTORY_FILE="$HOME/.local/state/oracle/portfolio-history.jsonl"
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
`portfolio_history` reads those observations. `portfolio_value_graph` returns an
|
|
136
|
-
SVG chart of known priced value and omits unavailable observations instead of
|
|
137
|
-
plotting them as zero.
|
|
138
|
-
|
|
139
106
|
---
|
|
140
107
|
|
|
141
108
|
## Where keys are stored
|
|
@@ -235,8 +202,7 @@ Gemini, Grok, a local Llama, or your own script at it. The tools are plain
|
|
|
235
202
|
functions and an MCP server.
|
|
236
203
|
|
|
237
204
|
```bash
|
|
238
|
-
npx oracle-data
|
|
239
|
-
npx oracle-data-mcp # MCP stdio server, works with any MCP client (needs oracle-data up)
|
|
205
|
+
npx oracle-data-mcp # MCP stdio server, works with any MCP client
|
|
240
206
|
```
|
|
241
207
|
|
|
242
208
|
**Why [Hermes](https://claude-code.nousresearch.com/docs) is the better host: per-profile routing.**
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "oracle-full-crypto",
|
|
3
3
|
"name": "Oracle Full Crypto Pack",
|
|
4
|
-
"version":
|
|
4
|
+
"version": 1,
|
|
5
5
|
"audience": "public-users",
|
|
6
|
-
"description": "Default capability set for public Oracle: trader, builder, analyzer,
|
|
6
|
+
"description": "Default capability set for public Oracle: trader, builder, analyzer, multichain scanner, meme-token sniping, RFQ routing, tokenized-asset buys, NFT mint gas-war limits, Solana, Bitcoin inscriptions, NFT/gacha/DEX launches, and protocol build under bound grants. User signs everything.",
|
|
7
7
|
"posture": {
|
|
8
8
|
"default": "DISARMED",
|
|
9
9
|
"custody": "self-custodial",
|
|
@@ -62,8 +62,6 @@
|
|
|
62
62
|
"oracle-protocol-builder",
|
|
63
63
|
"oracle-contract-research",
|
|
64
64
|
"oracle-protocol-security",
|
|
65
|
-
"oracle-multichain-token-launch",
|
|
66
|
-
"oracle-multichain-nft-launch",
|
|
67
65
|
"oracle-nft-gacha-launch",
|
|
68
66
|
"oracle-dex-launch",
|
|
69
67
|
"protocol-api-key-integration",
|
|
@@ -82,8 +80,6 @@
|
|
|
82
80
|
"oracle-nft-gacha-launch",
|
|
83
81
|
"oracle-dex-launch",
|
|
84
82
|
"oracle-protocol-security",
|
|
85
|
-
"oracle-multichain-token-launch",
|
|
86
|
-
"oracle-multichain-nft-launch",
|
|
87
83
|
"oracle-nft-mint-gas-war"
|
|
88
84
|
],
|
|
89
85
|
"analysis": [
|
|
@@ -92,7 +88,6 @@
|
|
|
92
88
|
"oracle-smart-wallet-scanner",
|
|
93
89
|
"oracle-meme-token-sniper",
|
|
94
90
|
"oracle-best-execution",
|
|
95
|
-
"balance",
|
|
96
91
|
"oracle-chain-graphs-telegram-cards",
|
|
97
92
|
"oracle-rfq-tokenized-assets"
|
|
98
93
|
]
|
|
@@ -114,9 +109,7 @@
|
|
|
114
109
|
"write-deploy-scripts",
|
|
115
110
|
"prepare-unsigned-deploy-txs",
|
|
116
111
|
"security-checklist",
|
|
117
|
-
"launchpad-public-surface"
|
|
118
|
-
"multichain-token-launch",
|
|
119
|
-
"multichain-nft-collection-launch"
|
|
112
|
+
"launchpad-public-surface"
|
|
120
113
|
],
|
|
121
114
|
"user_signs": true,
|
|
122
115
|
"grant_actions_example": [
|
|
@@ -126,7 +119,7 @@
|
|
|
126
119
|
"prepare:verify"
|
|
127
120
|
],
|
|
128
121
|
"targets": "user-provided factory/router/implementations only unless added to destination allowlist",
|
|
129
|
-
"copy": "Oracle can
|
|
122
|
+
"copy": "Oracle can design and prepare protocol, NFT/gacha, and DEX launches. You still sign. Grants bind chain, spend, and destinations."
|
|
130
123
|
},
|
|
131
124
|
"profiles": [
|
|
132
125
|
{
|
|
@@ -208,12 +201,10 @@
|
|
|
208
201
|
"oracle-protocol-security",
|
|
209
202
|
"oracle-nft-gacha-launch",
|
|
210
203
|
"oracle-dex-launch",
|
|
211
|
-
"oracle-receipts"
|
|
212
|
-
"oracle-multichain-token-launch",
|
|
213
|
-
"oracle-multichain-nft-launch"
|
|
204
|
+
"oracle-receipts"
|
|
214
205
|
],
|
|
215
206
|
"label": "protocol builder",
|
|
216
|
-
"description": "
|
|
207
|
+
"description": "Scaffold, review, and prepare deploys for NFT, gacha, DEX, and protocol launches."
|
|
217
208
|
}
|
|
218
209
|
],
|
|
219
210
|
"default_tools": {
|
|
@@ -225,14 +216,7 @@
|
|
|
225
216
|
"btc_health",
|
|
226
217
|
"btc_fees",
|
|
227
218
|
"btc_inscription_info",
|
|
228
|
-
"solana_balance"
|
|
229
|
-
"portfolio_balance",
|
|
230
|
-
"portfolio_snapshot",
|
|
231
|
-
"portfolio_history",
|
|
232
|
-
"portfolio_value_graph",
|
|
233
|
-
"nft_inventory",
|
|
234
|
-
"nft_gallery",
|
|
235
|
-
"nft_pnl"
|
|
219
|
+
"solana_balance"
|
|
236
220
|
],
|
|
237
221
|
"prepare": [
|
|
238
222
|
"evm_prepare",
|
|
@@ -240,8 +224,7 @@
|
|
|
240
224
|
"evm_protocol_quote",
|
|
241
225
|
"jupiter_quote",
|
|
242
226
|
"jupiter_prepare",
|
|
243
|
-
"bitcoin_inscribe_prepare"
|
|
244
|
-
"nft_prepare_list"
|
|
227
|
+
"bitcoin_inscribe_prepare"
|
|
245
228
|
],
|
|
246
229
|
"never_public_default": [
|
|
247
230
|
"evm_sign",
|
|
@@ -285,11 +268,6 @@
|
|
|
285
268
|
"solana-nft-marketplace",
|
|
286
269
|
"solana-nft-mint",
|
|
287
270
|
"hypercore-hype-staking",
|
|
288
|
-
"hypercore-validator-delegation"
|
|
289
|
-
"multichain-token-launch",
|
|
290
|
-
"multichain-nft-collection-launch",
|
|
291
|
-
"multichain-balance",
|
|
292
|
-
"nft-inventory-gallery-pnl",
|
|
293
|
-
"portfolio-balance-history-graph"
|
|
271
|
+
"hypercore-validator-delegation"
|
|
294
272
|
]
|
|
295
273
|
}
|
package/bin/oracle-data-mcp.mjs
CHANGED
|
@@ -2,17 +2,14 @@
|
|
|
2
2
|
// Oracle data MCP — read-only multichain market/discovery plane.
|
|
3
3
|
// Loaded by an Oracle/Hermes profile. Never expose signing or broadcast here.
|
|
4
4
|
//
|
|
5
|
-
// Every tool is a READ
|
|
6
|
-
//
|
|
7
|
-
// which is unauthenticated on loopback.
|
|
5
|
+
// Every tool is a READ. No key, no signing, no broadcast. Routes to the Oracle
|
|
6
|
+
// data server read plane (/data/call), which is unauthenticated on loopback.
|
|
8
7
|
// This is the "scan all public APIs" surface: DeFiLlama protocol TVL, DexScreener
|
|
9
8
|
// token/search, LI.FI + Uniswap quotes, multi-chain RPC balances/blocks.
|
|
10
9
|
//
|
|
11
10
|
// Mirrors the Oracle exec MCP stdio JSON-RPC shape.
|
|
12
11
|
|
|
13
|
-
import fs from "node:fs";
|
|
14
12
|
import process from "node:process";
|
|
15
|
-
import { fileURLToPath } from "node:url";
|
|
16
13
|
import { env } from "../src/oracle-env.mjs";
|
|
17
14
|
|
|
18
15
|
const DATA_URL = (env("ORACLE_DATA_URL", "MAD_DESK_URL", "http://127.0.0.1:8787")).replace(/\/$/, "");
|
|
@@ -99,201 +96,6 @@ const tools = [
|
|
|
99
96
|
},
|
|
100
97
|
},
|
|
101
98
|
},
|
|
102
|
-
{
|
|
103
|
-
name: "portfolio_balance",
|
|
104
|
-
description:
|
|
105
|
-
"Read-only balance aggregation across every configured EVM chain plus Solana, Bitcoin, and Hyperliquid. Reports native assets, supported token/collectible reads, failed or missing providers, unverified assets, timestamps, and known priced value without pretending it is a complete total. Public addresses only; never signs or broadcasts.",
|
|
106
|
-
inputSchema: {
|
|
107
|
-
type: "object",
|
|
108
|
-
properties: {
|
|
109
|
-
addresses: {
|
|
110
|
-
type: "object",
|
|
111
|
-
description: "Optional public wallet addresses by family. Omit configured families to use ORACLE_*_ADDRESS defaults.",
|
|
112
|
-
properties: {
|
|
113
|
-
evm: { type: "string" },
|
|
114
|
-
solana: { type: "string" },
|
|
115
|
-
bitcoin: { type: "string" },
|
|
116
|
-
hyperliquid: { type: "string" },
|
|
117
|
-
},
|
|
118
|
-
},
|
|
119
|
-
evmChainIds: {
|
|
120
|
-
type: "array",
|
|
121
|
-
items: { type: "number" },
|
|
122
|
-
description: "Optional EVM chain subset. Default is every configured EVM chain.",
|
|
123
|
-
},
|
|
124
|
-
includeTokens: { type: "boolean", description: "Include supported token reads. Default true." },
|
|
125
|
-
includeCollectibles: { type: "boolean", description: "Include supported collectible reads. Default true." },
|
|
126
|
-
includePrices: { type: "boolean", description: "Fetch current DeFiLlama native prices. Default true." },
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
},
|
|
130
|
-
{
|
|
131
|
-
name: "portfolio_snapshot",
|
|
132
|
-
description:
|
|
133
|
-
"Query the current multichain balance and NFT inventory, then append one compact profile-local observation for historical tracking. Stores public-address fingerprint, coverage, breakdown, and known priced value only; no keys, signatures, executable payloads, or raw NFT metadata. Returns the live balance, NFT result, and recorded snapshot.",
|
|
134
|
-
inputSchema: {
|
|
135
|
-
type: "object",
|
|
136
|
-
properties: {
|
|
137
|
-
addresses: {
|
|
138
|
-
type: "object",
|
|
139
|
-
properties: {
|
|
140
|
-
evm: { type: "string" },
|
|
141
|
-
solana: { type: "string" },
|
|
142
|
-
bitcoin: { type: "string" },
|
|
143
|
-
hyperliquid: { type: "string" },
|
|
144
|
-
},
|
|
145
|
-
},
|
|
146
|
-
evmChainIds: { type: "array", items: { type: "number" } },
|
|
147
|
-
includeTokens: { type: "boolean" },
|
|
148
|
-
includeCollectibles: { type: "boolean" },
|
|
149
|
-
includePrices: { type: "boolean" },
|
|
150
|
-
includeNfts: { type: "boolean", description: "Include provider-estimated NFT value. Default true." },
|
|
151
|
-
},
|
|
152
|
-
},
|
|
153
|
-
},
|
|
154
|
-
{
|
|
155
|
-
name: "portfolio_history",
|
|
156
|
-
description:
|
|
157
|
-
"Read profile-local portfolio observations in a time range. Unknown or unavailable values remain null and are never converted to zero. Defaults to the configured public-address portfolio fingerprint.",
|
|
158
|
-
inputSchema: {
|
|
159
|
-
type: "object",
|
|
160
|
-
properties: {
|
|
161
|
-
addresses: {
|
|
162
|
-
type: "object",
|
|
163
|
-
properties: {
|
|
164
|
-
evm: { type: "string" },
|
|
165
|
-
solana: { type: "string" },
|
|
166
|
-
bitcoin: { type: "string" },
|
|
167
|
-
hyperliquid: { type: "string" },
|
|
168
|
-
},
|
|
169
|
-
},
|
|
170
|
-
portfolioId: { type: "string" },
|
|
171
|
-
allPortfolios: { type: "boolean", description: "Return observations for every public-address fingerprint in this profile." },
|
|
172
|
-
since: { type: "string", description: "Inclusive ISO 8601 lower bound." },
|
|
173
|
-
until: { type: "string", description: "Inclusive ISO 8601 upper bound." },
|
|
174
|
-
limit: { type: "number", description: "1-1000, default 100." },
|
|
175
|
-
order: { type: "string", enum: ["asc", "desc"] },
|
|
176
|
-
},
|
|
177
|
-
},
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
name: "portfolio_value_graph",
|
|
181
|
-
description:
|
|
182
|
-
"Render a static SVG line graph from profile-local portfolio snapshots. Plots known priced value only, labels incomplete coverage, includes provider-estimated NFT values when recorded, and omits unavailable observations instead of plotting fake zeroes.",
|
|
183
|
-
inputSchema: {
|
|
184
|
-
type: "object",
|
|
185
|
-
properties: {
|
|
186
|
-
addresses: {
|
|
187
|
-
type: "object",
|
|
188
|
-
properties: {
|
|
189
|
-
evm: { type: "string" },
|
|
190
|
-
solana: { type: "string" },
|
|
191
|
-
bitcoin: { type: "string" },
|
|
192
|
-
hyperliquid: { type: "string" },
|
|
193
|
-
},
|
|
194
|
-
},
|
|
195
|
-
portfolioId: { type: "string" },
|
|
196
|
-
allPortfolios: { type: "boolean" },
|
|
197
|
-
since: { type: "string" },
|
|
198
|
-
until: { type: "string" },
|
|
199
|
-
limit: { type: "number", description: "Historical observations to consider, max 1000." },
|
|
200
|
-
maxPoints: { type: "number", description: "Rendered point cap, 2-500, default 200." },
|
|
201
|
-
},
|
|
202
|
-
},
|
|
203
|
-
},
|
|
204
|
-
{
|
|
205
|
-
name: "nft_inventory",
|
|
206
|
-
description:
|
|
207
|
-
"Read all discoverable NFTs for configured EVM, Solana, and Bitcoin addresses. Returns normalized assets, image URLs, estimated values, per-chain partial failures, flagged/NSFW items separated from visible items, listing coverage, and explicit unsupported chains. Read-only.",
|
|
208
|
-
inputSchema: {
|
|
209
|
-
type: "object",
|
|
210
|
-
properties: {
|
|
211
|
-
addresses: {
|
|
212
|
-
type: "object",
|
|
213
|
-
properties: {
|
|
214
|
-
evm: { type: "string" },
|
|
215
|
-
solana: { type: "string" },
|
|
216
|
-
bitcoin: { type: "string" },
|
|
217
|
-
},
|
|
218
|
-
},
|
|
219
|
-
chains: { type: "array", items: { type: "string" }, description: "Optional chain slug subset." },
|
|
220
|
-
pageSize: { type: "number", description: "OpenSea items per page, max 200." },
|
|
221
|
-
maxPages: { type: "number", description: "Maximum pages per chain, max 25." },
|
|
222
|
-
bitcoinLimit: { type: "number" },
|
|
223
|
-
includePnl: { type: "boolean", description: "Include OpenSea-indexed account PnL when available. Default true." },
|
|
224
|
-
costBasis: { type: "object", description: "Optional user-supplied assetKey -> {costUsd,feesUsd,acquiredAt,source}. Missing basis stays unavailable, never zero." },
|
|
225
|
-
},
|
|
226
|
-
},
|
|
227
|
-
},
|
|
228
|
-
{
|
|
229
|
-
name: "nft_gallery",
|
|
230
|
-
description:
|
|
231
|
-
"Generate one static NFT contact-sheet image page from wallet inventory or supplied normalized items. External metadata is never rendered as HTML; only allowlisted JPEG/PNG/WebP bytes are embedded and unsafe or missing media becomes a placeholder.",
|
|
232
|
-
inputSchema: {
|
|
233
|
-
type: "object",
|
|
234
|
-
properties: {
|
|
235
|
-
addresses: {
|
|
236
|
-
type: "object",
|
|
237
|
-
properties: { evm: { type: "string" }, solana: { type: "string" }, bitcoin: { type: "string" } },
|
|
238
|
-
},
|
|
239
|
-
chains: { type: "array", items: { type: "string" } },
|
|
240
|
-
items: { type: "array", items: { type: "object" }, description: "Optional normalized inventory items. Omit to scan configured wallets." },
|
|
241
|
-
page: { type: "number", description: "1-based gallery page." },
|
|
242
|
-
pageSize: { type: "number", description: "1-12 items per page." },
|
|
243
|
-
},
|
|
244
|
-
},
|
|
245
|
-
},
|
|
246
|
-
{
|
|
247
|
-
name: "nft_pnl",
|
|
248
|
-
description:
|
|
249
|
-
"NFT PnL with explicit methodology. Returns OpenSea-indexed account-level PnL where available plus per-item unrealized estimates only when both user-supplied acquisition cost and provider estimated current value exist. Missing history is unavailable, never zero.",
|
|
250
|
-
inputSchema: {
|
|
251
|
-
type: "object",
|
|
252
|
-
properties: {
|
|
253
|
-
addresses: {
|
|
254
|
-
type: "object",
|
|
255
|
-
properties: { evm: { type: "string" }, solana: { type: "string" }, bitcoin: { type: "string" } },
|
|
256
|
-
},
|
|
257
|
-
chains: { type: "array", items: { type: "string" } },
|
|
258
|
-
costBasis: { type: "object" },
|
|
259
|
-
},
|
|
260
|
-
},
|
|
261
|
-
},
|
|
262
|
-
{
|
|
263
|
-
name: "nft_prepare_list",
|
|
264
|
-
description:
|
|
265
|
-
"Prepare an NFT listing for OpenSea EVM, Magic Eden Solana, or Satflow Bitcoin after explicit user review. Requires userConfirmed=true plus asset, marketplace, price, currency where applicable, and future expiry. Returns approval/signing actions, an unsigned Solana transaction, or an unsigned Bitcoin PSBT. Never signs, submits, or broadcasts.",
|
|
266
|
-
inputSchema: {
|
|
267
|
-
type: "object",
|
|
268
|
-
required: ["marketplace", "userConfirmed"],
|
|
269
|
-
properties: {
|
|
270
|
-
marketplace: { type: "string", enum: ["opensea", "magiceden", "magiceden-sol", "satflow"] },
|
|
271
|
-
userConfirmed: { type: "boolean" },
|
|
272
|
-
chain: { type: "string" },
|
|
273
|
-
seller: { type: "string" },
|
|
274
|
-
contract: { type: "string" },
|
|
275
|
-
tokenId: { type: "string" },
|
|
276
|
-
quantity: { type: "number" },
|
|
277
|
-
priceAmount: { type: "string" },
|
|
278
|
-
currency: { type: "string" },
|
|
279
|
-
endTime: { type: "string" },
|
|
280
|
-
useCreatorFee: { type: "boolean" },
|
|
281
|
-
taker: { type: "string" },
|
|
282
|
-
tokenMint: { type: "string" },
|
|
283
|
-
tokenATA: { type: "string" },
|
|
284
|
-
auctionHouse: { type: "string" },
|
|
285
|
-
priceSol: { type: "string" },
|
|
286
|
-
expiry: { description: "Future ISO 8601 timestamp or Unix seconds." },
|
|
287
|
-
inscriptionId: { type: "string" },
|
|
288
|
-
runesOutput: { type: "string" },
|
|
289
|
-
ordAddress: { type: "string" },
|
|
290
|
-
receiveAddress: { type: "string" },
|
|
291
|
-
priceSats: { type: "string" },
|
|
292
|
-
tapKey: { type: "string" },
|
|
293
|
-
collectionSlug: { type: "string" },
|
|
294
|
-
},
|
|
295
|
-
},
|
|
296
|
-
},
|
|
297
99
|
{
|
|
298
100
|
name: "rpc_balance",
|
|
299
101
|
description:
|
|
@@ -502,6 +304,43 @@ const tools = [
|
|
|
502
304
|
properties: { collectionIds: { type: "string" }, limit: { type: "number" } },
|
|
503
305
|
},
|
|
504
306
|
},
|
|
307
|
+
{
|
|
308
|
+
name: "scanner_coverage",
|
|
309
|
+
description:
|
|
310
|
+
"Which chains the on-chain scanner supports and which capabilities each one has " +
|
|
311
|
+
"(blockNumber, nativeBalance, tokenBalance, resolveToken, resolvePools, scanBlocks, " +
|
|
312
|
+
"scoreRisk, quote, sellSimulation, prepareUnsignedTx). Read-only.",
|
|
313
|
+
inputSchema: { type: "object", properties: {} },
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
name: "scan_token",
|
|
317
|
+
description:
|
|
318
|
+
"Resolve a token on an EVM chain: metadata, its pools, and a risk score. Works on " +
|
|
319
|
+
"chains no indexer covers because it reads the chain directly. Read-only.",
|
|
320
|
+
inputSchema: {
|
|
321
|
+
type: "object",
|
|
322
|
+
properties: {
|
|
323
|
+
chainId: { type: "number", description: "EVM chain id, e.g. 4663 for Robinhood Chain" },
|
|
324
|
+
token: { type: "string", description: "token contract address" },
|
|
325
|
+
},
|
|
326
|
+
required: ["chainId", "token"],
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
name: "wallet_balances",
|
|
331
|
+
description:
|
|
332
|
+
"Native balance for an address on an EVM chain, plus one token balance when a token " +
|
|
333
|
+
"address is supplied. Read-only; never signs.",
|
|
334
|
+
inputSchema: {
|
|
335
|
+
type: "object",
|
|
336
|
+
properties: {
|
|
337
|
+
chainId: { type: "number" },
|
|
338
|
+
address: { type: "string", description: "wallet address to inspect" },
|
|
339
|
+
token: { type: "string", description: "optional token contract for a token balance" },
|
|
340
|
+
},
|
|
341
|
+
required: ["chainId", "address"],
|
|
342
|
+
},
|
|
343
|
+
},
|
|
505
344
|
{
|
|
506
345
|
name: "best_swap_route",
|
|
507
346
|
description:
|
|
@@ -625,24 +464,6 @@ const ROUTES = {
|
|
|
625
464
|
fromAddress: a.fromAddress || DEFAULT_ADDRESS || undefined,
|
|
626
465
|
},
|
|
627
466
|
}),
|
|
628
|
-
portfolio_balance: (a) => ({
|
|
629
|
-
provider: "portfolio",
|
|
630
|
-
op: "balances",
|
|
631
|
-
args: {
|
|
632
|
-
addresses: a.addresses,
|
|
633
|
-
evmChainIds: a.evmChainIds,
|
|
634
|
-
includeTokens: a.includeTokens,
|
|
635
|
-
includeCollectibles: a.includeCollectibles,
|
|
636
|
-
includePrices: a.includePrices,
|
|
637
|
-
},
|
|
638
|
-
}),
|
|
639
|
-
portfolio_snapshot: (a) => ({ provider: "portfolio", op: "snapshot", args: a }),
|
|
640
|
-
portfolio_history: (a) => ({ provider: "portfolio", op: "history", args: a }),
|
|
641
|
-
portfolio_value_graph: (a) => ({ provider: "portfolio", op: "valueGraph", args: a }),
|
|
642
|
-
nft_inventory: (a) => ({ provider: "nft-portfolio", op: "inventory", args: a }),
|
|
643
|
-
nft_gallery: (a) => ({ provider: "nft-portfolio", op: "gallery", args: a }),
|
|
644
|
-
nft_pnl: (a) => ({ provider: "nft-portfolio", op: "pnl", args: a }),
|
|
645
|
-
nft_prepare_list: (a) => ({ provider: "nft-portfolio", op: "prepareList", args: a }),
|
|
646
467
|
rpc_balance: (a) => {
|
|
647
468
|
const addr = a.address || DEFAULT_ADDRESS;
|
|
648
469
|
if (!addr) throw new Error("address required (set ORACLE_DEFAULT_ADDRESS to supply a default)");
|
|
@@ -747,6 +568,39 @@ async function callTool(name, args = {}) {
|
|
|
747
568
|
const { prepareBestBridgeRoute } = await import("../src/router/prepare-bridge.mjs");
|
|
748
569
|
return prepareBestBridgeRoute(args);
|
|
749
570
|
}
|
|
571
|
+
// Smart-wallet / token scanner surface.
|
|
572
|
+
//
|
|
573
|
+
// The scanner engine already covered 11 chains and every capability below, but no
|
|
574
|
+
// MCP tool exposed it -- so an agent could scan nothing on EVM while the code sat
|
|
575
|
+
// there working. Same class of gap as a route source with no preparer: the
|
|
576
|
+
// capability existed, the handle did not.
|
|
577
|
+
//
|
|
578
|
+
// In-process for the same reason routing is: no desk server required.
|
|
579
|
+
// Read-only. scoreRisk and sellSimulation inspect; they never sign.
|
|
580
|
+
if (name === "scanner_coverage") {
|
|
581
|
+
const s = await import("../src/scanner/index.mjs");
|
|
582
|
+
s.registerBuiltinScanners?.();
|
|
583
|
+
return s.scannerCoverage();
|
|
584
|
+
}
|
|
585
|
+
if (name === "scan_token") {
|
|
586
|
+
const s = await import("../src/scanner/index.mjs");
|
|
587
|
+
s.registerBuiltinScanners?.();
|
|
588
|
+
const sc = s.getScanner(Number(args.chainId));
|
|
589
|
+
if (!sc) throw new Error(`no scanner registered for chainId ${args.chainId}`);
|
|
590
|
+
const token = await sc.resolveToken(args.token);
|
|
591
|
+
const pools = sc.supports?.("resolvePools") ? await sc.resolvePools(args.token).catch(() => null) : null;
|
|
592
|
+
const risk = sc.supports?.("scoreRisk") ? await sc.scoreRisk({ token: args.token }).catch(() => null) : null;
|
|
593
|
+
return { chainId: Number(args.chainId), token, pools, risk };
|
|
594
|
+
}
|
|
595
|
+
if (name === "wallet_balances") {
|
|
596
|
+
const s = await import("../src/scanner/index.mjs");
|
|
597
|
+
s.registerBuiltinScanners?.();
|
|
598
|
+
const sc = s.getScanner(Number(args.chainId));
|
|
599
|
+
if (!sc) throw new Error(`no scanner registered for chainId ${args.chainId}`);
|
|
600
|
+
const native = await sc.nativeBalance(args.address);
|
|
601
|
+
const token = args.token ? await sc.tokenBalance(args.address, args.token).catch(() => null) : null;
|
|
602
|
+
return { chainId: Number(args.chainId), address: args.address, native, token };
|
|
603
|
+
}
|
|
750
604
|
if (name === "data_catalog") return httpJson(`${DATA_URL}/data/catalog`);
|
|
751
605
|
if (name === "data_health") return httpJson(`${DATA_URL}/data/health`);
|
|
752
606
|
if (name === "data_call") {
|
|
@@ -783,21 +637,7 @@ async function handle(request) {
|
|
|
783
637
|
if (method === "tools/call") {
|
|
784
638
|
try {
|
|
785
639
|
const result = await callTool(params?.name, params?.arguments || {});
|
|
786
|
-
|
|
787
|
-
const { dataBase64, ...metadata } = result;
|
|
788
|
-
send({
|
|
789
|
-
jsonrpc: "2.0",
|
|
790
|
-
id,
|
|
791
|
-
result: {
|
|
792
|
-
content: [
|
|
793
|
-
{ type: "image", data: dataBase64, mimeType: result.mimeType },
|
|
794
|
-
{ type: "text", text: JSON.stringify(metadata, null, 2) },
|
|
795
|
-
],
|
|
796
|
-
},
|
|
797
|
-
});
|
|
798
|
-
} else {
|
|
799
|
-
send({ jsonrpc: "2.0", id, result: { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] } });
|
|
800
|
-
}
|
|
640
|
+
send({ jsonrpc: "2.0", id, result: { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] } });
|
|
801
641
|
} catch (err) {
|
|
802
642
|
send({ jsonrpc: "2.0", id, result: { isError: true, content: [{ type: "text", text: err.message }] } });
|
|
803
643
|
}
|
|
@@ -826,20 +666,7 @@ function startStdioLoop() {
|
|
|
826
666
|
});
|
|
827
667
|
}
|
|
828
668
|
|
|
829
|
-
|
|
830
|
-
// fails for `npx oracle-data-mcp` / node_modules/.bin/oracle-data-mcp and the
|
|
831
|
-
// process exits without ever starting the stdio loop (Hermes sees Connection closed).
|
|
832
|
-
function isMainModule() {
|
|
833
|
-
const entry = process.argv[1];
|
|
834
|
-
if (!entry) return false;
|
|
835
|
-
try {
|
|
836
|
-
return fs.realpathSync(entry) === fs.realpathSync(fileURLToPath(import.meta.url));
|
|
837
|
-
} catch {
|
|
838
|
-
return false;
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
if (isMainModule()) {
|
|
669
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
843
670
|
startStdioLoop();
|
|
844
671
|
}
|
|
845
672
|
|