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 +125 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +386 -0
- package/package.json +42 -0
- package/src/index.ts +466 -0
- package/tsconfig.json +15 -0
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
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|