pumpclaw-mcp 1.0.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 ADDED
@@ -0,0 +1,125 @@
1
+ # pumpclaw-mcp
2
+
3
+ MCP (Model Context Protocol) server for [PumpClaw](https://pumpclaw.com) — the free token launcher on Base (Uniswap V4).
4
+
5
+ Connect any MCP-compatible AI agent (Claude, GPT, OpenClaw, etc.) to deploy and manage tokens on Base blockchain.
6
+
7
+ ## Features
8
+
9
+ - 🔍 **list_tokens** — Browse all tokens launched on PumpClaw
10
+ - 📊 **get_token** — Get detailed info for any token (metadata, trade links)
11
+ - 📈 **get_stats** — Protocol statistics (total tokens, unique creators, fees)
12
+ - 🚀 **deploy_token** — Launch a new token (free, 0 ETH cost)
13
+
14
+ ## Quick Start
15
+
16
+ ### Claude Desktop
17
+
18
+ Add to your `claude_desktop_config.json`:
19
+
20
+ ```json
21
+ {
22
+ "mcpServers": {
23
+ "pumpclaw": {
24
+ "command": "npx",
25
+ "args": ["-y", "pumpclaw-mcp"]
26
+ }
27
+ }
28
+ }
29
+ ```
30
+
31
+ ### With private key (for deploying tokens)
32
+
33
+ ```json
34
+ {
35
+ "mcpServers": {
36
+ "pumpclaw": {
37
+ "command": "npx",
38
+ "args": ["-y", "pumpclaw-mcp"],
39
+ "env": {
40
+ "BASE_PRIVATE_KEY": "your-private-key-here"
41
+ }
42
+ }
43
+ }
44
+ }
45
+ ```
46
+
47
+ ### OpenClaw / Clawdbot
48
+
49
+ ```json
50
+ {
51
+ "mcpServers": {
52
+ "pumpclaw": {
53
+ "command": "npx",
54
+ "args": ["-y", "pumpclaw-mcp"]
55
+ }
56
+ }
57
+ }
58
+ ```
59
+
60
+ ## Tools
61
+
62
+ ### list_tokens
63
+
64
+ List all tokens launched on PumpClaw with trade links.
65
+
66
+ | Parameter | Type | Description |
67
+ |-----------|------|-------------|
68
+ | `limit` | number | Max tokens to return (default: all) |
69
+ | `offset` | number | Starting offset (default: 0) |
70
+
71
+ ### get_token
72
+
73
+ Get detailed info about a specific token.
74
+
75
+ | Parameter | Type | Description |
76
+ |-----------|------|-------------|
77
+ | `address` | string | Token contract address (0x...) |
78
+
79
+ ### get_stats
80
+
81
+ Get protocol statistics. No parameters.
82
+
83
+ Returns: total tokens, unique creators, chain info, fee structure, contract addresses.
84
+
85
+ ### deploy_token
86
+
87
+ Deploy a new token on Base via Uniswap V4. **Free — 0 ETH cost.**
88
+
89
+ | Parameter | Type | Description |
90
+ |-----------|------|-------------|
91
+ | `name` | string | Token name (e.g. "My Cool Token") |
92
+ | `symbol` | string | Ticker symbol (e.g. "MCT") |
93
+ | `imageUrl` | string? | Token logo URL |
94
+ | `websiteUrl` | string? | Project website URL |
95
+ | `totalSupply` | string? | Total supply in tokens (default: 1,000,000,000) |
96
+ | `initialFdv` | string? | Initial FDV in ETH (default: 20) |
97
+ | `creator` | string? | Creator address for fee attribution |
98
+
99
+ Requires `BASE_PRIVATE_KEY` environment variable.
100
+
101
+ ## Resources
102
+
103
+ The server also exposes a `pumpclaw://info` resource with protocol documentation.
104
+
105
+ ## Why PumpClaw?
106
+
107
+ | Feature | PumpClaw | Clanker |
108
+ |---------|----------|---------|
109
+ | Launch cost | **Free** | Free |
110
+ | Creator fee share | **80%** | 40% |
111
+ | DEX | **Uniswap V4** | Uniswap V3 |
112
+ | LP locked | **Forever** | Forever |
113
+ | Agent integration | **MCP + CLI + API** | Farcaster only |
114
+ | On-chain registry | ✅ | ❌ |
115
+
116
+ ## Links
117
+
118
+ - 🌐 [pumpclaw.com](https://pumpclaw.com)
119
+ - 📦 [npm: pumpclaw-cli](https://www.npmjs.com/package/pumpclaw-cli)
120
+ - 🔗 [API: tokens.json](https://pumpclaw.com/api/v1/tokens.json)
121
+ - 💻 [GitHub](https://github.com/clawd800/pumpclaw)
122
+
123
+ ## License
124
+
125
+ MIT
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * PumpClaw MCP Server
4
+ *
5
+ * Model Context Protocol server for PumpClaw — the free token launcher on Base (Uniswap V4).
6
+ * Allows any MCP-compatible AI agent to list, inspect, and deploy tokens.
7
+ *
8
+ * Read-only tools (no key needed): list_tokens, get_token, get_stats
9
+ * Write tools (BASE_PRIVATE_KEY env): deploy_token
10
+ */
11
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,386 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * PumpClaw MCP Server
4
+ *
5
+ * Model Context Protocol server for PumpClaw — the free token launcher on Base (Uniswap V4).
6
+ * Allows any MCP-compatible AI agent to list, inspect, and deploy tokens.
7
+ *
8
+ * Read-only tools (no key needed): list_tokens, get_token, get_stats
9
+ * Write tools (BASE_PRIVATE_KEY env): deploy_token
10
+ */
11
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
12
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
13
+ import { z } from "zod";
14
+ import { createPublicClient, createWalletClient, http, formatEther, parseEther, } from "viem";
15
+ import { base } from "viem/chains";
16
+ import { privateKeyToAccount } from "viem/accounts";
17
+ // ─── Constants ──────────────────────────────────────────────────────────────
18
+ const BASE_RPC = "https://base-rpc.publicnode.com";
19
+ const CONTRACTS = {
20
+ FACTORY: "0xe5bCa0eDe9208f7Ee7FCAFa0415Ca3DC03e16a90",
21
+ LP_LOCKER: "0x9047c0944c843d91951a6C91dc9f3944D826ACA8",
22
+ SWAP_ROUTER: "0x3A9c65f4510de85F1843145d637ae895a2Fe04BE",
23
+ FEE_VIEWER: "0xd25Da746946531F6d8Ba42c4bC0CbF25A39b4b39",
24
+ };
25
+ const DEFAULT_SUPPLY = 1000000000n * 10n ** 18n; // 1B tokens
26
+ const DEFAULT_FDV = 20n * 10n ** 18n; // 20 ETH
27
+ // ─── ABIs (minimal, only what we need) ──────────────────────────────────────
28
+ const FACTORY_ABI = [
29
+ {
30
+ type: "function",
31
+ name: "getTokenCount",
32
+ inputs: [],
33
+ outputs: [{ name: "", type: "uint256" }],
34
+ stateMutability: "view",
35
+ },
36
+ {
37
+ type: "function",
38
+ name: "getTokens",
39
+ inputs: [
40
+ { name: "startIndex", type: "uint256" },
41
+ { name: "endIndex", type: "uint256" },
42
+ ],
43
+ outputs: [
44
+ {
45
+ name: "",
46
+ type: "tuple[]",
47
+ components: [
48
+ { name: "token", type: "address" },
49
+ { name: "creator", type: "address" },
50
+ { name: "positionId", type: "uint256" },
51
+ { name: "totalSupply", type: "uint256" },
52
+ { name: "initialFdv", type: "uint256" },
53
+ { name: "createdAt", type: "uint256" },
54
+ { name: "name", type: "string" },
55
+ { name: "symbol", type: "string" },
56
+ ],
57
+ },
58
+ ],
59
+ stateMutability: "view",
60
+ },
61
+ {
62
+ type: "function",
63
+ name: "getTokenInfo",
64
+ inputs: [{ name: "token", type: "address" }],
65
+ outputs: [
66
+ {
67
+ name: "",
68
+ type: "tuple",
69
+ components: [
70
+ { name: "token", type: "address" },
71
+ { name: "creator", type: "address" },
72
+ { name: "positionId", type: "uint256" },
73
+ { name: "totalSupply", type: "uint256" },
74
+ { name: "initialFdv", type: "uint256" },
75
+ { name: "createdAt", type: "uint256" },
76
+ { name: "name", type: "string" },
77
+ { name: "symbol", type: "string" },
78
+ ],
79
+ },
80
+ ],
81
+ stateMutability: "view",
82
+ },
83
+ {
84
+ type: "function",
85
+ name: "getTokenMetadata",
86
+ inputs: [{ name: "token", type: "address" }],
87
+ outputs: [
88
+ { name: "imageUrl", type: "string" },
89
+ { name: "websiteUrl", type: "string" },
90
+ ],
91
+ stateMutability: "view",
92
+ },
93
+ {
94
+ type: "function",
95
+ name: "createToken",
96
+ inputs: [
97
+ { name: "name", type: "string" },
98
+ { name: "symbol", type: "string" },
99
+ { name: "imageUrl", type: "string" },
100
+ { name: "websiteUrl", type: "string" },
101
+ { name: "totalSupply", type: "uint256" },
102
+ { name: "initialFdv", type: "uint256" },
103
+ { name: "creator", type: "address" },
104
+ ],
105
+ outputs: [
106
+ { name: "token", type: "address" },
107
+ { name: "positionId", type: "uint256" },
108
+ ],
109
+ stateMutability: "nonpayable",
110
+ },
111
+ ];
112
+ const TOKEN_ABI = [
113
+ {
114
+ type: "function",
115
+ name: "totalSupply",
116
+ inputs: [],
117
+ outputs: [{ name: "", type: "uint256" }],
118
+ stateMutability: "view",
119
+ },
120
+ {
121
+ type: "function",
122
+ name: "balanceOf",
123
+ inputs: [{ name: "account", type: "address" }],
124
+ outputs: [{ name: "", type: "uint256" }],
125
+ stateMutability: "view",
126
+ },
127
+ ];
128
+ // ─── Clients ────────────────────────────────────────────────────────────────
129
+ const publicClient = createPublicClient({
130
+ chain: base,
131
+ transport: http(BASE_RPC),
132
+ });
133
+ function getWalletClient() {
134
+ const key = process.env.BASE_PRIVATE_KEY;
135
+ if (!key)
136
+ throw new Error("BASE_PRIVATE_KEY env var required for deploy_token");
137
+ const privateKey = (key.startsWith("0x") ? key : `0x${key}`);
138
+ const account = privateKeyToAccount(privateKey);
139
+ return createWalletClient({ account, chain: base, transport: http(BASE_RPC) });
140
+ }
141
+ // ─── Helpers ────────────────────────────────────────────────────────────────
142
+ function formatToken(t) {
143
+ return {
144
+ address: t.token,
145
+ name: t.name,
146
+ symbol: t.symbol,
147
+ creator: t.creator,
148
+ totalSupply: formatEther(t.totalSupply),
149
+ initialFdv: `${formatEther(t.initialFdv)} ETH`,
150
+ createdAt: new Date(Number(t.createdAt) * 1000).toISOString(),
151
+ links: {
152
+ pumpclaw: `https://pumpclaw.com/token/${t.token}/`,
153
+ dexscreener: `https://dexscreener.com/base/${t.token}`,
154
+ basescan: `https://basescan.org/token/${t.token}`,
155
+ matcha: `https://matcha.xyz/tokens/base/${t.token}?sellChain=8453&sellAddress=0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee`,
156
+ },
157
+ };
158
+ }
159
+ // ─── MCP Server ─────────────────────────────────────────────────────────────
160
+ const server = new McpServer({
161
+ name: "pumpclaw",
162
+ version: "1.0.0",
163
+ });
164
+ // ─── Resources ──────────────────────────────────────────────────────────────
165
+ server.resource("protocol-info", "pumpclaw://info", async (uri) => ({
166
+ contents: [
167
+ {
168
+ uri: uri.href,
169
+ mimeType: "text/markdown",
170
+ text: `# PumpClaw — Free Token Launcher on Base
171
+
172
+ ## What is PumpClaw?
173
+ PumpClaw lets anyone launch a token on Base (Ethereum L2) for free using Uniswap V4.
174
+
175
+ ## Key Features
176
+ - **Free to launch** — 0 ETH cost, no upfront payment
177
+ - **80% creator fees** — earn 80% of trading fees (vs 40% on Clanker)
178
+ - **LP locked forever** — no rug pulls, liquidity is permanent
179
+ - **Uniswap V4** — latest DEX infrastructure, full-range liquidity
180
+ - **On-chain registry** — all tokens discoverable via smart contract
181
+
182
+ ## Contracts (Base Mainnet)
183
+ - Factory: ${CONTRACTS.FACTORY}
184
+ - SwapRouter: ${CONTRACTS.SWAP_ROUTER}
185
+ - LPLocker: ${CONTRACTS.LP_LOCKER}
186
+
187
+ ## Links
188
+ - Website: https://pumpclaw.com
189
+ - GitHub: https://github.com/clawd800/pumpclaw
190
+ - API: https://pumpclaw.com/api/v1/tokens.json
191
+
192
+ ## Protocol Config
193
+ - Default supply: 1,000,000,000 tokens
194
+ - Default FDV: 20 ETH
195
+ - Fee split: 80% creator / 20% protocol
196
+ - Price range: 100x multiplier
197
+ - LP fee: 1%
198
+ `,
199
+ },
200
+ ],
201
+ }));
202
+ // ─── Tools ──────────────────────────────────────────────────────────────────
203
+ // 1. list_tokens
204
+ server.tool("list_tokens", "List all tokens launched on PumpClaw. Returns name, symbol, address, creator, and trade links.", {
205
+ limit: z.number().optional().describe("Max tokens to return (default: all)"),
206
+ offset: z.number().optional().describe("Starting offset (default: 0)"),
207
+ }, async ({ limit, offset }) => {
208
+ const count = (await publicClient.readContract({
209
+ address: CONTRACTS.FACTORY,
210
+ abi: FACTORY_ABI,
211
+ functionName: "getTokenCount",
212
+ }));
213
+ const start = BigInt(offset ?? 0);
214
+ const end = limit ? BigInt(Math.min(Number(start) + limit, Number(count))) : count;
215
+ const tokens = (await publicClient.readContract({
216
+ address: CONTRACTS.FACTORY,
217
+ abi: FACTORY_ABI,
218
+ functionName: "getTokens",
219
+ args: [start, end],
220
+ }));
221
+ const formatted = tokens.map(formatToken);
222
+ return {
223
+ content: [
224
+ {
225
+ type: "text",
226
+ text: JSON.stringify({ totalTokens: Number(count), showing: formatted.length, tokens: formatted }, null, 2),
227
+ },
228
+ ],
229
+ };
230
+ });
231
+ // 2. get_token
232
+ server.tool("get_token", "Get detailed info about a specific PumpClaw token by its contract address.", {
233
+ address: z.string().describe("Token contract address (0x...)"),
234
+ }, async ({ address }) => {
235
+ const tokenAddr = address;
236
+ const [info, metadata, supply] = await Promise.all([
237
+ publicClient.readContract({
238
+ address: CONTRACTS.FACTORY,
239
+ abi: FACTORY_ABI,
240
+ functionName: "getTokenInfo",
241
+ args: [tokenAddr],
242
+ }),
243
+ publicClient
244
+ .readContract({
245
+ address: CONTRACTS.FACTORY,
246
+ abi: FACTORY_ABI,
247
+ functionName: "getTokenMetadata",
248
+ args: [tokenAddr],
249
+ })
250
+ .catch(() => ["", ""]),
251
+ publicClient.readContract({
252
+ address: tokenAddr,
253
+ abi: TOKEN_ABI,
254
+ functionName: "totalSupply",
255
+ }),
256
+ ]);
257
+ const [imageUrl, websiteUrl] = metadata;
258
+ const formatted = formatToken(info);
259
+ return {
260
+ content: [
261
+ {
262
+ type: "text",
263
+ text: JSON.stringify({
264
+ ...formatted,
265
+ imageUrl: imageUrl || null,
266
+ websiteUrl: websiteUrl || null,
267
+ circulatingSupply: formatEther(supply),
268
+ }, null, 2),
269
+ },
270
+ ],
271
+ };
272
+ });
273
+ // 3. get_stats
274
+ server.tool("get_stats", "Get PumpClaw protocol statistics: total tokens, unique creators, fee structure.", {}, async () => {
275
+ const count = (await publicClient.readContract({
276
+ address: CONTRACTS.FACTORY,
277
+ abi: FACTORY_ABI,
278
+ functionName: "getTokenCount",
279
+ }));
280
+ const tokens = (await publicClient.readContract({
281
+ address: CONTRACTS.FACTORY,
282
+ abi: FACTORY_ABI,
283
+ functionName: "getTokens",
284
+ args: [0n, count],
285
+ }));
286
+ const uniqueCreators = new Set(tokens.map((t) => t.creator.toLowerCase()));
287
+ return {
288
+ content: [
289
+ {
290
+ type: "text",
291
+ text: JSON.stringify({
292
+ totalTokens: Number(count),
293
+ uniqueCreators: uniqueCreators.size,
294
+ chain: "Base (8453)",
295
+ dex: "Uniswap V4",
296
+ launchCost: "0 ETH (free)",
297
+ creatorFeeShare: "80%",
298
+ lpLocked: "forever",
299
+ factory: CONTRACTS.FACTORY,
300
+ website: "https://pumpclaw.com",
301
+ api: "https://pumpclaw.com/api/v1/tokens.json",
302
+ }, null, 2),
303
+ },
304
+ ],
305
+ };
306
+ });
307
+ // 4. deploy_token
308
+ server.tool("deploy_token", "Deploy a new token on PumpClaw (Base, Uniswap V4). Requires BASE_PRIVATE_KEY env var. Free — no ETH cost.", {
309
+ name: z.string().describe("Token name (e.g. 'My Cool Token')"),
310
+ symbol: z.string().describe("Token ticker symbol (e.g. 'MCT')"),
311
+ imageUrl: z.string().optional().describe("Token logo URL (optional)"),
312
+ websiteUrl: z.string().optional().describe("Project website URL (optional)"),
313
+ totalSupply: z.string().optional().describe("Total supply in tokens (default: 1000000000)"),
314
+ initialFdv: z.string().optional().describe("Initial FDV in ETH (default: 20)"),
315
+ creator: z.string().optional().describe("Creator address for fee attribution (default: deployer wallet)"),
316
+ }, async ({ name, symbol, imageUrl, websiteUrl, totalSupply, initialFdv, creator }) => {
317
+ try {
318
+ const wallet = getWalletClient();
319
+ const deployerAddr = wallet.account.address;
320
+ const creatorAddr = (creator || deployerAddr);
321
+ const supply = totalSupply ? parseEther(totalSupply) : DEFAULT_SUPPLY;
322
+ const fdv = initialFdv ? parseEther(initialFdv) : DEFAULT_FDV;
323
+ const hash = await wallet.writeContract({
324
+ address: CONTRACTS.FACTORY,
325
+ abi: FACTORY_ABI,
326
+ functionName: "createToken",
327
+ args: [name, symbol, imageUrl ?? "", websiteUrl ?? "", supply, fdv, creatorAddr],
328
+ });
329
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
330
+ // Parse the token address from logs
331
+ let tokenAddress = "";
332
+ for (const log of receipt.logs) {
333
+ if (log.address.toLowerCase() === CONTRACTS.FACTORY.toLowerCase() && log.topics.length >= 3) {
334
+ // TokenCreated event: topic[1] = token address
335
+ tokenAddress = `0x${log.topics[1].slice(26)}`;
336
+ break;
337
+ }
338
+ }
339
+ return {
340
+ content: [
341
+ {
342
+ type: "text",
343
+ text: JSON.stringify({
344
+ success: true,
345
+ token: {
346
+ address: tokenAddress,
347
+ name,
348
+ symbol,
349
+ totalSupply: formatEther(supply),
350
+ initialFdv: `${formatEther(fdv)} ETH`,
351
+ creator: creatorAddr,
352
+ },
353
+ transaction: hash,
354
+ links: {
355
+ pumpclaw: `https://pumpclaw.com/token/${tokenAddress}/`,
356
+ dexscreener: `https://dexscreener.com/base/${tokenAddress}`,
357
+ basescan: `https://basescan.org/tx/${hash}`,
358
+ matcha: `https://matcha.xyz/tokens/base/${tokenAddress}?sellChain=8453&sellAddress=0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee`,
359
+ },
360
+ }, null, 2),
361
+ },
362
+ ],
363
+ };
364
+ }
365
+ catch (err) {
366
+ return {
367
+ content: [
368
+ {
369
+ type: "text",
370
+ text: JSON.stringify({ success: false, error: err.message }, null, 2),
371
+ },
372
+ ],
373
+ isError: true,
374
+ };
375
+ }
376
+ });
377
+ // ─── Start ──────────────────────────────────────────────────────────────────
378
+ async function main() {
379
+ const transport = new StdioServerTransport();
380
+ await server.connect(transport);
381
+ console.error("PumpClaw MCP server running on stdio");
382
+ }
383
+ main().catch((err) => {
384
+ console.error("Fatal error:", err);
385
+ process.exit(1);
386
+ });
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "pumpclaw-mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for PumpClaw - free token launcher on Base (Uniswap V4)",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "pumpclaw-mcp": "dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "start": "node dist/index.js",
13
+ "dev": "tsx src/index.ts"
14
+ },
15
+ "keywords": [
16
+ "mcp",
17
+ "model-context-protocol",
18
+ "pumpclaw",
19
+ "base",
20
+ "token-launcher",
21
+ "uniswap-v4",
22
+ "ai-agent",
23
+ "crypto"
24
+ ],
25
+ "author": "clawd",
26
+ "license": "MIT",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/clawd800/pumpclaw",
30
+ "directory": "mcp-server"
31
+ },
32
+ "homepage": "https://pumpclaw.com",
33
+ "dependencies": {
34
+ "@modelcontextprotocol/sdk": "^1.26.0",
35
+ "viem": "^2.0.0"
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^25.2.2",
39
+ "tsx": "^4.0.0",
40
+ "typescript": "^5.0.0"
41
+ }
42
+ }
package/src/index.ts ADDED
@@ -0,0 +1,466 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * PumpClaw MCP Server
4
+ *
5
+ * Model Context Protocol server for PumpClaw — the free token launcher on Base (Uniswap V4).
6
+ * Allows any MCP-compatible AI agent to list, inspect, and deploy tokens.
7
+ *
8
+ * Read-only tools (no key needed): list_tokens, get_token, get_stats
9
+ * Write tools (BASE_PRIVATE_KEY env): deploy_token
10
+ */
11
+
12
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
13
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
14
+ import { z } from "zod";
15
+ import {
16
+ createPublicClient,
17
+ createWalletClient,
18
+ http,
19
+ formatEther,
20
+ parseEther,
21
+ type Address,
22
+ } from "viem";
23
+ import { base } from "viem/chains";
24
+ import { privateKeyToAccount } from "viem/accounts";
25
+
26
+ // ─── Constants ──────────────────────────────────────────────────────────────
27
+
28
+ const BASE_RPC = "https://base-rpc.publicnode.com";
29
+
30
+ const CONTRACTS = {
31
+ FACTORY: "0xe5bCa0eDe9208f7Ee7FCAFa0415Ca3DC03e16a90" as Address,
32
+ LP_LOCKER: "0x9047c0944c843d91951a6C91dc9f3944D826ACA8" as Address,
33
+ SWAP_ROUTER: "0x3A9c65f4510de85F1843145d637ae895a2Fe04BE" as Address,
34
+ FEE_VIEWER: "0xd25Da746946531F6d8Ba42c4bC0CbF25A39b4b39" as Address,
35
+ };
36
+
37
+ const DEFAULT_SUPPLY = 1_000_000_000n * 10n ** 18n; // 1B tokens
38
+ const DEFAULT_FDV = 20n * 10n ** 18n; // 20 ETH
39
+
40
+ // ─── ABIs (minimal, only what we need) ──────────────────────────────────────
41
+
42
+ const FACTORY_ABI = [
43
+ {
44
+ type: "function",
45
+ name: "getTokenCount",
46
+ inputs: [],
47
+ outputs: [{ name: "", type: "uint256" }],
48
+ stateMutability: "view",
49
+ },
50
+ {
51
+ type: "function",
52
+ name: "getTokens",
53
+ inputs: [
54
+ { name: "startIndex", type: "uint256" },
55
+ { name: "endIndex", type: "uint256" },
56
+ ],
57
+ outputs: [
58
+ {
59
+ name: "",
60
+ type: "tuple[]",
61
+ components: [
62
+ { name: "token", type: "address" },
63
+ { name: "creator", type: "address" },
64
+ { name: "positionId", type: "uint256" },
65
+ { name: "totalSupply", type: "uint256" },
66
+ { name: "initialFdv", type: "uint256" },
67
+ { name: "createdAt", type: "uint256" },
68
+ { name: "name", type: "string" },
69
+ { name: "symbol", type: "string" },
70
+ ],
71
+ },
72
+ ],
73
+ stateMutability: "view",
74
+ },
75
+ {
76
+ type: "function",
77
+ name: "getTokenInfo",
78
+ inputs: [{ name: "token", type: "address" }],
79
+ outputs: [
80
+ {
81
+ name: "",
82
+ type: "tuple",
83
+ components: [
84
+ { name: "token", type: "address" },
85
+ { name: "creator", type: "address" },
86
+ { name: "positionId", type: "uint256" },
87
+ { name: "totalSupply", type: "uint256" },
88
+ { name: "initialFdv", type: "uint256" },
89
+ { name: "createdAt", type: "uint256" },
90
+ { name: "name", type: "string" },
91
+ { name: "symbol", type: "string" },
92
+ ],
93
+ },
94
+ ],
95
+ stateMutability: "view",
96
+ },
97
+ {
98
+ type: "function",
99
+ name: "getTokenMetadata",
100
+ inputs: [{ name: "token", type: "address" }],
101
+ outputs: [
102
+ { name: "imageUrl", type: "string" },
103
+ { name: "websiteUrl", type: "string" },
104
+ ],
105
+ stateMutability: "view",
106
+ },
107
+ {
108
+ type: "function",
109
+ name: "createToken",
110
+ inputs: [
111
+ { name: "name", type: "string" },
112
+ { name: "symbol", type: "string" },
113
+ { name: "imageUrl", type: "string" },
114
+ { name: "websiteUrl", type: "string" },
115
+ { name: "totalSupply", type: "uint256" },
116
+ { name: "initialFdv", type: "uint256" },
117
+ { name: "creator", type: "address" },
118
+ ],
119
+ outputs: [
120
+ { name: "token", type: "address" },
121
+ { name: "positionId", type: "uint256" },
122
+ ],
123
+ stateMutability: "nonpayable",
124
+ },
125
+ ] as const;
126
+
127
+ const TOKEN_ABI = [
128
+ {
129
+ type: "function",
130
+ name: "totalSupply",
131
+ inputs: [],
132
+ outputs: [{ name: "", type: "uint256" }],
133
+ stateMutability: "view",
134
+ },
135
+ {
136
+ type: "function",
137
+ name: "balanceOf",
138
+ inputs: [{ name: "account", type: "address" }],
139
+ outputs: [{ name: "", type: "uint256" }],
140
+ stateMutability: "view",
141
+ },
142
+ ] as const;
143
+
144
+ // ─── Clients ────────────────────────────────────────────────────────────────
145
+
146
+ const publicClient = createPublicClient({
147
+ chain: base,
148
+ transport: http(BASE_RPC),
149
+ });
150
+
151
+ function getWalletClient() {
152
+ const key = process.env.BASE_PRIVATE_KEY;
153
+ if (!key) throw new Error("BASE_PRIVATE_KEY env var required for deploy_token");
154
+ const privateKey = (key.startsWith("0x") ? key : `0x${key}`) as `0x${string}`;
155
+ const account = privateKeyToAccount(privateKey);
156
+ return createWalletClient({ account, chain: base, transport: http(BASE_RPC) });
157
+ }
158
+
159
+ // ─── Helpers ────────────────────────────────────────────────────────────────
160
+
161
+ function formatToken(t: any) {
162
+ return {
163
+ address: t.token,
164
+ name: t.name,
165
+ symbol: t.symbol,
166
+ creator: t.creator,
167
+ totalSupply: formatEther(t.totalSupply),
168
+ initialFdv: `${formatEther(t.initialFdv)} ETH`,
169
+ createdAt: new Date(Number(t.createdAt) * 1000).toISOString(),
170
+ links: {
171
+ pumpclaw: `https://pumpclaw.com/token/${t.token}/`,
172
+ dexscreener: `https://dexscreener.com/base/${t.token}`,
173
+ basescan: `https://basescan.org/token/${t.token}`,
174
+ matcha: `https://matcha.xyz/tokens/base/${t.token}?sellChain=8453&sellAddress=0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee`,
175
+ },
176
+ };
177
+ }
178
+
179
+ // ─── MCP Server ─────────────────────────────────────────────────────────────
180
+
181
+ const server = new McpServer({
182
+ name: "pumpclaw",
183
+ version: "1.0.0",
184
+ });
185
+
186
+ // ─── Resources ──────────────────────────────────────────────────────────────
187
+
188
+ server.resource("protocol-info", "pumpclaw://info", async (uri) => ({
189
+ contents: [
190
+ {
191
+ uri: uri.href,
192
+ mimeType: "text/markdown",
193
+ text: `# PumpClaw — Free Token Launcher on Base
194
+
195
+ ## What is PumpClaw?
196
+ PumpClaw lets anyone launch a token on Base (Ethereum L2) for free using Uniswap V4.
197
+
198
+ ## Key Features
199
+ - **Free to launch** — 0 ETH cost, no upfront payment
200
+ - **80% creator fees** — earn 80% of trading fees (vs 40% on Clanker)
201
+ - **LP locked forever** — no rug pulls, liquidity is permanent
202
+ - **Uniswap V4** — latest DEX infrastructure, full-range liquidity
203
+ - **On-chain registry** — all tokens discoverable via smart contract
204
+
205
+ ## Contracts (Base Mainnet)
206
+ - Factory: ${CONTRACTS.FACTORY}
207
+ - SwapRouter: ${CONTRACTS.SWAP_ROUTER}
208
+ - LPLocker: ${CONTRACTS.LP_LOCKER}
209
+
210
+ ## Links
211
+ - Website: https://pumpclaw.com
212
+ - GitHub: https://github.com/clawd800/pumpclaw
213
+ - API: https://pumpclaw.com/api/v1/tokens.json
214
+
215
+ ## Protocol Config
216
+ - Default supply: 1,000,000,000 tokens
217
+ - Default FDV: 20 ETH
218
+ - Fee split: 80% creator / 20% protocol
219
+ - Price range: 100x multiplier
220
+ - LP fee: 1%
221
+ `,
222
+ },
223
+ ],
224
+ }));
225
+
226
+ // ─── Tools ──────────────────────────────────────────────────────────────────
227
+
228
+ // 1. list_tokens
229
+ server.tool(
230
+ "list_tokens",
231
+ "List all tokens launched on PumpClaw. Returns name, symbol, address, creator, and trade links.",
232
+ {
233
+ limit: z.number().optional().describe("Max tokens to return (default: all)"),
234
+ offset: z.number().optional().describe("Starting offset (default: 0)"),
235
+ },
236
+ async ({ limit, offset }) => {
237
+ const count = (await publicClient.readContract({
238
+ address: CONTRACTS.FACTORY,
239
+ abi: FACTORY_ABI,
240
+ functionName: "getTokenCount",
241
+ })) as bigint;
242
+
243
+ const start = BigInt(offset ?? 0);
244
+ const end = limit ? BigInt(Math.min(Number(start) + limit, Number(count))) : count;
245
+
246
+ const tokens = (await publicClient.readContract({
247
+ address: CONTRACTS.FACTORY,
248
+ abi: FACTORY_ABI,
249
+ functionName: "getTokens",
250
+ args: [start, end],
251
+ })) as any[];
252
+
253
+ const formatted = tokens.map(formatToken);
254
+
255
+ return {
256
+ content: [
257
+ {
258
+ type: "text" as const,
259
+ text: JSON.stringify(
260
+ { totalTokens: Number(count), showing: formatted.length, tokens: formatted },
261
+ null,
262
+ 2
263
+ ),
264
+ },
265
+ ],
266
+ };
267
+ }
268
+ );
269
+
270
+ // 2. get_token
271
+ server.tool(
272
+ "get_token",
273
+ "Get detailed info about a specific PumpClaw token by its contract address.",
274
+ {
275
+ address: z.string().describe("Token contract address (0x...)"),
276
+ },
277
+ async ({ address }) => {
278
+ const tokenAddr = address as Address;
279
+
280
+ const [info, metadata, supply] = await Promise.all([
281
+ publicClient.readContract({
282
+ address: CONTRACTS.FACTORY,
283
+ abi: FACTORY_ABI,
284
+ functionName: "getTokenInfo",
285
+ args: [tokenAddr],
286
+ }) as Promise<any>,
287
+ publicClient
288
+ .readContract({
289
+ address: CONTRACTS.FACTORY,
290
+ abi: FACTORY_ABI,
291
+ functionName: "getTokenMetadata",
292
+ args: [tokenAddr],
293
+ })
294
+ .catch(() => ["", ""]),
295
+ publicClient.readContract({
296
+ address: tokenAddr,
297
+ abi: TOKEN_ABI,
298
+ functionName: "totalSupply",
299
+ }) as Promise<bigint>,
300
+ ]);
301
+
302
+ const [imageUrl, websiteUrl] = metadata as [string, string];
303
+ const formatted = formatToken(info);
304
+
305
+ return {
306
+ content: [
307
+ {
308
+ type: "text" as const,
309
+ text: JSON.stringify(
310
+ {
311
+ ...formatted,
312
+ imageUrl: imageUrl || null,
313
+ websiteUrl: websiteUrl || null,
314
+ circulatingSupply: formatEther(supply),
315
+ },
316
+ null,
317
+ 2
318
+ ),
319
+ },
320
+ ],
321
+ };
322
+ }
323
+ );
324
+
325
+ // 3. get_stats
326
+ server.tool(
327
+ "get_stats",
328
+ "Get PumpClaw protocol statistics: total tokens, unique creators, fee structure.",
329
+ {},
330
+ async () => {
331
+ const count = (await publicClient.readContract({
332
+ address: CONTRACTS.FACTORY,
333
+ abi: FACTORY_ABI,
334
+ functionName: "getTokenCount",
335
+ })) as bigint;
336
+
337
+ const tokens = (await publicClient.readContract({
338
+ address: CONTRACTS.FACTORY,
339
+ abi: FACTORY_ABI,
340
+ functionName: "getTokens",
341
+ args: [0n, count],
342
+ })) as any[];
343
+
344
+ const uniqueCreators = new Set(tokens.map((t: any) => t.creator.toLowerCase()));
345
+
346
+ return {
347
+ content: [
348
+ {
349
+ type: "text" as const,
350
+ text: JSON.stringify(
351
+ {
352
+ totalTokens: Number(count),
353
+ uniqueCreators: uniqueCreators.size,
354
+ chain: "Base (8453)",
355
+ dex: "Uniswap V4",
356
+ launchCost: "0 ETH (free)",
357
+ creatorFeeShare: "80%",
358
+ lpLocked: "forever",
359
+ factory: CONTRACTS.FACTORY,
360
+ website: "https://pumpclaw.com",
361
+ api: "https://pumpclaw.com/api/v1/tokens.json",
362
+ },
363
+ null,
364
+ 2
365
+ ),
366
+ },
367
+ ],
368
+ };
369
+ }
370
+ );
371
+
372
+ // 4. deploy_token
373
+ server.tool(
374
+ "deploy_token",
375
+ "Deploy a new token on PumpClaw (Base, Uniswap V4). Requires BASE_PRIVATE_KEY env var. Free — no ETH cost.",
376
+ {
377
+ name: z.string().describe("Token name (e.g. 'My Cool Token')"),
378
+ symbol: z.string().describe("Token ticker symbol (e.g. 'MCT')"),
379
+ imageUrl: z.string().optional().describe("Token logo URL (optional)"),
380
+ websiteUrl: z.string().optional().describe("Project website URL (optional)"),
381
+ totalSupply: z.string().optional().describe("Total supply in tokens (default: 1000000000)"),
382
+ initialFdv: z.string().optional().describe("Initial FDV in ETH (default: 20)"),
383
+ creator: z.string().optional().describe("Creator address for fee attribution (default: deployer wallet)"),
384
+ },
385
+ async ({ name, symbol, imageUrl, websiteUrl, totalSupply, initialFdv, creator }) => {
386
+ try {
387
+ const wallet = getWalletClient();
388
+ const deployerAddr = wallet.account.address;
389
+ const creatorAddr = (creator || deployerAddr) as Address;
390
+ const supply = totalSupply ? parseEther(totalSupply) : DEFAULT_SUPPLY;
391
+ const fdv = initialFdv ? parseEther(initialFdv) : DEFAULT_FDV;
392
+
393
+ const hash = await wallet.writeContract({
394
+ address: CONTRACTS.FACTORY,
395
+ abi: FACTORY_ABI,
396
+ functionName: "createToken",
397
+ args: [name, symbol, imageUrl ?? "", websiteUrl ?? "", supply, fdv, creatorAddr],
398
+ });
399
+
400
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
401
+
402
+ // Parse the token address from logs
403
+ let tokenAddress = "";
404
+ for (const log of receipt.logs) {
405
+ if (log.address.toLowerCase() === CONTRACTS.FACTORY.toLowerCase() && log.topics.length >= 3) {
406
+ // TokenCreated event: topic[1] = token address
407
+ tokenAddress = `0x${log.topics[1]!.slice(26)}`;
408
+ break;
409
+ }
410
+ }
411
+
412
+ return {
413
+ content: [
414
+ {
415
+ type: "text" as const,
416
+ text: JSON.stringify(
417
+ {
418
+ success: true,
419
+ token: {
420
+ address: tokenAddress,
421
+ name,
422
+ symbol,
423
+ totalSupply: formatEther(supply),
424
+ initialFdv: `${formatEther(fdv)} ETH`,
425
+ creator: creatorAddr,
426
+ },
427
+ transaction: hash,
428
+ links: {
429
+ pumpclaw: `https://pumpclaw.com/token/${tokenAddress}/`,
430
+ dexscreener: `https://dexscreener.com/base/${tokenAddress}`,
431
+ basescan: `https://basescan.org/tx/${hash}`,
432
+ matcha: `https://matcha.xyz/tokens/base/${tokenAddress}?sellChain=8453&sellAddress=0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee`,
433
+ },
434
+ },
435
+ null,
436
+ 2
437
+ ),
438
+ },
439
+ ],
440
+ };
441
+ } catch (err: any) {
442
+ return {
443
+ content: [
444
+ {
445
+ type: "text" as const,
446
+ text: JSON.stringify({ success: false, error: err.message }, null, 2),
447
+ },
448
+ ],
449
+ isError: true,
450
+ };
451
+ }
452
+ }
453
+ );
454
+
455
+ // ─── Start ──────────────────────────────────────────────────────────────────
456
+
457
+ async function main() {
458
+ const transport = new StdioServerTransport();
459
+ await server.connect(transport);
460
+ console.error("PumpClaw MCP server running on stdio");
461
+ }
462
+
463
+ main().catch((err) => {
464
+ console.error("Fatal error:", err);
465
+ process.exit(1);
466
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "outDir": "dist",
7
+ "rootDir": "src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "declaration": true,
12
+ "resolveJsonModule": true
13
+ },
14
+ "include": ["src/**/*"]
15
+ }