@blockrun/mcp 0.22.3 → 0.23.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/README.md +2 -2
- package/dist/index.js +227 -128
- package/package.json +6 -3
- package/skills/exa-research/SKILL.md +177 -0
- package/skills/image-prompting/SKILL.md +308 -0
- package/skills/image-prompting/example-100t-poster.jpg +0 -0
- package/skills/modal/SKILL.md +118 -0
- package/skills/phone/SKILL.md +176 -0
- package/skills/prediction-markets/SKILL.md +263 -0
- package/skills/rpc/SKILL.md +106 -0
- package/skills/search/SKILL.md +96 -0
- package/skills/surf/SKILL.md +295 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: phone
|
|
3
|
+
description: Use when the user wants phone-number intelligence (lookup, carrier, line type, SIM-swap / call-forwarding fraud signals), US/CA number provisioning (rent a phone number), or outbound AI voice calls (Bland.ai under the hood — schedule, confirm, follow-up). Pay per call in USDC.
|
|
4
|
+
triggers:
|
|
5
|
+
- "phone lookup"
|
|
6
|
+
- "carrier lookup"
|
|
7
|
+
- "phone number intelligence"
|
|
8
|
+
- "sim swap"
|
|
9
|
+
- "call forwarding"
|
|
10
|
+
- "phone fraud"
|
|
11
|
+
- "buy phone number"
|
|
12
|
+
- "rent phone number"
|
|
13
|
+
- "us phone number"
|
|
14
|
+
- "ai voice call"
|
|
15
|
+
- "voice call"
|
|
16
|
+
- "outbound call"
|
|
17
|
+
- "appointment confirmation"
|
|
18
|
+
- "bland.ai"
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
# Phone & Voice
|
|
22
|
+
|
|
23
|
+
Two namespaces in one tool: **`/v1/phone/*`** for number intelligence + provisioning, **`/v1/voice/*`** for outbound AI calls. Pay per call in USDC.
|
|
24
|
+
|
|
25
|
+
Phone numbers use **E.164 format** — `+` followed by country code and subscriber digits (US: `+1` + 10 digits; UK: `+44` + 10 digits; etc.). The examples below use `<+E.164-number>` as a placeholder — the LLM should substitute the actual number from the user's request, not copy the literal placeholder.
|
|
26
|
+
|
|
27
|
+
## How to Call from MCP
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
const targetNumber = "<+E.164-number-from-user>" // e.g. user said "call my doctor at 415-..."
|
|
31
|
+
|
|
32
|
+
// Lookup carrier + line type
|
|
33
|
+
blockrun_phone({ path: "phone/lookup", body: { phoneNumber: targetNumber } })
|
|
34
|
+
|
|
35
|
+
// Buy a 30-day US number
|
|
36
|
+
blockrun_phone({ path: "phone/numbers/buy", body: { country: "US", areaCode: "415" } })
|
|
37
|
+
|
|
38
|
+
// Outbound AI call (requires `from` — see below)
|
|
39
|
+
const r = await blockrun_phone({ path: "voice/call", body: {
|
|
40
|
+
to: targetNumber,
|
|
41
|
+
from: "<+E.164-number-you-own>", // from phone/numbers/buy
|
|
42
|
+
task: "Confirm appointment for Friday at 3pm with Dr. Wong.",
|
|
43
|
+
voice: "june"
|
|
44
|
+
}})
|
|
45
|
+
// poll the result (free GET, no body)
|
|
46
|
+
blockrun_phone({ path: `voice/call/${r.call_id}` })
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Endpoint Catalog
|
|
50
|
+
|
|
51
|
+
### Phone intelligence + numbers (`/v1/phone/*`)
|
|
52
|
+
|
|
53
|
+
| Path | Body | Price | Effect |
|
|
54
|
+
|---|---|---|---|
|
|
55
|
+
| `phone/lookup` | `{ phoneNumber }` | $0.01 | Carrier, line type (mobile/landline/VoIP) |
|
|
56
|
+
| `phone/lookup/fraud` | `{ phoneNumber }` | $0.05 | + SIM-swap signals, call-forwarding detection |
|
|
57
|
+
| `phone/numbers/buy` | `{ country?: "US"\|"CA", areaCode? }` | $5.00 | 30-day lease, US or CA |
|
|
58
|
+
| `phone/numbers/renew` | `{ phoneNumber }` | $5.00 | Extend lease 30 days |
|
|
59
|
+
| `phone/numbers/list` | `{}` | $0.001 | Your wallet-owned numbers |
|
|
60
|
+
| `phone/numbers/release` | `{ phoneNumber }` | free | Return to pool |
|
|
61
|
+
|
|
62
|
+
### Outbound AI calls (`/v1/voice/*`)
|
|
63
|
+
|
|
64
|
+
| Path | Method | Body | Price |
|
|
65
|
+
|---|---|---|---|
|
|
66
|
+
| `voice/call` | POST | `{ to, task, from, voice?, max_duration?, language?, first_sentence?, wait_for_greeting? }` | $0.54 flat |
|
|
67
|
+
| `voice/call/{call_id}` | GET (no body) | – | free poll |
|
|
68
|
+
|
|
69
|
+
> **`from` is REQUIRED and must be a number your wallet owns.** Provision one first with `phone/numbers/buy` ($5, 30-day lease). 400 errors from `voice/call` are almost always missing `from`.
|
|
70
|
+
|
|
71
|
+
## Voice Call Body Fields
|
|
72
|
+
|
|
73
|
+
| Field | Required | Default | Notes |
|
|
74
|
+
|---|---|---|---|
|
|
75
|
+
| `to` | yes | – | Destination E.164 number |
|
|
76
|
+
| `task` | yes | – | What the AI should do on the call (10–4000 chars) |
|
|
77
|
+
| `from` | **yes** | – | Your provisioned BlockRun caller-ID number (from `phone/numbers/buy`) |
|
|
78
|
+
| `voice` | no | `nat` | `nat` / `josh` / `maya` / `june` / `paige` / `derek` / `florian` |
|
|
79
|
+
| `max_duration` | no | 5 | Minutes, 1–30 |
|
|
80
|
+
| `language` | no | `en-US` | Language code, BCP-47 |
|
|
81
|
+
| `first_sentence` | no | – | Custom opening line for the AI |
|
|
82
|
+
| `wait_for_greeting` | no | false | Let recipient speak first, then AI starts |
|
|
83
|
+
|
|
84
|
+
## Voice Presets
|
|
85
|
+
|
|
86
|
+
| Voice | Tone |
|
|
87
|
+
|---|---|
|
|
88
|
+
| `nat` | Neutral / professional male, default |
|
|
89
|
+
| `josh` | Friendly male |
|
|
90
|
+
| `maya` | Warm female |
|
|
91
|
+
| `june` | Calm professional female |
|
|
92
|
+
| `paige` | Energetic female |
|
|
93
|
+
| `derek` | Deep male |
|
|
94
|
+
| `florian` | European-accented male |
|
|
95
|
+
|
|
96
|
+
## Worked Examples
|
|
97
|
+
|
|
98
|
+
### 1. Triage an inbound number for fraud
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
blockrun_phone({ path: "phone/lookup/fraud", body: { phoneNumber: "+14155550150" } })
|
|
102
|
+
```
|
|
103
|
+
Returns carrier, line type, SIM-swap indicator, call-forwarding state. **Cost: $0.05.**
|
|
104
|
+
|
|
105
|
+
### 2. Spin up a US number with a 415 area code
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
blockrun_phone({ path: "phone/numbers/buy", body: { country: "US", areaCode: "415" } })
|
|
109
|
+
// returns { phoneNumber: "+14155550199", expires_at: "..." }
|
|
110
|
+
```
|
|
111
|
+
Best-effort area code match. **Cost: $5.00 for 30 days.**
|
|
112
|
+
|
|
113
|
+
### 3. Confirm an appointment via AI voice call
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
// Step 0 (one-time): provision a number you'll use as caller ID
|
|
117
|
+
const { phoneNumber: myNumber } = await blockrun_phone({
|
|
118
|
+
path: "phone/numbers/buy", body: { country: "US", areaCode: "415" }
|
|
119
|
+
}) // $5.00, 30-day lease
|
|
120
|
+
|
|
121
|
+
// Step 1: place the call (from is REQUIRED)
|
|
122
|
+
const r = await blockrun_phone({ path: "voice/call", body: {
|
|
123
|
+
to: "+14155550100",
|
|
124
|
+
from: myNumber,
|
|
125
|
+
task: "Call Dr. Wong's office. Confirm the appointment for Sarah Chen on Friday May 24th at 3pm. If the time isn't available, ask for the next opening on Friday afternoon and report back.",
|
|
126
|
+
voice: "june",
|
|
127
|
+
max_duration: 5,
|
|
128
|
+
wait_for_greeting: true
|
|
129
|
+
}})
|
|
130
|
+
// returns { call_id: "call_abc..." } — call runs async
|
|
131
|
+
|
|
132
|
+
// Poll until done
|
|
133
|
+
while (true) {
|
|
134
|
+
const status = await blockrun_phone({ path: `voice/call/${r.call_id}` })
|
|
135
|
+
if (status.status === "completed") {
|
|
136
|
+
console.log(status.summary, status.transcript)
|
|
137
|
+
break
|
|
138
|
+
}
|
|
139
|
+
await new Promise(r => setTimeout(r, 5000))
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
**Cost: $0.54 flat for the call.** Status polling is free.
|
|
143
|
+
|
|
144
|
+
### 4. List your wallet's leased numbers + release one
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
const { numbers } = await blockrun_phone({ path: "phone/numbers/list", body: {} })
|
|
148
|
+
// Release the oldest
|
|
149
|
+
await blockrun_phone({ path: "phone/numbers/release", body: { phoneNumber: numbers[0].phoneNumber } })
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Best Practices
|
|
153
|
+
|
|
154
|
+
- **Always include task context the AI can act on.** "Confirm appointment" is vague; "Confirm Sarah Chen's appointment for Friday May 24 at 3pm with Dr. Wong" is actionable.
|
|
155
|
+
- **Use `wait_for_greeting: true`** for human-answered calls (most cases). Set to `false` for known-IVR / known-bot destinations to skip the greeting wait.
|
|
156
|
+
- **`max_duration` is a hard cap** — the call ends regardless of conversation state. Default 5 min covers most scripted tasks.
|
|
157
|
+
- **`from` matters** for trust** — using a provisioned BlockRun number you own (from `numbers/buy`) makes the call look less spammy than a random caller ID.
|
|
158
|
+
|
|
159
|
+
## When NOT to Use
|
|
160
|
+
|
|
161
|
+
- **High-volume autodialer / robocall workloads** — pricing is per-call ($0.54 each) which doesn't amortize like wholesale telephony
|
|
162
|
+
- **Receiving inbound calls** — `numbers/buy` provisions a number but inbound routing is not exposed via MCP; use Bland directly
|
|
163
|
+
- **Two-way human-to-human calls** — this is for AI-driven outbound; for real-time human bridging, use a SIP provider
|
|
164
|
+
|
|
165
|
+
## Notes
|
|
166
|
+
|
|
167
|
+
- `phone/lookup` is cheap reconnaissance; `phone/lookup/fraud` is 5× the price but adds SIM-swap + call-forwarding signals you can't get from a basic carrier lookup
|
|
168
|
+
- Voice calls return immediately with a `call_id`; the call runs in the background. Always poll `voice/call/{call_id}` to get the transcript
|
|
169
|
+
- The poll endpoint is free — poll as often as you want, but every 5s is plenty
|
|
170
|
+
- Calls that fail upstream (recipient hangs up, no answer) still charge the flat $0.54 — Bland's pricing model
|
|
171
|
+
|
|
172
|
+
## Reference
|
|
173
|
+
|
|
174
|
+
- Phone endpoints: `POST /v1/phone/*`
|
|
175
|
+
- Voice endpoints: `POST /v1/voice/call` and `GET /v1/voice/call/{call_id}`
|
|
176
|
+
- Upstream: number intelligence + provisioning is internal; voice calls are Bland.ai
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: prediction-markets
|
|
3
|
+
description: Use when user asks about event probabilities, prediction market odds, what people are betting on, Polymarket or Kalshi prices, sports markets, wallet identity / clustering, or wants to find markets on a specific topic (elections, crypto, sports, macro events). Predexon v2 endpoints (canonical cross-venue markets, sports, wallet identity, keyset pagination) live in production as of 2026-05.
|
|
4
|
+
triggers:
|
|
5
|
+
- "polymarket"
|
|
6
|
+
- "kalshi"
|
|
7
|
+
- "dflow"
|
|
8
|
+
- "prediction market"
|
|
9
|
+
- "event probability"
|
|
10
|
+
- "betting odds"
|
|
11
|
+
- "what are people betting on"
|
|
12
|
+
- "election odds"
|
|
13
|
+
- "crypto market odds"
|
|
14
|
+
- "binance futures"
|
|
15
|
+
- "yes/no market"
|
|
16
|
+
- "implied probability"
|
|
17
|
+
- "sports markets"
|
|
18
|
+
- "wallet identity"
|
|
19
|
+
- "wallet cluster"
|
|
20
|
+
- "cross-venue markets"
|
|
21
|
+
- "predexon"
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# Prediction Markets
|
|
25
|
+
|
|
26
|
+
Real-time prediction market data via BlockRun (powered by Predexon v2). Covers canonical cross-venue markets, Polymarket, Kalshi, Limitless, Opinion, Predict.Fun, dFlow, sports, and Binance Futures.
|
|
27
|
+
|
|
28
|
+
## Quick Decision Table
|
|
29
|
+
|
|
30
|
+
| User wants... | Method | Path | Cost |
|
|
31
|
+
|--------------|--------|------|------|
|
|
32
|
+
| **Canonical cross-venue markets** | `client.pm(path)` | `"markets"` | $0.001 |
|
|
33
|
+
| **Venue-native flattened listings** | `client.pm(path)` | `"markets/listings"` | $0.001 |
|
|
34
|
+
| **Resolve canonical outcome ID** | `client.pm(path)` | `"outcomes/{predexon_id}"` | $0.001 |
|
|
35
|
+
| **Search across all venues** | `client.pm(path, q=...)` | `"markets/search"` | $0.005 |
|
|
36
|
+
| Active Polymarket events | `client.pm(path)` | `"polymarket/events"` | $0.001 |
|
|
37
|
+
| Polymarket events (keyset paginated) | `client.pm(path, pagination_key=...)` | `"polymarket/events/keyset"` | $0.001 |
|
|
38
|
+
| Polymarket markets (keyset paginated) | `client.pm(path, pagination_key=...)` | `"polymarket/markets/keyset"` | $0.001 |
|
|
39
|
+
| Specific Kalshi market | `client.pm(path)` | `"kalshi/markets/TICKER"` | $0.001 |
|
|
40
|
+
| **Sports categories** | `client.pm(path)` | `"sports/categories"` | $0.001 |
|
|
41
|
+
| **Sports markets (by league/sport)** | `client.pm(path, league=...)` | `"sports/markets"` | $0.001 |
|
|
42
|
+
| **Wallet identity (single)** | `client.pm(path)` | `"polymarket/wallet/identity/{wallet}"` | $0.005 |
|
|
43
|
+
| **Wallet identity (bulk, up to 200)** | `client.pm_query(path, body)` | `"polymarket/wallet/identities"` | $0.005 |
|
|
44
|
+
| **Wallet on-chain cluster** | `client.pm(path)` | `"polymarket/wallet/{address}/cluster"` | $0.005 |
|
|
45
|
+
| dFlow markets | `client.pm(path)` | `"dflow/..."` | $0.001-$0.005 |
|
|
46
|
+
| Binance Futures | `client.pm(path)` | `"binance/..."` | $0.005 |
|
|
47
|
+
|
|
48
|
+
## Instructions
|
|
49
|
+
|
|
50
|
+
### 1. Initialize
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
import os
|
|
54
|
+
from pathlib import Path
|
|
55
|
+
|
|
56
|
+
chain_file = Path.home() / ".blockrun" / ".chain"
|
|
57
|
+
chain = chain_file.read_text().strip() if chain_file.exists() else "base"
|
|
58
|
+
|
|
59
|
+
if chain == "solana":
|
|
60
|
+
from blockrun_llm import setup_agent_solana_wallet
|
|
61
|
+
client = setup_agent_solana_wallet()
|
|
62
|
+
else:
|
|
63
|
+
from blockrun_llm import setup_agent_wallet
|
|
64
|
+
client = setup_agent_wallet()
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 2. List Active Events
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
# All active Polymarket events
|
|
71
|
+
events = client.pm("polymarket/events")
|
|
72
|
+
for event in events.get("data", events if isinstance(events, list) else [])[:10]:
|
|
73
|
+
print(f"{event.get('title', '?')} — {event.get('slug', '')}")
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 3. Search by Topic
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
# Search Polymarket for a topic
|
|
80
|
+
results = client.pm("polymarket/search", q="bitcoin ETF")
|
|
81
|
+
for market in results.get("data", results if isinstance(results, list) else [])[:10]:
|
|
82
|
+
title = market.get("title", "?")
|
|
83
|
+
# Outcome prices are in the market object
|
|
84
|
+
print(f"{title}")
|
|
85
|
+
for outcome in market.get("outcomes", []):
|
|
86
|
+
print(f" {outcome.get('title', '?')}: {outcome.get('price', '?')}")
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 4. Specific Kalshi Market
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
# Get a specific Kalshi market by ticker
|
|
93
|
+
market = client.pm("kalshi/markets/KXBTC-25MAR14")
|
|
94
|
+
print(f"Market: {market.get('title', market.get('ticker', '?'))}")
|
|
95
|
+
yes_price = market.get("yes_bid", market.get("yes_price", "?"))
|
|
96
|
+
no_price = market.get("no_bid", market.get("no_price", "?"))
|
|
97
|
+
print(f"YES: {yes_price} | NO: {no_price}")
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 5. Structured Query (POST)
|
|
101
|
+
|
|
102
|
+
Use for complex filtering — active markets only, sorted by volume, with pagination.
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
# Polymarket: active markets sorted by volume, limit 20
|
|
106
|
+
data = client.pm_query("polymarket/query", {
|
|
107
|
+
"filter": "active",
|
|
108
|
+
"limit": 20,
|
|
109
|
+
"order": "volume",
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
# Kalshi: all markets in a specific series
|
|
113
|
+
data = client.pm_query("kalshi/query", {
|
|
114
|
+
"series_ticker": "KXBTC",
|
|
115
|
+
"limit": 50,
|
|
116
|
+
})
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 6. Canonical Cross-Venue Markets (v2)
|
|
120
|
+
|
|
121
|
+
Predexon v2 unifies markets across Polymarket, Kalshi, Limitless, Opinion, Predict.Fun behind canonical IDs. One call returns the same question regardless of venue.
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
# All canonical markets (filter by venue, status, category, league, event_id)
|
|
125
|
+
markets = client.pm("markets", venue="polymarket", status="active")
|
|
126
|
+
for m in markets.get("markets", [])[:10]:
|
|
127
|
+
print(f"{m.get('predexon_id')} — {m.get('title')}")
|
|
128
|
+
|
|
129
|
+
# Flattened venue-native listings (each row = a tradable listing on one venue)
|
|
130
|
+
listings = client.pm("markets/listings", category="elections")
|
|
131
|
+
|
|
132
|
+
# Resolve a canonical outcome ID across venues
|
|
133
|
+
detail = client.pm("outcomes/PXM-12345")
|
|
134
|
+
print(detail.get("title"), detail.get("venue_listings"))
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### 7. Sports Markets (v2)
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
# List sports categories (NBA, NFL, MLB, soccer leagues, …)
|
|
141
|
+
categories = client.pm("sports/categories")
|
|
142
|
+
|
|
143
|
+
# Sports markets grouped by game — filter by league or venue
|
|
144
|
+
nba = client.pm("sports/markets", league="NBA", status="open")
|
|
145
|
+
for game in nba.get("markets", [])[:10]:
|
|
146
|
+
print(f"{game.get('title')} @ {game.get('start_time')}")
|
|
147
|
+
for venue in game.get("venue_listings", []):
|
|
148
|
+
print(f" {venue.get('venue')}: {venue.get('price')}")
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### 8. Keyset Pagination (v2)
|
|
152
|
+
|
|
153
|
+
For large Polymarket result sets, prefer keyset pagination over offset. It is stable across writes and faster on big tables.
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
# First page
|
|
157
|
+
page = client.pm("polymarket/markets/keyset", limit="100")
|
|
158
|
+
markets = page.get("markets", [])
|
|
159
|
+
next_key = page.get("pagination", {}).get("next_key")
|
|
160
|
+
|
|
161
|
+
# Subsequent pages
|
|
162
|
+
while next_key:
|
|
163
|
+
page = client.pm("polymarket/markets/keyset", limit="100", pagination_key=next_key)
|
|
164
|
+
markets.extend(page.get("markets", []))
|
|
165
|
+
next_key = page.get("pagination", {}).get("next_key")
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 9. Wallet Identity & On-Chain Clustering (v2, Tier 2)
|
|
169
|
+
|
|
170
|
+
Cross-context wallet labels (ENS, Twitter, Discord, portfolio metrics) plus on-chain relationship graph data — exposed as three endpoints. **All Tier 2 ($0.005/call).**
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
# Single wallet identity
|
|
174
|
+
ident = client.pm("polymarket/wallet/identity/0xabc...")
|
|
175
|
+
print(ident.get("ens_name"), ident.get("twitter"), ident.get("portfolio_value"))
|
|
176
|
+
|
|
177
|
+
# Bulk identity lookup (POST, up to 200 wallets per call)
|
|
178
|
+
batch = client.pm_query("polymarket/wallet/identities", {
|
|
179
|
+
"addresses": ["0xabc...", "0xdef...", "0x123..."],
|
|
180
|
+
})
|
|
181
|
+
for row in batch.get("results", []):
|
|
182
|
+
print(row.get("wallet"), row.get("label"))
|
|
183
|
+
|
|
184
|
+
# Cluster — discover wallets connected via on-chain transfers + identity proofs
|
|
185
|
+
cluster = client.pm("polymarket/wallet/0xabc.../cluster")
|
|
186
|
+
for related in cluster.get("cluster", []):
|
|
187
|
+
print(related.get("wallet"), related.get("relationship_type"), related.get("confidence_score"))
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Common Workflows
|
|
191
|
+
|
|
192
|
+
**"What are people betting on in crypto right now?"**
|
|
193
|
+
```python
|
|
194
|
+
events = client.pm("polymarket/search", q="crypto bitcoin ethereum")
|
|
195
|
+
for e in events.get("data", [])[:5]:
|
|
196
|
+
print(e.get("title", "?"))
|
|
197
|
+
for o in e.get("outcomes", []):
|
|
198
|
+
print(f" {o.get('title')}: {o.get('price')} (implies {round(float(o.get('price', 0))*100)}%)")
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**"Track a smart wallet's identity + cluster"**
|
|
202
|
+
```python
|
|
203
|
+
seed = "0xabc..."
|
|
204
|
+
ident = client.pm(f"polymarket/wallet/identity/{seed}")
|
|
205
|
+
cluster = client.pm(f"polymarket/wallet/{seed}/cluster")
|
|
206
|
+
print(f"{ident.get('label')} (ENS {ident.get('ens_name')})")
|
|
207
|
+
print(f" Cluster size: {len(cluster.get('cluster', []))} wallets")
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**"What's the probability of X event?"**
|
|
211
|
+
```python
|
|
212
|
+
# 1. Search for the event
|
|
213
|
+
results = client.pm("polymarket/search", q="US election 2026")
|
|
214
|
+
|
|
215
|
+
# 2. Get specific market details
|
|
216
|
+
if results.get("data"):
|
|
217
|
+
market_id = results["data"][0].get("id", results["data"][0].get("slug"))
|
|
218
|
+
detail = client.pm(f"polymarket/events/{market_id}")
|
|
219
|
+
print(detail)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**"Show me all active Kalshi markets"**
|
|
223
|
+
```python
|
|
224
|
+
data = client.pm_query("kalshi/query", {"limit": 50, "status": "open"})
|
|
225
|
+
markets = data.get("markets", data.get("data", []))
|
|
226
|
+
for m in markets[:10]:
|
|
227
|
+
print(f"{m.get('ticker')} — {m.get('title')}: YES={m.get('yes_bid')} NO={m.get('no_bid')}")
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Data Case Study: Sentiment Signal from Markets
|
|
231
|
+
|
|
232
|
+
Prediction markets are often better probability estimates than polls or pundit takes. Pattern:
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
import json
|
|
236
|
+
|
|
237
|
+
# 1. Find relevant markets
|
|
238
|
+
crypto_markets = client.pm("polymarket/search", q="bitcoin price end of year")
|
|
239
|
+
|
|
240
|
+
# 2. Extract implied probabilities
|
|
241
|
+
for market in crypto_markets.get("data", [])[:3]:
|
|
242
|
+
print(f"\n{market.get('title', '?')}")
|
|
243
|
+
for outcome in market.get("outcomes", []):
|
|
244
|
+
p = float(outcome.get("price", 0)) * 100
|
|
245
|
+
print(f" {outcome.get('title')}: {p:.0f}% implied probability")
|
|
246
|
+
|
|
247
|
+
# 3. Save for later analysis
|
|
248
|
+
with open(os.path.expanduser("~/.blockrun/data/markets_snapshot.json"), "w") as f:
|
|
249
|
+
json.dump(crypto_markets, f, indent=2, default=str)
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Notes on Response Shape
|
|
253
|
+
|
|
254
|
+
Predexon returns raw API responses. Structure varies by exchange:
|
|
255
|
+
- **Polymarket**: usually `{ "data": [...] }` or `{ "events": [...] }`
|
|
256
|
+
- **Kalshi**: usually `{ "markets": [...] }` with `ticker`, `yes_bid`, `no_bid` fields
|
|
257
|
+
- **Print raw response first** when exploring a new path: `print(json.dumps(result, indent=2)[:1000])`
|
|
258
|
+
|
|
259
|
+
## Requirements
|
|
260
|
+
|
|
261
|
+
- BlockRun SDK: `pip install blockrun-llm`
|
|
262
|
+
- USDC wallet funded (`client.get_balance()`)
|
|
263
|
+
- Kalshi tickers: format is `KXBTC-25MAR14` (series + expiry date)
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rpc
|
|
3
|
+
description: Use when the user needs raw blockchain JSON-RPC access — contract reads (eth_call), native balances, blocks, transactions, logs, gas estimates, or any chain-native RPC method across 40+ chains. One endpoint per chain via BlockRun's Tatum-backed gateway, $0.002 per call, no node, no API key. Prefer blockrun_price / blockrun_dex / blockrun_surf when they already cover the question.
|
|
4
|
+
triggers:
|
|
5
|
+
- "rpc"
|
|
6
|
+
- "json-rpc"
|
|
7
|
+
- "eth_call"
|
|
8
|
+
- "contract read"
|
|
9
|
+
- "raw chain"
|
|
10
|
+
- "node access"
|
|
11
|
+
- "blockchain rpc"
|
|
12
|
+
- "getBalance"
|
|
13
|
+
- "block number"
|
|
14
|
+
- "transaction receipt"
|
|
15
|
+
- "event logs"
|
|
16
|
+
- "tatum"
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# Multi-chain RPC — 40+ chains, one tool
|
|
20
|
+
|
|
21
|
+
`blockrun_rpc` POSTs a standard JSON-RPC 2.0 body to `/v1/rpc/{network}`. Flat **$0.002 per call**; a JSON-RPC **batch array charges per element**. Settlement in USDC via x402. Responses are cached briefly server-side for identical read calls (`X-Cache: HIT` is free of upstream latency but still billed).
|
|
22
|
+
|
|
23
|
+
## When to use which tool
|
|
24
|
+
|
|
25
|
+
| Question | Tool |
|
|
26
|
+
|---|---|
|
|
27
|
+
| "What's ETH trading at?" | `blockrun_price` (free) |
|
|
28
|
+
| "PEPE/WETH pool liquidity?" | `blockrun_dex` (free) |
|
|
29
|
+
| "What's this wallet labeled as / holding?" | `blockrun_surf` |
|
|
30
|
+
| "Call `balanceOf(0x...)` on this ERC-20" | **`blockrun_rpc`** |
|
|
31
|
+
| "Latest block / tx receipt / event logs / gas price" | **`blockrun_rpc`** |
|
|
32
|
+
| "Solana account info / slot / signatures" | **`blockrun_rpc`** |
|
|
33
|
+
|
|
34
|
+
## Networks
|
|
35
|
+
|
|
36
|
+
EVM (use `eth_*` methods):
|
|
37
|
+
|
|
38
|
+
| Key | Chain | | Key | Chain |
|
|
39
|
+
|---|---|---|---|---|
|
|
40
|
+
| `ethereum` | Ethereum | | `zksync` | zkSync Era |
|
|
41
|
+
| `base` | Base | | `berachain` | Berachain |
|
|
42
|
+
| `arbitrum` | Arbitrum One | | `unichain` | Unichain |
|
|
43
|
+
| `arbitrum-nova` | Arbitrum Nova | | `monad` | Monad |
|
|
44
|
+
| `optimism` | Optimism | | `chiliz` | Chiliz |
|
|
45
|
+
| `polygon` | Polygon | | `moonbeam` | Moonbeam |
|
|
46
|
+
| `bsc` | BNB Smart Chain | | `aurora` | Aurora |
|
|
47
|
+
| `avalanche` | Avalanche C-Chain | | `flare` | Flare |
|
|
48
|
+
| `fantom` | Fantom | | `oasis` | Oasis Sapphire |
|
|
49
|
+
| `cronos` | Cronos | | `kaia` | Kaia (Klaytn) |
|
|
50
|
+
| `celo` | Celo | | `sonic` | Sonic |
|
|
51
|
+
| `gnosis` | Gnosis | | `xdc` | XDC Network |
|
|
52
|
+
| `abstract` | Abstract | | `hyperevm` | HyperEVM |
|
|
53
|
+
| `plume` | Plume | | `ronin` | Ronin |
|
|
54
|
+
| `rootstock` | Rootstock (RSK) | | | |
|
|
55
|
+
|
|
56
|
+
Non-EVM (chain-native methods):
|
|
57
|
+
|
|
58
|
+
| Key | Chain | Method family |
|
|
59
|
+
|---|---|---|
|
|
60
|
+
| `solana` | Solana | `getSlot`, `getBalance`, `getAccountInfo`, `getSignaturesForAddress` |
|
|
61
|
+
| `bitcoin` | Bitcoin | `getblockchaininfo`, `getblockcount`, `getrawtransaction` |
|
|
62
|
+
| `litecoin` / `dogecoin` / `bitcoin-cash` / `zcash` | UTXO chains | Bitcoin-style RPC |
|
|
63
|
+
| `near` | NEAR | `status`, `query` |
|
|
64
|
+
| `sui` | Sui | `sui_getLatestCheckpointSequenceNumber`, `suix_getBalance` |
|
|
65
|
+
| `ripple` | XRP Ledger | `account_info`, `ledger` |
|
|
66
|
+
| `polkadot` / `kusama` | Substrate | `chain_getHeader`, `state_getStorage` |
|
|
67
|
+
|
|
68
|
+
Unknown-but-wellformed slugs pass through to the Tatum gateway untouched — newly added chains work without an MCP update. A bad slug returns HTTP 400 with the supported list (no charge).
|
|
69
|
+
|
|
70
|
+
## Recipes
|
|
71
|
+
|
|
72
|
+
ERC-20 balance (eth_call):
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
blockrun_rpc({
|
|
76
|
+
network: "base",
|
|
77
|
+
method: "eth_call",
|
|
78
|
+
params: [{
|
|
79
|
+
to: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC on Base
|
|
80
|
+
data: "0x70a08231000000000000000000000000<ADDRESS_NO_0x_PADDED_TO_32B>"
|
|
81
|
+
}, "latest"]
|
|
82
|
+
})
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Native balance: `method: "eth_getBalance", params: ["0x...", "latest"]`
|
|
86
|
+
Tx receipt: `method: "eth_getTransactionReceipt", params: ["0x<txhash>"]`
|
|
87
|
+
Event logs: `method: "eth_getLogs", params: [{ fromBlock, toBlock, address, topics }]` — keep ranges small; huge ranges are rejected upstream (no charge).
|
|
88
|
+
Gas: `method: "eth_gasPrice"` / `eth_estimateGas`
|
|
89
|
+
|
|
90
|
+
Solana token account: `network: "solana", method: "getTokenAccountsByOwner", params: ["<owner>", {"mint": "<mint>"}, {"encoding": "jsonParsed"}]`
|
|
91
|
+
|
|
92
|
+
Batch (charged per element — 3 calls = $0.006):
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
blockrun_rpc({ network: "ethereum", body: [
|
|
96
|
+
{ jsonrpc: "2.0", id: 1, method: "eth_blockNumber" },
|
|
97
|
+
{ jsonrpc: "2.0", id: 2, method: "eth_gasPrice" },
|
|
98
|
+
{ jsonrpc: "2.0", id: 3, method: "eth_chainId" }
|
|
99
|
+
]})
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Failure semantics
|
|
103
|
+
|
|
104
|
+
- Upstream timeout / 5xx → **no charge**, retry safe.
|
|
105
|
+
- Malformed body / bad network → HTTP 400, **no charge**.
|
|
106
|
+
- JSON-RPC-level errors (e.g. revert on `eth_call`) come back inside `result.error` — the call **is** charged (the node did the work).
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: search
|
|
3
|
+
description: Use when the user wants real-time web, news, or X/Twitter results with AI-summarized answers and citations — Grok Live Search via BlockRun. Cheapest path for "what just happened" questions where freshness beats neural-semantic ranking.
|
|
4
|
+
triggers:
|
|
5
|
+
- "live search"
|
|
6
|
+
- "grok live"
|
|
7
|
+
- "what just happened"
|
|
8
|
+
- "real time news"
|
|
9
|
+
- "breaking news"
|
|
10
|
+
- "today's news"
|
|
11
|
+
- "search with citations"
|
|
12
|
+
- "cited search"
|
|
13
|
+
- "x search"
|
|
14
|
+
- "twitter search"
|
|
15
|
+
- "news search"
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# Live Search (Grok)
|
|
19
|
+
|
|
20
|
+
Real-time web + X/Twitter + news search with AI-summarized results and citations. **$0.025 × `max_results`** (default 10 → $0.25 per call). Best for *fresh* queries; for semantic / neural research use `blockrun_exa` instead.
|
|
21
|
+
|
|
22
|
+
## How to Call from MCP
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
blockrun_search({ body: {
|
|
26
|
+
query: "what's the consensus on the Fed's next move",
|
|
27
|
+
sources: ["web", "news"],
|
|
28
|
+
max_results: 10
|
|
29
|
+
}})
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Body Shape
|
|
33
|
+
|
|
34
|
+
| Field | Required | Type | Notes |
|
|
35
|
+
|---|---|---|---|
|
|
36
|
+
| `query` | yes | string | Natural-language search query |
|
|
37
|
+
| `sources` | no | string[] | Subset of `["web","x","news"]`. Default: all three. Does NOT multiply price. |
|
|
38
|
+
| `max_results` | no | number | 1–50, default 10. **Drives the price** at $0.025 each. |
|
|
39
|
+
| `from_date` | no | string | `YYYY-MM-DD` lower bound on result date |
|
|
40
|
+
| `to_date` | no | string | `YYYY-MM-DD` upper bound |
|
|
41
|
+
|
|
42
|
+
## When to Reach for Which Source
|
|
43
|
+
|
|
44
|
+
| User intent | `sources` setting |
|
|
45
|
+
|---|---|
|
|
46
|
+
| Breaking news / today's headlines | `["news"]` |
|
|
47
|
+
| What's the CT / KOL sentiment on X | `["x"]` |
|
|
48
|
+
| Backgrounder / explainer / docs | `["web"]` |
|
|
49
|
+
| General "find current info" question | omit — defaults to all three |
|
|
50
|
+
|
|
51
|
+
## Worked Examples
|
|
52
|
+
|
|
53
|
+
### 1. "What's the latest on the ETH ETF approval timeline?"
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
blockrun_search({ body: { query: "Ethereum ETF approval SEC", sources: ["news","web"], max_results: 8 } })
|
|
57
|
+
```
|
|
58
|
+
**Cost: $0.025 × 8 = $0.20.**
|
|
59
|
+
|
|
60
|
+
### 2. "What is X saying about Solana's latest outage?"
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
blockrun_search({ body: { query: "Solana outage today", sources: ["x"], max_results: 15 } })
|
|
64
|
+
```
|
|
65
|
+
**Cost: $0.025 × 15 = $0.375.**
|
|
66
|
+
|
|
67
|
+
### 3. "Background on Pectra upgrade, last 90 days only"
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
blockrun_search({ body: {
|
|
71
|
+
query: "Ethereum Pectra upgrade",
|
|
72
|
+
sources: ["web","news"],
|
|
73
|
+
from_date: "2026-02-17"
|
|
74
|
+
}})
|
|
75
|
+
```
|
|
76
|
+
**Cost: $0.025 × 10 (default) = $0.25.**
|
|
77
|
+
|
|
78
|
+
## search vs exa — Pick the Right Tool
|
|
79
|
+
|
|
80
|
+
| Use case | Tool |
|
|
81
|
+
|---|---|
|
|
82
|
+
| "What's happening *right now*?" — freshness matters | `blockrun_search` |
|
|
83
|
+
| "Find the canonical paper on X" — semantic relevance matters | `blockrun_exa` |
|
|
84
|
+
| "Pull the full text of these 5 URLs" — content fetch | `blockrun_exa` `contents` |
|
|
85
|
+
| "Cited answer to a question" | both work; `blockrun_exa answer` is grounded in pre-indexed corpus, `blockrun_search` searches the live web |
|
|
86
|
+
|
|
87
|
+
## Notes
|
|
88
|
+
|
|
89
|
+
- Returns AI-summarized text + a list of sources with URLs. The summary is one paragraph; sources let you drill in.
|
|
90
|
+
- **Price is per result, not per source.** `max_results: 20` with one source or three sources both cost $0.025 × 20 = $0.50. Pass a smaller `max_results` to cap spend.
|
|
91
|
+
- Date filters are strict — results outside the window are dropped, not down-ranked.
|
|
92
|
+
|
|
93
|
+
## Reference
|
|
94
|
+
|
|
95
|
+
- Endpoint: `POST /v1/search`
|
|
96
|
+
- Upstream: xAI Grok Live Search
|