mcp-boilerplate

repository·main·Indexed 21 days ago

https://github.com/iannuttall/mcp-boilerplate

A template for creating remote Model Context Protocol (MCP) servers hosted on Cloudflare Workers. It features built-in support for Google and GitHub user authentication, Stripe-based payments, and subscription management. The boilerplate allows developers to implement free, subscription-based, metered-usage, and one-time payment AI tools.

Tokens
9K
Snippets
24
Records
26
Agent score
76%

What's inside mcp-boilerplate

  1. Set up Stripe Webhooks for complex payment scenarios

    main

    While the boilerplate includes built-in Stripe integration that verifies payments automatically, you can optionally set up webhooks to handle advanced logic like usage-based billing, custom subscription workflows, or customer dashboards.

    To configure webhooks:

    1. In your Stripe Dashboard, navigate to Developers > Webhooks and click Add endpoint.
    2. Set the endpoint URL based on your environment:
      • Local development: http://localhost:8787/webhooks/stripe
      • Production: https://your-worker-name.your-account.workers.dev/webhooks/stripe
    3. Select relevant events (e.g., checkout.session.completed, invoice.payment_succeeded, customer.subscription.updated).
    4. Copy the Signing secret and add it to your environment configuration.
    # Local development (.dev.vars)
    STRIPE_WEBHOOK_SECRET="whsec_your-webhook-secret-here"
    # Production (using Wrangler)
    npx wrangler secret put STRIPE_WEBHOOK_SECRET
  2. Configure GitHub OAuth login

    main

    To use GitHub for login instead of Google:

    1. GitHub Settings: Go to Developer settings > OAuth Apps > New OAuth App.
    2. URLs:
      • Homepage URL: http://localhost:8787
      • Authorization callback URL: http://localhost:8787/callback/github
    3. Credentials: Copy the Client ID and generate a Client Secret.
    4. Environment Variables: Add GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET to .dev.vars.
    5. Code Update: You must manually switch the handler in src/index.ts:
    // Replace GoogleHandler with GitHubHandler
    import { GitHubHandler } from "./auth/github-handler";
    
    // Change defaultHandler
    defaultHandler: GitHubHandler as any,

    Note: For production, update the GitHub Authorization callback URL to your Cloudflare worker URL.

    import { GitHubHandler } from "./auth/github-handler";
    // ...
    defaultHandler: GitHubHandler as any,
  3. Create a one-time payment tool

    main

    Use this option to charge a single fee for access to a tool.

    Stripe Setup:

    1. Create a Product in Stripe.
    2. Add a Price with the "One time" model.
    3. Note the price_xxxxxxxxxxxxxx ID.

    Implementation:

    1. Create a file in src/tools/.
    2. Use agent.paidTool.
    3. In the configuration object, set checkout.mode to 'payment'.
    4. Register the tool in src/index.ts passing STRIPE_ONE_TIME_PRICE_ID and BASE_URL.
    5. Add STRIPE_ONE_TIME_PRICE_ID to your .dev.vars or Cloudflare secrets.

    Environment Variable Setup:

    • Local: Add to .dev.vars
    • Production: npx wrangler secret put STRIPE_ONE_TIME_PRICE_ID
    import { z } from "zod";
    import { experimental_PaidMcpAgent as PaidMcpAgent } from "@stripe/agent-toolkit/cloudflare";
    import { REUSABLE_PAYMENT_REASON } from "../helpers/constants";
    
    export function myOnetimeTool(
      agent: PaidMcpAgent<Env, any, any>,
      env?: { STRIPE_ONE_TIME_PRICE_ID: string; BASE_URL: string }
    ) {
      const priceId = env?.STRIPE_ONE_TIME_PRICE_ID || null;
      const baseUrl = env?.BASE_URL || null;
    
      if (!priceId || !baseUrl) {
        throw new Error("Stripe One-Time Price ID and Base URL must be provided for this tool");
      }
    
      agent.paidTool(
        "my_onetime_tool_name",
        {
          input1: z.string(),
        },
        async ({ input1 }: { input1: string }) => ({
          content: [
            { type: "text", text: `You processed: ${input1}` },
          ],
        }),
        {
          checkout: {
            success_url: `${baseUrl}/payment/success`,
            line_items: [
              {
                price: priceId,
                quantity: 1,
              },
            ],
            mode: 'payment',
          },
          paymentReason: "Enter a clear reason for this one-time charge.",
        }
      );
    }
  4. Create a free AI tool

    main

    To create a free tool that users can access without payment, follow these steps:

    1. Create a new file in src/tools/ (e.g., myTool.ts).
    2. Implement the tool using the agent.server.tool method. You must define the tool name, a description, and input parameters using zod schemas.
    3. Export the tool function.
    4. Add the export to src/tools/index.ts using export * from './myTool';.
    5. Register the tool in src/index.ts inside the init() method by calling tools.myTool(this);.
    import { z } from "zod";
    import { experimental_PaidMcpAgent as PaidMcpAgent } from "@stripe/agent-toolkit/cloudflare";
    
    export function myTool(agent: PaidMcpAgent<Env, any, any>) {
      const server = agent.server;
      // @ts-ignore
      server.tool(
        "my_tool_name",                      // The tool name
        "This tool does something cool.",    // Description of what your tool does
        {
          input1: z.string(),                // Parameter definitions using Zod
          input2: z.number()
        },
        async ({ input1, input2 }: { input1: string; input2: number }) => ({
          content: [{ type: "text", text: `You provided: ${input1} and ${input2}` }],
        })
      );
    }
  5. Configure Stripe payments and billing portal

    main

    The boilerplate includes Stripe integration for processing payments and managing subscriptions via a billing portal.

    1. Stripe Setup:

    • Get your Secret key (sk_test_...) from the Stripe Dashboard.
    • Create a product and copy its Price ID (price_...).
    • Add these to .dev.vars:
    STRIPE_SECRET_KEY="sk_test_your-key-here"
    STRIPE_SUBSCRIPTION_PRICE_ID="price_your-price-id-here"
    STRIPE_METERED_PRICE_ID="your-stripe-metered-price-id"

    2. Billing Portal Configuration: To use the check_user_subscription_status tool, you must activate the Stripe Customer Portal:

    • Visit the URL provided in the tool's error message (e.g., https://dashboard.stripe.com/test/settings/billing/portal) and save your settings.
    • To allow users to switch plans, enable "Customers can switch plans" in the Customer Portal settings and select the eligible products.
  6. Create a metered-usage paid tool

    main

    Use this option to charge users based on usage (e.g., per unit used).

    Stripe Setup:

    1. Create a Product in Stripe.
    2. Add a Price and check "Usage is metered" under Price options.
    3. Define a meter in Stripe and note the event name (e.g., metered_add_usage).

    Implementation:

    1. Create a file in src/tools/.
    2. Use agent.paidTool.
    3. In the configuration object, set checkout.mode to 'subscription'.
    4. Crucially, provide the meterEvent string that matches your Stripe meter setup.
    5. Register the tool in src/index.ts passing STRIPE_METERED_PRICE_ID and BASE_URL.
    import { z } from "zod";
    import { experimental_PaidMcpAgent as PaidMcpAgent } from "@stripe/agent-toolkit/cloudflare";
    import { METERED_TOOL_PAYMENT_REASON } from "../helpers/constants";
    
    export function myMeteredTool(
      agent: PaidMcpAgent<Env, any, any>,
      env?: { STRIPE_METERED_PRICE_ID: string; BASE_URL: string }
    ) {
      const priceId = env?.STRIPE_METERED_PRICE_ID || null;
      const baseUrl = env?.BASE_URL || null;
    
      if (!priceId || !baseUrl) {
        throw new Error("Stripe Metered Price ID and Base URL must be provided for metered tools");
      }
    
      agent.paidTool(
        "my_metered_tool_name",
        {
          a: z.number(),
          b: z.number(),
        },
        async ({ a, b }: { a: number; b: number }) => {
          const result = a + b;
          return {
            content: [{ type: "text", text: String(result) }],
          };
        },
        {
          checkout: {
            success_url: `${baseUrl}/payment/success`,
            line_items: [
              {
                price: priceId,
              },
            ],
            mode: 'subscription',
          },
          paymentReason: "METER INFO: Details about your metered usage. " + METERED_TOOL_PAYMENT_REASON,
          meterEvent: "your_meter_event_name_from_stripe",
        }
      );
    }
  7. Deploy the MCP server to Cloudflare

    main

    To go live, deploy your worker to Cloudflare and configure production secrets.

    1. Deploy:
    npx wrangler deploy
    1. Update OAuth Providers: Add your production worker URL (e.g., https://your-worker-name.your-account.workers.dev/callback/google) to your Google/GitHub redirect URIs.
    2. Set Production Secrets: Run the following commands to upload your environment variables to Cloudflare:
    npx wrangler secret put BASE_URL
    npx wrangler secret put COOKIE_ENCRYPTION_KEY
    npx wrangler secret put GOOGLE_CLIENT_ID
    npx wrangler secret put GOOGLE_CLIENT_SECRET
    npx wrangler secret put STRIPE_SECRET_KEY
    npx wrangler secret put STRIPE_SUBSCRIPTION_PRICE_ID
    npx wrangler secret put STRIPE_METERED_PRICE_ID
    npx wrangler deploy
    npx wrangler secret put BASE_URL
    npx wrangler secret put COOKIE_ENCRYPTION_KEY
    npx wrangler secret put GOOGLE_CLIENT_ID
    npx wrangler secret put GOOGLE_CLIENT_SECRET
    npx wrangler secret put STRIPE_SECRET_KEY
    npx wrangler secret put STRIPE_SUBSCRIPTION_PRICE_ID
    npx wrangler secret put STRIPE_METERED_PRICE_ID
  8. Create a subscription-based paid tool

    main

    Use this option to charge users a recurring fee (e.g., monthly) for access to a tool.

    Stripe Setup:

    1. Create a Product in the Stripe Dashboard.
    2. Add a Price with the "Recurring" model.
    3. Note the price_xxxxxxxxxxxxxx ID.

    Implementation:

    1. Create a file in src/tools/.
    2. Use agent.paidTool instead of server.tool.
    3. Pass a configuration object containing priceId, successUrl, and paymentReason.
    4. Register the tool in src/index.ts passing the required environment variables (STRIPE_SUBSCRIPTION_PRICE_ID and BASE_URL).
    import { z } from "zod";
    import { experimental_PaidMcpAgent as PaidMcpAgent } from "@stripe/agent-toolkit/cloudflare";
    import { REUSABLE_PAYMENT_REASON } from "../helpers/constants";
    
    export function mySubscriptionTool(
      agent: PaidMcpAgent<Env, any, any>,
      env?: { STRIPE_SUBSCRIPTION_PRICE_ID: string; BASE_URL: string }
    ) {
      const priceId = env?.STRIPE_SUBSCRIPTION_PRICE_ID || null;
      const baseUrl = env?.BASE_URL || null;
    
      if (!priceId || !baseUrl) {
        throw new Error("Stripe Price ID and Base URL must be provided for paid tools");
      }
    
      agent.paidTool(
        "my_subscription_tool_name",
        {
          input1: z.string(),
          input2: z.number(),
        },
        async ({ input1, input2 }: { input1: string; input2: number }) => ({
          content: [
            { type: "text", text: `You provided: ${input1} and ${input2}` },
          ],
        }),
        {
          priceId,
          successUrl: `${baseUrl}/payment/success`,
          paymentReason: REUSABLE_PAYMENT_REASON,
        }
      );
    }
  9. Install and set up MCP Boilerplate

    main

    To get started with the MCP Boilerplate, clone the repository, install dependencies, and set up the required Cloudflare KV namespace for OAuth.

    Prerequisites:

    • Node.js installed
    • A Cloudflare account
    • A Google or GitHub account
    • A Stripe account

    Setup Steps:

    1. Clone and install:
    git clone https://github.com/iannuttall/mcp-boilerplate.git
    cd mcp-boilerplate
    npm install
    1. Install Wrangler globally:
    npm install -g wrangler
    1. Create the required OAuth KV namespace (the name must be OAUTH_KV):
    npx wrangler kv namespace create "OAUTH_KV"
    1. Update wrangler.jsonc with the id and preview_id provided by the command above.
    git clone https://github.com/iannuttall/mcp-boilerplate.git
    cd mcp-boilerplate
    npm install
    npm install -g wrangler
    npx wrangler kv namespace create "OAUTH_KV"
  10. Run and test the MCP server locally

    main

    Start your local development server using Wrangler:

    npx wrangler dev

    Your server will be available at http://localhost:8787. The SSE endpoint for AI tools is http://localhost:8787/sse.

    Testing Methods:

    1. Cloudflare AI Playground: Enter http://localhost:8787/sse to test via browser.
    2. Claude Desktop: Add the server to your Claude configuration:
    {
      "mcpServers": {
        "my_server": {
          "command": "npx",
          "args": [
            "mcp-remote",
            "http://localhost:8787/sse"
          ]
        }
      }
    }
    1. MCP Inspector: Use the inspector for debugging (Note: use version 0.11.0):
    npx @modelcontextprotocol/inspector@0.11.0

    Then enter http://localhost:8787/sse in the web interface.

  11. Configure Google OAuth login

    main

    To enable Google login, create a project in the Google Cloud Console and configure an OAuth client ID.

    1. OAuth Consent Screen: Set User Type to "External".
    2. Credentials: Create an "OAuth client ID" for a "Web application".
    3. Redirect URI: Add http://localhost:8787/callback/google for local development.
    4. Environment Variables: Add the credentials to your .dev.vars file.

    Note: When deploying to production, you must add your Cloudflare worker URL to the authorized redirect URIs (e.g., https://your-worker-name.your-account.workers.dev/callback/google) and set the app to "Production" in the OAuth consent screen.

    GOOGLE_CLIENT_ID="paste-your-client-id-here"
    GOOGLE_CLIENT_SECRET="paste-your-client-secret-here"
  12. Configure OAuth and routing in BoilerplateMCP

    main

    The server uses a standard Cloudflare Workers fetch handler to route incoming requests. The routing logic handles:

    • Static Pages: The root path (/) and /payment/success serve HTML pages.
    • Webhooks: The /webhooks/stripe path is delegated to the stripeWebhookHandler.
    • OAuth/MCP Traffic: All other routes are handled by an OAuthProvider instance, which manages authentication and the MCP SSE (Server-Sent Events) connection.

    The OAuthProvider is configured with specific endpoints for authorization, tokens, and client registration, and it uses a defaultHandler (like GoogleHandler) to manage the authentication flow.

    const oauthProvider = new OAuthProvider({
    	apiRoute: "/sse",
    	apiHandler: BoilerplateMCP.mount("/sse") as any,
    	defaultHandler: GoogleHandler as any,
    	authorizeEndpoint: "/authorize",
    	tokenEndpoint: "/token",
    	clientRegistrationEndpoint: "/register",
    });
    
    export default {
    	async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    		// ... routing logic ...
    		return oauthProvider.fetch(request, env, ctx);
    	};
    };