Stripe MCP Server
by DanFrModa
README.md
# Stripe MCP Server
Servidor MCP (Model Context Protocol) que expone la API de Stripe a Claude.
Pensado para desplegarse en Railway en **modo solo lectura**.
👉 **Los pasos de despliegue están en [DEPLOY-RAILWAY.md](./DEPLOY-RAILWAY.md).**
---
## Qué hace
Da a Claude 17 herramientas para consultar Stripe: clientes, cobros, pagos,
suscripciones, facturas, catálogo, saldo, transacciones, payouts, eventos y
disputas — más una herramienta genérica que alcanza cualquier endpoint, otra que
resuelve cualquier objeto por su id, y un reporte agregado de ingresos.
| Herramienta | Endpoint |
|---|---|
| `stripe_whoami` | verifica la key, el modo y los permisos |
| `stripe_request` | cualquier endpoint, cualquier método |
| `stripe_get_object` | resuelve cualquier id (`cus_`, `ch_`, `sub_`...) |
| `stripe_search` | `GET /v1/{recurso}/search` |
| `stripe_list_customers` | `GET /v1/customers` |
| `stripe_list_charges` | `GET /v1/charges` |
| `stripe_list_payment_intents` | `GET /v1/payment_intents` |
| `stripe_list_subscriptions` | `GET /v1/subscriptions` |
| `stripe_list_invoices` | `GET /v1/invoices` |
| `stripe_list_products` | `GET /v1/products` |
| `stripe_list_prices` | `GET /v1/prices` |
| `stripe_get_balance` | `GET /v1/balance` |
| `stripe_list_balance_transactions` | `GET /v1/balance_transactions` |
| `stripe_list_payouts` | `GET /v1/payouts` |
| `stripe_list_events` | `GET /v1/events` |
| `stripe_list_disputes` | `GET /v1/disputes` |
| `stripe_revenue_summary` | pagina y suma bruto/comisiones/neto |
---
## Variables de entorno
| Variable | Requerida | Default | Descripción |
|---|---|---|---|
| `STRIPE_API_KEY` | sà | — | Restricted key (`rk_...`) o secret key (`sk_...`) |
| `STRIPE_MCP_TRANSPORT` | en Railway | `stdio` | `http` para servidor remoto |
| `MCP_AUTH_TOKEN` | si `http` | — | Secreto que protege el endpoint. MÃnimo 32 caracteres |
| `STRIPE_READ_ONLY` | no | ver abajo | `1` bloquea toda escritura |
| `STRIPE_ALLOW_LIVE_WRITES` | no | `0` | Segunda reja: escrituras con key **live** |
| `STRIPE_API_BASE` | no | `https://api.stripe.com` | Para apuntar a un mock |
| `STRIPE_API_VERSION` | no | — | Fija el header `Stripe-Version` |
| `STRIPE_ACCOUNT` | no | — | Cuenta de Connect (`acct_...`) para todas las llamadas |
| `STRIPE_MAX_CHARS` | no | `20000` | Truncado de respuestas |
| `STRIPE_TIMEOUT` | no | `45` | Timeout en segundos |
| `PORT` | no | `8000` | Railway lo inyecta solo |
---
## Las tres rejas de escritura
Stripe mueve dinero real: un `POST` equivocado reembolsa a un cliente, cancela
una suscripción o borra un registro para siempre. Por eso hay tres candados
independientes, y una escritura tiene que pasar por los tres.
**1. `STRIPE_READ_ONLY` — el default depende del transporte**
- **`stdio` (local):** escrituras **permitidas** por default. Un solo usuario de
confianza en su propia máquina.
- **`http` (remoto):** escrituras **bloqueadas** por default. Hay que poner
`STRIPE_READ_ONLY=0` a propósito para habilitarlas.
Olvidar la variable en un despliegue público lo deja en solo lectura, no abierto.
**2. `STRIPE_ALLOW_LIVE_WRITES` — la reja de modo live**
Aunque apagues el read-only, una key **live** sigue rechazando escrituras hasta
que pongas `STRIPE_ALLOW_LIVE_WRITES=1`. Las keys de test (`sk_test_`, `rk_test_`)
no pasan por esta reja: experimentar contra datos de prueba no debe requerir
ceremonia.
**3. `confirm=true` — la reja por endpoint**
Los endpoints destructivos (`/v1/refunds`, `/v1/payouts`, `/v1/transfers`,
cancelaciones de suscripción, borrado de clientes, cierre de disputas) exigen
`confirm=true` en la llamada, además de todo lo anterior. Una herramienta
disparada por error no llega ahà sola.
Cada rechazo dice **cuál** de las tres rejas lo detuvo.
---
## Autenticación del endpoint
El protocolo MCP no trae autenticación propia. En modo `http`, este servidor
exige `Authorization: Bearer <MCP_AUTH_TOKEN>` en cada request, o el secreto
embebido en la ruta (`/s/<secreto>/mcp`) para los connectors de Claude.
`/healthz` es la única ruta pública.
El servidor **se niega a arrancar** si `MCP_AUTH_TOKEN` falta o tiene menos de
32 caracteres. Es a propósito: evita publicar un gateway abierto a tu cuenta de
Stripe por descuido.
---
## Correr en local
```bash
pip install -r requirements.txt
# stdio (para Claude Desktop)
STRIPE_API_KEY=rk_test_xxx python stripe_mcp.py
# http (como en Railway)
STRIPE_MCP_TRANSPORT=http \
STRIPE_API_KEY=rk_test_xxx \
MCP_AUTH_TOKEN=$(python3 -c "import secrets;print(secrets.token_urlsafe(48))") \
PORT=8000 python stripe_mcp.py
```
Al arrancar imprime en qué modo quedó:
```
[stripe-mcp] streamable-http on 0.0.0.0:8000 mode=test read_only=True allow_live_writes=False writes_enabled=False
```
---
## Notas sobre la API de Stripe
Cosas que el servidor ya resuelve por ti, y que conviene saber al leer las
respuestas:
- **Stripe no acepta JSON en el body.** Todo va `application/x-www-form-urlencoded`
con notación de corchetes: `metadata[order]=6735`, `expand[]=customer`,
`items[0][price]=price_1`. El servidor traduce dicts y listas anidadas solo.
- **El dinero es un entero en la unidad mÃnima.** `2500` con `usd` es $25.00.
Y hay monedas de cero decimales (JPY, KRW, CLP…) donde el entero *es* el monto
— dividir entre 100 ahà serÃa un error. Las salidas formateadas lo respetan.
- **Test y live son universos separados.** Un id creado en test devuelve 404 con
una key live y viceversa. Es la causa más común de un 404 con un id correcto.
- **La paginación es por cursor, no por página.** Se toma el último id y se pasa
como `starting_after`. Cuando hay más, la respuesta incluye `next_page_hint`
con el valor exacto.
- **No hay `PATCH` ni `PUT`.** Stripe usa `POST` tanto para crear como para
actualizar. Solo `GET`, `POST` y `DELETE`.
- **Las listas no filtran por `status`** en charges ni payment_intents. Hay que
filtrar sobre los registros devueltos o usar `stripe_search`.
- **`stripe_list_subscriptions` omite las canceladas por default.** Para
preguntas de churn hay que pasar `status='all'` o la respuesta sale mal en
silencio.
- **El Ãndice de búsqueda tarda hasta un minuto** en ver un objeto recién creado.
Para objetos nuevos, buscarlos por id.
- **Los eventos se guardan solo 30 dÃas.** Más atrás hay que ir a los objetos.
- **No hay endpoint de agregación.** Por eso `stripe_revenue_summary` pagina y
suma del lado del servidor MCP, y avisa con `complete=false` cuando el tope de
páginas cortó el conteo — esos totales son parciales y no deben reportarse
como finales.
- Cada `POST` sale con un `Idempotency-Key`, asà que un reintento no cobra dos
veces. Stripe los recuerda 24 horas.
---
## Seguridad
- Los secretos van en variables de entorno, nunca en el código. El `.gitignore`
bloquea archivos `.env`.
- **Usa una restricted key (`rk_...`), no la secret key.** Se crean en el
dashboard cuantas veces quieras y se les dan permisos por recurso. Para este
despliegue: todo en *Read*, nada en *Write*. Una `sk_live_` da acceso total a
la cuenta, incluido mover dinero.
- Un solo token compartido significa cero trazabilidad por persona. Todo lo que
haga cualquiera queda registrado como la misma key.
- No hay rate limiting propio. Quien tenga el `MCP_AUTH_TOKEN` puede pegarle a
Stripe hasta toparse con el lÃmite de Stripe (~100 req/s de lectura en live).
- Para cortar el acceso de golpe: borra la restricted key en
https://dashboard.stripe.com/apikeys — el servidor queda inútil al instante.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues