MCP Tools for AI Agents: How to Build Production-Grade Integrations
A practical guide to building Model Context Protocol (MCP) integrations for AI agents. Covers Stripe, X/Twitter, Reddit, and Web3 patterns with real code examples.
The Model Context Protocol (MCP) is becoming the standard way to give AI agents access to external tools. But most MCP implementations are toy examples — a weather API here, a calculator there.
This guide covers how to build production-grade MCP integrations that handle real-world concerns: authentication, rate limiting, idempotency, error recovery, and cost control.
What Is MCP?
MCP is a protocol that lets AI agents discover and invoke external tools through a standardized interface. Instead of hardcoding API calls into your agent's prompt, you define tools as structured schemas:
{
"name": "create_checkout_session",
"description": "Create a Stripe checkout session for a product",
"parameters": {
"type": "object",
"properties": {
"priceCents": { "type": "number" },
"currency": { "type": "string" },
"productName": { "type": "string" }
},
"required": ["priceCents", "currency"]
}
}
The agent calls this tool, the MCP server executes it, and returns structured results. Clean separation of concerns.
Pattern 1: Stripe MCP (Payments)
Stripe's API is well-designed, but there are subtleties when wrapping it for AI agents:
Idempotency keys are mandatory. AI agents retry. Network hiccups happen. Every Stripe API call should include an idempotency key:
async createCheckoutSession(params: CheckoutParams) {
const idempotencyKey = `checkout_${params.orderId}`;
return this.stripe.checkout.sessions.create({
line_items: [{ price_data: {
currency: params.currency,
product_data: { name: params.productName },
unit_amount: params.priceCents,
}, quantity: 1 }],
mode: 'payment',
metadata: { orderId: params.orderId, type: params.type },
success_url: `${PUBLIC_URL}/success?order=${params.orderId}`,
cancel_url: `${PUBLIC_URL}/cancel`,
}, { idempotencyKey });
}
Webhook verification is critical. Always verify the Stripe signature before processing:
const event = stripe.webhooks.constructEvent(body, sig, webhookSecret);
Metadata is your friend. Store business logic context in Stripe metadata — it comes back in webhooks and lets you route events to the right handler.
Pattern 2: X/Twitter MCP (Social)
The X API v2 has strict rate limits and requires OAuth 1.0a. Key lessons:
Rate limit tracking. X returns x-rate-limit-reset headers. Track them in memory and wait before retrying:
if (response.status === 429) {
const resetAt = parseInt(response.headers.get('x-rate-limit-reset') || '0');
const waitMs = Math.max(0, resetAt * 1000 - Date.now()) + 1000;
await sleep(waitMs);
return this.postTweet(text); // Retry
}
Idempotency via tweet IDs. Store every posted tweet ID in your database. Before posting, check if you already posted this content. X doesn't have built-in idempotency.
Simulation mode. When API keys are missing (development, testing), the MCP should return realistic fake responses instead of crashing:
if (!this.apiKey) {
return { id: `sim_${Date.now()}`, text, simulated: true };
}
Pattern 3: Web3 MCP (On-Chain)
For agents that interact with blockchains (Base L2, Ethereum), the MCP pattern is especially useful:
Separate signing from sending. The MCP should handle transaction construction, gas estimation, and signing internally, but expose a clean burn() or transfer() interface.
Gas estimation with buffer. Always add 20% buffer to gas estimates:
const gasEstimate = await publicClient.estimateGas(tx);
const gasWithBuffer = gasEstimate * 120n / 100n;
Nonce management. For concurrent transactions, track nonces locally to avoid conflicts.
Pattern 4: Reddit MCP (Community)
Reddit's OAuth2 flow for script apps is straightforward but has quirks:
Token caching. Reddit tokens expire in 1 hour. Cache them and refresh proactively:
async getToken() {
if (this.token && this.tokenExpiry > Date.now()) {
return this.token;
}
// Refresh token...
}
User-Agent requirements. Reddit requires a descriptive User-Agent. Using the default will get you rate-limited or banned.
Cross-Cutting Concerns
Error Recovery
Every MCP should implement a circuit breaker pattern. After N consecutive failures, stop calling the API for a cooldown period:
if (this.consecutiveErrors >= MAX_ERRORS) {
if (Date.now() < this.circuitOpenUntil) {
throw new Error('Circuit breaker open');
}
this.consecutiveErrors = 0; // Half-open: try again
}
Cost Tracking
If your MCP calls paid APIs (LLMs, Stripe, blockchain gas), track costs per operation:
const cost = this.estimateCost(response);
await this.traceService.record({
task: 'stripe_checkout',
model: 'stripe-api',
costUsd: cost,
tokens: 0,
});
Logging
Every MCP operation should be logged with: timestamp, operation name, input params (sanitized), output summary, duration, and cost. This is critical for debugging autonomous agents that run without supervision.
Testing MCPs
Test each MCP in three modes:
- Unit tests with mocked API responses
- Integration tests against sandbox/test environments
- Simulation tests with the harness (no real API calls)
The simulation mode is crucial for CI/CD — you don't want your test suite making real Stripe charges or posting real tweets.
The MCP Marketplace
We're building a marketplace for production-grade MCP integrations at Claw Mart. If you've built an MCP that handles a real-world integration with proper error handling, rate limiting, and idempotency, we'd love to feature it.
Good MCPs are the difference between an AI agent that works in a demo and one that works in production. Build them right.