isocast-mcp
# isocast-mcp
MCP server for **[Isocast](https://isocast.dev)** — Polymarket weather-market
bucket-transition signals for AI agents. Discover cities and inspect the signal shape
for free; buy prepaid signal bundles with USDC on Base via **x402**, poll the signals
you are entitled to, and register **Telegram** push delivery.
- **5 free tools** — no wallet, no API key.
- **4 paid/keyed tools** — unlocked by an `EVM_PRIVATE_KEY` (USDC on Base Mainnet for purchases).
An Isocast signal fires when a city's expected daily-high **temperature bucket** flips on
the corresponding Polymarket market — the moment the market's implied outcome moves from
one bucket to the next.
---
## Quick start
### Free tier (no wallet)
```json
{
"mcpServers": {
"isocast": {
"command": "npx",
"args": ["-y", "isocast-mcp"]
}
}
}
```
You get `get_health`, `get_cities`, `get_city`, `get_sample_signal`, `get_signal_meta`.
### Paid tier (with wallet)
Add an EVM wallet private key (0x-prefixed) that holds USDC on **Base Mainnet**:
```json
{
"mcpServers": {
"isocast": {
"command": "npx",
"args": ["-y", "isocast-mcp"],
"env": { "EVM_PRIVATE_KEY": "0x..." }
}
}
}
```
This additionally unlocks `buy_signals`, `poll_signals`, `set_telegram`, `get_spot`.
### Install snippets
Claude Code:
```bash
claude mcp add isocast -- npx -y isocast-mcp
# with a wallet:
claude mcp add isocast --env EVM_PRIVATE_KEY=0x... -- npx -y isocast-mcp
```
Claude Desktop / Cursor: add the JSON block above to your MCP config
(`claude_desktop_config.json` or `.cursor/mcp.json`).
---
## Tools
### Free tools
| Tool | Description |
| --- | --- |
| `get_health` | Isocast API health, payment mode, OFAC status + signal freshness. |
| `get_cities` | List every active city (slug, unit, timezone, station, latest signal seq). |
| `get_city` | One city by slug, with its current target market day + Polymarket URL. |
| `get_sample_signal` | A free real sample signal for a city (fixed seq-1, never the latest; cross-city fallback via `sampleNote`) — shows the exact signal shape. |
| `get_signal_meta` | Pricing + payment metadata: unit price, min spend, network, payTo, tier table. |
### Paid / keyed tools
| Tool | Description | Cost |
| --- | --- | --- |
| `buy_signals` | Buy the next N signals for a city (by `count` or `spend_usdc`). | USDC via x402 |
| `poll_signals` | Read the signals you are entitled to, using a bundle receipt. | free (uses receipt) |
| `set_telegram` | Register/delete the Telegram chat_id that receives your signals. | free (signed intent) |
| `get_spot` | Snapshot of a city's LATEST signal — exactly one, never history. Stock x402, no binding nonce; uncharged on unknown city / no data yet. | $0.01 via x402 |
---
## Pricing
Signals are sold in prepaid bundles. The effective unit price drops as the requested
count crosses volume tiers (live values come from `get_signal_meta`):
| Tier | Min count | Unit price (USDC) |
| --- | --- | --- |
| Min | 2 | $0.005 |
| Starter | 20 | $0.005 |
| Standard | 100 | $0.0045 |
| Pro | 500 | $0.004 |
| Whale | 2000 | $0.0035 |
**Minimum spend: $0.01** (i.e. 2 signals). `buy_signals` with `spend_usdc` derives the
count from these tiers and refuses a spend that cannot meet the minimum.
**Single latest signal:** `get_spot` is a flat **$0.01 USDC** per call — one snapshot of a
city's latest signal (never history). It is a plain x402 pay-per-call, not a bundle, so it
carries no receipt token and needs no binding nonce.
---
## How payments work (x402)
`buy_signals` speaks the [x402](https://x402.org) `exact` scheme over EIP-3009
`transferWithAuthorization` on Base:
1. `POST /v1/subscribe?city=…&count=…` with no payment → the API replies **HTTP 402**
with the accepted payment terms (`accepts[]`: `amount` in USDC atomic units, `payTo`,
`network`) and an `extra.binding` block.
2. The MCP constructs a signed USDC authorization and retries the request with the
payment attached.
3. On success the API returns your `paidThroughSeq`, a 24h **receiptToken**, and the
settlement `txHash`. The receipt is cached so `poll_signals` can read your bundle
without re-paying.
### The binding nonce (important)
Isocast is **anti-grief**: the EIP-3009 authorization `nonce` is not random — it is bound
to `(payer, citySlug, count)`:
```
nonce = keccak256(abi.encodePacked(payer, citySlug, uint256 count, bytes32 salt))
```
The 402 challenge publishes the `salt`, and the server recomputes and verifies this exact
nonce **before** settling — so a stock x402 client that sends a random nonce is
**rejected**. `isocast-mcp` computes the binding nonce for you automatically; you never
have to think about it.
> The binding nonce applies only to bundle purchases (`buy_signals` / `/v1/subscribe`).
> `get_spot` (`GET /v1/spot`) is a **stock** x402 call with **no** binding nonce — any
> standard x402 client can pay it, and this MCP settles it with the vanilla
> `@x402/fetch` flow.
---
## Environment variables
| Variable | Required | Description |
| --- | --- | --- |
| `EVM_PRIVATE_KEY` | No | 0x-prefixed wallet key with USDC on Base Mainnet. Enables the paid/keyed tools. Free tools work without it. |
| `ISOCAST_API_URL` | No | Override the API base URL (default `https://api.isocast.dev`). |
---
## Disclaimer
Isocast provides **informational data only**. It is **not** financial, trading, or betting
advice, and there is no guarantee of accuracy or of any market outcome. You assume all
risk for any decisions you make. Prediction markets are restricted or prohibited in some
jurisdictions — it is your responsibility to comply with the laws that apply to you. See
<https://isocast.dev/terms>.
---
## License
MIT © jcislo
TDQS
Scored across 5 tools
Each tool has a clearly distinct purpose: health check, list cities, get a single city, get a sample signal, and get pricing metadata. No two tools overlap in functionality, so an agent can unambiguously select the right one.
All tool names follow the same verb_noun pattern: get_health, get_cities, get_city, get_sample_signal, get_signal_meta. This is highly predictable and consistent.
With 5 tools, the server is well-scoped for a read-only API providing public data and metadata. Each tool earns its place without redundancy or bloat.
The description explicitly references buy_signals and poll_signals as the way to get live entitled signals, but these tools are missing from the server. This leaves a significant gap in the core workflow of actually obtaining signals, making the tool surface incomplete for the apparent domain.