Arcus Agent Gateway

io.github.alekskram由 GitHub 帳號 alekskram 發布v0.2.4更新於 Oct 24, 2026

Read-only MCP gateway to 194 tokenized US equities on Robinhood Chain: quotes, holders, whale moves.

已驗證STDIO僅桌面資料庫金融資料與分析

概覽

AI 產生的概覽

唯讀 MCP 閘道,接入 Robinhood Chain 上 194 檔代幣化美股,讓助理取得報價、持有人資料與巨鯨轉帳活動。

可以做什麼
透過 stdio 提供 13 個唯讀工具。token_list 與 search 用來尋找有效代號;quote 與 quotes 回傳即時價格,包含原始與乘數調整後的買賣價與價差,以及停牌與乘數資訊;token_detail 補充合約、ISIN 與公司行動中介資料。鏈上工具包括 holder_snapshot(前幾大持有人及其占比)、wallet_holdings(某地址持有 194 檔股票中的哪些)與 transfer_history(近期 ERC-20 轉帳,可用最小金額篩選)。market_status、sector_view、corporate_actions 與 price_history 提供市場健康度、類股均值、拆股股利以及選用的本機 OHLCV 歷史。
適用情境
當助理需要 Robinhood Chain 代幣化股票的市場資料時使用:查詢某代號的持有人、持股集中度、某錢包的持倉,或大額轉帳流向。它適合唯讀研究與監控,而非交易,因為不提供下單或寫入操作。當僅有即時報價不夠時,選用記錄器可用來建立本機價格歷史。
執行需求
以 PyPI 套件形式在本機透過 stdio 執行(uvx arcus-agent-gateway);清單未宣告任何 API 金鑰、帳號或驗證。需要網路存取 Robinhood Chain 公開 REST API、公開 JSON-RPC 端點以及 Blockscout 瀏覽器。選用擴充:browser 擴充用於 Cloudflare 模擬,recorder 擴充(pyarrow)用於價格歷史。選用環境變數包括 ARCUS_RPC_URL、ARCUS_RPC_FALLBACK_URL、ARCUS_EXPLORER_URL、ARCUS_GATEWAY_DATA 與 ARCUS_INTERVAL_SEC。
安裝前請注意
唯讀且免金鑰,不寫入資料,也沒有遙測(R115、R118)。價格依上游原樣提供,調整後欄位是本伺服器自行計算,並非上游資料;README 說明資料僅供參考,不用於交易決策,行動前應向官方來源查證(R98、R99)。選用記錄器會把 parquet 檔案寫入本機磁碟,其 systemd 單元預設停用,是否啟用由你決定(R104、R107)。依賴區塊瀏覽器的工具可能因 Cloudflare 驗證而失敗(R90、R92)。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Arcus Agent Gateway,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

arcus-agent-gateway

[CI] [PyPI] [PyPI downloads] [MCP Catalog] [License: MIT] [Python 3.11+]

Robinhood Chain (Arcus) carries 194 tokenized US equities on-chain, and its public API answers questions most people don't think to ask: who actually holds AAPL, what a wallet's stock portfolio looks like, which way the whales moved NVDA today. This MCP server hands all of that to an AI agent. Read-only, keyless, no writes; every tool is a GET against api.robinhood.com/rhj plus two public on-chain sources, cached and rate-limited so an enthusiastic agent can't hammer the upstream.

Use cases

Ask it who holds AAPL and you get the top holder addresses with their share of total supply, flagged contract or wallet. One holder contract turns out to sit on 35% of NVDA; that is the kind of concentration risk a quote page will never show you.

wallet_holdings reads any address across all 194 equities and values the positions it has fresh quotes for. transfer_history walks recent ERC-20 Transfers with a min_value filter, which is how you catch the large prints instead of the dust.

Prices are the trap here, so they ship split-safe: raw and multiplier-adjusted values travel together on every quote, and a pending split (NVDA queues a 4.0 multiplier for 2026-11) comes with a warning and the effective time. A morning scan of market health, halted tokens and 13-sector averages costs two cheap calls.

Full walkthroughs with real outputs: examples/use-cases.md.

Quickstart

Claude Code:

bash
claude mcp add arcus -- uvx arcus-agent-gateway

Run over stdio (the default, for local agents):

bash
uvx arcus-agent-gateway

Standard config for Claude Desktop / Cursor (claude_desktop_config.json / .cursor/mcp.json):

json
{  "mcpServers": {    "arcus": {      "command": "uvx",      "args": ["arcus-agent-gateway"]    }  }}
Codex (~/.codex/config.toml)
toml
[mcp_servers.arcus]command = "uvx"args = ["arcus-agent-gateway"]
ZCode: register the server and copy the agent skill
bash
# 1) start the gateway (keep it running)uvx arcus-agent-gateway --http --port 8902 &
# 2) register it (merges into ~/.zcode/cli/config.json; workspace .zcode/config.json works too)python3 - <<'PY'import json, osp = os.path.expanduser("~/.zcode/cli/config.json")os.makedirs(os.path.dirname(p), exist_ok=True)cfg = json.load(open(p)) if os.path.exists(p) else {}cfg.setdefault("mcp", {}).setdefault("servers", {})["arcus"] = {    "type": "http", "url": "http://127.0.0.1:8902/mcp"}json.dump(cfg, open(p, "w"), indent=2)print("arcus MCP server registered:", p)PY
# 3) copy the agent skill (tool guide + watchlist cron recipe)git clone -q --depth 1 https://github.com/alekskram/arcus-agent-gateway /tmp/aagcp -r /tmp/aag/.agents/skills/arcus-gateway ~/.zcode/skills/ && rm -rf /tmp/aagecho "ZCode setup done, restart your session and call any arcus tool"

Hosted form, streamable HTTP on port 8902:

bash
uvx arcus-agent-gateway --http               # 127.0.0.1:8902curl http://127.0.0.1:8902/health            # -> {"ok": true, "service": "arcus-agent-gateway"}

Tools

All 13 tools are read-only (annotated readOnlyHint: true). Names and parameters are exactly as registered by arcus_mcp/server.py.

#ToolSignatureWhat it does
1token_listtoken_list(status="ACTIVE", limit=100)Tokenized equities, one row per token (symbol, name, status, multiplier, tradable); status filters the ASSET_STATUS_* prefix, 'ALL' disables. Start here for valid symbols.
2quotequote(symbol)Live quote joined with asset metadata: raw + multiplier-adjusted bid/ask/spread, is_halted, trading capabilities, multiplier block. Unknown symbol raises with a pointer to token_list().
3quotesquotes(symbols)Batch of quote() rows, max 20 per call (more raises). Unknown symbols land in errors without failing the batch. Requests run in parallel (semaphore 8); 10 cold symbols take 0.6-1 s instead of ~3 s.
4token_detailtoken_detail(symbol)Full dossier: contract/chain/ISIN metadata, embedded quote, last 5 corporate actions, multiplier block with history note, warnings (pending split).
5market_statusmarket_status()Market-wide health from assets only (never fetches 194 prices): totals, untradable count, cached-halted list, extended-hours estimate.
6corporate_actionscorporate_actions(symbol=None, limit=10)Splits/dividends across all tokens or for one symbol; tolerant to the API's field-name variants.
7searchsearch(query, limit=10)Local fuzzy search over the token list; apple → AAPL; top limit (cap 50) with scores and sectors.
8sector_viewsector_view(warm=False, sector=None)13-sector static map with sizes and multiplier-adjusted sector averages. Default (warm=False): caches only, zero requests, warmed: false. warm=True, sector="...": fans out fresh quotes for that sector only and reports requests_made.
9onchain_infoonchain_info(symbol)On-chain footprint joined from three independent sources (each fails to a warnings[] entry, never silently): contract address, chain id (4663, Robinhood Chain), network, decimals, ISIN (REST) + total_supply via totalSupply() eth_call (source rpc) + holders_count, circulating_market_cap (source explorer). supply_crosscheck compares the REST-implied cap (total_supply × multiplier × mid) with the explorer's; >1% divergence → warning. Per-field source tags on every derived value.
10price_historyprice_history(symbol, timeframe="daily", limit=90)OHLCV history from the optional recorder's local parquet store (see below). Honest degradation: missing pyarrow or data → actionable error dict, never a silent empty list.
11holder_snapshotholder_snapshot(symbol, limit=20)Top holders of a token's contract from the Blockscout explorer (one page, max 50 rows, 600 s cache). Rows: address, value (float token units), share_pct = value / total_supply × 100, is_contract. total_supply from the RPC with an explorer fallback (source-tagged); no supply at all → share_pct: null + warning. Errors → error dict with kind + hint.
12wallet_holdingswallet_holdings(address)Which of the 194 tokenized equities a wallet holds (explorer token-balances ∩ assets() universe). Rows: symbol, name, value (float token units). est_position_usd / portfolio_usd_total computed ONLY from quotes already in the price cache (no fan-out); missing/stale quotes → null estimates + explanatory note. Cached 120 s.
13transfer_historytransfer_history(symbol, limit=25, min_value=None)Recent ERC-20 Transfer events from the public RPC's adaptive walk-back (windows start 48 blocks wide, shrink 48→32→16→8 on archive 403s, ≤14 getLogs requests — (see On-chain sources & limits)). Rows (newest first): ts (ISO, from the log's own blockTimestamp), from, to, value (float), tx_hash, block. min_value filters in token units; window exhausted with 0 logs → explicit note pointing at the explorer. Cached 60 s.
—watchlist—Not a tool. Price tracking is done by your agent's scheduler (cron) calling quotes() on an interval — (see .agents/skills/arcus-gateway/SKILL.md).

Multiplier logic (read this before using prices)

Robinhood Chain tokens carry a multiplier, the corporate-action adjustment factor for the token contract (1.0 = untouched). Splits change it; for example NVDA's 2026-11 split queues pendingMultiplier: "4.0".

  • The REST API returns RAW prices. bid/ask from /prices/{symbol} are in token-contract units and are not multiplier-adjusted.
  • Adjusted values are computed by this server, never taken from upstream: price_adjusted = round(price_raw × currentMultiplier, 6).
  • Raw and adjusted always travel together. Every quote carries bid_raw/ask_raw/spread_raw and bid_adjusted/ask_adjusted/ mid_adjusted next to the multiplier block, never one without the other.
  • On-chain quantities (token balances, mint/burn volumes) are natively in adjusted (multiplied) units; REST prices are not. If you compare the two, go through the *_adjusted fields.

Worked example (live fixture, 2026-09-03):

AAPL   currentMultiplier = 1.000566080061092436       bid_raw   = 327.77   →  bid_adjusted = round(327.77 × 1.000566…, 6) = 327.955544       ask_raw   = 327.78   →  ask_adjusted = 327.965550       mid                      mid_adjusted = 327.960547

Pending split warning. When pendingMultiplier is queued (non-empty) and differs from the current one, token_detail() adds a warning like pending split: 1→4.0 on 2026-11-06T00:00:00Z, and quote()'s multiplier block exposes pending + effective_time. After the split lands, raw prices jump by the ratio while *_adjusted fields stay comparable, another reason to always read adjusted values next to the multiplier.

Why a gateway and not the raw API?

api.robinhood.com/rhj + the public RPC are open, and every agent hitting them directly rediscovers the same traps:

Raw sources give youYou would have to build
RAW, non-multiplier-adjusted prices (bid/ask in contract units)the multiplier math, raw/adjusted pairs on every quote, pending-split detection with effective times
no price history endpoint at alla recorder (opt-in here): 5-min snapshots → parquet → idempotent daily OHLCV rollup
60 req/s upstream limita polite rate-limited client (≤50 req/s), per-endpoint caches, parallel batched quotes
RPC archive window that 403s outside ~45–60 blocks behind headadaptive walk-back (48→32→16→8 block windows, ≤14 getLogs)
a Blockscout explorer behind a Cloudflare UA check, 40 s hangs on contract walletsbrowser UA, timeouts, honest error/warnings[] degradation, never a silent empty list

API limits & caching

  • Upstream allows 60 req/s without a key; this client self-limits to ≤ 50 req/s (a 20 ms politeness interval between requests, thread-safe).
  • Transient failures (429/502/503/504, network errors) are retried up to 3 times with 2s × (attempt+1) backoff.
  • Response caches (per process): /assets 5 min, /prices/{symbol} 15 s, /corporate-actions 1 h. market_status() and sector_view() are computed from caches and assets only; they never fan out 194 price requests.

On-chain sources & limits

The v0.2 on-chain tools read two keyless public sources next to the REST API. Both are free, rate-limited and partially restricted. Every tool above degrades honestly (per-field omission + warnings[] / error dicts), never with a silent empty answer.

  • Public JSON-RPC (default robinhood-rpc.publicnode.com, override with ARCUS_RPC_URL): eth_call (e.g. totalSupply()) works normally. eth_getLogs only answers inside a floating ~45–60-block window behind the latest block; wider or older ranges get HTTP 403 "Archive requests require a personal token" (the backend is Alchemy). The window drifts minute to minute, so transfer_history() walks back in windows that start 48 blocks wide and shrink 48→32→16→8 on each 403, capped at ~14 getLogs requests. eth_getLogs log objects carry blockTimestamp directly, so no per-block lookups are needed.
  • Fallback RPC (robinhood.drpc.org, ARCUS_RPC_FALLBACK_URL): has no eth_getLogs and no eth_call (JSON-RPC "method not available"); it is used only for eth_chainId / eth_blockNumber.
  • Blockscout v2 explorer (robinhoodchain.blockscout.com/api/v2, ARCUS_EXPLORER_URL): requires a browser User-Agent on every request, and since Sep 2026 also passes Cloudflare's TLS-fingerprint bot management, so a plain client gets 403 even with browser headers. The gateway retries every Cloudflare 403 once with curl_cffi Chrome impersonation when the optional extra is installed: pip install "arcus-agent-gateway[browser]". Without it, the two explorer-backed tools (holder_snapshot, wallet_holdings) return an honest cloudflare error with this exact hint; everything else keeps working. Token pages (holders_count, circulating_market_cap, total_supply), one holders page (max 50 rows, no pagination loops) and address token-balances come from here, cached 600 s. token-balances answers in ~0.5 s on plain wallets but hangs 40 s+ on huge contract addresses, and the client fails honestly after 15 s with kind explorer-timeout.
  • On-chain activity ≠ trades. The chain records Transfer, mint and redeem events between addresses; it knows nothing about order-book trades or prices. Use quote()/quotes() for prices and transfer_history() for token movement.

Raw prices disclaimer

Prices are served exactly as they arrive from Robinhood (RAW); they are not multiplier-adjusted, and the *_adjusted fields are our computation, not upstream data. All data is for information only, not for trading decisions, and should be verified against the official source before you act on it. No warranty of completeness, accuracy or timeliness.

Optional price history recorder

The Robinhood Chain REST API has no price history endpoint, only current quotes. For the 194 tokenized equities this recorder is the only history source. It is opt-in and disabled by default; nothing is recorded unless you explicitly enable it.

How it works. One tick every 5 minutes (default): fetch a quote for every ACTIVE tradable token through the same rate-limited client (50 req/s cap; average load ≈ 0.65 req/s), append one row per symbol to data/history/snapshots_YYYYMM.parquet (monthly rotation), and maintain a daily OHLCV rollup data/history/daily.parquet (open/high/low/close on mid_adjusted, volume = max of the day's cumulative daily_volume). The rollup runs at the first tick after midnight UTC for the previous day and is idempotent (re-running a day overwrites it, never duplicates).

Enable it:

bash
pip install "arcus-agent-gateway[recorder]"   # adds pyarrow (optional extra)# systemd (recommended): units ship DISABLED - enabling is your decisionsudo cp deploy/arcus-recorder.* /etc/systemd/system/sudo systemctl enable --now arcus-recorder.timer   # OnCalendar=*:0/5, Persistent# or run one tick / a debug loop manually:python -m arcus_mcp.recorder --oncepython -m arcus_mcp.recorder --limit 5            # debug: first 5 symbols onlyARCUS_INTERVAL_SEC=60 python -m arcus_mcp.recorder  # custom interval loop# (from a git checkout, `python scripts/recorder.py ...` still works -#  it is a thin shim that delegates to arcus_mcp.recorder)

Data weight & rotation. Full universe (194 symbols) at a 5-minute tick ≈ 2–3 MB/day of snapshots plus ≈ 10 KB/day for the daily rollup. Snapshots rotate monthly (snapshots_YYYYMM.parquet); delete old months when you no longer need raw granularity. daily.parquet is the compact long-term store. Data lands in ~/.local/state/arcus-agent-gateway/history/ (override with ARCUS_GATEWAY_DATA).

Reading it back: the price_history tool serves daily bars and raw snapshots from the same directory. Without pyarrow or data it returns an actionable error pointing here: install the [recorder] extra, never a silent empty answer.

Security & privacy

  • Keyless and read-only. No API keys, no auth, no writes. Every tool is annotated readOnlyHint: true / destructiveHint: false on the MCP wire.
  • Rate-limited by design. Client caps at 50 req/s against the public REST surface, public RPC requests go through the same limiter, Blockscout calls carry a standard browser User-Agent and their own timeouts.
  • No telemetry, no logging of your prompts. The server caches public market data in memory (and parquet files only if you enable the optional recorder); nothing leaves your machine except the API reads themselves.

Part of the suite

Four sibling read-only MCP gateways, one style: keyless, cached, honest degradation.

GatewayFocus
dydx-agent-gatewaydYdX v4: verified trader PnL, funding/OI anomaly detectors, leaderboard
arcus-agent-gateway (you are here)194 tokenized US equities on Robinhood Chain: quotes, holders, whale transfers
hyperliquid-agent-gatewayHyperliquid: 233 perps + spot, funding carry, account risk, HyperEVM
aster-agent-gatewayAster DEX: ~580 futures incl. 24/7 TradFi perps, funding caps/floors

All four are on glama.ai and PyPI; any of them installs with uvx <name>.

License

MIT, see LICENSE. Not affiliated with Robinhood Markets, Inc.

來源:README.md,提交 986aaae

工具

0
SourceWeft 尚未在沙箱中啟動過該軟體包,因此其工具未知。

版本歷史

2
  1. v0.2.4最新Sep 23, 2026
  2. v0.2.2Sep 22, 2026

評分

已滿足適用檢查項的 50%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分

    請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。

  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具15 分

    請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。

  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分