Stripe MCP server for Stallari — billing-v1 (subscriptions, products, prices, invoices) + payments-v1 (PaymentIntents, refunds, disputes, webhooks). Token-efficient pipe-delimited output, mandatory env isolation, Restricted-Key-only, per-resource write-gating.
Project description
stripe-blade-mcp
⚠️ Phase 0 scaffold — pre-alpha (
0.1.0a1). All 45 tools currently raiseNotImplementedError. The scaffold exists to validate the/build-blade-mcpskill conventions (security, privacy, token-efficiency, devops baseline) end-to-end. Production implementation of Phases A–E is gated on a real Stallari build trigger perBUILD_PLAN.md. Do not install for production use. Use@stripe/mcpfor direct Claude Desktop / Cursor Stripe access in the meantime.
Stripe MCP server for Stallari workloads. Token-efficient, Restricted-Key-only, per-resource write-gated. Closes the gaps in Stripe's own
@stripe/mcpfor Stallari pack-workload integration.
Dual-contract blade implementing both billing-v1 (subscriptions, products, prices, invoices) and payments-v1 (PaymentIntents, refunds, disputes, webhooks). First blade where one repo cleanly implements both Stallari payment contracts end-to-end.
Why another Stripe MCP?
Stripe ships @stripe/mcp. It covers 23 tools across 11 resource categories and works well for direct Claude Desktop / Cursor use. This blade exists because:
- Missing
payments-v1Required ops.@stripe/mcpshipslist_payment_intents+create_refundonly — missingpayment_intent_create/capture/cancel/get,refund_get/list, and the fullwebhook_verify+webhook_subscription_*block that Stallari'spayments-v1contract requires. - HTTP-only transport —
@stripe/mcpis HTTP only; DD-242 requires stdio when launched by Stallari. - Raw JSON responses —
@stripe/mcpreturns standard MCP JSON-RPC payloads. This blade emits pipe-delimited summaries on list ops (~16× token reduction measured on equivalent paddle-blade ops), with field selection and null-omit on detail views. - Advisory-only write-gating —
@stripe/mcprecommends human confirmation but doesn't enforce. This blade enforces per-resourceSTRIPE_WRITE_*env gates AND aconfirm=trueparameter on destructive ops.
If you only need basic Stripe access in Claude Desktop, use @stripe/mcp. If you're running Stallari pack workloads, use this blade.
Comparison
| Capability | stripe-blade-mcp |
@stripe/mcp |
|---|---|---|
| Tool count | ~45 (full billing-v1 + payments-v1) |
23 (partial coverage) |
payments-v1 Required ops |
8/8 | 2/8 |
billing-v1 Required ops |
6/6 | 3/6 |
| Webhook HMAC verification | Built-in stripe_webhook_verify |
Not present |
| Webhook subscription management | 4-op block (list/create/delete/replay) | Not present |
| Token-efficient responses | Pipe-delimited, field selection, null-omit, money-formatted | Raw JSON |
| Write gating | Per-op env var, fail-closed | Advisory only |
| Destructive op confirmation | confirm=true enforced |
None enforced |
| Sandbox/live isolation | Mandatory STRIPE_ENV, fail-closed |
Yes (per-env access) |
| API key security | Env var only, credential scrubbing | Env var OR --api-key CLI arg (ps leak) |
| Restricted-Key enforcement | Startup check + scope coherence warn | None |
| Stdio when harness-launched | Yes (DD-242) | No — HTTP only |
| Money formatting | Human-readable ($29.00 USD) |
Raw cents |
| Field selection | fields= parameter |
Not available |
| Pagination hints | Cursor + "N more" | Standard has_more only |
| Idempotency-key auto-attach | Yes (stallari-... prefix) |
Unclear |
| Connect (multi-tenant) | Out of scope v1 — explicit refuse | Possibly available |
| Runtime | Python (uv) | TypeScript (npm) |
Token efficiency: before and after
Raw Stripe API (≈ @stripe/mcp shape) — 1 page of 25 subscriptions:
{
"object": "list",
"url": "/v1/subscriptions",
"has_more": true,
"data": [
{
"id": "sub_1NkVJ8...",
"object": "subscription",
"application": null,
"application_fee_percent": null,
"automatic_tax": {"enabled": false, "liability": null},
"billing_cycle_anchor": 1715000000,
"billing_thresholds": null,
"cancel_at": null,
"cancel_at_period_end": false,
"canceled_at": null,
"cancellation_details": {"comment": null, "feedback": null, "reason": null},
"collection_method": "charge_automatically",
"created": 1715000000,
... ~50 more fields per subscription
},
... 24 more
]
}
~12,000 tokens for the page.
stripe-blade-mcp pipe-delimited:
[env=test]
sub_1NkVJ8 | acme inc. | active | $29.00 USD / month | 3 days ago
sub_1NkVK2 | beta corp | active | $99.00 USD / month | 7 days ago
sub_1NkVL5 | trial co. | trialing | $0.00 USD / month | 1 day ago
... 22 more (pass starting_after=sub_1NkVL5 to continue)
~280 tokens. ≈ 40× reduction.
Need a single field? Call stripe_subscription_get with fields=["id", "status", "current_period_end"] to project exactly what you need.
Installation
As a Stallari blade
stripe-blade-mcp installs as a Stallari pack. The blade resolves credentials
through the Stallari CredentialStore (DD-186) at launch — never commit secrets.
stallari pack install groupthink-dev/stripe-blade-mcp
Standalone (Claude Desktop, Cursor, etc.)
uv tool install stripe-blade-mcp
Claude Desktop MCP config:
{
"mcpServers": {
"stripe": {
"command": "stripe-blade-mcp",
"env": {
"STRIPE_ENV": "test",
"STRIPE_API_KEY": "rk_test_..."
}
}
}
}
Quickstart
- Create a Restricted Key at https://dashboard.stripe.com/apikeys/create — Standard Keys are rejected
- Enable the resources your workload needs (Payments / Subscriptions / Refunds)
- Configure the env:
export STRIPE_ENV=test # or 'live' for production
export STRIPE_API_KEY=rk_test_... # restricted key only
export STRIPE_WRITE_PAYMENTS=1 # only enable writes you need
- Validate:
stripe-blade-mcpthen invokestripe_account_info
Usage examples
# List recent subscriptions (pipe-delimited)
await stripe_subscription_list(after=None, limit=10)
# Get one with field selection
await stripe_subscription_get(id="sub_1NkVJ8", fields=["id", "status", "current_period_end"])
# Create a PaymentIntent (gated — requires STRIPE_WRITE_PAYMENTS=1)
await stripe_payment_intent_create(
payload={"amount": 2900, "currency": "usd", "customer": "cus_QABCDEF", "confirm": True}
)
# Verify a Stripe webhook
await stripe_webhook_verify(
body=raw_request_body,
signature_header=req.headers["Stripe-Signature"],
secret=os.environ["STRIPE_WEBHOOK_SECRET"]
)
# → {"verified": True, "event_type": "payment_intent.succeeded", "event_id": "evt_...", "age_seconds": 2}
Security posture
- Mandatory sandbox/live env isolation. Refuses to start without
STRIPE_ENV=test|live. - Restricted Keys only.
sk_live_*Standard Keys rejected at startup; onlyrk_*accepted in live mode. - Per-resource write gates. Read is always permitted; write requires opt-in per-resource (
STRIPE_WRITE_PAYMENTS=1, etc.). Defaults to all off. - Confirm gates on destructive ops.
cancel,void,deleterequireconfirm=trueparameter. - Credential scrubbing. Every error path (incl. exception cause chain) is scrubbed of
rk_*,sk_*,whsec_*, and genericapi_key-named fields. - PCI allowlist. Card data projected to
last_4+brand+exp_*+fingerprintonly (DD-179). Rawpayment_method/cardobjects never reach the response. - Idempotency. Auto-attached
stallari-<date>T<run_id>-<uuid>key on every mutation; caller override always wins. - Stdio transport when harness-launched (DD-242). HTTP transport opt-in is loopback-only with mandatory bearer token.
- No telemetry, no phone-home (convention #19).
- Mesh exposure denied — blade tools never advertised on daemon
:9847/mcp(DD-240 invariant #8).
Sandbox vs production
| Env | STRIPE_ENV |
Key prefix | Notes |
|---|---|---|---|
| Test | test |
rk_test_* or sk_test_* |
Stripe test mode — no real money. Use freely. |
| Live | live |
rk_live_* (only) |
Real-money production. sk_live_* rejected. |
The blade echoes _env: test or _env: live on every response so misconfig is visible immediately.
Tool inventory
Required (billing-v1 + payments-v1 conformance)
| Tool | Wraps | Contract op |
|---|---|---|
stripe_product_list / stripe_product_get |
Products | billing-v1 products / product |
stripe_price_list |
Prices | billing-v1 prices |
stripe_customer_list / stripe_customer_get |
Customers | billing-v1 customers + payments-v1 customer_* |
stripe_subscription_list / stripe_subscription_get |
Subscriptions | billing-v1 subscriptions / subscription |
stripe_transaction_list |
Invoices | billing-v1 transactions |
stripe_payment_intent_create / _capture / _cancel / _get / _list |
PaymentIntents | payments-v1 payment_* |
stripe_refund_create / _get / _list |
Refunds | payments-v1 refund_* |
stripe_webhook_verify |
(local HMAC) | payments-v1 webhook_verify |
Recommended (~14 tools)
stripe_customer_create / _update · stripe_subscription_update / _cancel · stripe_invoice_list / _get · stripe_adjustment_list / _create · stripe_discount_list / _apply · stripe_event_list · stripe_credit_balance_get · stripe_payment_method_list · stripe_dispute_list
Optional / target-specific
stripe_preview_transaction · stripe_notification_list · stripe_simulate · stripe_payout_list / _get · stripe_balance_get · stripe_dispute_get / _evidence_submit · stripe_webhook_endpoint_list / _create / _delete · stripe_replay_notification · stripe_webhook_event_types · stripe_account_info
Out of scope for v1
Stripe Connect · Stripe Issuing · Stripe Treasury · Stripe Climate · Stripe Identity. Each refused upfront at the server boundary with a pointer to a future DD.
Webhook verification
from stripe_blade_mcp.tools import stripe_webhook_verify
result = await stripe_webhook_verify(
config,
body=raw_request_body, # bytes or str
signature_header=req.headers["Stripe-Signature"],
secret=webhook_endpoint_secret,
tolerance_seconds=300, # default 5min replay window
)
# → {
# "verified": True,
# "event_type": "payment_intent.succeeded",
# "event_id": "evt_1NkVJ8...",
# "created_at": "2026-05-11T15:32:00+10:00",
# "age_seconds": 4,
# "data": {...} # parsed event payload
# }
The HMAC scheme follows Stripe's canonical format: t={timestamp},v1={hmac}. The blade enforces the timestamp tolerance to defeat replay attacks.
Rate limits + retry policy
| Mode | Global | Per endpoint |
|---|---|---|
| Live | 100 req/s | 25 req/s |
| Test | 25 req/s | 25 req/s |
Plus: PaymentIntent updates ≤ 1000/hour, Search ≤ 20 req/s.
On 429, the blade surfaces RateLimited verbatim with the Stripe-Rate-Limited-Reason header (global-rate / endpoint-rate / global-concurrency / resource-specific). No internal retry beyond 2 attempts — configure token-bucket throttling at the Stallari workload layer if you need it.
Development
git clone https://github.com/groupthink-dev/stripe-blade-mcp
cd stripe-blade-mcp
uv sync --group test --group dev
make test
make lint
make typecheck
The build plan lives in BUILD_PLAN.md. Each phase is a fresh PR.
Contributing
This is a first-party Groupthink blade. Issues + PRs welcome via github.com/groupthink-dev/stripe-blade-mcp/issues.
For Stallari pack development conventions, see the Stallari developer docs.
License
MIT. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file stripe_blade_mcp-0.1.0a1.tar.gz.
File metadata
- Download URL: stripe_blade_mcp-0.1.0a1.tar.gz
- Upload date:
- Size: 132.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4f2efacacd90d60cc7fc0c52242c280f4928feeb0940157738cb5673a4ac31e3
|
|
| MD5 |
23c6c33dfd6813a6e1b7ade716b5f913
|
|
| BLAKE2b-256 |
f984a81a66022cce6140f6ed4ad9d70533a1bb25fe08bd49efd3e244daaf3856
|
Provenance
The following attestation bundles were made for stripe_blade_mcp-0.1.0a1.tar.gz:
Publisher:
publish.yml on Groupthink-dev/stripe-blade-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stripe_blade_mcp-0.1.0a1.tar.gz -
Subject digest:
4f2efacacd90d60cc7fc0c52242c280f4928feeb0940157738cb5673a4ac31e3 - Sigstore transparency entry: 1503217863
- Sigstore integration time:
-
Permalink:
Groupthink-dev/stripe-blade-mcp@10751646c56cb0387a9652c81a5cb5a441aafb09 -
Branch / Tag:
refs/tags/v0.1.0a1 - Owner: https://github.com/Groupthink-dev
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@10751646c56cb0387a9652c81a5cb5a441aafb09 -
Trigger Event:
push
-
Statement type:
File details
Details for the file stripe_blade_mcp-0.1.0a1-py3-none-any.whl.
File metadata
- Download URL: stripe_blade_mcp-0.1.0a1-py3-none-any.whl
- Upload date:
- Size: 52.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e2ac8a5f5fdf5c3505340dd82b813d85d3faff6207efe1fc45f7c167f24f6d40
|
|
| MD5 |
512ae550103153d5afdfd81c8903509e
|
|
| BLAKE2b-256 |
2124db76673655638c17d61f4e4dea9ae86e6d3f71a65b9bbdeeaee86c257ed7
|
Provenance
The following attestation bundles were made for stripe_blade_mcp-0.1.0a1-py3-none-any.whl:
Publisher:
publish.yml on Groupthink-dev/stripe-blade-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stripe_blade_mcp-0.1.0a1-py3-none-any.whl -
Subject digest:
e2ac8a5f5fdf5c3505340dd82b813d85d3faff6207efe1fc45f7c167f24f6d40 - Sigstore transparency entry: 1503218714
- Sigstore integration time:
-
Permalink:
Groupthink-dev/stripe-blade-mcp@10751646c56cb0387a9652c81a5cb5a441aafb09 -
Branch / Tag:
refs/tags/v0.1.0a1 - Owner: https://github.com/Groupthink-dev
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@10751646c56cb0387a9652c81a5cb5a441aafb09 -
Trigger Event:
push
-
Statement type: