pegcheck
# pegcheck-mcp
A read-only [MCP](https://modelcontextprotocol.io) server that checks whether a **Robinhood Chain Stock Token** is currently trading at a fair price relative to the real-world stock it represents — designed to be called *by an AI agent*, right before it trades, not read off a dashboard by a human.
```
✅ Verdict: FAIR (deviation: 0.11%)
```
## The problem
Robinhood Chain's [Stock Tokens](https://docs.robinhood.com/chain/stock-tokens/) are self-custodiable ERC-20s that track real stocks and trade 24/7 on onchain DEXs. Real stock markets are only open ~6.5 hours a day, 5 days a week. Outside those hours there's no active market to arbitrage the token price back to fair value, so it can drift.
An AI agent trading these tokens directly onchain — holding a wallet key and swapping against a DEX pool — has **no built-in way to know if the price it's about to pay is trustworthy**. Every existing tool that surfaces this gap (dashboards, Telegram alert bots, trading terminals) is built for a *human* to look at. None of them are built to be *called by an agent* mid-decision.
## The solution
Two focused MCP tools:
1. **`check_stock_token_price`** — give it a ticker. It returns a structured, session-aware verdict an agent can act on — before it signs a trade, whether that trade goes through Robinhood's own Trading MCP or directly against an onchain DEX.
2. **`verify_stock_token`** — give it a ticker and a contract address. It tells you whether that contract is the *genuine, issuer-recognized* Stock Token. Robinhood Chain is permissionless, so ticker-impersonating tokens are rampant — onchain analysis has found hundreds of fake contracts per major ticker, with fake volume at times out-trading the genuine tokens 2:1. Symbol search alone is not safe; the registry check is the ground truth.
```json
{
"symbol": "AAPL",
"onchain": { "priceUsd": 310.32, "method": "liquidity-weighted-average", "poolsUsed": 10 },
"reference": { "tokenEquivalentPriceUsd": 309.99, "isTradingHalt": false },
"deviation": { "pct": 0.11, "direction": "premium" },
"marketSession": { "state": "weekend" },
"verdict": "fair",
"warnings": []
}
```
This server is **read-only**. It never holds a private key, never signs a transaction, and never places a trade.
## How it works
Two independent data sources, compared:
1. **Onchain price** — every indexed Robinhood Chain pool for the token, from [DexScreener's public API](https://docs.dexscreener.com/api/reference), filtered to pools quoted in a stable reference asset (USDG/USDC) with at least $500 of liquidity, combined into a **liquidity-weighted average** (so one deep pool dominates over several shallow, noisy ones). Falls back to the single deepest pool — flagged as low-confidence — if nothing passes the filter.
2. **Reference price** — Robinhood's own public [Stock Token API](https://docs.robinhood.com/chain/stock-token-apis/) (`/rhj/prices/{symbol}`), which reports the raw underlying-equity bid/ask. This is scaled by the token's current [ERC-8056](https://docs.robinhood.com/chain/building-with-stock-tokens/) `multiplier` (from `/rhj/assets`) to get the token-equivalent fair price, since dividends are reinvested into the multiplier rather than paid out in cash.
The deviation between the two is compared against a **threshold table that depends on the current US market session** (`regular`, `pre-market`, `after-hours`, `weekend`, `holiday`, `closed`), computed locally with no external dependency — a 2% gap on a Saturday night is expected; the same gap at 11am on a Tuesday is not. Session detection includes a full **NYSE holiday calendar**, computed algorithmically (nth-weekday-of-month rules, an Easter calculation for Good Friday, and the standard weekend-observance shift) rather than fetched from any external API, so it works for any year with no network dependency, no API key, and no static file to go stale. Verified against NYSE's officially published 2026-2028 calendar in [`test/nyseHolidays.test.ts`](./test/nyseHolidays.test.ts). See [`src/config.ts`](./src/config.ts) for the exact thresholds and [`src/nyseHolidays.ts`](./src/nyseHolidays.ts) for the holiday rules.
## Install & run
```bash
git clone https://github.com/kushal613/pegcheck-mcp.git
cd pegcheck-mcp
npm install
npm run build
```
No API keys, no `.env` file, no wallet — every data source used is a free, public, keyless API.
### Try it from the terminal
```bash
npm run check -- AAPL
npm run check -- TSLA
npm run check -- verify TSLA 0x322F0929c4625eD5bAd873c95208D54E1c003b2d
```
### Connect it to an MCP client
**Claude Desktop / Claude Code** (`claude_desktop_config.json` or `.mcp.json`):
```json
{
"mcpServers": {
"pegcheck": {
"command": "node",
"args": ["/absolute/path/to/pegcheck-mcp/dist/index.js"]
}
}
}
```
**Cursor** (`.cursor/mcp.json`): same shape as above.
Once connected, ask your agent: *"Before you buy any TSLA stock token, check pegcheck to see if the price is fair right now."*
## Tool reference
### `check_stock_token_price`
| Field | Type | Description |
|---|---|---|
| `symbol` (input) | `string` | Ticker, e.g. `"AAPL"`. |
| `onchain.priceUsd` | `number \| null` | Liquidity-weighted onchain price. |
| `onchain.method` | `string` | `liquidity-weighted-average`, `deepest-pool` (low confidence), or `none`. |
| `reference.tokenEquivalentPriceUsd` | `number \| null` | Real stock mid-price, scaled by the corporate-action multiplier. |
| `deviation.pct` | `number \| null` | Signed % deviation, onchain vs. reference. |
| `marketSession.state` | `string` | `regular` \| `pre-market` \| `after-hours` \| `weekend` \| `holiday` \| `closed`. |
| `marketSession.holidayName` | `string \| null` | e.g. `"Thanksgiving Day"`, when `state` is `"holiday"`. |
| `verdict` | `string` | `fair` \| `caution` \| `unreliable` \| `no_liquidity` \| `unknown_symbol`. |
| `warnings` | `string[]` | Human-readable caveats (stale quote, trading halt, low-confidence pool, etc.). |
### `verify_stock_token`
| Field | Type | Description |
|---|---|---|
| `symbol` (input) | `string` | Ticker the token claims to be, e.g. `"TSLA"`. |
| `address` (input) | `string` | Contract address to verify (0x-prefixed EVM address). |
| `canonicalAddress` | `string \| null` | The genuine contract address from Robinhood's asset registry. |
| `verdict` | `string` | `canonical` \| `impostor` \| `unknown_symbol` \| `no_deployment`. |
| `warnings` | `string[]` | Human-readable explanation (e.g. the correct address when an impostor is detected). |
## Architecture
```
src/
config.ts Every tunable threshold, in one place
types.ts Shared types for the whole pipeline
http.ts Fetch wrapper with timeout + consistent errors
robinhoodApi.ts Reference leg: /rhj/assets + /rhj/prices (Robinhood's own APIs)
dexscreener.ts Onchain leg: liquidity-weighted price across Robinhood Chain pools
marketSession.ts Local US-market-session calculation (no external dependency)
nyseHolidays.ts Algorithmic NYSE holiday calendar (no external dependency)
pegCheck.ts Combines both legs into one PegCheckResult
verifyToken.ts Impostor guard: registry-backed token-identity verification
server.ts MCP tool registration
index.ts stdio entrypoint
cli.ts Standalone terminal usage (no MCP client needed)
scripts/
smoke-test.mjs End-to-end MCP protocol handshake test (initialize -> tools/list -> tools/call)
```
## Known limitations (v0.1)
- **No early-close handling.** NYSE's 1:00 PM closes (day after Thanksgiving, a weekday Christmas Eve) are not modeled as a distinct session — those afternoons will still report `after-hours`/`closed` a few hours later than the real early close. Full-day holiday closures are fully covered.
- **No venue-specific slippage estimate.** This reports the aggregate liquidity-weighted price, not "what would *my* $500 trade cost against *this specific* pool." That's a deliberate v0.1 scope boundary (a natural v0.2: an `advanced` mode that takes a trade size and a specific pool/venue and estimates price impact).
- **DexScreener coverage dependency.** If DexScreener hasn't indexed a very new pool yet, `onchain.method` will be `"none"` and the verdict will be `no_liquidity`. This is a "fail loud," not a silent wrong answer.
- **Not a substitute for due diligence.** See the disclaimer below.
## Disclaimer
This project is **not affiliated with or endorsed by Robinhood**. It is an independent, informational tool. Data is sourced from Robinhood's public APIs and DexScreener's public API and may be delayed, incomplete, or inaccurate. Nothing here is financial advice. This server cannot place trades and holds no funds or keys.
## License
MIT — see [LICENSE](./LICENSE).
TDQS
Scored across 1 tool
With only a single tool, there is no possibility of confusion or overlap. The tool's purpose is clearly distinct from anything else, meeting the 'clearly distinct purpose' criterion perfectly.
The tool name 'check_stock_token_price' follows a clean verb_noun pattern, and with only one tool there is no inconsistency. The name accurately describes the action and object.
A single tool feels thin for most servers, but here the scope is narrowly defined around one specific check. It is borderline acceptable but on the low end of the typical range, making it a 3 per calibration guidelines.
The tool fully addresses its stated purpose—checking price fairness before trading. It provides a verdict, deviation percentage, and supporting data, with no obvious missing operations for that specific task. It is not a CRUD domain, so the completeness is judged against the narrow mission.