Zuplo
API Monetization

Building a Monetized API, Part 3: Adding a Gated MCP Server

Martyn DaviesMartyn Davies
April 2, 2026
6 min read

Add an MCP server to your monetized API and gate access to paid subscribers using custom code and the MonetizationInbound policy's subscription data.

This is Part 3 of the “Building a Monetized API” series. In Part 1, we set up the API gateway with authentication, consumer isolation, and rate limiting. In Part 2, we added meters, plans, Stripe integration, and a self-serve developer portal. Now we’re adding an MCP server on top of the same API and gating access so only paid subscribers can use it.

Building a Monetized API, Part 3
15:23
Video Tutorial

Building a Monetized API, Part 3

Watch the video walkthrough of adding an MCP server and gating access to paid plan subscribers with custom code.

Why gate MCP access behind a paid plan?

In Part 2, we set up three subscription tiers: Free, Starter, and Pro. The Starter and Pro plans include a boolean feature called mcp_server with an entitlement set to true. The Free plan doesn’t have it.

This is a deliberate product decision. The API itself is available on every plan (with different usage limits), but MCP server access is a premium feature. Offering AI-powered tooling as a paid upgrade is a natural way to add value to higher tiers without changing the underlying API at all.

Use this approach if you're:
  • You have an existing Zuplo project with monetization already configured (see Part 2)
  • You want to expose your API as an MCP server that AI assistants can use
  • You need to restrict MCP access to specific subscription tiers using entitlements

Step 1: Adding the MCP server

Zuplo has native MCP server support built into the gateway. Adding one takes about 30 seconds.

Go to your project’s route list and click the Add tab. You’ll see an MCP Server option. Select it, and configure the basics:

  • Name: ChangeLoggle MCP Server
  • Version: 0.0.1
  • Description: Create, update, and discover team changelog info.

That creates the MCP server endpoint at /mcp on your gateway.

The MCP Server Options panel in Zuplo showing the server name, version, and description fields

MCP Servers with Zuplo

Learn how to turn your existing API gateway into a toolset that AI systems can discover and use.

Step 2: Selecting which routes to expose as tools

After creating the MCP server, you need to choose which API routes become MCP tools. Click Select Tools to see all available routes.

You can be selective here. If you only want a read-only MCP experience, pick just the GET endpoints. If you want full read-write access (create projects, update changelogs, search entries), select everything.

For our changelog API, we’re exposing all 12 endpoints as tools. Every operation that’s available via the REST API is also available through MCP.

Be thoughtful about this in your own projects. Each tool adds to the context that AI assistants need to process. If you have dozens of endpoints, ask yourself whether all of them make sense as MCP tools, or whether a focused subset delivers a better experience.

Click Update Tools to save your selection.

The MCP Tools panel showing all 12 API routes selected as MCP tools with their HTTP methods

Step 3: Testing the MCP server

Before adding access controls, verify that the MCP server works. You can test it with any MCP client. In the video, we use MCP Jam, but any compatible client works.

To connect:

  1. Use your gateway’s deployment URL with /mcp appended as the endpoint
  2. Set the connection type to HTTP
  3. Set authentication to Bearer Token and paste in an API key from the developer portal

If the connection succeeds, you can interact with your API through the MCP client. Try asking it to list projects or create a new one. Every request flows through the same gateway, uses the same API key authentication, and hits the same origin API.

At this point there’s no entitlement check on the MCP route, so any valid API key works regardless of plan. That’s fine for testing. We’ll lock it down in the next step.

MCP Jam showing a successful list projects request returning the Changeloggle project with its details

Step 4: Writing the access check policy

To gate MCP access, we need a custom inbound policy that inspects the subscriber’s entitlements and blocks the request if they don’t have MCP access.

The key is the MonetizationInboundPolicy class from @zuplo/runtime. Because the MonetizationInbound policy runs before this custom code, it has already authenticated the API key and loaded the subscriber’s plan data. Calling MonetizationInboundPolicy.getSubscriptionData(context) gives you the full subscription context for the current consumer, including their entitlements.

Create a new module in your Zuplo project called check-mcp-access:

TypeScripttypescript
import {
  MonetizationInboundPolicy,
  ZuploContext,
  ZuploRequest,
} from "@zuplo/runtime";

export default async function policy(
  request: ZuploRequest,
  context: ZuploContext,
  options: never,
  policyName: string,
) {
  const subscription = MonetizationInboundPolicy.getSubscriptionData(context);

  if (!subscription?.entitlements?.mcp_server?.hasAccess) {
    return new Response(
      JSON.stringify({
        error:
          "MCP access requires a Starter or Pro plan, please consider upgrading.",
      }),
      {
        status: 403,
        headers: { "Content-Type": "application/json" },
      },
    );
  }

  return request;
}

Here’s what’s happening:

  1. MonetizationInboundPolicy.getSubscriptionData(context) retrieves the subscription for the consumer making the request. The MonetizationInbound policy that runs before this code has already authenticated the API key and populated the context with subscription data.
  2. The code checks subscription.entitlements.mcp_server.hasAccess. The mcp_server key matches the feature we set up in Part 2 when configuring the Starter and Pro plans. Entitlements are accessed as properties on the entitlements object, keyed by the feature’s identifier.
  3. If the entitlement doesn’t exist (Free plan) or hasAccess is false, the request gets a 403 JSON response telling the consumer to upgrade.
  4. If the entitlement exists and hasAccess is true, the request passes through to the MCP server handler.

Monetization Inbound Policy

Full policy reference including metering options, subscription data access, and exposed functions.

Step 5: Wiring the policies to the MCP endpoint

Unlike the policies we added in Parts 1 and 2 (which apply to every route), the MCP access check only needs to go on one endpoint: the /mcp route.

Open the MCP server route and add two inbound policies in this order:

  1. Monetization Inbound (existing policy): handles API key authentication and request metering. This needs to run first so the consumer’s identity and subscription are available to downstream policies.
  2. Custom Code Inbound pointing to check-mcp-access: runs the entitlement check we just wrote.

The Custom Code Inbound policy just needs to point at the module:

JSONjson
{
  "export": "default",
  "module": "$import(./modules/check-mcp-access)"
}

Save and deploy. The MCP endpoint now authenticates the request, meters it, and checks entitlements before the MCP server handler ever runs.

Custom Code Inbound Policy

Write custom request handling logic that runs as part of your policy pipeline.

Step 6: Testing the access gate

With the policies in place, we can verify that free-plan subscribers are blocked and paid-plan subscribers get through.

Free plan test. Connect an MCP client using an API key from a free-plan subscription. The connection attempt fails with a 403 and the message telling the consumer to upgrade. The MCP server never processes the request.

MCP Jam showing a failed connection with a 403 error: MCP access requires a Starter or Pro plan

Paid plan test. Either upgrade the same subscription to Starter or Pro through the developer portal, or use an API key from an existing paid subscription. Nothing changes about the connection setup: same URL, same API key. This time, the connection succeeds and the MCP client can list tools and make requests.

MCP Jam showing a successful connection on a paid plan with the tools list response visible

The API key itself doesn’t change when a subscriber upgrades plans. The entitlement check happens at request time against the current subscription data, so the moment someone upgrades, their existing API key starts working with the MCP server.

What we built in Part 3

The monetized API now has an MCP server with plan-gated access:

  • MCP server added to the gateway with all API routes exposed as tools
  • Custom access policy using MonetizationInboundPolicy.getSubscriptionData to check entitlements
  • Free plan subscribers blocked from MCP access with a clear upgrade message
  • Paid plan subscribers (Starter and Pro) get full MCP server access
  • MCP requests are metered the same way as standard API requests

The MCP server sits on a single endpoint, so the access check only needed to be added once. Every other route in the project continues to work exactly as before. And because the MonetizationInbound policy handles both authentication and metering, MCP requests count toward the subscriber’s usage just like any other API call.

What’s next

In Part 4, we’ll finish the developer portal. Right now it has the default documentation that Zuplo generates from the OpenAPI spec, but it needs polish: better descriptions, branded styling, and a layout that makes the API easy to explore. That’s the last step before this is production-ready.