Overview

This page provides a complete, working example of a basic MCP server that uses ATXP to charge for tool calls and is deployed on Cloudflare Workers. The code for this example implementation can be found at atxp-dev/atxp-cloudflare-mcp-server-example.

Project setup

In this tutorial, we’ll build a TypeScript MCP server that exposes tools that can only be called after a successful payment is made using ATXP. We’ll use Cloudflare Workers to build and deploy a scalable MCP server that can be accessed from Goose or other MCP clients. Our MCP server will expose a simple hello_world tool that provides a personalized message, with each call costing 0.01 USDC.

Initialize the project

First, we need to create a new Cloudflare Workers project using the MCP template. We’ll use the Cloudflare CLI to initialize the project.
npm create cloudflare@latest atxp-mcp-cloudflare-server -- --template cloudflare/ai/demos/remote-mcp-authless
cd atxp-mcp-cloudflare-server
This will create a new project with the basic structure for an MCP server on Cloudflare Workers.

Install ATXP dependencies

We need to install the ATXP Cloudflare package to add payment capabilities:
npm install @atxp/cloudflare bignumber.js --save

Configure the project

The project comes pre-configured with TypeScript and Cloudflare Workers settings. You should verify that your package.json includes the necessary scripts and dependencies:
package.json
{
  "name": "atxp-mcp-cloudflare-server",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "deploy": "wrangler deploy",
    "dev": "wrangler dev",
    "start": "wrangler dev",
    "test": "vitest",
    "cf-typegen": "wrangler types"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.0.0",
    "@atxp/cloudflare": "^0.4.0", 
    "bignumber.js": "^9.1.2", 
    "zod": "^3.23.8"
  },
  "devDependencies": {
    "@cloudflare/workers-types": "^4.20241112.0",
    "typescript": "^5.5.2",
    "vitest": "2.0.5",
    "wrangler": "^3.86.1"
  }
}

Configure environment variables

Create your local environment configuration
touch .dev.vars

Set up how you will receive payments

In order to receive payments, you will need to set up a payment destination. You can do this by creating a free ATXP account OR by specifying a wallet address and network.

Create an ATXP account

If you don’t have an ATXP account yet, create one and copy your wallet address. Once you have set up your account, you should add the environment variable
echo 'ATXP_CONNECTION_STRING=your_connection_string' >> .dev.vars
For production deployments, set ALLOW_INSECURE_HTTP_REQUESTS_DEV_ONLY_PLEASE to "false" and use wrangler secret put for sensitive values like wallet addresses instead of storing them in wrangler.jsonc.

Develop the MCP server

We’re ready to start coding our MCP server now. The template provides a basic structure, but we need to modify it to integrate ATXP payments.

Set up the MCP Agent with ATXP

Replace the contents of src/index.ts with the following code that integrates ATXP payments:
index.ts
import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { BigNumber } from "bignumber.js";

// Import ATXP code
import {
	requirePayment,
	atxpCloudflare,
	type ATXPMCPAgentProps,
	type ATXPCloudflareOptions,
} from "@atxp/cloudflare";
import { ATXPPaymentDestination } from "@atxp/server";


const createOptions = (env: Env) => {
	const paymentDestination = new ATXPPaymentDestination(
		env.ATXP_CONNECTION_STRING,
	); 

	paymentDestination.destination =
		paymentDestination.destination.bind(paymentDestination);
	return {
		mcpAgent: MyMCP,
		payeeName: "ATXP MCP Server Demo",
		allowHttp: env.ALLOW_INSECURE_HTTP_REQUESTS_DEV_ONLY_PLEASE === "true",
		// Don't create the payment destination here - create it when needed
		paymentDestination,
	} as ATXPCloudflareOptions;
};

// Define our MCP agent with ATXP payment integration
export class MyMCP extends McpAgent<Env, unknown, ATXPMCPAgentProps> {
	server = new McpServer({
		name: "ATXP-Protected Hello World MCP Server",
		version: "1.0.0",
	});

	async init() {
		// This method will be called when the agent is initialized
		// We'll define our tools here
	}
}

// Use the ATXP Cloudflare Worker wrapper as the default export
export default {
	async fetch(request: Request, env: any, ctx: ExecutionContext): Promise<Response> {
		// Create the handler with environment-based configuration
		const cloudflareOptions = createOptions(env);
		const handler = atxpCloudflare(cloudflareOptions);

		return handler.fetch(request, env, ctx);
	}
};
At this point, we should be able to build our MCP server without errors by running npm run dev. However, we haven’t defined any tools yet, so it won’t be very useful.

Define the MCP tools

Now let’s add a hello_world tool that requires payment. This tool will take an optional name parameter and return a personalized greeting.
index.ts
// Define our MCP agent with ATXP payment integration
export class MyMCP extends McpAgent<Env, unknown, ATXPMCPAgentProps> {
	server = new McpServer({
		name: "ATXP-Protected Hello World MCP Server",
		version: "1.0.0",
	});

	async init() {
		// Payment-protected hello_world tool
		this.server.tool(
			"hello_world",
			{ name: z.string().optional() },
			async ({ name }) => {
				if (!this.props) {
					throw new Error("ATXP props are not initialized");
				}

				const options = createOptions(this.env);
				await requirePayment(
					{
						price: new BigNumber(0.01),
					},
					options,
					this.props,
				);

				const greeting = name ? `Hello, ${name}!` : "Hello, World!";
				const userInfo = this.props.tokenCheck?.data?.sub || "anonymous user";
				const message = `${greeting} Thanks for your 0.01 USDC payment, ${userInfo}! 💰`;

				return {
					content: [{ type: "text", text: message }],
				};
			},
		);
	}
}
Our MCP server is now ready to be used and will require a payment of $0.01 USDC for each call to the hello_world tool. Now let’s test the payment integration by running the MCP server locally and then deploying it to Cloudflare.

Testing the MCP server locally

You can test your MCP server locally before deploying it to Cloudflare.

Run the MCP server locally

Start the development server using Wrangler:
npm run dev
This will start your MCP server locally, typically on http://localhost:8788/sse.

Test with MCP Inspector

You can use the MCP Inspector to test your server:
  1. Open the MCP Inspector in your browser at http://localhost:5173
  2. Connect to your local server using the URL http://localhost:8788/sse
  3. Test the hello_world tool to verify it works correctly

Deploying to Cloudflare

Once your MCP server is working locally, you can deploy it to Cloudflare Workers.

Deploy the server

Deploy your MCP server using Wrangler:
npm run deploy
This will deploy your server to Cloudflare Workers and provide you with a URL like https://atxp-mcp-cloudflare-server.<YOUR_SUBDOMAIN>.workers.dev/sse.

Configure production environment

For production deployment, follow these steps:
  1. Set environment variables in wrangler.jsonc or use wrangler secret put for sensitive values:
# For sensitive data like wallet addresses, use secrets:
wrangler secret put FUNDING_DESTINATION

# For non-sensitive configuration, update wrangler.jsonc:
# Set ALLOW_INSECURE_HTTP_REQUESTS_DEV_ONLY_PLEASE to "false"
  1. Update your wrangler.jsonc for production settings:
    • Set ALLOW_INSECURE_HTTP_REQUESTS_DEV_ONLY_PLEASE to "false"
    • Consider using wrangler secret put for sensitive values instead of storing them directly in the config

Connect to MCP clients

Now that your MCP server is deployed, you can connect it to MCP clients like Goose.

Connect to Goose

To connect your deployed MCP server to Goose:
  1. Go to Extensions in the Goose sidebar
  2. Click Add custom extension
  3. Provide a name for your extension (e.g., “ATXP Greeting Server”)
  4. Select Streamable HTTP as the type
  5. Enter your Cloudflare Worker URL: https://atxp-mcp-cloudflare-server.<YOUR_SUBDOMAIN>.workers.dev/sse
  6. Click Add Extension
Your browser will open a new tab where you must authorize an ATXP wallet to pay for using your MCP server’s tools.

Test the payment flow

Once connected to Goose, test the payment flow:
  1. Send a message like “Greet me with the name Alice”
  2. Goose will show that payment is required
  3. Click the payment URL to complete the payment
  4. Return to Goose and confirm the tool call was successful
Congratulations! You’ve successfully built and deployed a paid MCP server on Cloudflare Workers using ATXP. Your server can now handle payments and scale automatically with Cloudflare’s infrastructure.

Key differences from Express

This Cloudflare approach offers several advantages over Express:
  • Serverless scaling: No need to manage server infrastructure
  • Global edge deployment: Reduced latency for users worldwide
  • Simplified deployment: Single command deployment with Wrangler
  • Built-in HTTPS: Secure connections by default
  • Environment management: Integrated secrets and environment variables
The ATXP Cloudflare package (@atxp/cloudflare) handles the complexity of integrating payments into your Cloudflare Worker, providing the same requirePayment functionality as the Express version but optimized for the Workers runtime.

Resources