@blockrun/mcp 0.1.0 → 0.2.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.
Files changed (3) hide show
  1. package/README.md +72 -84
  2. package/dist/index.js +361 -283
  3. package/package.json +4 -3
package/README.md CHANGED
@@ -1,81 +1,82 @@
1
1
  # @blockrun/mcp
2
2
 
3
- **Access 30+ AI models in Claude Code with zero API keys.**
3
+ ## The Problem
4
4
 
5
- One wallet. Pay-per-request. All major AI models.
5
+ Want to use GPT-5, Gemini, or DeepSeek in Claude Code? Today you need to:
6
+
7
+ 1. Create accounts with 5+ AI providers
8
+ 2. Manage 5+ API keys and billing systems
9
+ 3. Pay $20-100/month minimums per provider
10
+ 4. Configure each provider separately
11
+
12
+ **That's too much friction.**
13
+
14
+ ## The Solution
15
+
16
+ BlockRun MCP gives you access to 30+ AI models with:
17
+
18
+ - **Zero API keys** - No accounts needed with OpenAI, Google, etc.
19
+ - **One wallet** - Single USDC balance for all providers
20
+ - **Pay-per-use** - No minimums, $5 gets you started
21
+ - **One command** - Install and go
6
22
 
7
23
  ```bash
8
24
  claude mcp add blockrun npx @blockrun/mcp
9
25
  ```
10
26
 
11
- ## Why BlockRun MCP?
12
-
13
- | Feature | Other Solutions | BlockRun MCP |
14
- |---------|-----------------|--------------|
15
- | **API Keys** | Need 5+ keys (OpenAI, Anthropic, Google...) | **None needed** |
16
- | **Billing** | Manage 5+ subscriptions | **One wallet, unified balance** |
17
- | **Setup** | Complex config per provider | **One command, auto-wallet** |
18
- | **Models** | Usually 1 provider | **30+ models, 6 providers** |
19
- | **Payment** | Monthly subscriptions | **Pay only what you use** |
20
- | **Minimum** | $20-100/month per provider | **$0 minimum, start with $5** |
27
+ > **Alternative:** Prefer Python? Try the [BlockRun Skill](https://github.com/BlockRunAI/claude-code-blockrun-agent) (`pip install blockrun-llm`) - same features, different integration style.
21
28
 
22
29
  ## Quick Start
23
30
 
24
- ### 1. Install (30 seconds)
31
+ ### 1. Install
25
32
 
26
33
  ```bash
27
- # Add to Claude Code
28
34
  claude mcp add blockrun npx @blockrun/mcp
29
35
  ```
30
36
 
31
- That's it! A wallet is automatically created for you.
37
+ A wallet is automatically created for you.
32
38
 
33
39
  ### 2. Get Your Wallet Address
34
40
 
35
- In Claude Code, run:
36
- ```
37
- Use blockrun_setup to get my wallet address
38
41
  ```
42
+ You: blockrun setup
39
43
 
40
- Or:
41
- ```
42
- Use blockrun_wallet to show my wallet info
44
+ Claude: Your wallet address is 0x...
45
+ Send USDC on Base network to fund it.
43
46
  ```
44
47
 
45
48
  ### 3. Fund Your Wallet
46
49
 
47
50
  Send USDC to your wallet address on **Base** network. Even $5 gets you hundreds of requests.
48
51
 
49
- **Funding Options:**
50
-
51
52
  | Method | Steps |
52
53
  |--------|-------|
53
54
  | **From Coinbase** | Send → USDC → Select "Base" network → Paste your address |
54
- | **Bridge** | Visit [bridge.base.org](https://bridge.base.org) → Bridge USDC to Base |
55
- | **Buy Direct** | Visit [Coinbase Onramp](https://www.coinbase.com/onramp) → Buy USDC on Base |
55
+ | **Bridge** | [bridge.base.org](https://bridge.base.org) → Bridge USDC to Base |
56
+ | **Buy Direct** | [Coinbase Onramp](https://www.coinbase.com/onramp) → Buy USDC on Base |
56
57
 
57
58
  ### 4. Start Using
58
59
 
60
+ Just ask naturally:
61
+
59
62
  ```
60
- You: Use blockrun_chat to ask claude-sonnet-4 what is quantum computing
63
+ You: blockrun ask GPT-5 to explain quantum computing
64
+
65
+ You: blockrun chat with Claude Opus about this error
61
66
 
62
- Claude: [calls blockrun_chat]
63
- Quantum computing is a type of computation that harnesses...
67
+ You: blockrun generate an image of a mountain sunset
64
68
  ```
65
69
 
66
- ## Available Tools
70
+ ## Usage Examples
67
71
 
68
- ### `blockrun_chat`
69
- Chat with any AI model.
72
+ ### Chat with Any Model
70
73
 
71
- ```javascript
72
- blockrun_chat({
73
- model: "anthropic/claude-sonnet-4", // Required
74
- message: "Explain quantum computing", // Required
75
- system: "You are a physics professor", // Optional
76
- max_tokens: 2000, // Optional (default: 1024)
77
- temperature: 0.7 // Optional (default: 1)
78
- })
74
+ ```
75
+ blockrun ask GPT-5 what causes aurora borealis
76
+
77
+ blockrun chat with Claude Opus about optimizing this algorithm
78
+
79
+ blockrun ask Gemini Pro to review this code for security issues
79
80
  ```
80
81
 
81
82
  **Popular Models:**
@@ -85,60 +86,47 @@ blockrun_chat({
85
86
  - `google/gemini-2.5-pro` - Great for long context (1M tokens)
86
87
  - `deepseek/deepseek-chat` - Very affordable
87
88
 
88
- ### `blockrun_smart`
89
- Auto-select the best model for your needs.
89
+ ### Smart Model Selection
90
+
91
+ Let BlockRun pick the best model for your needs:
90
92
 
91
- ```javascript
92
- blockrun_smart({
93
- mode: "balanced", // Required: fast | balanced | powerful | cheap | reasoning
94
- message: "Hello!" // Required
95
- })
96
93
  ```
94
+ blockrun smart fast: what's 2+2
95
+
96
+ blockrun smart powerful: analyze this complex codebase
97
97
 
98
- | Mode | Models Used | Best For | Cost |
99
- |------|-------------|----------|------|
100
- | `fast` | Gemini Flash, GPT-4o-mini | Quick responses | $ |
101
- | `balanced` | GPT-4o, Claude Sonnet | Daily tasks | $$ |
102
- | `powerful` | GPT-5.2, Claude Opus, o3 | Complex work | $$$$ |
103
- | `cheap` | Gemini Flash, DeepSeek | Budget-conscious | $ |
104
- | `reasoning` | o3, o1, DeepSeek Reasoner | Logic & math | $$$ |
105
-
106
- ### `blockrun_models`
107
- List all available models with pricing.
108
-
109
- ```javascript
110
- blockrun_models({
111
- category: "chat", // Optional: all, chat, reasoning, image, embedding
112
- provider: "openai" // Optional: filter by provider
113
- })
98
+ blockrun smart cheap: summarize this text
114
99
  ```
115
100
 
116
- ### `blockrun_image`
117
- Generate images with AI.
101
+ | Mode | Models Used | Best For |
102
+ |------|-------------|----------|
103
+ | `fast` | Gemini Flash, GPT-4o-mini | Quick responses |
104
+ | `balanced` | GPT-4o, Claude Sonnet | Daily tasks |
105
+ | `powerful` | GPT-5.2, Claude Opus, o3 | Complex work |
106
+ | `cheap` | Gemini Flash, DeepSeek | Budget-conscious |
107
+ | `reasoning` | o3, o1, DeepSeek Reasoner | Logic & math |
108
+
109
+ ### Generate Images
118
110
 
119
- ```javascript
120
- blockrun_image({
121
- prompt: "A sunset over mountains", // Required
122
- model: "openai/dall-e-3", // Optional
123
- size: "1024x1024", // Optional: 1024x1024, 1792x1024, 1024x1792
124
- quality: "hd" // Optional: standard, hd
125
- })
126
111
  ```
112
+ blockrun generate an image of a cyberpunk cityscape
127
113
 
128
- ### `blockrun_wallet`
129
- Check your wallet information.
114
+ blockrun create a watercolor painting of mountains
115
+ ```
116
+
117
+ ### List Available Models
130
118
 
131
- ```javascript
132
- blockrun_wallet({})
133
- // Returns: address, network, balance link, funding options
134
119
  ```
120
+ blockrun list models
135
121
 
136
- ### `blockrun_setup`
137
- Get detailed setup and funding instructions.
122
+ blockrun show OpenAI models with pricing
123
+ ```
138
124
 
139
- ```javascript
140
- blockrun_setup({})
141
- // Returns: complete setup guide with step-by-step funding instructions
125
+ ### Wallet Management
126
+
127
+ ```
128
+ blockrun setup # First-time setup instructions
129
+ blockrun wallet # Check your wallet address
142
130
  ```
143
131
 
144
132
  ## Supported Models & Pricing
@@ -262,16 +250,16 @@ claude mcp add blockrun npx @blockrun/mcp --env BLOCKRUN_WALLET_KEY=0x...
262
250
  ## Troubleshooting
263
251
 
264
252
  ### "Payment was rejected"
265
- Your wallet needs funding. Run `blockrun_setup` to get your address and funding instructions.
253
+ Your wallet needs funding. Say `blockrun setup` to get your address and funding instructions.
266
254
 
267
255
  ### "Wallet key required"
268
256
  The MCP couldn't find or create a wallet. Check that `~/.blockrun/` directory is writable.
269
257
 
270
258
  ### Model not responding
271
- Some models have rate limits. Try `blockrun_smart` with mode `fast` or `cheap` to use alternative models.
259
+ Some models have rate limits. Try `blockrun smart cheap` or `blockrun smart fast` to use alternative models.
272
260
 
273
261
  ### Check wallet balance
274
- Visit: `https://basescan.org/address/YOUR_ADDRESS`
262
+ Say `blockrun wallet` or visit: `https://basescan.org/address/YOUR_ADDRESS`
275
263
 
276
264
  ## Configuration
277
265
 
package/dist/index.js CHANGED
@@ -1,12 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
- import {
7
- CallToolRequestSchema,
8
- ListToolsRequestSchema
9
- } from "@modelcontextprotocol/sdk/types.js";
6
+ import { z } from "zod";
10
7
  import { LLMClient } from "@blockrun/llm";
11
8
  import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
12
9
  import * as fs from "fs";
@@ -65,6 +62,23 @@ function getClient() {
65
62
  }
66
63
  return client;
67
64
  }
65
+ function getWalletInfo() {
66
+ const llm = getClient();
67
+ const address = llm.getWalletAddress();
68
+ return {
69
+ address,
70
+ network: "Base",
71
+ chainId: 8453,
72
+ currency: "USDC",
73
+ isNew: walletWasCreated,
74
+ basescanUrl: `https://basescan.org/address/${address}`,
75
+ fundingOptions: {
76
+ coinbase: "Send USDC, select 'Base' network",
77
+ bridge: "https://bridge.base.org",
78
+ buy: "https://www.coinbase.com/onramp"
79
+ }
80
+ };
81
+ }
68
82
  function getWalletSetupInstructions() {
69
83
  if (!walletAddress) {
70
84
  getClient();
@@ -116,9 +130,13 @@ SECURITY NOTE:
116
130
  ================================================================================
117
131
  `;
118
132
  }
119
- var tools = [
133
+ var server = new McpServer({
134
+ name: "blockrun-mcp",
135
+ version: "0.2.0"
136
+ });
137
+ server.registerTool(
138
+ "blockrun_chat",
120
139
  {
121
- name: "blockrun_chat",
122
140
  description: `Chat with any AI model via BlockRun. Supports 30+ models including GPT-5, Claude Opus 4, Gemini 3, and more.
123
141
  Pay-per-request with x402 micropayments - no API keys needed.
124
142
 
@@ -131,34 +149,34 @@ Popular models:
131
149
 
132
150
  Use blockrun_models to see all available models with pricing.`,
133
151
  inputSchema: {
134
- type: "object",
135
- properties: {
136
- model: {
137
- type: "string",
138
- description: "Model ID (e.g., 'anthropic/claude-sonnet-4', 'openai/gpt-4o'). Use blockrun_models to list all."
139
- },
140
- message: {
141
- type: "string",
142
- description: "Your message to the AI"
143
- },
144
- system: {
145
- type: "string",
146
- description: "Optional system prompt to set context/behavior"
147
- },
148
- max_tokens: {
149
- type: "number",
150
- description: "Maximum tokens in response (default: 1024)"
151
- },
152
- temperature: {
153
- type: "number",
154
- description: "Creativity level 0-2 (default: 1)"
155
- }
156
- },
157
- required: ["model", "message"]
152
+ model: z.string().describe("Model ID (e.g., 'anthropic/claude-sonnet-4', 'openai/gpt-4o'). Use blockrun_models to list all."),
153
+ message: z.string().describe("Your message to the AI"),
154
+ system: z.string().optional().describe("Optional system prompt to set context/behavior"),
155
+ max_tokens: z.number().optional().default(1024).describe("Maximum tokens in response"),
156
+ temperature: z.number().optional().default(1).describe("Creativity level 0-2")
158
157
  }
159
158
  },
159
+ async ({ model, message, system, max_tokens, temperature }) => {
160
+ try {
161
+ const llm = getClient();
162
+ const response = await llm.chat(model, message, {
163
+ system,
164
+ maxTokens: max_tokens,
165
+ temperature
166
+ });
167
+ return { content: [{ type: "text", text: response }] };
168
+ } catch (error) {
169
+ const errorMessage = error instanceof Error ? error.message : String(error);
170
+ return {
171
+ content: [{ type: "text", text: formatError(errorMessage) }],
172
+ isError: true
173
+ };
174
+ }
175
+ }
176
+ );
177
+ server.registerTool(
178
+ "blockrun_smart",
160
179
  {
161
- name: "blockrun_smart",
162
180
  description: `Smart model routing - automatically picks the best model based on your needs.
163
181
 
164
182
  Modes:
@@ -170,49 +188,112 @@ Modes:
170
188
 
171
189
  Example: blockrun_smart({ mode: "fast", message: "Hello" })`,
172
190
  inputSchema: {
173
- type: "object",
174
- properties: {
175
- mode: {
176
- type: "string",
177
- enum: ["fast", "balanced", "powerful", "cheap", "reasoning"],
178
- description: "Routing mode: fast, balanced, powerful, cheap, or reasoning"
179
- },
180
- message: {
181
- type: "string",
182
- description: "Your message to the AI"
183
- },
184
- system: {
185
- type: "string",
186
- description: "Optional system prompt"
187
- },
188
- max_tokens: {
189
- type: "number",
190
- description: "Maximum tokens in response (default: 1024)"
191
- }
192
- },
193
- required: ["mode", "message"]
191
+ mode: z.enum(["fast", "balanced", "powerful", "cheap", "reasoning"]).describe("Routing mode"),
192
+ message: z.string().describe("Your message to the AI"),
193
+ system: z.string().optional().describe("Optional system prompt"),
194
+ max_tokens: z.number().optional().default(1024).describe("Maximum tokens in response")
195
+ },
196
+ outputSchema: {
197
+ model_used: z.string().describe("The model that was used"),
198
+ response: z.string().describe("The AI response")
194
199
  }
195
200
  },
201
+ async ({ mode, message, system, max_tokens }) => {
202
+ const models = MODEL_TIERS[mode];
203
+ let lastError = null;
204
+ for (const model of models) {
205
+ try {
206
+ const llm = getClient();
207
+ const response = await llm.chat(model, message, {
208
+ system,
209
+ maxTokens: max_tokens
210
+ });
211
+ const result = { model_used: model, response };
212
+ return {
213
+ content: [{ type: "text", text: `[Used: ${model}]
214
+
215
+ ${response}` }],
216
+ structuredContent: result
217
+ };
218
+ } catch (error) {
219
+ lastError = error;
220
+ continue;
221
+ }
222
+ }
223
+ const errorMessage = lastError?.message || "All models failed";
224
+ return {
225
+ content: [{ type: "text", text: formatError(errorMessage) }],
226
+ isError: true
227
+ };
228
+ }
229
+ );
230
+ server.registerTool(
231
+ "blockrun_models",
196
232
  {
197
- name: "blockrun_models",
198
233
  description: "List all available AI models with pricing. Use this to discover models and compare costs.",
199
234
  inputSchema: {
200
- type: "object",
201
- properties: {
202
- category: {
203
- type: "string",
204
- enum: ["all", "chat", "reasoning", "image", "embedding"],
205
- description: "Filter by category (default: all)"
206
- },
207
- provider: {
208
- type: "string",
209
- description: "Filter by provider (e.g., 'openai', 'anthropic', 'google')"
210
- }
211
- }
235
+ category: z.enum(["all", "chat", "reasoning", "image", "embedding"]).optional().default("all").describe("Filter by category"),
236
+ provider: z.string().optional().describe("Filter by provider (e.g., 'openai', 'anthropic', 'google')")
237
+ },
238
+ outputSchema: {
239
+ count: z.number().describe("Number of models returned"),
240
+ models: z.array(z.object({
241
+ id: z.string(),
242
+ name: z.string().optional(),
243
+ inputPrice: z.number().optional(),
244
+ outputPrice: z.number().optional()
245
+ })).describe("List of available models")
212
246
  }
213
247
  },
248
+ async ({ category, provider }) => {
249
+ const llm = getClient();
250
+ if (!cachedModels) {
251
+ cachedModels = await llm.listModels();
252
+ setTimeout(() => {
253
+ cachedModels = null;
254
+ }, 5 * 60 * 1e3);
255
+ }
256
+ let models = cachedModels;
257
+ if (provider) {
258
+ const p = provider.toLowerCase();
259
+ models = models.filter((m) => m.id.toLowerCase().startsWith(p + "/"));
260
+ }
261
+ if (category && category !== "all") {
262
+ if (category === "image") {
263
+ models = models.filter(
264
+ (m) => m.id.includes("dall-e") || m.id.includes("flux") || m.id.includes("banana")
265
+ );
266
+ } else if (category === "reasoning") {
267
+ models = models.filter(
268
+ (m) => m.id.includes("/o1") || m.id.includes("/o3") || m.id.includes("reasoner")
269
+ );
270
+ } else if (category === "embedding") {
271
+ models = models.filter((m) => m.id.includes("embed"));
272
+ }
273
+ }
274
+ const lines = models.map((m) => {
275
+ const input = m.inputPrice ? `$${m.inputPrice}/M in` : "";
276
+ const output = m.outputPrice ? `$${m.outputPrice}/M out` : "";
277
+ const pricing = [input, output].filter(Boolean).join(", ");
278
+ return `- ${m.id}: ${m.name || ""} ${pricing ? `(${pricing})` : ""}`;
279
+ });
280
+ const structuredModels = models.map((m) => ({
281
+ id: m.id,
282
+ name: m.name,
283
+ inputPrice: m.inputPrice,
284
+ outputPrice: m.outputPrice
285
+ }));
286
+ return {
287
+ content: [{ type: "text", text: `Available models (${models.length}):
288
+
289
+ ${lines.join("\n")}` }],
290
+ structuredContent: { count: models.length, models: structuredModels }
291
+ };
292
+ }
293
+ );
294
+ server.registerTool(
295
+ "blockrun_image",
214
296
  {
215
- name: "blockrun_image",
216
297
  description: `Generate images using AI models. Supports DALL-E 3, Flux, and Nano Banana.
217
298
 
218
299
  Models:
@@ -220,264 +301,261 @@ Models:
220
301
  - together/flux-schnell: Fast generation ($0.02/image)
221
302
  - google/nano-banana: Experimental Google model`,
222
303
  inputSchema: {
223
- type: "object",
224
- properties: {
225
- prompt: {
226
- type: "string",
227
- description: "Description of the image to generate"
228
- },
229
- model: {
230
- type: "string",
231
- description: "Image model (default: openai/dall-e-3)",
232
- enum: ["openai/dall-e-3", "together/flux-schnell", "google/nano-banana"]
233
- },
234
- size: {
235
- type: "string",
236
- description: "Image size (default: 1024x1024)",
237
- enum: ["1024x1024", "1792x1024", "1024x1792"]
238
- },
239
- quality: {
240
- type: "string",
241
- description: "Quality level for DALL-E 3 (default: standard)",
242
- enum: ["standard", "hd"]
243
- }
244
- },
245
- required: ["prompt"]
246
- }
247
- },
248
- {
249
- name: "blockrun_wallet",
250
- description: "Get information about your BlockRun wallet address. Shows address, network, and quick funding options.",
251
- inputSchema: {
252
- type: "object",
253
- properties: {}
304
+ prompt: z.string().describe("Description of the image to generate"),
305
+ model: z.enum(["openai/dall-e-3", "together/flux-schnell", "google/nano-banana"]).optional().default("openai/dall-e-3").describe("Image model"),
306
+ size: z.enum(["1024x1024", "1792x1024", "1024x1792"]).optional().default("1024x1024").describe("Image size"),
307
+ quality: z.enum(["standard", "hd"]).optional().default("standard").describe("Quality level for DALL-E 3")
308
+ },
309
+ outputSchema: {
310
+ url: z.string().describe("URL of the generated image"),
311
+ prompt: z.string().describe("The prompt used"),
312
+ model: z.string().describe("The model used")
254
313
  }
255
314
  },
256
- {
257
- name: "blockrun_setup",
258
- description: `Get detailed wallet setup and funding instructions. Use this for first-time setup or if you need help adding funds to your wallet.
315
+ async ({ prompt, model, size, quality }) => {
316
+ const apiUrl = "https://blockrun.ai/api/v1/images/generations";
317
+ const body = {
318
+ model,
319
+ prompt,
320
+ size,
321
+ quality,
322
+ n: 1
323
+ };
324
+ const response = await fetch(apiUrl, {
325
+ method: "POST",
326
+ headers: { "Content-Type": "application/json" },
327
+ body: JSON.stringify(body)
328
+ });
329
+ if (response.status === 402) {
330
+ return {
331
+ content: [{ type: "text", text: `Image generation requires payment. Please ensure your wallet has USDC on Base.
259
332
 
260
- Returns:
261
- - Your wallet address
262
- - Step-by-step funding instructions (Coinbase, bridge, direct purchase)
263
- - Pricing information
264
- - Security details`,
265
- inputSchema: {
266
- type: "object",
267
- properties: {}
333
+ To generate "${prompt}" with ${model}, the approximate cost is $0.04-0.08 per image.` }],
334
+ isError: true
335
+ };
268
336
  }
269
- }
270
- ];
271
- async function handleChat(args) {
272
- const llm = getClient();
273
- const response = await llm.chat(args.model, args.message, {
274
- system: args.system,
275
- maxTokens: args.max_tokens,
276
- temperature: args.temperature
277
- });
278
- return response;
279
- }
280
- async function handleSmartRoute(args) {
281
- const models = MODEL_TIERS[args.mode];
282
- if (!models) {
283
- throw new Error(`Invalid mode: ${args.mode}. Use: fast, balanced, powerful, cheap, or reasoning`);
284
- }
285
- let lastError = null;
286
- for (const model of models) {
287
- try {
288
- const response = await handleChat({
289
- model,
290
- message: args.message,
291
- system: args.system,
292
- max_tokens: args.max_tokens
293
- });
294
- return `[Used: ${model}]
295
-
296
- ${response}`;
297
- } catch (error) {
298
- lastError = error;
299
- continue;
337
+ if (!response.ok) {
338
+ return {
339
+ content: [{ type: "text", text: formatError(`Image generation failed: ${response.status}`) }],
340
+ isError: true
341
+ };
300
342
  }
301
- }
302
- throw lastError || new Error("All models failed");
303
- }
304
- async function handleListModels(args) {
305
- const llm = getClient();
306
- if (!cachedModels) {
307
- cachedModels = await llm.listModels();
308
- setTimeout(() => {
309
- cachedModels = null;
310
- }, 5 * 60 * 1e3);
311
- }
312
- let models = cachedModels;
313
- if (args.provider) {
314
- const provider = args.provider.toLowerCase();
315
- models = models.filter((m) => m.id.toLowerCase().startsWith(provider + "/"));
316
- }
317
- if (args.category && args.category !== "all") {
318
- const category = args.category.toLowerCase();
319
- if (category === "image") {
320
- models = models.filter(
321
- (m) => m.id.includes("dall-e") || m.id.includes("flux") || m.id.includes("banana")
322
- );
323
- } else if (category === "reasoning") {
324
- models = models.filter(
325
- (m) => m.id.includes("/o1") || m.id.includes("/o3") || m.id.includes("reasoner")
326
- );
327
- } else if (category === "embedding") {
328
- models = models.filter((m) => m.id.includes("embed"));
343
+ const data = await response.json();
344
+ const imageUrl = data.data?.[0]?.url;
345
+ if (!imageUrl) {
346
+ return {
347
+ content: [{ type: "text", text: formatError("No image URL in response") }],
348
+ isError: true
349
+ };
329
350
  }
330
- }
331
- const lines = models.map((m) => {
332
- const input = m.inputPrice ? `$${m.inputPrice}/M in` : "";
333
- const output = m.outputPrice ? `$${m.outputPrice}/M out` : "";
334
- const pricing = [input, output].filter(Boolean).join(", ");
335
- return `- ${m.id}: ${m.name || ""} ${pricing ? `(${pricing})` : ""}`;
336
- });
337
- return `Available models (${models.length}):
338
-
339
- ${lines.join("\n")}`;
340
- }
341
- async function handleImageGeneration(args) {
342
- const model = args.model || "openai/dall-e-3";
343
- const llm = getClient();
344
- const apiUrl = "https://blockrun.ai/api/v1/images/generations";
345
- const body = {
346
- model,
347
- prompt: args.prompt,
348
- size: args.size || "1024x1024",
349
- quality: args.quality || "standard",
350
- n: 1
351
- };
352
- const response = await fetch(apiUrl, {
353
- method: "POST",
354
- headers: {
355
- "Content-Type": "application/json"
356
- },
357
- body: JSON.stringify(body)
358
- });
359
- if (response.status === 402) {
360
- return `Image generation requires payment. Please ensure your wallet has USDC on Base.
361
-
362
- To generate "${args.prompt}" with ${model}, the approximate cost is $0.04-0.08 per image.`;
363
- }
364
- if (!response.ok) {
365
- throw new Error(`Image generation failed: ${response.status}`);
366
- }
367
- const data = await response.json();
368
- const imageUrl = data.data?.[0]?.url;
369
- if (!imageUrl) {
370
- throw new Error("No image URL in response");
371
- }
372
- return `Image generated successfully!
351
+ return {
352
+ content: [{ type: "text", text: `Image generated successfully!
373
353
 
374
354
  URL: ${imageUrl}
375
355
 
376
- Prompt: ${args.prompt}
377
- Model: ${model}`;
378
- }
379
- function handleWalletInfo() {
380
- const llm = getClient();
381
- const address = llm.getWalletAddress();
382
- const isNewWallet = walletWasCreated;
383
- let response = `BlockRun Wallet Information
356
+ Prompt: ${prompt}
357
+ Model: ${model}` }],
358
+ structuredContent: { url: imageUrl, prompt, model }
359
+ };
360
+ }
361
+ );
362
+ server.registerTool(
363
+ "blockrun_wallet",
364
+ {
365
+ description: "Get information about your BlockRun wallet address. Shows address, network, and quick funding options.",
366
+ inputSchema: {},
367
+ outputSchema: {
368
+ address: z.string().describe("Wallet address"),
369
+ network: z.string().describe("Network name"),
370
+ chainId: z.number().describe("Chain ID"),
371
+ currency: z.string().describe("Currency"),
372
+ isNew: z.boolean().describe("Whether this is a newly created wallet"),
373
+ basescanUrl: z.string().describe("Link to view on Basescan")
374
+ }
375
+ },
376
+ async () => {
377
+ const info = getWalletInfo();
378
+ const isNewWallet = info.isNew;
379
+ let text = `BlockRun Wallet Information
384
380
  ============================
385
381
 
386
- Address: ${address}
387
- Network: Base (Chain ID: 8453)
388
- Currency: USDC
382
+ Address: ${info.address}
383
+ Network: ${info.network} (Chain ID: ${info.chainId})
384
+ Currency: ${info.currency}
389
385
 
390
- View on Basescan: https://basescan.org/address/${address}
386
+ View on Basescan: ${info.basescanUrl}
391
387
  `;
392
- if (isNewWallet) {
393
- response += `
388
+ if (isNewWallet) {
389
+ text += `
394
390
  STATUS: NEW WALLET - NEEDS FUNDING
395
391
  ${getWalletSetupInstructions()}`;
396
- } else {
397
- response += `
392
+ } else {
393
+ text += `
398
394
  HOW TO ADD FUNDS:
399
395
  -----------------
400
396
  Send USDC to the address above on Base network.
401
397
 
402
398
  Quick options:
403
- 1. From Coinbase: Send USDC, select "Base" network
404
- 2. Bridge: https://bridge.base.org
405
- 3. Buy: https://www.coinbase.com/onramp
399
+ 1. From Coinbase: ${info.fundingOptions.coinbase}
400
+ 2. Bridge: ${info.fundingOptions.bridge}
401
+ 3. Buy: ${info.fundingOptions.buy}
406
402
 
407
403
  Full instructions: Run blockrun_setup tool
408
404
  `;
405
+ }
406
+ return {
407
+ content: [{ type: "text", text }],
408
+ structuredContent: {
409
+ address: info.address,
410
+ network: info.network,
411
+ chainId: info.chainId,
412
+ currency: info.currency,
413
+ isNew: info.isNew,
414
+ basescanUrl: info.basescanUrl
415
+ }
416
+ };
409
417
  }
410
- return response;
411
- }
412
- function handleSetup() {
413
- getClient();
414
- return getWalletSetupInstructions();
415
- }
416
- var server = new Server(
418
+ );
419
+ server.registerTool(
420
+ "blockrun_setup",
417
421
  {
418
- name: "blockrun-mcp",
419
- version: "0.1.0"
422
+ description: `Get detailed wallet setup and funding instructions. Use this for first-time setup or if you need help adding funds to your wallet.
423
+
424
+ Returns:
425
+ - Your wallet address
426
+ - Step-by-step funding instructions (Coinbase, bridge, direct purchase)
427
+ - Pricing information
428
+ - Security details`,
429
+ inputSchema: {}
420
430
  },
431
+ async () => {
432
+ getClient();
433
+ return { content: [{ type: "text", text: getWalletSetupInstructions() }] };
434
+ }
435
+ );
436
+ server.registerResource(
437
+ "wallet",
438
+ "blockrun://wallet",
421
439
  {
422
- capabilities: {
423
- tools: {}
440
+ description: "Your BlockRun wallet address and status",
441
+ mimeType: "application/json"
442
+ },
443
+ async () => {
444
+ const info = getWalletInfo();
445
+ return {
446
+ contents: [{
447
+ uri: "blockrun://wallet",
448
+ mimeType: "application/json",
449
+ text: JSON.stringify(info, null, 2)
450
+ }]
451
+ };
452
+ }
453
+ );
454
+ server.registerResource(
455
+ "models",
456
+ "blockrun://models",
457
+ {
458
+ description: "List of all available AI models with pricing",
459
+ mimeType: "application/json"
460
+ },
461
+ async () => {
462
+ const llm = getClient();
463
+ if (!cachedModels) {
464
+ cachedModels = await llm.listModels();
465
+ setTimeout(() => {
466
+ cachedModels = null;
467
+ }, 5 * 60 * 1e3);
424
468
  }
469
+ return {
470
+ contents: [{
471
+ uri: "blockrun://models",
472
+ mimeType: "application/json",
473
+ text: JSON.stringify(cachedModels, null, 2)
474
+ }]
475
+ };
425
476
  }
426
477
  );
427
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
428
- tools
429
- }));
430
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
431
- const { name, arguments: args } = request.params;
432
- try {
433
- let result;
434
- switch (name) {
435
- case "blockrun_chat":
436
- result = await handleChat(args);
437
- break;
438
- case "blockrun_smart":
439
- result = await handleSmartRoute(args);
440
- break;
441
- case "blockrun_models":
442
- result = await handleListModels(args);
443
- break;
444
- case "blockrun_image":
445
- result = await handleImageGeneration(args);
446
- break;
447
- case "blockrun_wallet":
448
- result = handleWalletInfo();
449
- break;
450
- case "blockrun_setup":
451
- result = handleSetup();
452
- break;
453
- default:
454
- throw new Error(`Unknown tool: ${name}`);
478
+ server.registerPrompt(
479
+ "quick_chat",
480
+ {
481
+ description: "Start a quick chat with a recommended model",
482
+ argsSchema: {
483
+ message: z.string().describe("Your message"),
484
+ style: z.enum(["concise", "detailed", "creative"]).optional().default("concise").describe("Response style")
455
485
  }
486
+ },
487
+ async ({ message, style }) => {
488
+ const systemPrompts = {
489
+ concise: "Be concise and direct. Give short, focused answers.",
490
+ detailed: "Provide thorough, comprehensive answers with examples.",
491
+ creative: "Be creative and imaginative in your responses."
492
+ };
456
493
  return {
457
- content: [{ type: "text", text: result }]
494
+ messages: [
495
+ {
496
+ role: "user",
497
+ content: {
498
+ type: "text",
499
+ text: `[System: ${systemPrompts[style || "concise"]}]
500
+
501
+ ${message}`
502
+ }
503
+ }
504
+ ]
458
505
  };
459
- } catch (error) {
460
- const message = error instanceof Error ? error.message : String(error);
461
- const isPaymentError = message.toLowerCase().includes("payment") || message.toLowerCase().includes("402") || message.toLowerCase().includes("balance") || message.toLowerCase().includes("insufficient");
462
- let errorText = `Error: ${message}`;
463
- if (isPaymentError) {
464
- errorText += `
506
+ }
507
+ );
508
+ server.registerPrompt(
509
+ "code_review",
510
+ {
511
+ description: "Get a code review from a powerful model",
512
+ argsSchema: {
513
+ code: z.string().describe("The code to review"),
514
+ language: z.string().optional().describe("Programming language"),
515
+ focus: z.enum(["bugs", "performance", "style", "all"]).optional().default("all").describe("What to focus on")
516
+ }
517
+ },
518
+ async ({ code, language, focus }) => {
519
+ const focusInstructions = {
520
+ bugs: "Focus on potential bugs, errors, and edge cases.",
521
+ performance: "Focus on performance issues and optimization opportunities.",
522
+ style: "Focus on code style, readability, and best practices.",
523
+ all: "Review for bugs, performance, and style."
524
+ };
525
+ return {
526
+ messages: [
527
+ {
528
+ role: "user",
529
+ content: {
530
+ type: "text",
531
+ text: `Please review this ${language || ""} code. ${focusInstructions[focus || "all"]}
532
+
533
+ \`\`\`${language || ""}
534
+ ${code}
535
+ \`\`\``
536
+ }
537
+ }
538
+ ]
539
+ };
540
+ }
541
+ );
542
+ function formatError(message) {
543
+ const isPaymentError = message.toLowerCase().includes("payment") || message.toLowerCase().includes("402") || message.toLowerCase().includes("balance") || message.toLowerCase().includes("insufficient");
544
+ let errorText = `Error: ${message}`;
545
+ if (isPaymentError) {
546
+ errorText += `
465
547
 
466
548
  This error usually means your wallet needs funding.
467
549
  Run the blockrun_setup tool to get your wallet address and funding instructions.
468
550
 
469
551
  Quick fix: Send USDC to your wallet on Base network.`;
470
- }
471
- return {
472
- content: [{ type: "text", text: errorText }],
473
- isError: true
474
- };
475
552
  }
476
- });
553
+ return errorText;
554
+ }
477
555
  async function main() {
478
556
  const transport = new StdioServerTransport();
479
557
  await server.connect(transport);
480
- console.error("BlockRun MCP Server started");
558
+ console.error("BlockRun MCP Server started (v0.1.0)");
481
559
  }
482
560
  main().catch((error) => {
483
561
  console.error("Fatal error:", error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/mcp",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "BlockRun MCP Server - Access 30+ AI models via x402 micropayments. No API keys needed.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -43,9 +43,10 @@
43
43
  "url": "https://github.com/blockrunai/blockrun-mcp/issues"
44
44
  },
45
45
  "dependencies": {
46
- "@modelcontextprotocol/sdk": "^1.0.0",
47
46
  "@blockrun/llm": "^0.1.1",
48
- "viem": "^2.21.0"
47
+ "@modelcontextprotocol/sdk": "^1.0.0",
48
+ "viem": "^2.21.0",
49
+ "zod": "^4.3.5"
49
50
  },
50
51
  "devDependencies": {
51
52
  "@types/node": "^20.0.0",