The Model Context Protocol (MCP) ecosystem is exploding. There are 17,000+ MCP servers out there. But when I looked for a production-grade Python MCP server for Stripe — one with proper OAuth 2.1 auth, comprehensive tool coverage, and actual tests — it didn't exist.
So I built one. 41 tools. OAuth 2.1. Full test suite. Dual transport. Here's the story.
The Gap I Found
Stripe has an official MCP server, but it's TypeScript-only for local use. The community Python alternatives? Basic — 5-8 tools, API keys hardcoded in env vars, no tests, outdated MCP spec versions.
Meanwhile, the MCP spec had evolved significantly:
The November 2025 spec introduced async Tasks and structured outputs
OAuth 2.1 became the standard (replacing API keys), but only ~8.5% of servers implement it
The Python SDK added full resource server support with RFC 9728
I saw an opportunity to build something that could actually be used in production.
What I Built
stripe-mcp-python — a Python MCP server that connects any AI client (Claude, ChatGPT, Cursor) to the full Stripe API.
Key decisions:
41 tools across 10 domains — not just the basics. Customers, Products, Payments, Subscriptions, Invoices, Refunds, Balance, Payment Links, Disputes, and Coupons. Each tool has proper MCP annotations (readOnlyHint, destructiveHint, etc.) so AI clients know what's safe to call.
Pydantic v2 for everything — every tool input is validated with regex patterns, min/max constraints, and type safety. If an AI agent passes "sub_abc" to a customer tool, it gets rejected before hitting the Stripe API. This prevents wasted API calls and gives clear error messages.
A _helpers.py pattern — I extracted do_list(), do_get(), do_create(), do_modify(), do_delete(), and do_search() into shared helpers. This turned each tool module from 100+ lines of boilerplate into clean, focused 5-line functions. DRY taken seriously.
OAuth 2.1 as a Resource Server — the server publishes RFC 9728 Protected Resource Metadata and validates tokens from any provider (Auth0, Okta, Cognito). Granular scopes (stripe:read, stripe:write, stripe:delete, stripe:admin) map to every tool.
Dual transport — stdio for local use with Claude Desktop/Cursor, Streamable HTTP for remote deployment. One codebase, two modes.
Architecture
The project follows a clean modular structure:
src/stripe_mcp/
├── server.py # FastMCP init + lifespan
├── cli.py # Click CLI (stdio + HTTP)
├── auth/ # OAuth 2.1 + scope enforcement
├── tools/ # 10 tool modules + shared helpers
├── models/ # Pydantic input/output models
└── utils/ # Stripe client, errors, pagination, formatting
Every Stripe API call goes through a centralized stripe_api_call() wrapper that handles logging and error formatting consistently. Errors return actionable JSON messages — so when an AI agent gets a 404, it knows to "check the ID is correct" instead of just failing.
What I'd Do Differently
Start with tests first. I wrote them after the implementation, which meant some refactoring. Next time I'd use TDD for the tool modules.
OAuth is complex. The MCP auth spec is still evolving (Client Metadata Documents are in draft). I'd recommend starting with API key auth and adding OAuth as a second phase.
Results
38 unit tests, all passing
41 MCP tools registered and discoverable
Works with Claude Desktop, Cursor, VS Code, and any MCP client
CI/CD with GitHub Actions (lint + test on Python 3.11/3.12/3.13)
Published to PyPI as stripe-mcp-python
Try It
bashpip install stripe-mcp-python
export STRIPE_API_KEY=sk_test_...
stripe-mcp
GitHub: github.com/mukanzibruce/stripe-mcp-python
If you're building MCP servers, I'd love to hear what gaps you're seeing in the ecosystem. What services need a good MCP integration?
Top comments (0)