@openpump/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.
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/middleware/api-key-auth.ts
4
+ var UNAUTHORIZED_RESPONSE = {
5
+ jsonrpc: "2.0",
6
+ error: { code: -32001, message: "Unauthorized: Invalid API key" },
7
+ id: null
8
+ };
9
+ function extractRawKey(req) {
10
+ const xApiKey = req.headers["x-api-key"];
11
+ if (typeof xApiKey === "string") return xApiKey;
12
+ const auth = req.headers["authorization"];
13
+ if (typeof auth === "string" && auth.startsWith("Bearer ")) {
14
+ return auth.slice(7);
15
+ }
16
+ return void 0;
17
+ }
18
+ async function validateViaRestApi(apiKey, apiBaseUrl) {
19
+ try {
20
+ const res = await fetch(`${apiBaseUrl}/api/auth/validate`, {
21
+ method: "GET",
22
+ headers: {
23
+ Authorization: `Bearer ${apiKey}`
24
+ }
25
+ });
26
+ if (!res.ok) return null;
27
+ const data = await res.json();
28
+ return {
29
+ userId: data.userId,
30
+ apiKeyId: data.apiKeyId ?? "",
31
+ scopes: data.scopes,
32
+ wallets: data.wallets.map((w, i) => ({
33
+ id: w.id,
34
+ publicKey: w.publicKey,
35
+ label: w.label,
36
+ index: w.index ?? i
37
+ })),
38
+ apiKey
39
+ };
40
+ } catch {
41
+ return null;
42
+ }
43
+ }
44
+ function createApiKeyMiddleware(apiBaseUrl) {
45
+ return async function apiKeyMiddleware(req, res, next) {
46
+ const rawKey = extractRawKey(req);
47
+ if (!rawKey?.startsWith("op_sk_live_")) {
48
+ res.status(401).json(UNAUTHORIZED_RESPONSE);
49
+ return;
50
+ }
51
+ const userContext = await validateViaRestApi(rawKey, apiBaseUrl);
52
+ if (!userContext) {
53
+ res.status(401).json(UNAUTHORIZED_RESPONSE);
54
+ return;
55
+ }
56
+ req.userContext = userContext;
57
+ next();
58
+ };
59
+ }
60
+ async function validateApiKey(apiKey, apiBaseUrl) {
61
+ return validateViaRestApi(apiKey, apiBaseUrl);
62
+ }
63
+
64
+ export {
65
+ createApiKeyMiddleware,
66
+ validateApiKey
67
+ };
@@ -0,0 +1,322 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli/init.ts
4
+ import * as p2 from "@clack/prompts";
5
+ import color from "picocolors";
6
+
7
+ // src/cli/config.ts
8
+ import { execSync } from "child_process";
9
+ import * as fs from "fs";
10
+ import * as os from "os";
11
+ import * as path from "path";
12
+ function getClaudeDesktopConfigPath() {
13
+ const home = os.homedir();
14
+ switch (process.platform) {
15
+ case "darwin": {
16
+ return path.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
17
+ }
18
+ case "win32": {
19
+ return path.join(
20
+ process.env["APPDATA"] ?? path.join(home, "AppData", "Roaming"),
21
+ "Claude",
22
+ "claude_desktop_config.json"
23
+ );
24
+ }
25
+ default: {
26
+ return path.join(home, ".config", "Claude", "claude_desktop_config.json");
27
+ }
28
+ }
29
+ }
30
+ function getCursorConfigPath() {
31
+ return path.join(os.homedir(), ".cursor", "mcp.json");
32
+ }
33
+ function detectClients() {
34
+ const clients = [];
35
+ const desktopPath = getClaudeDesktopConfigPath();
36
+ const desktopDir = path.dirname(desktopPath);
37
+ const desktopDirExists = fs.existsSync(desktopDir);
38
+ const desktopFileExists = fs.existsSync(desktopPath);
39
+ if (desktopDirExists || desktopFileExists) {
40
+ clients.push({
41
+ type: "claude-desktop",
42
+ name: "Claude Desktop",
43
+ configPath: desktopPath,
44
+ exists: desktopFileExists
45
+ });
46
+ }
47
+ const cursorPath = getCursorConfigPath();
48
+ const cursorDir = path.dirname(cursorPath);
49
+ const cursorDirExists = fs.existsSync(cursorDir);
50
+ const cursorFileExists = fs.existsSync(cursorPath);
51
+ if (cursorDirExists || cursorFileExists) {
52
+ clients.push({
53
+ type: "cursor",
54
+ name: "Cursor",
55
+ configPath: cursorPath,
56
+ exists: cursorFileExists
57
+ });
58
+ }
59
+ try {
60
+ execSync("claude --version", { stdio: "ignore" });
61
+ clients.push({
62
+ type: "claude-code",
63
+ name: "Claude Code",
64
+ configPath: null,
65
+ exists: true
66
+ });
67
+ } catch {
68
+ }
69
+ return clients;
70
+ }
71
+ function buildServerEntry(apiKey) {
72
+ return {
73
+ command: "npx",
74
+ args: ["-y", "@openpump/mcp@latest"],
75
+ env: {
76
+ OPENPUMP_API_KEY: apiKey
77
+ }
78
+ };
79
+ }
80
+ function readConfigFile(configPath) {
81
+ if (!fs.existsSync(configPath)) {
82
+ return { mcpServers: {} };
83
+ }
84
+ try {
85
+ const raw = fs.readFileSync(configPath, "utf8");
86
+ if (!raw.trim()) {
87
+ return { mcpServers: {} };
88
+ }
89
+ const parsed = JSON.parse(raw);
90
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
91
+ return { mcpServers: {} };
92
+ }
93
+ return parsed;
94
+ } catch {
95
+ return { mcpServers: {} };
96
+ }
97
+ }
98
+ function mergeServerEntry(config, apiKey) {
99
+ const mcpServers = typeof config["mcpServers"] === "object" && config["mcpServers"] !== null && !Array.isArray(config["mcpServers"]) ? { ...config["mcpServers"] } : {};
100
+ const alreadyExists = "openpump" in mcpServers;
101
+ mcpServers["openpump"] = buildServerEntry(apiKey);
102
+ return {
103
+ config: { ...config, mcpServers },
104
+ alreadyExists
105
+ };
106
+ }
107
+ function writeConfigFile(configPath, config) {
108
+ const dir = path.dirname(configPath);
109
+ if (!fs.existsSync(dir)) {
110
+ fs.mkdirSync(dir, { recursive: true });
111
+ }
112
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf8");
113
+ }
114
+ function writeClientConfigs(clients, apiKey) {
115
+ const outcomes = [];
116
+ for (const client of clients) {
117
+ try {
118
+ if (client.type === "claude-code") {
119
+ execSync(
120
+ "claude mcp add openpump --transport stdio -- npx -y @openpump/mcp@latest",
121
+ {
122
+ stdio: "ignore",
123
+ env: { ...process.env, OPENPUMP_API_KEY: apiKey }
124
+ }
125
+ );
126
+ outcomes.push({
127
+ clientName: client.name,
128
+ success: true,
129
+ message: "Configured via `claude mcp add`"
130
+ });
131
+ continue;
132
+ }
133
+ if (!client.configPath) {
134
+ outcomes.push({
135
+ clientName: client.name,
136
+ success: false,
137
+ message: "No config path available"
138
+ });
139
+ continue;
140
+ }
141
+ const existing = readConfigFile(client.configPath);
142
+ const { config: merged } = mergeServerEntry(existing, apiKey);
143
+ writeConfigFile(client.configPath, merged);
144
+ outcomes.push({
145
+ clientName: client.name,
146
+ success: true,
147
+ message: `Updated ${client.configPath}`
148
+ });
149
+ } catch (error) {
150
+ outcomes.push({
151
+ clientName: client.name,
152
+ success: false,
153
+ message: `Failed: ${error instanceof Error ? error.message : String(error)}`
154
+ });
155
+ }
156
+ }
157
+ return outcomes;
158
+ }
159
+
160
+ // src/cli/prompts.ts
161
+ import * as p from "@clack/prompts";
162
+ function validateApiKey(value) {
163
+ if (!value.trim()) return "API key is required";
164
+ if (!value.startsWith("op_sk_live_"))
165
+ return 'API key must start with "op_sk_live_"';
166
+ if (value.length < 20) return "API key appears too short";
167
+ return void 0;
168
+ }
169
+ function validateApiKeyPrompt(value) {
170
+ return validateApiKey(value ?? "");
171
+ }
172
+ async function runPrompts(detected, flagApiKey) {
173
+ let apiKey;
174
+ if (flagApiKey) {
175
+ const error = validateApiKey(flagApiKey);
176
+ if (error) {
177
+ p.log.error(`Invalid --api-key: ${error}`);
178
+ return null;
179
+ }
180
+ apiKey = flagApiKey;
181
+ p.log.info("Using API key from --api-key flag");
182
+ } else {
183
+ const keyResult = await p.text({
184
+ message: "Enter your OpenPump API key:",
185
+ placeholder: "op_sk_live_...",
186
+ validate: validateApiKeyPrompt
187
+ });
188
+ if (p.isCancel(keyResult)) {
189
+ p.cancel("Setup cancelled.");
190
+ return null;
191
+ }
192
+ apiKey = keyResult;
193
+ }
194
+ const clientOptions = detected.map((c) => ({
195
+ value: c.type,
196
+ label: c.name,
197
+ hint: c.exists ? "config file found" : "directory found, will create config"
198
+ }));
199
+ const selectedTypes = await p.multiselect({
200
+ message: "Which clients do you want to configure?",
201
+ options: clientOptions,
202
+ required: true
203
+ });
204
+ if (p.isCancel(selectedTypes)) {
205
+ p.cancel("Setup cancelled.");
206
+ return null;
207
+ }
208
+ const selectedClients = [];
209
+ for (const clientType of selectedTypes) {
210
+ const client = detected.find((c) => c.type === clientType);
211
+ if (!client) continue;
212
+ let overwrite = false;
213
+ if (client.configPath && client.exists) {
214
+ const existing = readConfigFile(client.configPath);
215
+ const mcpServers = existing["mcpServers"];
216
+ if (typeof mcpServers === "object" && mcpServers !== null && "openpump" in mcpServers) {
217
+ const confirmResult = await p.confirm({
218
+ message: `${client.name} already has an OpenPump entry. Overwrite?`
219
+ });
220
+ if (p.isCancel(confirmResult)) {
221
+ p.cancel("Setup cancelled.");
222
+ return null;
223
+ }
224
+ if (!confirmResult) {
225
+ p.log.info(`Skipping ${client.name}`);
226
+ continue;
227
+ }
228
+ overwrite = true;
229
+ }
230
+ }
231
+ selectedClients.push({
232
+ type: client.type,
233
+ name: client.name,
234
+ configPath: client.configPath,
235
+ overwrite
236
+ });
237
+ }
238
+ if (selectedClients.length === 0) {
239
+ p.log.warn("No clients selected for configuration.");
240
+ return null;
241
+ }
242
+ return { apiKey, clients: selectedClients };
243
+ }
244
+
245
+ // src/cli/init.ts
246
+ function parseArgs(argv) {
247
+ const args = argv.slice(2);
248
+ let apiKey;
249
+ let help = false;
250
+ for (let i = 0; i < args.length; i++) {
251
+ const arg = args[i];
252
+ if (arg === "--help" || arg === "-h") {
253
+ help = true;
254
+ } else if (arg === "--api-key" && args[i + 1]) {
255
+ apiKey = args[i + 1];
256
+ i++;
257
+ }
258
+ }
259
+ return { apiKey, help };
260
+ }
261
+ function printHelp() {
262
+ console.log(`
263
+ ${color.bold("@openpump/mcp init")} -- Configure MCP clients for OpenPump
264
+
265
+ ${color.dim("Usage:")}
266
+ npx @openpump/mcp init [options]
267
+
268
+ ${color.dim("Options:")}
269
+ --api-key <key> OpenPump API key (op_sk_live_...)
270
+ --help, -h Show this help message
271
+
272
+ ${color.dim("Supported Clients:")}
273
+ - Claude Desktop (macOS, Windows, Linux)
274
+ - Cursor
275
+ - Claude Code (via claude mcp add)
276
+
277
+ ${color.dim("Get your API key at:")}
278
+ https://openpump.io/dashboard/api-keys
279
+ `);
280
+ }
281
+ try {
282
+ const { apiKey: flagApiKey, help } = parseArgs(process.argv);
283
+ if (help) {
284
+ printHelp();
285
+ process.exit(0);
286
+ }
287
+ p2.intro(color.bgCyan(color.black(" @openpump/mcp init ")));
288
+ const detected = detectClients();
289
+ if (detected.length === 0) {
290
+ p2.log.warn("No supported MCP clients detected on this system.");
291
+ p2.log.info("Supported clients: Claude Desktop, Cursor, Claude Code");
292
+ p2.outro("Install a supported client and try again.");
293
+ process.exit(0);
294
+ }
295
+ p2.log.info(`Found ${detected.length.toString()} MCP client(s): ${detected.map((c) => c.name).join(", ")}`);
296
+ const result = await runPrompts(detected, flagApiKey);
297
+ if (!result) {
298
+ process.exit(0);
299
+ }
300
+ const s = p2.spinner();
301
+ s.start("Writing configuration files...");
302
+ const outcomes = writeClientConfigs(result.clients, result.apiKey);
303
+ s.stop("Configuration complete!");
304
+ for (const outcome of outcomes) {
305
+ if (outcome.success) {
306
+ p2.log.success(`${outcome.clientName}: ${outcome.message}`);
307
+ } else {
308
+ p2.log.error(`${outcome.clientName}: ${outcome.message}`);
309
+ }
310
+ }
311
+ const configuredClients = outcomes.filter((o) => o.success).map((o) => o.clientName);
312
+ if (configuredClients.length > 0) {
313
+ p2.outro(
314
+ `Restart ${configuredClients.join(", ")} to activate. Problems? ${color.underline(color.cyan("https://openpump.io/docs/mcp"))}`
315
+ );
316
+ } else {
317
+ p2.outro("No clients were configured. Run again to retry.");
318
+ }
319
+ } catch (error) {
320
+ p2.log.error(String(error));
321
+ process.exit(1);
322
+ }
package/dist/index.js ADDED
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ if (process.argv[2] === "init") {
5
+ await import("./cli/init.js");
6
+ } else {
7
+ const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
8
+ const { createMcpServer } = await import("./server-MDSOEEF3.js");
9
+ const { validateApiKey } = await import("./api-key-auth-VAQIKP2Q.js");
10
+ const API_KEY = process.env["OPENPUMP_API_KEY"] ?? "";
11
+ const API_BASE_URL = process.env["OPENPUMP_API_URL"] ?? "https://openpump.io";
12
+ if (!API_KEY) {
13
+ console.error("ERROR: OPENPUMP_API_KEY environment variable is required.");
14
+ console.error("");
15
+ console.error("Set it to your OpenPump API key (op_sk_live_...) and try again.");
16
+ console.error("");
17
+ console.error("Example:");
18
+ console.error(" OPENPUMP_API_KEY=op_sk_live_abc123 npx @openpump/mcp");
19
+ process.exit(1);
20
+ }
21
+ if (!API_KEY.startsWith("op_sk_live_")) {
22
+ console.error('ERROR: OPENPUMP_API_KEY must start with "op_sk_live_".');
23
+ console.error("");
24
+ console.error(`Received: "${API_KEY.slice(0, 12)}..."`);
25
+ process.exit(1);
26
+ }
27
+ console.error("Validating API key...");
28
+ const userContext = await validateApiKey(API_KEY, API_BASE_URL);
29
+ if (!userContext) {
30
+ console.error("ERROR: API key validation failed.");
31
+ console.error("");
32
+ console.error("Possible causes:");
33
+ console.error(" - Invalid or expired API key");
34
+ console.error(" - API server unreachable at " + API_BASE_URL);
35
+ console.error(" - Network connectivity issue");
36
+ process.exit(1);
37
+ }
38
+ console.error(`Authenticated as user ${userContext.userId} with ${userContext.wallets.length.toString()} wallet(s).`);
39
+ try {
40
+ const res = await fetch(`${API_BASE_URL}/api/wallets`, {
41
+ headers: { Authorization: `Bearer ${API_KEY}` }
42
+ });
43
+ if (res.ok) {
44
+ const body = await res.json();
45
+ const list = body.data ?? [];
46
+ const balanceMap = new Map(
47
+ list.filter((w) => w.solBalance != null).map((w) => [w.id, w.solBalance])
48
+ );
49
+ if (balanceMap.size > 0) {
50
+ for (const wallet of userContext.wallets) {
51
+ const bal = balanceMap.get(wallet.id);
52
+ if (bal !== void 0) wallet.solBalance = bal;
53
+ }
54
+ }
55
+ }
56
+ } catch {
57
+ }
58
+ const server = createMcpServer(userContext, API_BASE_URL);
59
+ const transport = new StdioServerTransport();
60
+ await server.connect(transport);
61
+ console.error("OpenPump MCP server running on stdio transport.");
62
+ }
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ createMcpServer
4
+ } from "./chunk-J3BWHCCH.js";
5
+ export {
6
+ createMcpServer
7
+ };
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ createMcpServer
4
+ } from "./chunk-J3BWHCCH.js";
5
+ import {
6
+ createApiKeyMiddleware
7
+ } from "./chunk-P5IDZOR3.js";
8
+
9
+ // src/server-http.ts
10
+ import express from "express";
11
+ import { randomUUID } from "crypto";
12
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
13
+ var API_BASE_URL = process.env["OPENPUMP_API_URL"] ?? "https://openpump.io";
14
+ var app = express();
15
+ app.use(express.json());
16
+ var sessions = /* @__PURE__ */ new Map();
17
+ var SESSION_TTL_HOURS = Number(process.env["MCP_SESSION_TTL_HOURS"] ?? 4);
18
+ var SESSION_TTL_MS = SESSION_TTL_HOURS * 60 * 60 * 1e3;
19
+ setInterval(() => {
20
+ const cutoff = Date.now() - SESSION_TTL_MS;
21
+ for (const [id, session] of sessions) {
22
+ if (session.lastActivity < cutoff) {
23
+ sessions.delete(id);
24
+ }
25
+ }
26
+ }, 6e4).unref();
27
+ async function enrichWalletBalances(userContext) {
28
+ try {
29
+ const res = await fetch(`${API_BASE_URL}/api/wallets`, {
30
+ headers: { Authorization: `Bearer ${userContext.apiKey}` }
31
+ });
32
+ if (!res.ok) return userContext;
33
+ const body = await res.json();
34
+ const list = body.data ?? [];
35
+ const balanceMap = new Map(
36
+ list.filter((w) => w.solBalance != null).map((w) => [w.id, w.solBalance])
37
+ );
38
+ if (balanceMap.size === 0) return userContext;
39
+ return {
40
+ ...userContext,
41
+ wallets: userContext.wallets.map((w) => ({
42
+ ...w,
43
+ solBalance: balanceMap.get(w.id) ?? w.solBalance
44
+ }))
45
+ };
46
+ } catch {
47
+ return userContext;
48
+ }
49
+ }
50
+ app.use("/mcp", createApiKeyMiddleware(API_BASE_URL));
51
+ app.all("/mcp", async (req, res) => {
52
+ const sessionId = req.headers["mcp-session-id"];
53
+ const sessionIdStr = typeof sessionId === "string" ? sessionId : void 0;
54
+ const userContext = req.userContext;
55
+ if (!userContext) {
56
+ res.status(401).json({ error: "Unauthorized" });
57
+ return;
58
+ }
59
+ if (req.method === "POST" && !sessionIdStr) {
60
+ const enrichedContext = await enrichWalletBalances(userContext);
61
+ const newSessionId = randomUUID();
62
+ const transport = new StreamableHTTPServerTransport({
63
+ sessionIdGenerator: () => newSessionId,
64
+ enableDnsRebindingProtection: false
65
+ });
66
+ const mcpServer = createMcpServer(enrichedContext, API_BASE_URL);
67
+ await mcpServer.connect(transport);
68
+ sessions.set(newSessionId, { transport, lastActivity: Date.now() });
69
+ await transport.handleRequest(req, res, req.body);
70
+ return;
71
+ }
72
+ if (!sessionIdStr) {
73
+ res.status(400).json({ error: "Missing mcp-session-id header" });
74
+ return;
75
+ }
76
+ let session = sessions.get(sessionIdStr);
77
+ if (!session) {
78
+ if (req.method !== "POST") {
79
+ res.status(404).json({ error: "Session not found or expired" });
80
+ return;
81
+ }
82
+ const enrichedContext = await enrichWalletBalances(userContext);
83
+ const recoveredTransport = new StreamableHTTPServerTransport({
84
+ sessionIdGenerator: () => sessionIdStr,
85
+ enableDnsRebindingProtection: false
86
+ });
87
+ const recoveredServer = createMcpServer(enrichedContext, API_BASE_URL);
88
+ await recoveredServer.connect(recoveredTransport);
89
+ const incomingMethod = req.body?.method;
90
+ if (incomingMethod !== "initialize") {
91
+ const syntheticInitBody = {
92
+ jsonrpc: "2.0",
93
+ id: "__recover__",
94
+ method: "initialize",
95
+ params: {
96
+ protocolVersion: "2024-11-05",
97
+ capabilities: {},
98
+ clientInfo: { name: "session-recover", version: "1.0" }
99
+ }
100
+ };
101
+ const noopSink = {
102
+ statusCode: 200,
103
+ headersSent: false,
104
+ setHeader: () => noopSink,
105
+ getHeader: () => void 0,
106
+ removeHeader: () => noopSink,
107
+ writeHead: () => {
108
+ noopSink.headersSent = true;
109
+ return noopSink;
110
+ },
111
+ write: () => true,
112
+ end: () => {
113
+ noopSink.headersSent = true;
114
+ return noopSink;
115
+ },
116
+ status: () => noopSink,
117
+ json: () => noopSink,
118
+ send: () => noopSink,
119
+ on: () => noopSink,
120
+ once: () => noopSink,
121
+ off: () => noopSink,
122
+ emit: () => true,
123
+ flushHeaders: () => {
124
+ }
125
+ };
126
+ try {
127
+ await recoveredTransport.handleRequest(
128
+ req,
129
+ noopSink,
130
+ syntheticInitBody
131
+ );
132
+ } catch {
133
+ }
134
+ }
135
+ session = { transport: recoveredTransport, lastActivity: Date.now() };
136
+ sessions.set(sessionIdStr, session);
137
+ await recoveredTransport.handleRequest(req, res, req.body);
138
+ return;
139
+ }
140
+ session.lastActivity = Date.now();
141
+ if (req.method === "DELETE") {
142
+ sessions.delete(sessionIdStr);
143
+ res.status(200).end();
144
+ return;
145
+ }
146
+ await session.transport.handleRequest(req, res, req.body);
147
+ });
148
+ var PORT = Number(process.env["PORT"] ?? 3001);
149
+ app.listen(PORT, () => {
150
+ console.error(`OpenPump MCP server running on http://localhost:${PORT.toString()}/mcp`);
151
+ console.error(`API base URL: ${API_BASE_URL}`);
152
+ });
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@openpump/mcp",
3
+ "version": "1.0.0",
4
+ "private": false,
5
+ "description": "OpenPump MCP server for Solana token operations on pump.fun - works with Claude Desktop, Cursor, and any MCP client",
6
+ "type": "module",
7
+ "license": "MIT",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/openpump/openpump",
11
+ "directory": "packages/mcp-client"
12
+ },
13
+ "homepage": "https://openpump.io/docs/mcp",
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "keywords": [
18
+ "mcp",
19
+ "model-context-protocol",
20
+ "solana",
21
+ "pump-fun",
22
+ "ai",
23
+ "claude",
24
+ "cursor"
25
+ ],
26
+ "bin": {
27
+ "openpump-mcp": "./dist/index.js",
28
+ "@openpump/mcp": "./dist/index.js"
29
+ },
30
+ "files": [
31
+ "dist"
32
+ ],
33
+ "main": "./dist/index.js",
34
+ "exports": {
35
+ ".": "./dist/index.js",
36
+ "./http": "./dist/server-http.js"
37
+ },
38
+ "scripts": {
39
+ "build": "tsup",
40
+ "dev": "tsx watch --env-file=../../.env src/index.ts",
41
+ "start": "node dist/index.js",
42
+ "start:http": "node dist/server-http.js",
43
+ "test": "vitest run",
44
+ "typecheck": "tsc --noEmit",
45
+ "lint": "eslint src/",
46
+ "prepublishOnly": "npm run build"
47
+ },
48
+ "dependencies": {
49
+ "@clack/prompts": "^1.1.0",
50
+ "@modelcontextprotocol/sdk": "1.27.1",
51
+ "express": "^4.18.0",
52
+ "picocolors": "^1.1.1",
53
+ "zod": "^4.0.0"
54
+ },
55
+ "devDependencies": {
56
+ "@openpump/typescript-config": "workspace:*",
57
+ "@types/express": "^4.17.21",
58
+ "tsup": "^8.0.0",
59
+ "tsx": "^4.7.0",
60
+ "typescript": "^5.3.0",
61
+ "vitest": "^1.2.0"
62
+ }
63
+ }