Spec Driven Development
MCP Server that facilitates spec-driven development workflows, not just Vibe Coding.
Description from the maker (original English)
Read-only decision table over ACH return codes: 17 codes, 5 decisions, rule shown.
These three are never merged into one score: opinions, measurements and popularity are different things.
We have not written a plain-language summary for this one yet. Below are the facts we read from the registry.
Pick the AI app you use, then copy the settings.
You need
Which app do you use?
Pick your app above. We remember it, so next time the button says “Install in …”.
Open Claude Desktop → Settings → Developer → Edit Config, and paste this into claude_desktop_config.json
{
"mcpServers": {
"paypilot-ach-returns": {
"command": "uvx",
"args": [
"paypilot-ach-returns-mcp"
]
}
}
}Run this in your terminal
claude mcp add paypilot-ach-returns -- uvx paypilot-ach-returns-mcpPaste into .cursor/mcp.json in your project, or ~/.cursor/mcp.json
{
"mcpServers": {
"paypilot-ach-returns": {
"command": "uvx",
"args": [
"paypilot-ach-returns-mcp"
]
}
}
}Your browser asks to open the app, then the app shows the settings and asks you to confirm. Key values are left empty for you to fill in.
Paste into .vscode/mcp.json in your project
{
"servers": {
"paypilot-ach-returns": {
"type": "stdio",
"command": "uvx",
"args": [
"paypilot-ach-returns-mcp"
]
}
}
}Your browser asks to open the app, then the app shows the settings and asks you to confirm. Key values are left empty for you to fill in.
This one runs on your computer, and ChatGPT can only connect to servers on the internet. Pick another app.
Your choice stays on this device in a cookie; no account needed.
These settings are generated from the maker’s registry entry. If the README says otherwise, follow the README.
Runs on your computer; the tool list appears in your AI app after you install it.
We check information anyone can see; we have not read the code. Checks that do not apply are left out of the count. · Checked Nov 7, 2026
The registry name was verified with the GitHub account IvanSFlowGit, which owns the repo
We have not read this repo from GitHub yet
We have not read this repo from GitHub yet
This one runs on your computer; there is no internet endpoint
Runs on your computer and uses local settings instead
Runs on your computer; we can only read tool lists from internet endpoints
Compared with every active server in this category in the registry. Stars count only when the publisher owns the repo.
It passed 1 of 1 applicable basic checks. We have not read the code. Before adding a key or token, see what access the server asks for and use a key with only the permissions you need.
The registry does not list a required key, though some servers ask you to sign in on first use.
It runs on your computer. Your AI app starts it when needed, and data does not pass through the maker’s servers (unless it calls an online service itself).
The GitHub repo last changed –, and the latest version (0.1.1) reached the registry 4 days ago.
Apps that support MCP, such as Claude Desktop, Claude Code, Cursor and VS Code. You also need Python (uv) on your computer.
In the same category: Spec Driven Development, Remote Desktop Commander, AvaHR
Servers in the same category from other makers, sorted by stars.
Reading a card:★ GitHub starsiThree pillars: reviews · checks · usageiUpdated = latest version published to the registryi
MCP Server that facilitates spec-driven development workflows, not just Vibe Coding.
Remote Desktop Commander: use files, terminal and processes on your own computers from any AI app.
Read hiring data and create inactive assessment and questionnaire drafts for human review.
Copied from the maker’s repo as written. PopNx MCP has not reviewed or endorsed it. HTML, images and scripts are removed. Links lead off this site.
An AI dunning agent that recovers failed subscription payments.
[Live demo -> paypilot.fly.dev](https://paypilot.fly.dev/) - try it in the browser, no setup or API key required.
When a recurring charge fails, most of that revenue is recoverable - the customer didn't *decide* to churn, their card just expired or a payment bounced. PayPilot turns each invoice.payment_failed event into a grounded, on-brand recovery action: it diagnoses *why* the payment failed, picks the right retry strategy for that reason, and drafts a warm, one-click dunning email - all in a single API call.
It's built as a small, readable LangGraph agent with retrieval-augmented generation (RAG) over a dunning playbook, exposed through a FastAPI endpoint. The whole thing runs its test suite with no API key and no network.
A failed-payment event flows through a seven-node LangGraph StateGraph. Each node enriches a shared, typed RecoveryState and hands it to the next:
flowchart LR
A[retrieve_context] --> R[assess_risk] R --> B[diagnose_reason] B --> C[choose_strategy] C --> S[schedule_retry] S --> D[draft_message] D --> E[finalize] E --> F([END])
| Node | What it does |
|---|---|
retrieve_context | Loads the customer record and pulls relevant snippets from the dunning playbook via the RAG retriever. |
assess_risk | Deterministic (no LLM): scores churn risk (low/medium/high) from the dunning attempt number and the customer's recent failure streak. |
diagnose_reason | Committed template by default, LLM only with PAYPILOT_LLM_DRAFT=1: a 1-2 sentence, playbook-grounded diagnosis of *why* the payment failed, reflecting the churn risk. |
choose_strategy | Deterministic (no LLM): maps the failure code to a fixed action + retry cadence, then tightens it when churn risk is high. Stable and unit-testable. |
schedule_retry | Deterministic (no LLM): turns the cadence into a concrete next_retry_at UTC time, ready to hand to a scheduler. |
draft_message | Committed template by default, LLM only with PAYPILOT_LLM_DRAFT=1: a short, warm dunning email with one clear call to action. |
finalize | Assembles the {diagnosis, risk, strategy, schedule, message, impact} response payload. |
The recovery quality depends on dunning best-practice - retry timing, tone, when to offer a grace period. Rather than bake that into prompts, PayPilot keeps it in an editable knowledge source (data/playbook.md) that the retriever feeds into the diagnosis and drafting nodes.
Which retriever depends on configuration, and it is worth being precise about this. With OPENAI_API_KEY set, app/ingest.py builds a FAISS index over the playbook using OpenAI embeddings (k=3). With no key - which is how the public demo runs - it falls back to a lexical keyword retriever, so no FAISS index and no embedding call is involved in anything a visitor sees.
And the playbook only changes the output on the LLM path. On the default zero-inference path the committed templates are rendered as-is and retrieved context is not consulted, so editing playbook.md changes nothing until PAYPILOT_LLM_DRAFT=1 is set. Playbook edits are input to the build-time generation step, not to every request.
choose_strategy is intentionally *not* an LLM call. Retry cadence and the chosen action come from a fixed rules table (app/decision.py) keyed on the failure code, which comes from Stripe, Recharge, Chargebee or Loop:
| Failure code | Retry in | Action | Tone |
|---|---|---|---|
card_expired | ~1 day | request card update | Friendly, routine |
insufficient_funds | ~3 days | wait and retry | Soft, no pressure |
generic_decline | ~2 days | retry and verify | Calm, helpful |
issuer_do_not_retry | never | request new payment method | Plain, no blame |
card_details_invalid | never | request card update | Plain, no blame |
direct_debit_not_retried | never | request manual payment | Plain, no blame |
retries_exhausted | never | request new payment method | Plain, no blame |
The LLM writes the *message*; the *policy* stays predictable.
irreversible-reach maps which model-produced values in an agent can reach an action that cannot be undone. Its PayPilot manifest marks sending an email as PayPilot's only irreversible action, with deterministic guards in front of it; remove those guards and it fails. A manifest describes code, it does not test it.
assess_risk reads the dunning attempt number and the customer's recent payment history (from data/customers.json) and buckets churn risk. When it's high - a third attempt, or a run of recent failures - choose_strategy tightens the retry cadence and marks the strategy escalated, the diagnosis calls out the urgency, and impact discounts the recovery odds for a customer who keeps bouncing. So the agent reasons about *history*, not just the single event in front of it.
# 1. Install
python -m venv .venv && source .venv/bin/activate pip install -r requirements.txt
cp .env.example .env # then add your OPENAI_API_KEY
PAYPILOT_TENANT=demo uvicorn app.api:app --reload
curl -s http://localhost:8000/payment-failed \
-H 'Content-Type: application/json' \ -d '{ "customer_id": "cust_001", "amount": 1499.0, "currency": "usd", "failure_code": "card_expired", "attempt": 1 }' | jq
{
"diagnosis": "The card on file for Acme Robotics has expired, so the Scale renewal couldn't be charged; ...", "risk": { "attempt": 1, "prior_failures": 0, "churn_risk": "low", "escalate": false }, "strategy": { "action": "request_card_update", "retry_in_days": 1, "offer": "...", "escalated": false }, "schedule": { "retry_in_days": 1, "next_retry_at": "2026-07-02T09:00:00+00:00", "retry_on": "2026-07-02", "timezone": "UTC" }, "message": "Hello Acme Robotics, we tried to renew your Scale plan but the card we have on file has expired ...", "impact": { "amount_at_risk": 1499.0, "currency": "USD", "recovery_likelihood": 0.7, "expected_recovered": 1049.3, "annual_value_at_risk": 17988.0, "churn_risk": "low" } }
GET /health returns {"status": "ok"} for liveness checks. The full response schema (typed with pydantic) is browsable at /docs.
The endpoint is rate limited per client IP, and the response payload is a typed RecoveryResponse (diagnosis, risk, strategy, schedule, message, impact), so the contract shows up precisely in the OpenAPI docs.
POST /payment-failed/batch runs a whole billing run (up to 50 events) in one call and adds a portfolio aggregate - total at risk, total expected recovered, and how many accounts are high churn risk. GET /portfolio-impact rolls that up across the demo customers and powers the recoverable-revenue headline on the landing page.
Status: no client traffic reaches this deployment, so no merchant's live Stripe delivery has either. The handler is exercised offline by tests/test_stripe.py.
POST /webhooks/stripe accepts a real Stripe invoice.payment_failed event. It verifies the Stripe-Signature header (HMAC-SHA256) when STRIPE_WEBHOOK_SECRET is set, acknowledges other event types with a 200 so Stripe won't retry, maps Stripe decline codes (expired_card, insufficient_funds, ...) to PayPilot's failure codes, and runs the recovery graph. Point a webhook (or stripe trigger invoice.payment_failed) at it; add metadata.paypilot_customer_id to resolve a demo customer.
Status: this deployment carries no client traffic, so no merchant's live Recharge delivery has reached it. The handler is exercised offline by tests/test_recharge.py.
POST /webhooks/recharge accepts Recharge charge/failed and charge/max_retries_reached deliveries for Shopify subscription stores. It checks X-Recharge-Hmac-Sha256 against RECHARGE_CLIENT_SECRET the way Recharge documents it, and maps every one of Recharge's 109 published error types to a PayPilot failure code (the full table). Recharge runs its own retries, so when it will retry, its retry_date is reported as the schedule with source="recharge" and no customer email is drafted (message_suppressed: "recharge_retry_scheduled"). Errors that are not a payment problem the customer can fix (inventory, shipping, tax, test mode) and error types PayPilot does not recognise are acknowledged and not run, so no customer gets an email about them.
A merchant comes on in three steps. They create an API token and copy the API Client Secret from their Recharge admin; the secret goes on the deployment as RECHARGE_CLIENT_SECRET; then RECHARGE_API_TOKEN=... python scripts/recharge_register_webhooks.py --apply registers both webhooks against this deployment (dry run without --apply, and safe to re-run).
The webhook is idempotent on the Stripe event id, so a retried delivery replays the stored result instead of re-running the graph. POST /payment-failed and /batch accept an optional Idempotency-Key header for the same guarantee. Every response carries X-Process-Time and X-Request-ID headers, emits a structured JSON access log, and 429s include Retry-After. GET /metrics (admin token required) returns a JSON snapshot (request counts by status, average latency, recoveries run, total expected recovered). Batches roll up per currency, so a mixed USD/EUR/GBP billing run stays correct (aggregate.by_currency).
For discovery, the app also serves /robots.txt, /sitemap.xml, and an /llms.txt summary for AI answer engines, and the landing page ships SoftwareApplication + FAQPage JSON-LD.
PayPilot does not stop at drafting. It records what failed, what it sent, and what happened next, so "we recovered X" is a figure you can audit rather than a claim.
invoice.payment_failed -> record -> strategy -> portal link -> email
| invoice.paid / payment_succeeded -> recovered <---------------+ customer.subscription.deleted -> churned
Per-invoice state machine: failed -> messaged -> clicked -> recovered | churned | exhausted. Illegal moves raise rather than silently overwrite, so the dashboard can never contradict revenue it already reported. Amounts are stored in integer minor units with a currency code. Floats appear only at the presentation edge, never in storage or arithmetic.
State only advances to `messaged` on a real send. A dry run, a suppr
Only the beginning is shown; read the rest in the repo