By the end of this tutorial you'll have Kong AI Gateway proxying the ˳ MCP server, with per-consumer Key Auth, rate limiting, and live usage metering flowing into Konnect M&B — ready to wire to Stripe for usage-based billing.
What You'll Build
10,000+ MCP servers exist. Zero have billing tutorials. This adds the missing layer.
Here's what we're assembling:
Kong AI Gateway on Kubernetes (existing install — see previous tutorial — install steps are NOT repeated here)
Key Auth plugin — each paying consumer gets a unique API key to authenticate
Rate Limiting Advanced — enforces per-consumer call limits so no one burns through your upstream quota
AI MCP Proxy plugin in
passthrough-listenermode — proxies MCP protocol traffic to GitHub's upstream MCP server with full observabilityMetering & Billing plugin — emits a usage event per request to Konnect M&B
Konnect M&B connected to Stripe — automatic invoicing at end of billing period
The end result: any MCP client (Claude Desktop, Cursor, VS Code 1.101+) can use your managed GitHub MCP endpoint — authenticated, rate-limited, and billed.
Prerequisites
Before starting, make sure you have:
Kong AI Gateway on Kubernetes already installed and connected to Konnect. If you haven't done this yet, follow the Kong AI Gateway on Kubernetes tutorial first.
Kong Gateway Enterprise 3.14+ —
ai-mcp-proxyrequires minimum 3.12;metering-and-billingrequires minimum 3.14Konnect account with the Metering & Billing add-on enabled (if M&B isn't visible in your Konnect left nav, contact Kong Sales)
GitHub Personal Access Token (PAT) with
reporead scopes — the upstream MCP server needs this to serve tool callsStripe account — free to create if you don't have one
decK CLI installed —
brew install kong/deck/deckor see decK docsHTTPie for testing —
brew install httpieAn MCP-compatible client — Claude Desktop, Cursor, or VS Code 1.101+
-
Environment variables set:
export KONNECT_TOKEN=<your-konnect-personal-access-token> export KONNECT_CP_NAME=<your-control-plane-name>
Overview
Here's the full sequence we'll walk through:
Create the MCP Gateway Service and Route
Inject the GitHub PAT for upstream auth
Add Key Auth to protect the route
Create a Kong Consumer (the billing subject)
Add Rate Limiting Advanced to enforce call limits
Add the AI MCP Proxy plugin in passthrough-listener mode
Set up Konnect Metering & Billing (UI steps)
Add the Metering & Billing plugin
Connect Stripe in Konnect M&B (UI steps)
Test the full flow end-to-end
Step 1: Create the MCP Gateway Service and Route
The Kong Service points to GitHub's remote MCP server at https://api.githubcopilot.com/mcp/. We'll use decK throughout for declarative config management — this keeps your configuration version-controlled and reproducible.
Because we are adding additional configuration to an already configured Gateway we need to have all the configuration in one place. Best thing to do here is have it all in 1 directory and then apply the sync to that directory.
mkdir ./config
cd config
Save the following as mcp-service.yaml:
# mcp-service.yaml
_format_version: "3.0"
services:
- name: github-mcp-service
url: https://api.githubcopilot.com/mcp/
connect_timeout: 30000
read_timeout: 60000
write_timeout: 60000
routes:
- name: github-mcp-route
paths:
- /mcp/github
strip_path: false
protocols:
- https
- http
A few things to note:
read_timeout: 60000(60 seconds) — GitHub's MCP server can be slow on first response; the default 60s gives it room.strip_path: false— we want/mcp/githubforwarded as-is to the upstream.
Apply it:
deck gateway sync . \
--konnect-token "$KONNECT_TOKEN" \
--konnect-control-plane-name "$KONNECT_CP_NAME" \
--konnect-addr https://eu.api.konghq.com
Step 2: Inject the GitHub PAT for Upstream Auth
GitHub's MCP server requires a valid GitHub PAT in the Authorization: Bearer header on every upstream request. We inject this at the service level using the Request Transformer plugin — so it applies automatically regardless of which consumer is calling.
Generating GitHub PAT
To create a GitHub PAT navigate here
Then follow the following steps:
Click: Generate new token
-
Configure:
- Token name:
GitHub MCP - Expiration: 90 days (or whatever your organisation allows)
- Resource owner: Your GitHub account or organisation
- Repository access: Either: Only select repositories (recommended) or All repositories
-
Add permissions: A good starting point for most MCP servers is:
- Contents: Read and Write
- Pull requests: Read and Write
- Issues: Read and Write
- Metadata: Read
- Commit statuses: Read and Write
- Actions: Read (if you want workflow information)
-
If you only want read-only access:
- Contents → Read
- Metadata → Read
- Pull Requests → Read
- Issues → Read
Click Generate token.
- Token name:
⚠️ Important: Copy it immediately - you won't be able to see it again.
export DECK_GH_PAT="github_pat_11ABCDEF..."
Then create github-pat-transformer.yaml:
# github-pat-transformer.yaml
_format_version: "3.0"
plugins:
- name: request-transformer
service: github-mcp-service
config:
add:
headers:
- "Authorization:Bearer ${{ env "DECK_GH_PAT" }}"
Then sync the configuration
deck gateway sync . \
--konnect-token "$KONNECT_TOKEN" \
--konnect-control-plane-name "$KONNECT_CP_NAME" \
--konnect-addr https://eu.api.konghq.com
💡 Tip: For production, store your PAT in a Konnect Vault and reference it with
{vault://konnect/<secret-name>}instead of hardcoding it. This prevents the token from appearing in your config files or version control.
Step 3: Add Key Auth — Protecting the Route
Without authentication, anyone who discovers your Kong proxy URL can use GitHub's MCP server on your dime. Key Auth solves this: each consumer gets a unique API key, and requests without a valid key are rejected before they hit upstream.
Save as key-auth.yaml:
# key-auth.yaml
_format_version: "3.0"
plugins:
- name: key-auth
route: github-mcp-route
config:
key_names:
- x-api-key
key_in_header: true
key_in_query: false
key_in_body: false
hide_credentials: true
hide_credentials: true strips the x-api-key header before forwarding to GitHub, so your consumers' keys never reach the upstream.
deck gateway sync . \
--konnect-token "$KONNECT_TOKEN" \
--konnect-control-plane-name "$KONNECT_CP_NAME" \
--konnect-addr https://eu.api.konghq.com
Step 4: Create a Kong Consumer
In Kong's model, each paying customer maps to a Consumer. The Consumer is the billing subject — it's what Rate Limiting tracks, what Key Auth validates, and what Metering & Billing uses as the subject for usage events.
Save as consumer.yaml:
# consumer.yaml
_format_version: "3.0"
consumers:
- username: alice
keyauth_credentials:
- key: alice-mcp-key-changeme
Then apply the change
deck gateway sync . \
--konnect-token "$KONNECT_TOKEN" \
--konnect-control-plane-name "$KONNECT_CP_NAME" \
--konnect-addr https://eu.api.konghq.com
💡 Tip: In production, generate API keys programmatically via the Konnect Admin API and rotate them regularly. The key above (
alice-mcp-key-changeme) is a placeholder — don't ship that.
Step 5: Add Rate Limiting Advanced
Here's a critical point: the Metering & Billing plugin only meters — it does not enforce limits. If you want to cap consumers at N calls per hour (or per day), you need Rate Limiting Advanced running alongside it.
Save as rate-limiting.yaml:
# rate-limiting.yaml
_format_version: "3.0"
plugins:
- name: rate-limiting-advanced
route: github-mcp-route
enabled: true
config:
limit:
- 1000
window_size:
- 3600
window_type: sliding
identifier: consumer
strategy: local
hide_client_headers: false
This gives each consumer 1,000 requests per hour. strategy: local means the counter is unique per each Kong node. In a multi-node deployments you would want to use redis so the counter is shared between every node.
Adjust limit to match your pricing tiers — e.g. 100 for a free tier, 10000 for enterprise.
deck gateway sync . \
--konnect-token "$KONNECT_TOKEN" \
--konnect-control-plane-name "$KONNECT_CP_NAME" \
--konnect-addr https://eu.api.konghq.com
Step 6: Add the AI MCP Proxy Plugin
The ai-mcp-proxy plugin in passthrough-listener mode tells Kong to understand MCP protocol on this route and proxy tool calls to the upstream GitHub MCP server. This unlocks MCP-level observability inside Konnect: tool call counts, session tracking, error rates — not just raw HTTP metrics.
⚠️ Important: Do NOT combine
ai-mcp-proxywith other AI plugins likeai-proxyon the same service or route. They conflict.
Save as mcp-proxy.yaml:
# mcp-proxy.yaml
_format_version: "3.0"
plugins:
- name: ai-mcp-proxy
route: github-mcp-route
config:
mode: passthrough-listener
deck gateway sync . \
--konnect-token "$KONNECT_TOKEN" \
--konnect-control-plane-name "$KONNECT_CP_NAME" \
--konnect-addr https://eu.api.konghq.com
Step 7: Set Up Konnect Metering & Billing
This step is UI-driven in the Konnect portal. You're creating a Meter (the thing being counted), link a consumer to a Customer, create a billable resource that a customer can consume, Feature, and define a pricing structure to charge these resources out using Plans and Rate Cards.
Create a meter
A Meter collects and aggregates raw usage events into measurable units, such as LLM tokens, API requests, or bandwidth. It is the foundation of Metering & Billing, converting gateway activity into usage that can later be priced and invoiced.
Navigate to Konnect → Metering & Billing (left nav). If it's not there, contact Kong Sales to enable it for your org.
Enable M&B for your org if prompted.
Go to Meters → Create Meter:
Choose Count API requests template
Set name:
GitHub MCP Tool CallsSet Key:
github_mcp_tool_callsSet Description:
Number of MCP Tool calls through GitHub MCP
- Click Create
Create a feature
A Feature represents a billable capability or resource that customers consume, such as "LLM Tokens" or "API Requests". A Feature is linked to a Meter so that measured usage becomes something that can be included in plans and assigned a price.
Left navigation: Product Catalog → Features.
Click Create Feature:
Name:
Tool CallsKey: auto-fills from the name (
tool_calls)Meter:
GitHub MCP Tool Calls(from the dropdown)
Click Save.
Create a Plan with usage-based Rate Cards
A Plan is a commercial offering that bundles together one or more Features, their pricing, and any usage allowances. Examples might include a Free, Standard, or Enterprise plan. Customers subscribe to Plans to determine how their usage is charged.
Product Catalog → Plans → New Plan:
Name:
ProBilling:
GBPBilling cadence:
1 monthClick Save
Add a rate card to the plan
A Rate Card defines the pricing rules for a Feature within a Plan. It specifies how usage is charged, such as fixed monthly fees, pay-as-you-go pricing, included usage, or tiered pricing. Every billable Feature in a Plan is priced through a Rate Card. Inside the new plan, add a rate card.
Link the rate card to our newly created feature
Create a Usage-based pricing model with price per unit £1
Rate card entitlements
Entitlements define what access or allowance a customer receives for a Feature as part of a Rate Card. They determine whether a customer simply has access to a feature, receives a fixed configuration, is allocated a consumable usage balance (such as LLM tokens), or receives no entitlement at all. The entitlement type you choose depends on whether you're controlling feature access, distributing configuration, or managing usage-based consumption and billing.
- None: Use None when the feature doesn't need an entitlement. This is typically used when customers simply pay for what they consume without any included allowance, quota, or access control.
- Boolean: Use Boolean when you want to enable or disable access to a feature. This is ideal for premium capabilities, feature flags, or functionality that customers either have access to or don't.
- Static: Use Static when you need to provide a fixed configuration or settings to customers. This is useful for storing values such as allowed models, configuration options, limits, or other JSON-based settings that your applications can consume.
- Metered: Use Metered when the feature represents a consumable resource that needs to be tracked over time, such as LLM tokens, API requests, storage, or bandwidth. Metered entitlements support allowances, usage balances, top-ups, and overage charging, making them the preferred choice for usage-based billing.
We will just go with No entitlement as we want our users to just pay for what they consume.
Click Save rate card
Finally click Publish Plan so that V1 of the plan is now live
Create customer
A Customer represents the person, team, application, or organisation that is responsible for paying for or being charged back for usage. Customers own subscriptions, accumulate usage, and receive invoices. In Kong Gateway scenarios, a Customer is typically mapped to one or more Consumers.
Click Billing
-
Create customer
- Name: Alice
- Key: alice
- Usage Attribute: Select gateway consumer alice
Add a subscription
A Subscription connects a Customer to a Plan, making the plan's pricing and entitlements active for that customer. Once a subscription is in place, the customer's metered usage is rated according to the plan and included in invoices.
Open the alice customer page and switch to the Subscriptions tab. Click Create subscription.
Subscription plan:
Pro(the plan with input-token and output-token rate cards)Starting Phase: Default
Start subscription: Immediately
Bill monthly starting: Start of subscription
Starting: Start of subscription
Settlement mode: Invoice overage
Click Next, then Start subscription on the confirmation step.
The subscription is now active. The next call to the gateway lands inside an active billing window and rolls into an invoice.
Lets see the draft invoice created.
Navigate to Billing -> Invoices
And you will see our invoice for our customer alice
Step 8: Add the Metering & Billing Plugin
Now to be able to actually get events into Konnect you will need to configure the Gateway plugin meter and billing. You need a Konnect token for this, but lets just re-use the System account token we have been using for deck. We just need to give it some more permissions
On your already created system account add the Metering role called ingest
💡 Tip: In production you would never share this token, but have a separate service account and token for each
Then lets add it as an env variable
export DECK_KONNECT_TOKEN=$KONNECT_TOKEN
Finally create the plugin and save it as metering.yaml:
# metering.yaml
_format_version: "3.0"
plugins:
- name: metering-and-billing
route: github-mcp-route
config:
api_token: "${{ env "DECK_KONNECT_TOKEN" }}"
ingest_endpoint: "https://eu.api.konghq.com/v3/openmeter/events"
meter_api_requests: true
meter_ai_token_usage: false
subject:
look_up_value_in: consumer
queue:
max_batch_size: 100
max_coalescing_delay: 1
max_retry_time: 60
Key config fields:
meter_api_requests: true— count every request through the gatewaymeter_ai_token_usage: false— we're not metering LLM token usage heresubject.look_up_value_in: consumer— the Consumer'scustom_idbecomes the usage event subject, enabling per-customer billingqueue.max_batch_size: 100— events are batched before sending (reduces ingest API calls)ingest_endpoint— if your Konnect org is US-hosted, usehttps://us.api.konghq.com/v3/openmeter/events
Sync the plugin to your gateway
deck gateway sync . \
--konnect-token "$KONNECT_TOKEN" \
--konnect-control-plane-name "$KONNECT_CP_NAME" \
--konnect-addr https://eu.api.konghq.com
💡 Tip: For production, move
api_tokento a Konnect Vault:{vault://konnect/mb-api-token}. Never commit the token to source control.
Step 9: Connect Stripe in Konnect M&B
Almost there. Now we wire Konnect M&B to Stripe so accumulated usage becomes an invoice.
For this step you will need a Stripe account and API key. Register for an account here: https://dashboard.stripe.com/register and we will use the Sandbox they provide.
To get your API key navigate to the dashboard. In the left hand menu at the bottom is Developers menu. Click that and then API Keys.
Locate the secret key at the bottom of the page, click it to copy the key
- Go to Konnect → Metering & Billing → Settings → Stripe → Install. Paste the secret key from above into the Konnect App and click
Install App
- In the Billing Profile select preset to
Send Invoiceand let this new preset be the new default Billing Profile.
Stripe is now installed in Konnect and ready to go
- In Stripe, create a Customer:
- Name:
Alice - Email: To send out invoices you will need this set (email in Konnect is currently ignore)
- Copy customer id (bottom right)
- Back in Konnect M&B → Customers → Edit
aliceCustomer :
- Navigate to Billing Profile
- Stripe Customer ID: paste Alice's Stripe customer ID (from your Stripe dashboard)
- Konnect M&B will report cumulative usage to Stripe at the end of each billing period. Stripe auto-generates and sends the invoice.
Step 10: Test the Full Flow
Let's test out the full flow. Make sure your port-forward is running first:
kubectl port-forward -n kong svc/kong-gateway-proxy 8000:80 &
Test without a key (should fail)
http POST :8000/mcp/github \
Content-Type:application/json
Expected response: HTTP 401 Unauthorized
Test with a key (MCP tools list)
http --print=hbB POST :8000/mcp/github \
Content-Type:application/json \
Accept:'application/json, text/event-stream' \
x-api-key:alice-mcp-key-changeme \
jsonrpc=2.0 \
id:=2 \
method=tools/list \
params:='{}' > mcp-response.txt
Expected: a JSON-RPC response with the list of available GitHub MCP tools (e.g. create_issue, search_repositories, get_file_contents, etc.).
The result is put into an text file so lets get out the actual data and see some tools
sed -n 's/^data: //p' mcp-response.txt \
| jq '.result.tools[] | {name, description}'
You will see something like this
{
"name": "add_comment_to_pending_review",
"description": "Add review comment to the requester's latest pending pull request review. A pending review needs to already exist to call this (check with the user if not sure)."
}
{
"name": "add_issue_comment",
"description": "Add a comment and/or reaction to a specific issue or issue comment in a GitHub repository. Use this tool with pull requests as well (in this case pass pull request number as issue_number), but only if user is not asking specifically to add or react to review comments. At least one of body or reaction is required."
}
....
Also check the rate limiting response headers:
head -n20 mcp-response.txt | grep X-RateLimit
X-RateLimit-Remaining-Hour: 999
X-RateLimit-Limit-Hour: 1000
Connect Claude Code
This connect Claude Code to our newly created MCP
claude mcp add --transport http github-via-kong \
http://localhost:8000/mcp/github \
--header "x-api-key: alice-mcp-key-changeme"
Then lets verify its working
% claude mcp list
github-via-kong: http://localhost:8000/mcp/github (HTTP) - ✔ Connected
And make a simple request
claude
Then ask
List the tools available from github-via-kong.
Expected result
Here are the tools available from the github-via-kong MCP server, grouped by function:
Identity & org
- get_me — get authenticated user info
- get_teams, get_team_members, search_users
Repositories
- create_repository, fork_repository, search_repositories
- list_branches, create_branch
- list_repository_collaborators
Files & content
- get_file_contents, create_or_update_file, delete_file, push_files
- search_code
Commits
- get_commit, list_commits, search_commits
Issues
- issue_read, issue_write, list_issues, search_issues
- add_issue_comment
- list_issue_fields, list_issue_types
- sub_issue_write
- get_label
Pull requests
- pull_request_read, list_pull_requests, search_pull_requests
- create_pull_request, update_pull_request, update_pull_request_branch
- merge_pull_request
- pull_request_review_write, add_comment_to_pending_review, add_reply_to_pull_request_comment
- request_copilot_review
Releases & tags
- get_latest_release, get_release_by_tag, list_releases
- get_tag, list_tags
Security
- run_secret_scanning
Then get something from your account
1. Using github-via-kong, list my GitHub repositories.
2. How many private and public repos do I have?
3. Do I have any outstanding pull requests?
4. List them with activity over last few months for all my repos
💡 Tip: You may need to accept a few of the calls around access to your GitHub user and account before
Verify events in Konnect M&B
After making a few requests, navigate to Konnect → Metering & Billing → Events. You should see usage events listed, attributed to consumer alice
Note: there may be a short buffering delay (up to max_coalescing_delay seconds) before events appear.
Step 11: Generate an invoice
The final part of this tutorial is to see invoices generated in Stripe. The integration between Konnect and Stripe will only happen at the end of your billing period so in order to test this you have two options:
shorten the billing period for testing
or manually generate/finalise an invoice
Lets manually generate an invoice in Konnect and see it appear in Stripe
Navigate to Meter & Billing -> Billing -> Invoices
You should see an invoice that is currently Gathering with a total.
Click on that invoice and then Invoice Now
💡 Tip: When creating an invoice by default there will be a 1 hour grace period to collect any delayed meter events so the invoice might not show up straight away
Once the invoice is issued you will see the following status
Now with the invoice generated in Konnect lets see it in Stripe, click the View in Stripe button
Because we have setup our Stripe account as send invoices this invoice should be automatically emailed to your customer as well.
💡 Tip: In your test/sandbox Stripe account emails wont get automatically sent out. You can test this by clicking the Resend Invoice button and view the invoice in your customers inbox.
We now have an automated end-to-end billing service for our MCP gateway usage.
Step 12: Clean Up
Stop the port-forward
kill %1
Remove the decK config
deck gateway reset \
--konnect-token "$KONNECT_TOKEN" \
--konnect-control-plane-name "$KONNECT_CP_NAME" \
--konnect-addr https://eu.api.konghq.com \
--force
Tear down the kind cluster
kind delete cluster --name kong-ai
This will remove the service, route, and all associated plugins. The Consumer and credentials will also be deleted. Remove the Meter and Stripe subscription manually via the Konnect UIs.
Also cleanup anything in your Stripe account that you don' want
Troubleshooting
1. HTTP 401 Unauthorized — No API key found in request
Key Auth is rejecting the request before it reaches the upstream. Check:
The header name you're sending matches
key_namesin the plugin config (x-api-key)The key value exactly matches what was created in the Consumer's
keyauth_credentialsThe Consumer and credentials were successfully applied — run
deck gateway dump(or check in the UI) and verify they appear
2. 502 Bad Gateway from MCP proxy
The request reached Kong but failed at the upstream (GitHub). Most likely causes:
Invalid or expired GitHub PAT — test the upstream directly:
http GET https://api.githubcopilot.com/mcp/ Authorization:"Bearer <your-pat>"Insufficient PAT scopes — ensure your PAT has at minimum
reporead access; some tools requireread:orgTimeout — if GitHub is responding slowly, increase
read_timeouton the service (try120000for 2 minutes)
3. Metering events not appearing in Konnect M&B
Ensure the Meter & Billing plugin has been created on your service
Confirm
api_tokenin the plugin config is correct and not expiredCheck the permissions on your system account are correct
Check gateway logs for ingest errors:
kubectl logs -n kong <kong-pod> | grep meteringRemember: events are batched — there's a buffering delay up to
max_coalescing_delaysecondsConfirm the
ingest_endpointmatches your Konnect region (US vs EU)
4. ai-mcp-proxy plugin not available
This plugin is only available in Kong Gateway Enterprise 3.12+. Verify your version:
kubectl exec -n kong <kong-pod> -- kong version
If you're on OSS or a version below 3.12, you'll need to upgrade to Enterprise 3.12+ (and 3.14+ for Metering & Billing).
5. Rate limit response headers missing
Rate Limiting Advanced requires Kong Enterprise. The open-source rate-limiting plugin doesn't support per-consumer cluster-sync strategy. Verify you're running Enterprise and that hide_client_headers: false is set in the plugin config.
What's Next
You now have a fully metered, billed MCP gateway. Here's where to take it:
Tiered access — create multiple consumers with different rate limits:
limit: 100for free tier,limit: 10000for pro,limit: -1(unlimited) for enterprise with flat-rate pricingconversion-listenermode — use AI MCP Proxy's conversion mode to wrap your own REST API as MCP tools and charge for them the same wayACL tool control — gate specific GitHub MCP tools (e.g.
create_issue,push_files) behind higher-priced tiers using the ACL plugin + Consumer groupsMCP Registry in Konnect (tech preview) — list your managed MCP server for discoverability by other teams or customers






















Top comments (0)