Spec Driven Development
MCP Server that facilitates spec-driven development workflows, not just Vibe Coding.
作者提供的说明(英文原文)
Read-only decision table over ACH return codes: 17 codes, 5 decisions, rule shown.
这三项永远不会合并成一个分数:意见、测量和人气是不同的东西。
我们还没有为它写通俗的介绍。下面是我们从注册中心读取到的事实。
选择你使用的 AI 应用,然后复制设置。
你需要
你用的是哪个应用?
在上方选择你的应用。我们会记住它,下次按钮会显示“安装到 …”。
打开 Claude Desktop → Settings → Developer → Edit Config,把下面的内容粘贴到 claude_desktop_config.json
{
"mcpServers": {
"paypilot-ach-returns": {
"command": "uvx",
"args": [
"paypilot-ach-returns-mcp"
]
}
}
}在终端中运行这条命令
claude mcp add paypilot-ach-returns -- uvx paypilot-ach-returns-mcp粘贴到项目中的 .cursor/mcp.json,或 ~/.cursor/mcp.json
{
"mcpServers": {
"paypilot-ach-returns": {
"command": "uvx",
"args": [
"paypilot-ach-returns-mcp"
]
}
}
}浏览器会先询问是否打开应用,然后应用会显示设置并请你确认。密钥的值会留空,由你自己填写。
粘贴到项目中的 .vscode/mcp.json
{
"servers": {
"paypilot-ach-returns": {
"type": "stdio",
"command": "uvx",
"args": [
"paypilot-ach-returns-mcp"
]
}
}
}浏览器会先询问是否打开应用,然后应用会显示设置并请你确认。密钥的值会留空,由你自己填写。
这个服务器在你的电脑上运行,而 ChatGPT 只能连接互联网上的服务器。请选择其他应用。
你的选择通过 Cookie 保存在这台设备上,无需账户。
这些设置是根据作者在注册中心的条目生成的。如果 README 的写法不同,请以 README 为准。
它在你的电脑上运行;安装后,工具列表会显示在你的 AI 应用中。
我们检查的是任何人都能看到的信息;我们没有阅读代码。不适用的检查项不计入总数。 · 检查于 2026年11月7日
注册名称已通过 GitHub 账户 IvanSFlowGit 验证,该账户拥有此仓库
我们还没有从 GitHub 读取这个仓库
我们还没有从 GitHub 读取这个仓库
它在你的电脑上运行,没有互联网端点
在你的电脑上运行,改用本地设置
它在你的电脑上运行;我们只能读取互联网端点的工具列表
与注册中心里这个分类中所有活跃的服务器比较。只有当发布者拥有该仓库时,星标数才计入。
它在 1 项适用的基础检查中通过了 1 项。我们没有阅读代码。在添加密钥或令牌之前,请先看看这个服务器要求哪些权限,并使用只具备所需权限的密钥。
注册中心没有列出必需的密钥,不过有些服务器会在首次使用时要求你登录。
它在你的电脑上运行。你的 AI 应用会在需要时启动它,数据不会经过作者的服务器(除非它自己调用在线服务)。
GitHub 仓库最后更新于–,最新版本(0.1.1)于4天前进入注册中心。
支持 MCP 的应用,例如 Claude Desktop、Claude Code、Cursor 和 VS Code。你的电脑上还需要有 Python (uv)。
同一分类中有:Spec Driven Development, Remote Desktop Commander, AvaHR
同一分类中其他作者的服务器,按星标数排序。
如何看卡片:★ GitHub 星标i三大支柱:评价 · 检查 · 使用情况i更新 = 最新版本发布到注册中心的时间i
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.
按原文从作者的仓库复制。PopNx MCP 没有审查或背书其内容。HTML、图片和脚本已被移除。链接会离开本站。
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
只显示了开头部分;其余内容请在仓库中阅读