arc-market-mcp 0.1.0
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/LICENSE +21 -0
- package/README.md +118 -0
- package/dist/arc.js +84 -0
- package/dist/index.js +96 -0
- package/package.json +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 BABA Capital
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Arc Market MCP
|
|
2
|
+
|
|
3
|
+
An [MCP](https://modelcontextprotocol.io) server that reads **live market state on Arc** — Circle's L1
|
|
4
|
+
(chainId `5042`) — through the BABA Capital Arc Data API.
|
|
5
|
+
|
|
6
|
+
Circle publishes an Arc MCP, but it serves **documentation**. This one reads **chain state**:
|
|
7
|
+
|
|
8
|
+
- **`arc_launchpads`** — launchpad concentration (HHI), pools created, v3 vs v4 launch counts, distinct
|
|
9
|
+
deployer callers, and per-launchpad share / graduations / dead-on-arrival.
|
|
10
|
+
- **`arc_lp_apr`** — realised range-APR for LP pools from the latest scan (filter by pair / venue / range).
|
|
11
|
+
- **`arc_rpc_consensus`** — how many of Arc's four public RPCs agree this block, before you trust a figure.
|
|
12
|
+
- **`arc_token_screen`** — cheap gate on a launched token: its USDC pool, fee, swap count, decoy verdict.
|
|
13
|
+
- **`arc_token_sellsim`** — a **live honeypot test**: buy-then-sell simulated against current chain state,
|
|
14
|
+
reporting the round-trip fraction recovered. Not a heuristic — an actual simulation.
|
|
15
|
+
- **`arc_pool_decoy`** — is this v4 poolId the real venue for its token, or a high-fee decoy?
|
|
16
|
+
- **`arc_rpc_consensus`**, **`arc_health`**, **`arc_agent_card`** — trust, liveness, and identity.
|
|
17
|
+
|
|
18
|
+
### Agent Directory — who is actually on Arc
|
|
19
|
+
|
|
20
|
+
~137 agents are registered in the ERC-8004 registry on Arc and nothing else lets you see who they
|
|
21
|
+
are. Circle builds the rails; this is the phone book.
|
|
22
|
+
|
|
23
|
+
- **`arc_agents_list`** — every registered agent: agentId, owner, agentURI, host, name, skills,
|
|
24
|
+
validation status, and how concentrated its host and owner are.
|
|
25
|
+
- **`arc_agent_get`** — one agent in full, including the **raw card as served** and a validation
|
|
26
|
+
report giving the evidence for every flag, so you can check the directory rather than trust it.
|
|
27
|
+
- **`arc_agents_search`** — *"find me an Arc agent that does X and that I can pay."*
|
|
28
|
+
- **`arc_agents_stats`** — the registry's shape in one call.
|
|
29
|
+
|
|
30
|
+
Two things worth knowing, both measured rather than assumed:
|
|
31
|
+
|
|
32
|
+
- **`x402=true` is much narrower than `payment=true`.** Most of this registry prices in USDC via
|
|
33
|
+
ERC-8183 settlement, which is *not* x402. Each match carries the exact key and value that prove it.
|
|
34
|
+
- **The registry is extremely concentrated.** One host holds **115 of 137 ids (83.9%)** and one owner
|
|
35
|
+
holds 100 (73%). The directory publishes those counts and lets you draw the conclusion — it attaches
|
|
36
|
+
no label or rating to any agent, because a directory that editorialises is worth nothing.
|
|
37
|
+
|
|
38
|
+
A card that will not resolve is listed as `UNRESOLVED`, never quietly dropped; a partial crawl comes
|
|
39
|
+
back `NO_DATA` with `coverage` naming the ids it could not reach. **BABA Capital operates agentIds 135
|
|
40
|
+
and 136**; they are listed on the same terms as everyone else and get no ranking advantage.
|
|
41
|
+
|
|
42
|
+
## Honesty model (read this)
|
|
43
|
+
|
|
44
|
+
Every data route returns one envelope:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{ "chain": "arc", "chainId": 5042, "asof": "…", "head": 123, "status": "OK|NO_DATA|REFUSED",
|
|
48
|
+
"coverage": {…}, "rpc_consensus": true, "data": {…}, "warnings": [], "source": "…", "version": "…" }
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
- **`NO_DATA` is never a zero.** A missing or partial reading returns `status: "NO_DATA"`, not an
|
|
52
|
+
empty success. Absence of a signal is stated, never disguised as a value of the signal.
|
|
53
|
+
- **`REFUSED`** means the API declined (e.g. fewer than 2 of 4 RPCs agreed). The reading was not cross-checked; weigh it accordingly.
|
|
54
|
+
- This server passes the envelope through **unchanged**. It never unwraps `data` and hides the status.
|
|
55
|
+
|
|
56
|
+
This is a **read-only** client. It holds no private key, signs nothing, and moves no funds.
|
|
57
|
+
This is a software tool that reports chain reads. A passing sell-simulation is a snapshot at one block, not a guarantee.
|
|
58
|
+
|
|
59
|
+
## Install
|
|
60
|
+
|
|
61
|
+
Runs over stdio via `npx` — no global install needed.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npx arc-market-mcp
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Claude Desktop
|
|
68
|
+
|
|
69
|
+
Add to `claude_desktop_config.json`:
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"mcpServers": {
|
|
74
|
+
"arc-market": {
|
|
75
|
+
"command": "npx",
|
|
76
|
+
"args": ["-y", "arc-market-mcp"]
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Configuration
|
|
83
|
+
|
|
84
|
+
| Env var | Default | Meaning |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| `ARC_API_BASE` | `https://api.babacapital.app` | Base for the `/arc/*` routes. |
|
|
87
|
+
| `ARC_AGENT_CARD_BASE` | `https://babacapital.app` | Base for `/.well-known/agents/<name>.json`. |
|
|
88
|
+
| `ARC_API_TIMEOUT_MS` | `20000` | Per-request timeout (the sellsim route runs a live simulation). |
|
|
89
|
+
|
|
90
|
+
## Pricing
|
|
91
|
+
|
|
92
|
+
The Arc Data API's **free tier is 20 calls/day/IP**. Paid calls settle in **USDC over Circle Gateway
|
|
93
|
+
x402** on Arc (`eip155:5042`). This package is the reader; payment, when required, is negotiated by the
|
|
94
|
+
API's `402` response. The reader never holds funds or keys.
|
|
95
|
+
|
|
96
|
+
## Identity (ERC-8004)
|
|
97
|
+
|
|
98
|
+
BABA Capital's Arc agents publish [ERC-8004](https://eips.ethereum.org/) agent-cards at
|
|
99
|
+
`https://babacapital.app/.well-known/agents/` — `baba-arc-data` and `baba-arc-yield`. Fetch one with
|
|
100
|
+
`arc_agent_card`. `agentId` is `null` until on-chain registration completes.
|
|
101
|
+
|
|
102
|
+
## Build from source
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npm install
|
|
106
|
+
npm run build
|
|
107
|
+
npm test
|
|
108
|
+
node dist/index.js
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Provenance
|
|
112
|
+
|
|
113
|
+
This repository is standalone. Its git history begins on **2026-09-17**; it shares no history with any
|
|
114
|
+
other BABA Capital repository.
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
MIT © 2026 BABA Capital
|
package/dist/arc.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin, honest client for the BABA Capital Arc Data API (the `/arc/*` routes).
|
|
3
|
+
*
|
|
4
|
+
* Two rules carried from the API's own doctrine and preserved here verbatim:
|
|
5
|
+
* 1. Every `/arc` route returns the §3.5 envelope
|
|
6
|
+
* { chain, chainId, asof, head, status, coverage, rpc_consensus, data, warnings, source, version }.
|
|
7
|
+
* This client passes that envelope through UNCHANGED. It never unwraps `data` and presents it as
|
|
8
|
+
* the whole answer, because a caller must be able to see `status` and `coverage`.
|
|
9
|
+
* 2. NO_DATA is never silently turned into a zero or an empty success. A NO_DATA envelope, a network
|
|
10
|
+
* failure, and a non-200 are three distinct, visible outcomes — none is dressed up as data.
|
|
11
|
+
*
|
|
12
|
+
* This client holds no keys. The Arc Data API's free tier is 20 calls/day/IP; paid calls settle in
|
|
13
|
+
* USDC over Gateway x402 (chainId 5042). Reading is free within the tier; this package never signs.
|
|
14
|
+
*/
|
|
15
|
+
/** Where the `/arc/*` routes live. Override with ARC_API_BASE for staging or a self-host. */
|
|
16
|
+
export const ARC_API_BASE = (process.env.ARC_API_BASE || "https://api.babacapital.app").replace(/\/+$/, "");
|
|
17
|
+
/** Where the ERC-8004 agent-cards live (`/.well-known/agents/<name>.json`). Usually the site apex. */
|
|
18
|
+
export const AGENT_CARD_BASE = (process.env.ARC_AGENT_CARD_BASE || "https://babacapital.app").replace(/\/+$/, "");
|
|
19
|
+
/** Per-request ceiling. The heavy routes (sellsim) run a live simulation server-side. */
|
|
20
|
+
const TIMEOUT_MS = Number(process.env.ARC_API_TIMEOUT_MS || 20000);
|
|
21
|
+
const UA = "arc-market-mcp/0.1.0 (+https://babacapital.app)";
|
|
22
|
+
async function getJson(url) {
|
|
23
|
+
const ctrl = new AbortController();
|
|
24
|
+
const t = setTimeout(() => ctrl.abort(), TIMEOUT_MS);
|
|
25
|
+
try {
|
|
26
|
+
const res = await fetch(url, {
|
|
27
|
+
method: "GET",
|
|
28
|
+
headers: { accept: "application/json", "user-agent": UA },
|
|
29
|
+
signal: ctrl.signal,
|
|
30
|
+
});
|
|
31
|
+
const text = await res.text();
|
|
32
|
+
let body;
|
|
33
|
+
try {
|
|
34
|
+
body = text ? JSON.parse(text) : null;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// A non-JSON body is a failure to be shown, not data to be guessed at.
|
|
38
|
+
return { ok: false, httpStatus: res.status, body: null, url,
|
|
39
|
+
error: `non-JSON response (${res.status}): ${text.slice(0, 200)}` };
|
|
40
|
+
}
|
|
41
|
+
return { ok: res.ok, httpStatus: res.status, body, url };
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
const msg = e instanceof Error ? (e.name === "AbortError" ? `timeout after ${TIMEOUT_MS}ms` : e.message) : String(e);
|
|
45
|
+
return { ok: false, httpStatus: 0, body: null, url, error: msg };
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
clearTimeout(t);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function qs(params) {
|
|
52
|
+
const p = new URLSearchParams();
|
|
53
|
+
for (const [k, v] of Object.entries(params)) {
|
|
54
|
+
if (v !== undefined && v !== null && `${v}`.length > 0)
|
|
55
|
+
p.set(k, `${v}`);
|
|
56
|
+
}
|
|
57
|
+
const s = p.toString();
|
|
58
|
+
return s ? `?${s}` : "";
|
|
59
|
+
}
|
|
60
|
+
/** A launched-token contract address, loosely validated before it ever reaches the network. */
|
|
61
|
+
export function isAddress(x) {
|
|
62
|
+
return /^0x[0-9a-fA-F]{40}$/.test(x.trim());
|
|
63
|
+
}
|
|
64
|
+
/** A v4 poolId is a bytes32. */
|
|
65
|
+
export function isPoolId(x) {
|
|
66
|
+
return /^0x[0-9a-fA-F]{64}$/.test(x.trim());
|
|
67
|
+
}
|
|
68
|
+
export const arc = {
|
|
69
|
+
health: () => getJson(`${ARC_API_BASE}/arc/health`),
|
|
70
|
+
launchpads: () => getJson(`${ARC_API_BASE}/arc/launchpads`),
|
|
71
|
+
lpApr: (o = {}) => getJson(`${ARC_API_BASE}/arc/pools/lp-apr${qs(o)}`),
|
|
72
|
+
rpcConsensus: () => getJson(`${ARC_API_BASE}/arc/rpc/consensus`),
|
|
73
|
+
tokenScreen: (ca) => getJson(`${ARC_API_BASE}/arc/token/${ca}/screen`),
|
|
74
|
+
tokenSellsim: (ca) => getJson(`${ARC_API_BASE}/arc/token/${ca}/sellsim`),
|
|
75
|
+
poolDecoy: (poolId) => getJson(`${ARC_API_BASE}/arc/pools/${poolId}/decoy`),
|
|
76
|
+
agentCard: (name) => getJson(`${AGENT_CARD_BASE}/.well-known/agents/${name}.json`),
|
|
77
|
+
// ── Arc Agent Directory ────────────────────────────────────────────────────────────────────────
|
|
78
|
+
// ~137 agents are registered in the ERC-8004 registry on Arc and nothing else lets you see who
|
|
79
|
+
// they are. These read a validated hourly crawl: facts and their evidence, never a verdict.
|
|
80
|
+
agentsList: (o = {}) => getJson(`${ARC_API_BASE}/arc/agents${qs(o)}`),
|
|
81
|
+
agentGet: (agentId) => getJson(`${ARC_API_BASE}/arc/agents/${agentId}`),
|
|
82
|
+
agentsSearch: (o = {}) => getJson(`${ARC_API_BASE}/arc/agents/search${qs(o)}`),
|
|
83
|
+
agentsStats: () => getJson(`${ARC_API_BASE}/arc/agents/stats`),
|
|
84
|
+
};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Arc Market MCP — read live Arc (chainId 5042) market state through the BABA Capital Arc Data API.
|
|
4
|
+
*
|
|
5
|
+
* Circle ships an Arc MCP, but it serves documentation. This one reads chain state: which launchpads
|
|
6
|
+
* are concentrating flow, what an LP range actually yielded, whether Arc's RPCs agree this block, and
|
|
7
|
+
* whether a freshly launched token is a honeypot (a live sell-simulation, not a heuristic).
|
|
8
|
+
*
|
|
9
|
+
* It is a stateless HTTP client of a public API. It holds no private key, signs nothing, and moves no
|
|
10
|
+
* funds. Paid calls settle in USDC over Gateway x402; the free tier is 20 calls/day/IP.
|
|
11
|
+
*/
|
|
12
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
13
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
import { arc, isAddress, isPoolId, ARC_API_BASE } from "./arc.js";
|
|
16
|
+
/**
|
|
17
|
+
* Turn an ArcResult into MCP content WITHOUT laundering its meaning. Three outcomes stay three:
|
|
18
|
+
* - transport failure -> isError, the reason shown
|
|
19
|
+
* - NO_DATA / REFUSED -> not an error, but the status is stated first so it can't be mistaken for data
|
|
20
|
+
* - OK -> the full §3.5 envelope, so status/coverage/warnings travel with the data
|
|
21
|
+
*/
|
|
22
|
+
function present(r) {
|
|
23
|
+
if (r.error) {
|
|
24
|
+
return { isError: true, content: [{ type: "text",
|
|
25
|
+
text: `REQUEST FAILED (${r.url})\n${r.error}\n\nThis is a transport failure, not a market signal. Nothing about Arc is implied.` }] };
|
|
26
|
+
}
|
|
27
|
+
const body = r.body;
|
|
28
|
+
const status = body && typeof body === "object" ? body["status"] : undefined;
|
|
29
|
+
const pretty = JSON.stringify(body, null, 2);
|
|
30
|
+
if (r.httpStatus >= 400 && status !== "NO_DATA") {
|
|
31
|
+
return { isError: true, content: [{ type: "text", text: `HTTP ${r.httpStatus} from ${r.url}\n${pretty}` }] };
|
|
32
|
+
}
|
|
33
|
+
let header = "";
|
|
34
|
+
if (status === "NO_DATA")
|
|
35
|
+
header = "STATUS: NO_DATA — the API has no complete reading to return. This is not zero and not an error; do not treat absence as a value.\n\n";
|
|
36
|
+
else if (status === "REFUSED")
|
|
37
|
+
header = "STATUS: REFUSED — the API declined to answer (e.g. RPCs did not agree). The reading was not cross-checked.\n\n";
|
|
38
|
+
return { content: [{ type: "text", text: header + pretty }] };
|
|
39
|
+
}
|
|
40
|
+
const server = new McpServer({ name: "arc-market-mcp", version: "0.1.0" });
|
|
41
|
+
server.tool("arc_health", "Liveness of the Arc Data API and the list of routes it serves. Returns the §3.5 envelope. Call this first to confirm the API base and see which live routes are wired.", {}, async () => present(await arc.health()));
|
|
42
|
+
server.tool("arc_launchpads", "Launchpad concentration on Arc over the recent window: HHI, pools created, v3 vs v4 launch counts, distinct deployer callers, and per-launchpad share/graduations/dead-on-arrival. NO_DATA until an hourly reading has been written; partial windows are NO_DATA, never a false zero.", {}, async () => present(await arc.launchpads()));
|
|
43
|
+
server.tool("arc_lp_apr", "Realised range-APR for Arc LP pools from the latest scan. Optional filters: pair (e.g. 'usdc/weth'), venue (e.g. 'aero-lite'), range_pct. APR assumes price stayed in range over the window; the warning states this. NO_DATA if no scan has been written.", {
|
|
44
|
+
pair: z.string().optional().describe("Filter by pair, case-insensitive, e.g. 'usdc/weth'."),
|
|
45
|
+
venue: z.string().optional().describe("Filter by venue, case-insensitive, e.g. 'aero-lite'."),
|
|
46
|
+
range_pct: z.number().optional().describe("Filter to rows computed at this range width in percent."),
|
|
47
|
+
}, async ({ pair, venue, range_pct }) => present(await arc.lpApr({ pair, venue, range_pct })));
|
|
48
|
+
server.tool("arc_rpc_consensus", "How many of Arc's four public RPCs agreed at the last reading (of 4), and the head they agreed on. Below 2 the status is REFUSED — treat any Arc figure as not cross-checked until they agree again. A cheap trust gate before acting on other Arc data.", {}, async () => present(await arc.rpcConsensus()));
|
|
49
|
+
server.tool("arc_token_screen", "Screen a launched Arc token by contract address: its USDC-paired pool, fee, recent swap count, and the decoy-pool verdict (a fee above the cap, or a dynamic pool whose swaps charged above the cap, is refused). A cheap gate — it does NOT run a live buy/sell. NO_DATA if the token has not traded in the API's discovery window.", { ca: z.string().describe("The token contract address (0x…40 hex).") }, async ({ ca }) => {
|
|
50
|
+
if (!isAddress(ca))
|
|
51
|
+
return { isError: true, content: [{ type: "text", text: `'${ca}' is not a contract address (expected 0x + 40 hex).` }] };
|
|
52
|
+
return present(await arc.tokenScreen(ca.trim()));
|
|
53
|
+
});
|
|
54
|
+
server.tool("arc_token_sellsim", "Honeypot test for an Arc token: a LIVE sell-simulation (buy then sell in one simulated transaction against current chain state) reporting the round-trip fraction recovered. Below ~0.9 the token is likely a honeypot or a punitive-tax trap. This is a heavy, paid-tier route. NO_DATA if the pool cannot be simulated (it is never scored zero on a failed simulation).", { ca: z.string().describe("The token contract address (0x…40 hex).") }, async ({ ca }) => {
|
|
55
|
+
if (!isAddress(ca))
|
|
56
|
+
return { isError: true, content: [{ type: "text", text: `'${ca}' is not a contract address (expected 0x + 40 hex).` }] };
|
|
57
|
+
return present(await arc.tokenSellsim(ca.trim()));
|
|
58
|
+
});
|
|
59
|
+
server.tool("arc_pool_decoy", "Decoy-pool verdict for a specific v4 poolId: whether this pool is the real venue for its token or a high-fee decoy that a naive buyer would route into. Explains the refusal reason. NO_DATA if the poolId is unknown to the API.", { poolId: z.string().describe("The v4 poolId (bytes32, 0x…64 hex).") }, async ({ poolId }) => {
|
|
60
|
+
if (!isPoolId(poolId))
|
|
61
|
+
return { isError: true, content: [{ type: "text", text: `'${poolId}' is not a poolId (expected 0x + 64 hex).` }] };
|
|
62
|
+
return present(await arc.poolDecoy(poolId.trim()));
|
|
63
|
+
});
|
|
64
|
+
server.tool("arc_agent_card", "Fetch a BABA Capital ERC-8004 agent-card (the /.well-known/agents/<name>.json identity document). Known names: 'baba-arc-data', 'baba-arc-yield'. Shows the agent's registry, agentId (null until registered), and x402 payment network.", { name: z.enum(["baba-arc-data", "baba-arc-yield"]).describe("Which agent-card to fetch.") }, async ({ name }) => present(await arc.agentCard(name)));
|
|
65
|
+
// ── Arc Agent Directory ──────────────────────────────────────────────────────────────────────────
|
|
66
|
+
// Discovery is the unclaimed position on Arc: Circle ships the rails, and nobody ships the phone
|
|
67
|
+
// book. These tools answer "who is actually on this chain, and which of them can I pay?"
|
|
68
|
+
server.tool("arc_agents_list", "List the ERC-8004 agents registered on Arc, from a validated hourly crawl. Each row carries agentId, owner, agentURI, host, name, skills, status (OK | UNRESOLVED | INVALID), whether it declares x402, and how concentrated its host and owner are. Optional filters: status, host, owner. Paged. An agent whose card will not resolve is listed as UNRESOLVED, never silently dropped.", {
|
|
69
|
+
status: z.enum(["OK", "UNRESOLVED", "INVALID", "SKIPPED_ROBOTS"]).optional()
|
|
70
|
+
.describe("Only agents with this validation status."),
|
|
71
|
+
host: z.string().optional().describe("Only agents whose agentURI is on this host, e.g. 'stubly.org'."),
|
|
72
|
+
owner: z.string().optional().describe("Only agents owned by this address."),
|
|
73
|
+
limit: z.number().optional().describe("Page size (default 50, max 200)."),
|
|
74
|
+
offset: z.number().optional().describe("Page offset."),
|
|
75
|
+
}, async (o) => present(await arc.agentsList(o)));
|
|
76
|
+
server.tool("arc_agent_get", "Full record for one Arc agent by agentId: its on-chain owner and agentURI, the RAW card as served, and a validation report showing the evidence behind every flag (which key supplied the name, which supplied the skills, the exact key and value that prove an x402 or payment declaration, redirects, robots handling, and any fetch error). Use this to check the directory's claims yourself rather than trusting them.", { agentId: z.number().int().min(0).describe("The ERC-8004 agentId (an ERC-721 tokenId on the registry).") }, async ({ agentId }) => present(await arc.agentGet(agentId)));
|
|
77
|
+
server.tool("arc_agents_search", "Find an Arc agent that does something, and optionally one you can pay. `q` matches name, host and skills; `skill` matches skill ids/names/tags. IMPORTANT: `x402=true` means the card declares x402 SPECIFICALLY — it is a much smaller set than `payment=true`, which includes other settlement schemes (most of this registry prices in USDC via ERC-8183, which is not x402). `resolvable=true` restricts to agents whose card actually fetched.", {
|
|
78
|
+
q: z.string().optional().describe("Free text over name, host and skills, e.g. 'translate'."),
|
|
79
|
+
skill: z.string().optional().describe("Match a skill id, name or tag."),
|
|
80
|
+
x402: z.enum(["true", "false"]).optional().describe("Declares x402 specifically."),
|
|
81
|
+
payment: z.enum(["true", "false"]).optional().describe("Declares any payment/pricing/settlement."),
|
|
82
|
+
resolvable: z.enum(["true", "false"]).optional().describe("Card actually resolved on the last crawl."),
|
|
83
|
+
limit: z.number().optional().describe("Page size (default 50, max 200)."),
|
|
84
|
+
offset: z.number().optional().describe("Page offset."),
|
|
85
|
+
}, async (o) => present(await arc.agentsSearch(o)));
|
|
86
|
+
server.tool("arc_agents_stats", "Shape of Arc's ERC-8004 agent registry in one call: total ids, distinct owners, distinct hosts, how many resolve, how many declare x402 vs payment generally, status counts, and the most concentrated hosts and owners with their share of the registry. Facts only — the concentration numbers are stated so you can draw your own conclusion.", {}, async () => present(await arc.agentsStats()));
|
|
87
|
+
async function main() {
|
|
88
|
+
const transport = new StdioServerTransport();
|
|
89
|
+
await server.connect(transport);
|
|
90
|
+
// stderr only — stdout is the MCP transport and must carry nothing but protocol frames.
|
|
91
|
+
process.stderr.write(`arc-market-mcp connected (API base: ${ARC_API_BASE})\n`);
|
|
92
|
+
}
|
|
93
|
+
main().catch((e) => {
|
|
94
|
+
process.stderr.write(`fatal: ${e instanceof Error ? e.stack || e.message : String(e)}\n`);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "arc-market-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for Arc (Circle's L1, chainId 5042). Reads live chain state: the ERC-8004 agent directory with every card fetched and checked, launchpad concentration, LP range-APR, RPC consensus, and sell simulation. Circle's own Arc MCP is docs-only; this one reads the chain.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"mcp",
|
|
7
|
+
"modelcontextprotocol",
|
|
8
|
+
"arc",
|
|
9
|
+
"circle",
|
|
10
|
+
"erc-8004",
|
|
11
|
+
"x402",
|
|
12
|
+
"defi",
|
|
13
|
+
"onchain",
|
|
14
|
+
"launchpad",
|
|
15
|
+
"honeypot",
|
|
16
|
+
"agent-directory",
|
|
17
|
+
"agents"
|
|
18
|
+
],
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"author": "BABA Capital",
|
|
21
|
+
"type": "module",
|
|
22
|
+
"bin": {
|
|
23
|
+
"arc-market-mcp": "dist/index.js"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"README.md",
|
|
28
|
+
"LICENSE"
|
|
29
|
+
],
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=18"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsc",
|
|
35
|
+
"prepare": "tsc",
|
|
36
|
+
"start": "node dist/index.js",
|
|
37
|
+
"test": "node --test test/*.test.mjs"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
41
|
+
"zod": "^3.23.8"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"typescript": "^5.5.4",
|
|
45
|
+
"@types/node": "^20.14.0"
|
|
46
|
+
},
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "git+https://github.com/babaanalytix-commits/arc-market-mcp.git"
|
|
50
|
+
},
|
|
51
|
+
"homepage": "https://babacapital.app"
|
|
52
|
+
}
|