UNPKG

pump-fun-chat-mcp

Version:

MCP server for pump.fun chat integration - connect to and read messages from pump.fun token chat rooms

409 lines 16.8 kB
#!/usr/bin/env node "use strict"; /** * @fileoverview MCP Server for pump.fun chat integration. * This server implements the Model Context Protocol (MCP) to allow AI assistants * like Claude to interact with pump.fun token chat rooms through standardized tools. * * @module pump-fun-chat-mcp * @author codingbutter * @license MIT */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js"); const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js"); const types_js_1 = require("@modelcontextprotocol/sdk/types.js"); const pump_chat_client_1 = require("pump-chat-client"); /** * Main MCP server class that manages the pump.fun chat connection * and exposes tools for AI assistants to interact with the chat. * * @class PumpFunChatServer * @example * ```typescript * const server = new PumpFunChatServer('YOUR_TOKEN_ADDRESS'); * server.run(); * ``` */ class PumpFunChatServer { /** * Creates a new PumpFunChatServer instance. * @param {string} token - The pump.fun token address to connect to * @constructor */ constructor(token) { /** PumpChatClient instance for WebSocket communication with pump.fun */ this.client = null; /** Current connection state of the chat client */ this.isConnected = false; /** * Buffer for storing incoming messages. * This allows the MCP server to access messages even if the client * wasn't actively listening when they arrived. */ this.messageBuffer = []; this.token = token; // Initialize the MCP server with metadata this.server = new index_js_1.Server({ name: "pump-fun-chat", version: "1.0.0", }, { // Declare that this server provides tools capabilities: { tools: {}, }, }); // Set up MCP request handlers this.setupHandlers(); // Connect to the pump.fun chat room this.connectToChat(); } /** * Establishes connection to the pump.fun chat room. * Sets up all necessary event listeners for the chat client. * @private */ connectToChat() { // Log connection attempt (using stderr to avoid interfering with MCP protocol) console.error(`Connecting to pump.fun chat for token: ${this.token}`); // Create a new chat client instance this.client = new pump_chat_client_1.PumpChatClient({ roomId: this.token, username: "mcp-client", // Identifier for messages sent by this MCP server messageHistoryLimit: 100, // Store last 100 messages in client memory }); /** * Handle successful connection to chat room. * This event is emitted when the WebSocket connection is established * and the room join is confirmed. */ this.client.on("connected", () => { this.isConnected = true; console.error(`Successfully connected to chat room for ${this.token}`); }); /** * Handle new incoming messages. * These are real-time messages posted by users in the chat. */ this.client.on("message", (message) => { // Add to our local buffer this.messageBuffer.push(message); // Prevent buffer from growing indefinitely // Keep only the last 1000 messages if (this.messageBuffer.length > 1000) { this.messageBuffer.shift(); // Remove oldest message } // Log the message for debugging console.error(`New message from ${message.username}: ${message.message}`); }); /** * Handle message history received from server. * This typically happens right after joining the room. */ this.client.on("messageHistory", (messages) => { console.error(`Received ${messages.length} historical messages`); }); /** * Handle connection errors. * These could be network issues, protocol errors, etc. */ this.client.on("error", (error) => { console.error(`Chat error:`, error); }); /** * Handle server-side errors. * These are typically application-level errors like authentication failures. */ this.client.on("serverError", (error) => { console.error(`Server error:`, error); // Provide helpful information for authentication errors if (error.error === "Authentication required") { console.error(`Note: Sending messages requires authentication with pump.fun`); } }); /** * Handle disconnection from chat room. * The client will automatically attempt to reconnect. */ this.client.on("disconnected", () => { this.isConnected = false; console.error(`Disconnected from chat room`); }); // Initiate the connection this.client.connect(); } /** * Sets up MCP protocol handlers for tool discovery and execution. * This defines what tools are available and how to handle tool calls. * @private */ setupHandlers() { /** * Handle ListTools requests. * This tells the AI assistant what tools are available and their schemas. */ this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, () => __awaiter(this, void 0, void 0, function* () { return ({ tools: [ { // Tool for reading chat messages name: "PumpFunChat_ReadMessages", description: `Read messages from the pump.fun chat room for token ${this.token}`, inputSchema: { type: "object", properties: { limit: { type: "number", description: "Number of messages to retrieve (default: all stored messages)", }, }, }, }, { // Tool for getting just the latest message name: "PumpFunChat_GetLatestMessage", description: `Get the most recent message from the pump.fun chat room for token ${this.token}`, inputSchema: { type: "object", properties: {}, // No parameters needed }, }, { // Tool for sending messages (requires auth) name: "PumpFunChat_SendMessage", description: `Send a message to the pump.fun chat room for token ${this.token}`, inputSchema: { type: "object", properties: { message: { type: "string", description: "The message to send to the chat", }, }, required: ["message"], // Message is required }, }, { // Tool for checking connection status name: "PumpFunChat_GetStatus", description: "Get the connection status and token information", inputSchema: { type: "object", properties: {}, // No parameters needed }, }, ], }); })); /** * Handle CallTool requests. * This routes tool calls to the appropriate handler methods. */ this.server.setRequestHandler(types_js_1.CallToolRequestSchema, (request) => __awaiter(this, void 0, void 0, function* () { // Route based on tool name switch (request.params.name) { case "PumpFunChat_ReadMessages": return this.handleReadMessages(request.params.arguments); case "PumpFunChat_GetLatestMessage": return this.handleGetLatestMessage(); case "PumpFunChat_SendMessage": return this.handleSendMessage(request.params.arguments); case "PumpFunChat_GetStatus": return this.handleGetStatus(); default: // Unknown tool name - this shouldn't happen if the client is well-behaved throw new Error(`Unknown tool: ${request.params.name}`); } })); } /** * Handles the ReadMessages tool call. * Retrieves messages from the chat room with optional limit. * @param {ReadMessagesArgs} args - Tool arguments * @returns {Promise<Object>} MCP response with message content * @private */ handleReadMessages(args) { return __awaiter(this, void 0, void 0, function* () { // Check if we're connected if (!this.client || !this.isConnected) { return { content: [ { type: "text", text: `Not connected to the chat room. The connection may still be establishing or has failed.`, }, ], }; } // Get messages from the client (respects the limit if provided) const messages = this.client.getMessages(args.limit); // Handle empty message list if (messages.length === 0) { return { content: [ { type: "text", text: `No messages available. The chat might be quiet or still loading.`, }, ], }; } // Format messages for display // Convert each message to a readable format with timestamp, username, and content const formattedMessages = messages.map(msg => `[${new Date(msg.timestamp).toLocaleTimeString()}] ${msg.username}: ${msg.message}`).join("\n"); // Return formatted messages return { content: [ { type: "text", text: `Messages from ${this.token} (showing ${messages.length} messages):\n\n${formattedMessages}`, }, ], }; }); } /** * Handles the GetLatestMessage tool call. * Retrieves only the most recent message from the chat. * @returns {Promise<Object>} MCP response with the latest message * @private */ handleGetLatestMessage() { return __awaiter(this, void 0, void 0, function* () { // Check connection status if (!this.client || !this.isConnected) { return { content: [ { type: "text", text: `Not connected to the chat room.`, }, ], }; } // Get the latest message const latestMessage = this.client.getLatestMessage(); // Handle case where no messages exist if (!latestMessage) { return { content: [ { type: "text", text: `No messages available.`, }, ], }; } // Return formatted latest message return { content: [ { type: "text", text: `Latest message:\n[${new Date(latestMessage.timestamp).toLocaleTimeString()}] ${latestMessage.username}: ${latestMessage.message}`, }, ], }; }); } /** * Handles the SendMessage tool call. * Attempts to send a message to the chat room. * Note: This requires authentication with pump.fun to work. * @param {SendMessageArgs} args - Tool arguments containing the message * @returns {Promise<Object>} MCP response confirming the send attempt * @private */ handleSendMessage(args) { return __awaiter(this, void 0, void 0, function* () { // Check connection status if (!this.client || !this.isConnected) { return { content: [ { type: "text", text: `Cannot send message - not connected to the chat room.`, }, ], }; } // Attempt to send the message // Note: This will fail without proper authentication this.client.sendMessage(args.message); // Return confirmation with authentication note return { content: [ { type: "text", text: `Message sent: "${args.message}"\nNote: Messages require pump.fun authentication to be delivered.`, }, ], }; }); } /** * Handles the GetStatus tool call. * Provides information about the current connection and token. * @returns {Promise<Object>} MCP response with status information * @private */ handleGetStatus() { return __awaiter(this, void 0, void 0, function* () { return { content: [ { type: "text", text: `Token: ${this.token}\nConnection Status: ${this.isConnected ? 'Connected' : 'Disconnected'}\nMessages in buffer: ${this.messageBuffer.length}`, }, ], }; }); } /** * Starts the MCP server and begins listening for requests. * Uses stdio transport for communication with the AI assistant. * @returns {Promise<void>} * @public */ run() { return __awaiter(this, void 0, void 0, function* () { // Create stdio transport for MCP communication // This uses standard input/output for protocol messages const transport = new stdio_js_1.StdioServerTransport(); // Connect the server to the transport yield this.server.connect(transport); // Log that we're running (to stderr to avoid protocol interference) console.error(`Pump.fun Chat MCP server running for token: ${this.token}`); }); } } /** * Main entry point for the MCP server. * Validates environment variables and starts the server. */ // Get token from environment variable // This is the preferred method for MCP servers to receive configuration const token = process.env.PUMP_FUN_TOKEN; // Validate that token was provided if (!token) { console.error("Error: PUMP_FUN_TOKEN environment variable is required"); console.error("Example: PUMP_FUN_TOKEN=y31hFyYbrVW4R53Zfka8WJfQpwpMLfCcAjVKAonpump pump-fun-chat-mcp"); process.exit(1); } // Create and start the server const server = new PumpFunChatServer(token); // Run the server and handle any startup errors server.run().catch((error) => { console.error("Failed to start MCP server:", error); process.exit(1); }); //# sourceMappingURL=mcp-server.js.map