DEV Community

Hidetaka Okamoto
Hidetaka Okamoto

Posted on

Building a Paid MCP Server with Cloudflare Workers and Stripe

Model Context Protocol (MCP) is gaining attention as a protocol that standardizes interactions with AI models. In this article, we'll explain a practical method for building a paid MCP server by combining Cloudflare Workers and Stripe.

What is an MCP Server?

MCP is a protocol that provides tools to AI models and enables the models to use them. By building a server that implements this protocol, you can provide APIs that can be used by various AI models.

Architecture Overview

This project combines the following components:

Cloudflare Workers runs the MCP server in a serverless environment. Stripe handles billing for paid features, and we've also implemented user authentication via GitHub OAuth. Additionally, we utilize the WordPress.org API as a data source.

Core Component Implementation

1. Basic Structure of the MCP Server

The MCP server instantiates the McpServer class and defines tools in the init() method.

export class MyMCP extends PaidMcpAgent<Env, State, Props> {
    server = new McpServer({
        name: "WordPress.org Search API",
        version: "0.1.0",
    });
    async init() {
        // Tool definitions...
    }
}
Enter fullscreen mode Exit fullscreen mode

2. Implementing Paid Tools

Paid tools are defined using the paidTool method:

this.paidTool(
    "wporg_query_themes",
    {
        search: z.string().optional().describe("Search keyword"),
        // Other parameters...
    },
    async ({ search, tag, author, page, per_page, browse, fields }) => {
        // Implementation logic...
        return await fetchThemesApi("query_themes", params, fields);
    },
    generatePaidToolConfig("search WordPress.org themes")
)
Enter fullscreen mode Exit fullscreen mode

Here, we specify the tool name, parameter schema definition, execution function, and billing configuration.

3. Core Implementation of Stripe Integration

The PaidMcpAgent class is central to Stripe integration:

export abstract class experimental_PaidMcpAgent<...> extends McpAgent<...> {
    stripe(): Stripe {
        return new Stripe(this.env.STRIPE_SECRET_KEY, {...});
    }

    async getCurrentCustomerID() {
        // Get/create the user's Stripe customer ID
    }

    paidTool<Args extends ZodRawShape>(/*...*/) {
        // Implementation of paid tool definition
    }
}
Enter fullscreen mode Exit fullscreen mode

This class provides key functions such as initializing the Stripe instance, managing customer IDs, checking billing status, and creating checkout sessions.

4. Billing Flow

When a paid tool is invoked, the following flow is executed:

First, the user's customer ID is verified (existing or newly created). Next, it checks if payment has already been made (subscription or one-time payment).

If unpaid, a Stripe checkout session is created and a payment link is presented to the user. Once payment is completed, tool execution is permitted.

5. GitHub Authentication Integration

The authentication flow is implemented using OAuthProvider:

export default new OAuthProvider({
    apiRoute: "/sse",
    apiHandler: MyMCP.mount("/sse"),
    defaultHandler: GitHubHandler,
    authorizeEndpoint: "/authorize",
    tokenEndpoint: "/token",
    clientRegistrationEndpoint: "/register",
});
Enter fullscreen mode Exit fullscreen mode

This configuration enables authentication using GitHub, allowing for user identification and personalized billing.

Implementation Points

State Management

We use Cloudflare Workers' Durable Objects to maintain billing states for each user:

export type PaymentState = {
  stripe?: StripeState;
};
type StripeState = {
  paidToolCalls: string[];
  subscriptions: string[];
  customerId: string;
  paidToolsToCheckoutSession: Record<string, string | null>;
};
Enter fullscreen mode Exit fullscreen mode

This state allows tracking of tools that users have already paid for.

Generating Billing Configurations

export function generatePaidToolConfig(featureName: string) {
    return {
        priceId: process.env.STRIPE_PRICE_ID,
        successUrl: "https://example.com/success",
        meterEvent: "api_usage",
        paymentReason: `Payment required to use ${featureName} feature`,
    }
}
Enter fullscreen mode Exit fullscreen mode

This function makes it easy to generate billing configurations for each tool.

Application Patterns for Implementation

Mix of Free and Paid Tools

You can mix free and paid tools within the same MCP server:

// Free tool
this.server.tool("free_tool", /*...*/);
// Paid tool
this.paidTool("paid_tool", /*...*/);
Enter fullscreen mode Exit fullscreen mode

Usage-Based Billing Model

You can also implement usage-based billing using the meterEvent parameter:

this.paidTool(
    "metered_tool",
    /*...*/,
    {
        priceId: "price_xxx",
        meterEvent: "api_usage_count"
    }
)
Enter fullscreen mode Exit fullscreen mode

Conclusion

By combining Cloudflare Workers and Stripe, you can build a scalable and easily manageable paid MCP server. This implementation pattern serves as a practical template for developers who want to provide API services to AI models.

As the standardization of the MCP protocol progresses, these server implementations will likely become increasingly important. We encourage you to apply these concepts to your own services.

References

Top comments (0)