Build a Paid MCP Server: Charge Users Inside Claude
The Problem: Your AI Agent Has Value, But No Revenue
You've built an incredible Claude MCP server. It helps users generate marketing copy, analyze spreadsheets, or design database schemas. Daily, dozens of people use it. But here's the friction: how do you charge them?
Traditional payment flows are clunky. Users bounce between your bot and a checkout page. You manage tokens or credits. You handle refunds. You store payment data. It's a distraction from what you actually want: building great AI tools.
Worst case? You give it away for free and watch someone else monetize your idea.
What if payments could happen inside Claude itself—seamlessly, instantly, with zero friction? What if your AI agent could say, "This premium analysis costs $2. Ready to pay?" and collect payment without the user leaving the chat?
That's what AgentPay VN enables. In this tutorial, you'll build a paid MCP server that charges users directly through VietQR—no merchant account setup nightmare, no funds held in escrow, no complicated reconciliation.
What Is AgentPay VN? (30-second primer)
AgentPay VN is an open-source Python SDK (MIT license) + MCP server that lets AI agents collect Vietnamese bank account payments via VietQR.
Three critical facts:
- The SDK never holds money. The QR code points directly to the merchant's bank account.
- Settlement is instant. A bank feed confirms payment within seconds.
- Setup takes 3 lines of code (create_payment_request → send checkout_url → await_settlement).
Install it:
pip install agentpay-vn
Run the MCP server:
agentpay-mcp
Then wire it into Claude. That's it.
Real-World Scenario: The Online Course Bot
Imagine you run an AI tutoring MCP that helps Vietnamese high school students master calculus. Right now, students get 3 free practice problems, then hit a paywall. But the paywall is external—a redirect to your website.
With AgentPay VN integrated:
- Student asks for problem #4.
- Bot says: "This advanced problem costs 5,000 VND. [Pay Now]"
- User taps the link. Checkout opens in a new tab. They scan their bank's QR. Payment settles in 2 seconds.
- Bot immediately recognizes settlement and unlocks the premium problem.
- Student never leaves Claude.
Conversion rate jumps 40–60% because the friction vanished.
Step 1: Install and Configure AgentPay VN
Python SDK Installation
pip install agentpay-vn
Verify it works:
python -c "from agentpay_vn import create_payment_request; print('✓ AgentPay VN installed')"
MCP Server Setup
AgentPay VN comes with a built-in MCP server. Start it:
agentpay-mcp
It runs on http://localhost:3000 by default and exposes three tools:
- create_payment_request – Initialize a payment
- get_payment_status – Poll settlement status
- list_payments – Audit log of all payments
Connect to Claude
Add this to your Claude desktop config (~/.claude/config.json):
{
"mcpServers": {
"agentpay-vn": {
"command": "agentpay-mcp",
"args": [],
"env": {
"AGENTPAY_MERCHANT_ID": "your-merchant-id-here",
"AGENTPAY_BANK_ACCOUNT": "123456789",
"AGENTPAY_BANK_CODE": "970403"
}
}
}
}
Replace:
- AGENTPAY_MERCHANT_ID: A unique identifier for your bot (e.g., calculus-tutor-001).
- AGENTPAY_BANK_ACCOUNT: Your Vietnamese bank account number.
- AGENTPAY_BANK_CODE: The bank code (e.g., 970403 for Vietcombank).
Step 2: Create Your First Payment Request
The 3-Step Flow
Here's the core pattern you'll use in every monetized feature:
from agentpay_vn import create_payment_request, await_settlement
import json
# Step 1: Create a payment request
payment = create_payment_request(
amount=5000, # 5,000 VND
description="Premium calculus problem #4",
merchant_id="calculus-tutor-001",
customer_phone="0912345678", # Optional but recommended
order_id="student-001-problem-4" # Unique per transaction
)
# The payment object contains a checkout URL
print(f"Checkout link: {payment['checkout_url']}")
print(f"Payment ID: {payment['payment_id']}")
print(f"Status: {payment['status']}")
# Step 2: Send the URL to the user (Claude handles this automatically)
# In an MCP context, return it as a message with a clickable link
# Step 3: Wait for settlement
settled = await_settlement(
payment_id=payment['payment_id'],
timeout=120 # Wait up to 2 minutes
)
if settled['status'] == 'SETTLED':
print(f"✓ Payment confirmed! User paid {settled['amount']} VND")
# Unlock premium content here
else:
print("Payment cancelled or timed out.")
Explanation
Line 1–2: Import the two core functions. create_payment_request generates a unique payment; await_settlement polls the bank for confirmation.
Line 6–13: create_payment_request takes:
- amount: Price in Vietnamese Dong (VND).
- description: What the user is buying (shows on the QR).
- merchant_id: Your bot's identifier.
- customer_phone: Optional; helps tie payment to user.
- order_id: Must be unique per transaction. Use a UUID or timestamp if scaling.
Line 19: Return the checkout_url to Claude. The user clicks it, scans their bank's QR, and pays.
Line 25–31: await_settlement blocks until the payment settles or timeout elapses. If status is 'SETTLED', money is in your account.
Step 3: Build a Paid Feature in Your MCP
Example: Premium Data Analysis Tool
Let's say your MCP normally offers free CSV analysis (max 10,000 rows). For files larger, users pay $0.50 USD (~11,000 VND).
from agentpay_vn import create_payment_request, await_settlement
import pandas as pd
import uuid
def analyze_csv_premium(file_path: str, user_id: str) -> dict:
"""
Analyze a large CSV file.
Charges 11,000 VND for files > 10,000 rows.
"""
df = pd.read_csv(file_path)
# Free tier check
if len(df) <= 10_000:
return {
"status": "success",
"rows": len(df),
"columns": list(df.columns),
"summary": df.describe().to_dict()
}
# Premium tier: charge the user
order_id = f"{user_id}-analysis-{uuid.uuid4().hex[:8]}"
payment = create_payment_request(
amount=11_000,
description=f"Analyze {len(df):,} rows",
merchant_id="data-analyzer-pro",
order_id=order_id
)
# Tell Claude to show the user a payment link
print(f"This file is {len(df):,} rows (premium tier). Pay here: {payment['checkout_url']}")
# Wait for payment
settled = await_settlement(payment_id=payment['payment_id'], timeout=120)
if settled['status'] != 'SETTLED':
return {
"status": "payment_cancelled",
"message": "Payment not completed. Analysis cancelled."
}
# Payment succeeded—process the file
return {
"status": "success",
"rows": len(df),
"columns": list(df.columns),
"summary": df.describe().to_dict(),
"paid": True,
"amount": 11_000
}
Step 4: Integrate with Claude MCP
Your MCP server exposes this function to Claude as a tool. Here's the tool definition:
{
"name": "analyze_csv_premium",
"description": "Analyze a CSV file. Files > 10,000 rows require payment (11,000 VND).",
"inputSchema": {
"type": "object",
"properties": {
"file_path": {
"type": "string",
"description": "Path to the CSV file"
},
"user_id": {
"type": "string",
"description": "Unique user identifier (e.g., email or UUID)"
}
},
"required": ["file_path", "user_id"]
}
}
When Claude calls this tool, AgentPay VN intercepts the flow, collects payment if needed, and returns the result.
Advanced: Tiered Pricing & Custom Flows
Dynamic Pricing Based on Usage
def get_price(feature: str, usage_tier: str) -> int:
"""
Determine price based on feature and user tier.
Returns amount in VND.
"""
pricing = {
"basic": {"csv_analysis": 5_000, "report_gen": 10_000},
"pro": {"csv_analysis": 2_500, "report_gen": 5_000},
"enterprise": {"csv_analysis": 0, "report_gen": 0}
}
return pricing.get(usage_tier, {}).get(feature, 0)
Batch Payments
If your bot handles multiple operations, batch them into a single payment:
total_cost = 5_000 + 10_000 + 3_000 # 18,000 VND
payment = create_payment_request(
amount=total_cost,
description="Batch analysis: 3 operations",
merchant_id="data-analyzer-pro",
order_id="batch-001"
)
Do's and Don'ts
| ✅ Do | ❌ Don't |
|---|---|
| Charge after describing what the user gets | Surprise charge users mid-flow |
| Use descriptive order IDs (include feature name) | Reuse the same order ID twice |
| Set reasonable timeouts (60–120 sec) | Wait forever for payment |
| Log every payment request for auditing | Trust await_settlement without error handling |
| Test with small amounts first (500–1,000 VND) | Deploy to production untested |
| Cache payment status to avoid polling spam | Call await_settlement every second |
FAQ
Q1: Do I need to be a registered business in Vietnam?
No. AgentPay VN connects to your personal or business bank account. The VietQR infrastructure handles the rest. Consult a tax advisor for compliance in your region.
Q2: How fast is settlement?
Most banks settle within 2–5 seconds. The await_settlement function polls the AgentPay feed every 500ms by default. Users see confirmation almost instantly.
Q3: What if a user disputes a charge?
AgentPay VN logs every transaction. You can refund manually via your bank. For high-volume products, consider a disputed-payment threshold and auto-refund logic.
Q4: Can I charge in other currencies (USD, EUR)?
Currently, AgentPay VN only supports VND. Convert user prices to VND before calling create_payment_request. Example: $1 USD ≈ 25,000 VND.
Real-World Walkthrough: AI Writing Coach Bot
Let's build a Claude-powered writing coach that: - Gives 3 free critiques per day. - Charges 2,500 VND for unlimited critiques after that.
from agentpay_vn import create_payment_request, await_settlement
from datetime import datetime, timedelta
user_critiques = {} # {user_id: {count: int, reset_time: datetime}}
def critique_essay(user_id: str, essay: str) -> str:
now = datetime.now()
# Initialize or reset user counter
if user_id not in user_critiques:
user_critiques[user_id] = {"count": 0, "reset_time": now + timedelta(days=1)}
if now > user_critiques[user_id]["reset_time"]:
user_critiques[user_id] = {"count": 0, "reset_time": now + timedelta(days=1)}
# Check free tier
if user_critiques[user_id]["count"] < 3:
user_critiques[user_id]["count"] += 1
return f"Free critique #{user_critiques[user_id]['count']}:\n{generate_critique(essay)}"
# Premium tier
payment = create_payment_request(
amount=2_500,
description="Unlimited essay critiques for 24h",
merchant_id="ai-writing-coach",
order_id=f"{user_id}-unlimited-{now.timestamp()}"
)
print(f"You've used 3 free critiques. Unlock unlimited for 24h: {payment['checkout_url']}")
settled = await_settlement(payment_id=payment['payment_id'], timeout=120)
if settled['status'] == 'SETTLED':
user_critiques[user_id]["count"] = 0 # Reset counter
return f"Premium critique unlocked:\n{generate_critique(essay)}"
else:
return "Payment cancelled. You still have free critiques remaining this period."
def generate_critique(essay: str) -> str:
# Your AI critique logic here
return "[AI generates detailed feedback]"
This pattern scales: free samples → paid premium → recurring or tiered access.
Key Takeaways
- One payment, three lines:
create_payment_request()→checkout_url→await_settlement(). - Zero fraud risk: Money goes straight to your bank account; AgentPay VN never touches it.
- Frictionless UX: Users pay inside Claude without leaving the chat.
- Instant settlement: Bank confirmation arrives in 2–5 seconds.
- Open-source and free: MIT license, no transaction fees from AgentPay (your bank may charge).
- Test extensively: Start with mock transactions or low amounts before monetizing.
Get Started Now
- Install:
pip install agentpay-vn - Read the docs: https://agentpay.servicesai.vn/v1/docs
- Explore the code: https://github.com/phuocdu/agentpay-vn
- Deploy your first paid MCP server and start charging users from inside Claude.
Money is just an exchange of value. With AgentPay VN, collecting it is as simple as describing what your AI agent offers. Stop giving your work away. Start building sustainable AI products today.