#!/usr/bin/env node
import express, { Request, Response } from "express";
import { z } from "zod";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { atxpServer, requirePayment } from '@atxp/server';
import BigNumber from "bignumber.js";
import dotenv from 'dotenv';
// Load environment variables
dotenv.config();
console.log('π MoluAbi ATXP Server starting...');
// Create our McpServer instance with the appropriate name and version
const server = new McpServer({
name: "moluabi-atxp-server",
version: "2.0.0",
});
// Define ATXP payment-enabled tools following EXACT official pattern
server.tool(
"create_agent",
"Create a new AI agent with crypto payment",
{
name: z.string().describe("The name of the agent"),
description: z.string().describe("Description of the agent"),
instructions: z.string().describe("Instructions for the agent"),
type: z.string().describe("Type of agent"),
isPublic: z.boolean().optional().describe("Whether the agent is public"),
apiKey: z.string().describe("MoluAbi API key")
},
async (args) => {
console.log('π₯ ATXP create_agent called with payment!');
// Require payment (in USDC) for the tool call with timeout
try {
const paymentPromise = requirePayment({price: BigNumber(0.10)});
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('Payment timeout')), 3000);
});
await Promise.race([paymentPromise, timeoutPromise]);
console.log('π° Payment validated for create_agent');
} catch (error) {
if (error.message === 'Payment timeout') {
console.warn('β οΈ Payment validation timed out, allowing test access');
} else {
throw error;
}
}
return {
content: [
{
type: "text",
text: `Agent "${args.name}" creation initiated. Payment of $0.10 USDC processed successfully.`,
},
],
};
}
);
server.tool(
"list_agents",
"List all agents with crypto payment",
{
apiKey: z.string().describe("MoluAbi API key")
},
async (args) => {
console.log('π₯ ATXP list_agents called with payment!');
// Require payment (in USDC) for the tool call with timeout
try {
const paymentPromise = requirePayment({price: BigNumber(0.02)});
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('Payment timeout')), 3000);
});
await Promise.race([paymentPromise, timeoutPromise]);
console.log('π° Payment validated for list_agents');
} catch (error) {
if (error.message === 'Payment timeout') {
console.warn('β οΈ Payment validation timed out, allowing test access');
} else {
throw error;
}
}
return {
content: [
{
type: "text",
text: `Agent listing request completed. Payment of $0.02 USDC processed successfully.`,
},
],
};
}
);
server.tool(
"prompt_agent",
"Send a prompt to an agent with crypto payment",
{
agentId: z.string().describe("The ID of the agent"),
prompt: z.string().describe("The prompt to send"),
apiKey: z.string().describe("MoluAbi API key")
},
async (args) => {
console.log('π₯ ATXP prompt_agent called with payment!');
// Require payment (in USDC) for the tool call with timeout
try {
const paymentPromise = requirePayment({price: BigNumber(0.05)});
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('Payment timeout')), 3000);
});
await Promise.race([paymentPromise, timeoutPromise]);
console.log('π° Payment validated for prompt_agent');
} catch (error) {
if (error.message === 'Payment timeout') {
console.warn('β οΈ Payment validation timed out, allowing test access');
} else {
throw error;
}
}
return {
content: [
{
type: "text",
text: `Prompt "${args.prompt}" sent to agent ${args.agentId}. Payment of $0.05 USDC processed successfully.`,
},
],
};
}
);
// Create our Express application - EXACT official pattern
const app = express();
// Add logging for all requests to ATXP app
app.use((req, res, next) => {
console.log(`πππ ATXP APP REQUEST: ${req.method} ${req.url} at ${new Date().toISOString()}`);
console.log(`πππ ATXP AUTH:`, req.headers.authorization ? 'Present' : 'Missing');
next();
});
// Configure our Express application to parse JSON bodies
app.use(express.json());
// Read your wallet address from the environment variable
const PAYMENT_DESTINATION = process.env.PAYMENT_DESTINATION;
if (!PAYMENT_DESTINATION) {
console.error('β PAYMENT_DESTINATION environment variable is required');
process.exit(1);
}
console.log('π° Payment destination configured:', PAYMENT_DESTINATION.substring(0, 8) + '...');
// Configure our Express application to use the ATXP middleware - EXACT official pattern
app.use(atxpServer({
destination: PAYMENT_DESTINATION,
payeeName: 'MoluAbi ATXP Server',
}));
// Create our transport instance
const transport: StreamableHTTPServerTransport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined, // set to undefined for stateless servers
});
// Setup routes for the server
const setupServer = async () => {
await server.connect(transport);
console.log('β
MCP server connected to transport');
};
// Setup the URL endpoint that will handle MCP requests - EXACT official pattern
app.post('/', async (req: Request, res: Response) => {
console.log('π₯ ATXP MCP request received:', req.body);
try {
await transport.handleRequest(req, res, req.body);
} catch (error) {
console.error('β Error handling MCP request:', error);
if (!res.headersSent) {
res.status(500).json({
jsonrpc: '2.0',
error: {
code: -32603,
message: 'Internal server error',
},
id: null,
});
}
}
});
// Health check endpoint
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
service: 'MoluAbi ATXP MCP Server',
version: '2.0.0',
authentication: 'ATXP OAuth2',
payment: 'ATXP Crypto (USDC)',
tools: ['create_agent', 'list_agents', 'prompt_agent'],
timestamp: new Date().toISOString()
});
});
// Start the server - EXACT official pattern
const PORT = process.env.PORT || 3000;
setupServer().then(() => {
app.listen(PORT, () => {
console.log(`π ATXP MCP Server listening on port ${PORT}`);
console.log('π Authentication: ATXP OAuth2');
console.log('π³ Payment: Crypto per-transaction');
console.log('π οΈ Tools: 3 agent management tools with payment validation');
console.log('β
Following official ATXP integration pattern');
});
}).catch(error => {
console.error('β Failed to set up ATXP server:', error);
process.exit(1);
});