@blockrun/mcp 0.22.2 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/mcp",
3
- "version": "0.22.2",
3
+ "version": "0.23.0",
4
4
  "mcpName": "io.github.BlockRunAI/blockrun-mcp",
5
5
  "description": "BlockRun MCP Server - Give your AI agent web search, deep research, prediction markets, crypto data, X/Twitter intelligence. Paid via x402 micropayments.",
6
6
  "type": "module",
@@ -11,6 +11,7 @@
11
11
  },
12
12
  "files": [
13
13
  "dist",
14
+ "skills",
14
15
  "README.md"
15
16
  ],
16
17
  "scripts": {
@@ -53,10 +54,12 @@
53
54
  "open": "^11.0.0",
54
55
  "qrcode": "^1.5.4",
55
56
  "rpc-websockets": "9.3.0",
56
- "sharp": "^0.34.5",
57
57
  "viem": "^2.21.0",
58
58
  "zod": "^4.3.5"
59
59
  },
60
+ "optionalDependencies": {
61
+ "sharp": "^0.34.5"
62
+ },
60
63
  "devDependencies": {
61
64
  "@types/node": "^20.0.0",
62
65
  "@types/qrcode": "^1.5.6",
@@ -65,6 +68,6 @@
65
68
  "typescript": "^5.0.0"
66
69
  },
67
70
  "engines": {
68
- "node": ">=18"
71
+ "node": ">=20.19"
69
72
  }
70
73
  }
@@ -0,0 +1,177 @@
1
+ ---
2
+ name: exa-research
3
+ description: Use when researching products, finding academic papers, discovering competitors, reading webpage content, or getting cited answers grounded in real web sources. Use over generic search when semantic relevance matters.
4
+ triggers:
5
+ - "research"
6
+ - "web research"
7
+ - "find papers"
8
+ - "academic papers"
9
+ - "competitor discovery"
10
+ - "find similar sites"
11
+ - "exa search"
12
+ - "cited answer"
13
+ - "scrape webpage"
14
+ - "neural search"
15
+ - "semantic search"
16
+ - "look up sources"
17
+ ---
18
+
19
+ # Exa Research
20
+
21
+ Neural web search via BlockRun. Understands meaning, not keywords. Four distinct actions for different research modes.
22
+
23
+ ## How to Call from MCP
24
+
25
+ As of v0.14.1 the `blockrun_exa` tool is path-based. Pass the endpoint name as `path` and the request as `body`:
26
+
27
+ ```ts
28
+ blockrun_exa({ path: "search", body: { query: "AI agent frameworks 2026", numResults: 10 } })
29
+ blockrun_exa({ path: "answer", body: { query: "What is speculative decoding?" } })
30
+ blockrun_exa({ path: "contents", body: { urls: ["https://example.com/a", "https://example.com/b"] } })
31
+ blockrun_exa({ path: "find-similar", body: { url: "https://arxiv.org/abs/2401.12345", numResults: 5 } })
32
+ ```
33
+
34
+ ## Quick Decision Table
35
+
36
+ | User wants... | Path | Body | Cost |
37
+ |--------------|------|------|------|
38
+ | Relevant URLs on a topic | `search` | `{ query, numResults?, category? }` | $0.01/call |
39
+ | Cited answer to a question | `answer` | `{ query }` | $0.01/call |
40
+ | Full text of URLs | `contents` | `{ urls: [...] }` | $0.002/URL |
41
+ | Pages like a given URL | `find-similar` | `{ url, numResults? }` | $0.01/call |
42
+ | Recent news | `search` + `category: "news"` | – | $0.01/call |
43
+ | Academic papers | `search` + `category: "research paper"` | – | $0.01/call |
44
+ | Company info | `search` + `category: "company"` | – | $0.01/call |
45
+
46
+ Valid `category` values for `search`: `"news"`, `"research paper"`, `"company"`, `"tweet"`, `"github"`, `"pdf"`.
47
+
48
+ ## Python SDK Instructions
49
+
50
+ ### 1. Initialize (Python SDK)
51
+
52
+ ```python
53
+ from blockrun_llm import setup_agent_wallet
54
+
55
+ chain = open(os.path.expanduser("~/.blockrun/.chain")).read().strip() if os.path.exists(os.path.expanduser("~/.blockrun/.chain")) else "base"
56
+ if chain == "solana":
57
+ from blockrun_llm import setup_agent_solana_wallet
58
+ client = setup_agent_solana_wallet()
59
+ else:
60
+ from blockrun_llm import setup_agent_wallet
61
+ client = setup_agent_wallet()
62
+ ```
63
+
64
+ ### 2. Search — Find Relevant URLs
65
+
66
+ ```python
67
+ # Basic search
68
+ result = client._request_with_payment_raw("/v1/exa/search", {
69
+ "query": "AI agent frameworks 2025",
70
+ "numResults": 10,
71
+ })
72
+ for r in result.get("results", []):
73
+ print(f"{r['title']} — {r['url']}")
74
+
75
+ # Filter by category
76
+ result = client._request_with_payment_raw("/v1/exa/search", {
77
+ "query": "transformer architecture improvements",
78
+ "numResults": 10,
79
+ "category": "research paper",
80
+ })
81
+
82
+ # Restrict to specific domains
83
+ result = client._request_with_payment_raw("/v1/exa/search", {
84
+ "query": "prediction market regulation",
85
+ "numResults": 10,
86
+ "includeDomains": ["reuters.com", "bloomberg.com", "wsj.com"],
87
+ })
88
+ ```
89
+
90
+ **Categories:** `"news"`, `"research paper"`, `"company"`, `"tweet"`, `"github"`, `"pdf"`
91
+
92
+ ### 3. Answer — Cited, Grounded Response
93
+
94
+ Use when the user asks a factual question and needs reliable sources (not Claude's training data).
95
+
96
+ ```python
97
+ result = client._request_with_payment_raw("/v1/exa/answer", {
98
+ "query": "What is the current market cap of Polymarket?",
99
+ })
100
+ print(result.get("answer", ""))
101
+ for c in result.get("citations", []):
102
+ print(f" [{c.get('title')}] {c.get('url')}")
103
+ ```
104
+
105
+ ### 4. Contents — Fetch URL Text
106
+
107
+ Use when you have URLs and need their full text for LLM context (scraping without a browser).
108
+
109
+ ```python
110
+ urls = [
111
+ "https://example.com/article-1",
112
+ "https://example.com/article-2",
113
+ ]
114
+ result = client._request_with_payment_raw("/v1/exa/contents", {
115
+ "urls": urls,
116
+ })
117
+ for item in result.get("results", []):
118
+ print(f"=== {item['url']} ===")
119
+ print(item.get("text", "")[:500])
120
+ ```
121
+
122
+ Up to 100 URLs per call. Returns Markdown-ready text.
123
+
124
+ ### 5. Similar — Find Related Pages
125
+
126
+ Use to discover competitors, related research, or sites with similar content.
127
+
128
+ ```python
129
+ result = client._request_with_payment_raw("/v1/exa/find-similar", {
130
+ "url": "https://polymarket.com",
131
+ "numResults": 10,
132
+ })
133
+ for r in result.get("results", []):
134
+ print(f"{r['title']} — {r['url']}")
135
+ ```
136
+
137
+ ## Common Research Workflows
138
+
139
+ **Competitor discovery:**
140
+ ```python
141
+ # 1. Find similar companies
142
+ similar = client._request_with_payment_raw("/v1/exa/find-similar", {"url": "https://target-company.com", "numResults": 15})
143
+ urls = [r["url"] for r in similar.get("results", [])]
144
+
145
+ # 2. Fetch their about pages
146
+ contents = client._request_with_payment_raw("/v1/exa/contents", {"urls": urls[:10]})
147
+ ```
148
+
149
+ **Research synthesis:**
150
+ ```python
151
+ # 1. Find papers
152
+ papers = client._request_with_payment_raw("/v1/exa/search", {
153
+ "query": "your topic",
154
+ "category": "research paper",
155
+ "numResults": 20,
156
+ })
157
+
158
+ # 2. Get answer with citations
159
+ answer = client._request_with_payment_raw("/v1/exa/answer", {
160
+ "query": "What are the key findings on your topic?",
161
+ })
162
+ ```
163
+
164
+ ## When to Use Exa vs `client.search()`
165
+
166
+ | Use `blockrun_exa` / `_request_with_payment_raw` | Use `client.search()` |
167
+ |---------------------------------------------------|----------------------|
168
+ | Finding specific URLs and fetching content | Getting a summarized answer with citations |
169
+ | Semantic similarity search | Web + X/Twitter + news combined |
170
+ | Academic paper discovery | Cheaper per call for simple lookups |
171
+ | Domain-filtered research | Already returns a SearchResult object |
172
+
173
+ ## Requirements
174
+
175
+ - BlockRun SDK: `pip install blockrun-llm`
176
+ - USDC wallet funded (see `client.get_balance()`)
177
+ - `_request_with_payment_raw` is the Python SDK entry point for Exa (no dedicated method yet)
@@ -0,0 +1,308 @@
1
+ ---
2
+ name: image-prompting
3
+ description: Use when generating or editing images via `blockrun_image` — especially with GPT Image 2, Nano Banana, or Grok Imagine for posters, UI mockups, marketing assets, product shots, or anything with on-image text. Turns vague user requests ("make me a cool poster") into structured, text-accurate prompts that actually render what you asked for.
4
+ triggers:
5
+ - "image prompt"
6
+ - "make a poster"
7
+ - "create poster"
8
+ - "ui mockup"
9
+ - "marketing asset"
10
+ - "product shot"
11
+ - "gpt image 2"
12
+ - "image with text"
13
+ - "typography poster"
14
+ - "social asset"
15
+ - "generate marketing image"
16
+ - "ai poster"
17
+ ---
18
+
19
+ # Image Prompting
20
+
21
+ Most image failures are prompt failures. This skill gives the MCP agent a repeatable structure for turning any user request into a prompt that renders clean typography, preserves layout on edits, and avoids AI slop. Defaults are tuned for **GPT Image 2** (best legible text), with fallbacks for Nano Banana, Grok Imagine, and CogView.
22
+
23
+ ## Quick Decision Table
24
+
25
+ | User wants... | Model | Mode | Size | Cost |
26
+ |---|---|---|---|---|
27
+ | Poster / typography-heavy asset | `openai/gpt-image-2` | generate | `1536x1024` or `1024x1536` | ~$0.04 |
28
+ | Clean product / UI mockup | `openai/gpt-image-2` | generate | `1024x1024` | ~$0.04 |
29
+ | Photoreal / fashion / editorial | `openai/gpt-image-2` or `google/nano-banana-pro` | generate | `1024x1024`+ | $0.04–0.10 |
30
+ | Artistic / stylized / fast | `google/nano-banana` | generate | `1024x1024` | ~$0.01 |
31
+ | Edit an existing image (localized change) | `openai/gpt-image-2` | edit | match source | ~$0.04 |
32
+ | Composite from multiple refs | `openai/gpt-image-2` | edit (multi-ref) | match target | ~$0.04 |
33
+
34
+ **Valid GPT Image 2 sizes:** `1024x1024` (square), `1536x1024` (landscape ~3:2), `1024x1536` (portrait ~2:3).
35
+
36
+ ## The 5-Section Prompt Framework
37
+
38
+ Write prompts as **five short blocks separated by blank lines.** This is the single biggest quality lever.
39
+
40
+ ```
41
+ SCENE: where/when/background/environment, one or two lines.
42
+
43
+ SUBJECT: the main focus (who/what), described concretely.
44
+
45
+ DETAILS: materials, texture, lighting, camera angle, composition, mood,
46
+ lens feel, depth of field, surface condition. Stack concrete nouns.
47
+
48
+ USE CASE: editorial photo / product mockup / poster / UI screen / infographic / concept frame.
49
+ (This single line tells the model what kind of image to produce.)
50
+
51
+ CONSTRAINTS: what must not drift. "No extra text." "No duplicate elements."
52
+ "Preserve face." "Legible typography." Repeat these on every edit.
53
+ ```
54
+
55
+ > The fifth slot is where most mediocre prompts fail silently. Describe the idea without bounding it and the model gets inventive in directions you will regret.
56
+
57
+ ## Text & Typography Rules (the #1 differentiator for GPT Image 2)
58
+
59
+ 1. **Wrap literal text in quotes or ALL CAPS.** `Headline (EXACT TEXT): "Fresh and clean."`
60
+ 2. **Specify** font style, weight, size, color, placement, letter-spacing.
61
+ 3. **Treat text as layout, not decoration:** hero vs. sub vs. caption with hierarchy + spacing.
62
+ 4. **State:** `No extra words. No duplicate text. No watermarks.`
63
+ 5. **Spell difficult words letter-by-letter** if the model keeps breaking them.
64
+ 6. **Mark each distinct piece of copy** with its role: `HERO:`, `SUB:`, `BOTTOM-LEFT TAG:`, `TOP BANNER:`.
65
+
66
+ ## Anti-Slop Rules (visual facts > excitement)
67
+
68
+ | Bad (vague / praise-loaded) | Good (concrete visual fact) |
69
+ |---|---|
70
+ | "stunning, epic, masterpiece" | "overcast daylight, brushed aluminum, 50mm feel" |
71
+ | "minimalist brutalist luxury editorial" | "cream background, heavy black condensed sans-serif, asymmetric type block, one hero object, studio tabletop light" |
72
+ | "it should contain a boarding pass feel" | "a boarding pass lies on the tray, barcode visible, creased corner" |
73
+ | "beautiful lighting" | "incandescent work lamp spilling warm light onto wet concrete" |
74
+
75
+ **Rules:**
76
+ - **Visual facts over praise.** Replace adjectives like *gorgeous/stunning/incredible* with observable specifics.
77
+ - **Style tags need targets.** Don't just name a style — describe the artifacts that style produces.
78
+ - **Say the real thing.** If the image must contain a boarding pass, say "boarding pass."
79
+ - **Name the lens.** 35mm, 50mm, medium format. Depth of field: shallow vs. deep.
80
+ - **Name the light.** Source + quality + direction + color temperature.
81
+
82
+ ## Instructions
83
+
84
+ ### 1. Initialize
85
+
86
+ ```python
87
+ import os
88
+ from pathlib import Path
89
+
90
+ chain_file = Path.home() / ".blockrun" / ".chain"
91
+ chain = chain_file.read_text().strip() if chain_file.exists() else "base"
92
+
93
+ if chain == "solana":
94
+ from blockrun_llm import setup_agent_solana_wallet, ImageClient
95
+ setup_agent_solana_wallet()
96
+ else:
97
+ from blockrun_llm import setup_agent_wallet, ImageClient
98
+ setup_agent_wallet()
99
+
100
+ image = ImageClient()
101
+ ```
102
+
103
+ ### 2. Generate from Scratch
104
+
105
+ ```python
106
+ prompt = """
107
+ SCENE: A realistic roadside billboard at sunset, empty two-lane highway,
108
+ soft gradient sky from peach to lavender, a few utility poles.
109
+
110
+ SUBJECT: A product billboard for a bottled water brand. Bottle on the right
111
+ third of the frame, catching warm rim light.
112
+
113
+ DETAILS: 35mm photo feel, shallow depth of field, matte-painted billboard,
114
+ clean kerning, precise print finish.
115
+
116
+ USE CASE: Product mockup for a marketing deck, landscape 3:2.
117
+
118
+ CONSTRAINTS:
119
+ - Headline (EXACT TEXT): "Fresh and clean."
120
+ - Bold sans-serif, high contrast, centered vertically in the left half.
121
+ - No extra words. No duplicate text. No watermark.
122
+ """
123
+
124
+ result = image.generate(
125
+ prompt,
126
+ model="openai/gpt-image-2",
127
+ size="1536x1024",
128
+ n=1,
129
+ )
130
+ print(result.data[0].url) # URL or data URL
131
+ ```
132
+
133
+ ### 3. Edit: Change / Preserve / Constraints
134
+
135
+ **The golden pattern** for iterative editing — one small change per turn. Repeat the preserve list every turn.
136
+
137
+ ```python
138
+ prompt = """
139
+ CHANGE: Make the light warmer — shift the sunset toward a deeper orange.
140
+ Remove the extra chair on the left.
141
+
142
+ PRESERVE: Keep the bottle position, label text, and billboard layout exactly
143
+ as in the source image. Keep the headline text verbatim.
144
+
145
+ CONSTRAINTS: No extra text. No duplicate elements. Same aspect ratio.
146
+ """
147
+
148
+ # `image` arg accepts a public URL OR a data URL (data:image/png;base64,...)
149
+ result = image.edit(
150
+ prompt,
151
+ image="https://example.com/source.png",
152
+ model="openai/gpt-image-2",
153
+ size="1536x1024",
154
+ )
155
+ print(result.data[0].url)
156
+ ```
157
+
158
+ **Why this works:** small atomic edits compound reliably. Giant rewrites ("redo this but nicer") drift everything.
159
+
160
+ ### 4. Multi-Reference Composition
161
+
162
+ Pass multiple reference images (up to ~16) via the `edit` endpoint. Label each reference's role in the prompt so the model knows how to use it.
163
+
164
+ ```python
165
+ prompt = """
166
+ COMPOSITE: Combine the three reference images as follows.
167
+ - REF 1 is the SUBJECT (the wristwatch): preserve exact dial, hands, and crown.
168
+ - REF 2 is the ENVIRONMENT (marble tabletop + window light): use as background.
169
+ - REF 3 is the STYLE REFERENCE: match its color grade and contrast.
170
+
171
+ USE CASE: E-commerce hero shot, square.
172
+
173
+ CONSTRAINTS: No extra objects. No text. Preserve watch proportions exactly.
174
+ """
175
+
176
+ # SDK: pass primary via `image=`; additional refs via a multipart request
177
+ # (check the MCP's `blockrun_image` tool for the multi-image payload shape)
178
+ ```
179
+
180
+ ## Worked Example: "make me a cool poster announcing 100 trillion tokens on blockrun.ai"
181
+
182
+ This is the real prompt that produced the image below — a vague, one-line user ask turned into a structured prompt that rendered every copy line correctly on the first shot.
183
+
184
+ ![100 Trillion Tokens poster generated with openai/gpt-image-2](./example-100t-poster.jpg)
185
+
186
+ **User asked for:** *"generate 1 cool poster showing we hit 100 Trillion Token LLM consumption on blockrun.ai"*
187
+
188
+ **Clarifying questions worth asking before prompting:**
189
+
190
+ - Audience → *social media flex for X/Twitter*
191
+ - Aesthetic → *retro-futuristic synthwave*
192
+ - Hero text → *"100 TRILLION TOKENS"*
193
+ - Supporting copy → *"The world's largest pay-per-call LLM gateway" · "Served on blockrun.ai" · "Powered by x402 micropayments" · "Now with Seedance + GPT Image 2"*
194
+ - Aspect ratio → *16:9 landscape for X timeline*
195
+
196
+ **Final prompt passed to `openai/gpt-image-2` at `size="1536x1024"`:**
197
+
198
+ ```
199
+ SCENE: A retro-futuristic synthwave scene, 80s vaporwave aesthetic, cinematic
200
+ 16:9 composition. Deep purple-to-magenta sunset sky with a giant glowing
201
+ pink-and-orange setting sun cut by thin horizontal neon lines. Palm tree
202
+ silhouettes on both sides. Faint city skyline in the distance. An infinite
203
+ chrome grid floor vanishing at the horizon with pink and cyan perspective lines.
204
+
205
+ SUBJECT: A milestone announcement poster with the hero text
206
+ "100 TRILLION TOKENS" dominating the center of the frame.
207
+
208
+ DETAILS: Hero text in huge glossy chrome letters with a pink-to-cyan gradient
209
+ and neon rim light, bold condensed sans-serif, CRT glow, slight scanline
210
+ texture across the letters. Faint CRT scanlines overlay the entire frame.
211
+ Subtle film grain. Chromatic aberration on edges. High contrast, symmetrical,
212
+ cinematic poster composition.
213
+
214
+ USE CASE: Social media announcement poster for X/Twitter, 16:9 landscape.
215
+
216
+ CONSTRAINTS:
217
+ - HERO (EXACT TEXT, centered): "100 TRILLION TOKENS"
218
+ - SUB under the hero (clean neon cyan, wide letter-spacing, EXACT TEXT):
219
+ "The world's largest pay-per-call LLM gateway"
220
+ - TOP-CENTER BANNER inside a thin neon outline (EXACT TEXT):
221
+ "NOW WITH SEEDANCE + GPT IMAGE 2"
222
+ - BOTTOM-LEFT TAG (monospace, magenta, EXACT TEXT):
223
+ "> Served on blockrun.ai"
224
+ - BOTTOM-RIGHT TAG (monospace, magenta, EXACT TEXT):
225
+ "> Powered by x402 micropayments"
226
+ - Legible crisp typography. No extra words. No duplicate text. No watermark.
227
+ ```
228
+
229
+ **Why this worked:**
230
+
231
+ 1. **Every piece of copy got a role label** (HERO / SUB / BANNER / TAG) with position and style — no guessing.
232
+ 2. **Every text string was marked `(EXACT TEXT)`** and wrapped in quotes.
233
+ 3. **Concrete visual facts** (CRT scanlines, chrome gradient, palm silhouettes) replaced vague words like "cool" and "awesome."
234
+ 4. **`No duplicate text. No extra words.`** was in the CONSTRAINTS block — GPT Image 2 loves to duplicate headlines if you don't forbid it.
235
+ 5. **Aspect ratio** chosen from the valid GPT Image 2 set (`1536x1024`) rather than asking for "16:9" and hoping.
236
+
237
+ ## Common Workflows
238
+
239
+ ### Poster / social asset
240
+
241
+ Use `1536x1024` for X/Twitter, `1024x1024` for IG grid, `1024x1536` for IG story.
242
+ Put every copy element on its own labeled line in the CONSTRAINTS block. Always include
243
+ `No duplicate text. No extra words.`
244
+
245
+ ### UI screen mockup
246
+
247
+ State the device, status bar, app name, screen title, each visible element with position
248
+ and state (checked, active, disabled), palette (hex-ish is fine: "deep navy accent"),
249
+ typography scale, corner radius, spacing feel.
250
+
251
+ ### Product shot
252
+
253
+ Surface + light source + lens + depth of field + one hero object. Name the material
254
+ of everything in frame. Avoid "beautiful" — describe what you'd see.
255
+
256
+ ### Concept/brand exploration
257
+
258
+ Generate 3–4 variants with the same 5-section skeleton but swap only the DETAILS
259
+ block (palette, lens, material). Keep SCENE/SUBJECT/USE CASE/CONSTRAINTS identical
260
+ so you're actually comparing one variable.
261
+
262
+ ## Prompt Template (copy-paste and fill in)
263
+
264
+ ```
265
+ SCENE: <where/when/background>
266
+
267
+ SUBJECT: <main focus, concrete nouns>
268
+
269
+ DETAILS: <materials, texture, lighting (source + quality + color),
270
+ camera angle, lens feel, depth of field, composition, mood>
271
+
272
+ USE CASE: <poster | UI screen | product shot | editorial photo | concept frame>
273
+
274
+ CONSTRAINTS:
275
+ - HERO (EXACT TEXT): "<verbatim copy>"
276
+ - SUB: "<verbatim copy>"
277
+ - <other copy with role labels>
278
+ - <typography rules: font style, weight, spacing, hierarchy>
279
+ - No extra words. No duplicate text. No watermark.
280
+ - <anything that must not drift: face, layout, aspect ratio>
281
+ ```
282
+
283
+ ## Three Operating Modes (at a glance)
284
+
285
+ | Mode | When | Endpoint | Pattern |
286
+ |---|---|---|---|
287
+ | Generate | From scratch | `/v1/images/generations` | 5-section framework |
288
+ | Edit | One image, localized change | `/v1/images/image2image` | Change / Preserve / Constraints |
289
+ | Combine | Multi-image composition | `/v1/images/image2image` (multi-ref) | Labeled refs (SUBJECT / ENV / STYLE) |
290
+
291
+ ## Notes & Gotchas
292
+
293
+ - **GPT Image 2 is currently the best for legible on-image text.** For artistic prompts with no copy, Nano Banana is 4× cheaper and often prettier.
294
+ - **Response shape:** `result.data[0].url` is either an HTTPS URL or a `data:image/...;base64,...` string. Save via `urllib.request.urlretrieve` for URLs or `base64.b64decode(item.b64_json)` for b64 payloads.
295
+ - **Aspect ratio:** only the three sizes above are valid for GPT Image 2. If the user asks for 16:9, use `1536x1024` — it reads as landscape on X/Twitter without cropping.
296
+ - **Iterative edits drift.** Repeat the PRESERVE list every turn, even when it feels redundant.
297
+ - **If text keeps breaking:** shorten it, spell difficult words, and move it to a dedicated `CONSTRAINTS` line marked `(EXACT TEXT)`.
298
+ - **Never use** words like *amazing, stunning, masterpiece, ultra-detailed, 8k, trending on artstation* — they waste tokens and pull toward generic AI-slop aesthetics.
299
+
300
+ ## Requirements
301
+
302
+ - BlockRun SDK: `pip install blockrun-llm`
303
+ - USDC wallet funded (`ImageClient().get_wallet_address()`; `setup_agent_wallet().get_balance()`)
304
+ - For `edit` / multi-ref: source images must be reachable by a public URL or passed as a data URL
305
+
306
+ ## Reference
307
+
308
+ BlockRun image models: `openai/gpt-image-2`, `openai/gpt-image-1`, `google/nano-banana`, `google/nano-banana-pro`, `zai/cogview-4`, `xai/grok-imagine-image`, `xai/grok-imagine-image-pro`
@@ -0,0 +1,118 @@
1
+ ---
2
+ name: modal
3
+ description: Use when the user needs to run isolated code remotely — a disposable container, optional GPU access (T4 → H100), or a safer place for untrusted / heavy code. Prefer local execution for normal repo work; use Modal sandboxes for isolation, hardware access, or one-shot heavy compute.
4
+ triggers:
5
+ - "modal sandbox"
6
+ - "remote python"
7
+ - "sandbox execution"
8
+ - "isolated code run"
9
+ - "gpu sandbox"
10
+ - "h100"
11
+ - "a100"
12
+ - "remote container"
13
+ - "ephemeral container"
14
+ - "run untrusted code"
15
+ ---
16
+
17
+ # Modal Sandboxes
18
+
19
+ Disposable remote containers (with optional GPU) via Modal, paid per call in USDC. No Modal account, no GPU procurement — pay only for what runs.
20
+
21
+ ## How to Call from MCP
22
+
23
+ ```ts
24
+ // 1. Create
25
+ blockrun_modal({ path: "sandbox/create", body: {
26
+ image: "python:3.11",
27
+ gpu: "A100",
28
+ timeout: 600,
29
+ setup_commands: ["pip install torch transformers"]
30
+ }})
31
+ // returns { sandbox_id, ... }
32
+
33
+ // 2. Exec
34
+ blockrun_modal({ path: "sandbox/exec", body: {
35
+ sandbox_id: "sb_abc...",
36
+ command: ["python", "-c", "import torch; print(torch.cuda.get_device_name(0))"]
37
+ }})
38
+
39
+ // 3. Terminate
40
+ blockrun_modal({ path: "sandbox/terminate", body: { sandbox_id: "sb_abc..." } })
41
+ ```
42
+
43
+ ## Endpoint Catalog
44
+
45
+ | Path | Method | Body | Price |
46
+ |---|---|---|---|
47
+ | `sandbox/create` | POST | `{ image?, timeout?, cpu?, memory?, gpu?, setup_commands? }` | $0.01 |
48
+ | `sandbox/exec` | POST | `{ sandbox_id, command: ["python","-c","..."], timeout? }` | $0.001 |
49
+ | `sandbox/status` | POST | `{ sandbox_id }` | $0.001 |
50
+ | `sandbox/terminate` | POST | `{ sandbox_id }` | $0.001 |
51
+
52
+ ## Field Reference
53
+
54
+ | Field | Default | Notes |
55
+ |---|---|---|
56
+ | `image` | `python:3.11` | Any public Docker image. `nvidia/cuda:12-runtime` if you bring GPU code. |
57
+ | `timeout` | 300 | Sandbox lifetime in seconds (idle eviction) |
58
+ | `cpu` | 1 | CPU cores |
59
+ | `memory` | 1024 | Memory in MB |
60
+ | `gpu` | none | `T4` / `L4` / `A10G` / `A100` / `A100-80GB` / `H100` |
61
+ | `setup_commands` | `[]` | Shell commands run once during sandbox provisioning |
62
+ | `command` (exec) | required | Array form: `["python","-c","print(2+2)"]` |
63
+
64
+ ## Worked Examples
65
+
66
+ ### 1. Quick Python eval
67
+
68
+ ```ts
69
+ const { structuredContent: sb } = await blockrun_modal({ path: "sandbox/create", body: {} })
70
+ await blockrun_modal({ path: "sandbox/exec", body: {
71
+ sandbox_id: sb.sandbox_id,
72
+ command: ["python", "-c", "import numpy; print(numpy.__version__)"]
73
+ }})
74
+ await blockrun_modal({ path: "sandbox/terminate", body: { sandbox_id: sb.sandbox_id } })
75
+ ```
76
+ **Cost: $0.012** ($0.01 + $0.001 + $0.001).
77
+
78
+ ### 2. GPU inference, A100, with deps pre-installed
79
+
80
+ ```ts
81
+ blockrun_modal({ path: "sandbox/create", body: {
82
+ image: "pytorch/pytorch:2.4.0-cuda12.1-cudnn9-runtime",
83
+ gpu: "A100",
84
+ timeout: 1200,
85
+ memory: 16384,
86
+ setup_commands: ["pip install --quiet transformers accelerate"]
87
+ }})
88
+ ```
89
+ Then `sandbox/exec` with your inference command. Sandbox auto-evicts after 1200s idle.
90
+
91
+ ### 3. Test untrusted code Claude generated
92
+
93
+ ```ts
94
+ blockrun_modal({ path: "sandbox/exec", body: {
95
+ sandbox_id,
96
+ command: ["bash", "-c", "<the generated script>"],
97
+ timeout: 60
98
+ }})
99
+ ```
100
+ Output is captured. No risk to your local machine.
101
+
102
+ ## When NOT to Use Modal
103
+
104
+ - **Normal repo edits / dev work** — use local tools, Modal adds latency and cost
105
+ - **Long-running services** — sandboxes are ephemeral, not server hosts
106
+ - **Anything you'd run hundreds of times per minute** — payment overhead dominates at high QPS
107
+
108
+ ## Notes
109
+
110
+ - `sandbox_id` is returned by `create` and required by every other endpoint
111
+ - `exec` is sync — blocks until command finishes or hits its `timeout`
112
+ - `terminate` is cheap; call it to free the sandbox even if `timeout` would expire shortly
113
+ - The free-tier `nvidia/*` LLM models in `blockrun_chat` are different infrastructure — Modal is for *your* arbitrary code
114
+
115
+ ## Reference
116
+
117
+ - Endpoints: `POST /v1/modal/sandbox/{create,exec,status,terminate}`
118
+ - Upstream: [Modal](https://modal.com)