stuck-order-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@stuck-order-mcpinvestigate order #1042 for payment drift"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Payment/Webhook Reconciliation MCP
A remotely hosted MCP server that lets an ops engineer investigate, classify, and triage drift between an internal order/payment record and a payment provider's ground truth — and escalate confirmed drift for human review. It does not retry, void, capture, or refund anything automatically; every correction is applied by a person, outside this MCP.
Why this exists
Commerce backends keep two records of the same payment: the internal order/payment state, and the provider's ground truth, delivered via webhooks. These drift apart in practice — webhooks arrive late, get missed, or arrive out of order — so an order can end up marked unpaid when the provider actually captured funds, or paid when no capture ever completed. Today an ops engineer manually cross-references the internal DB, the provider dashboard, and raw webhook logs to spot this. This MCP encapsulates that investigation and classification logic directly, rather than exposing plain CRUD over the tables.
Related MCP server: Lucid Observability Agent
Scope
In: single-order investigation, fleet-wide drift triage (including cross-order duplicate-charge detection), an idempotent human-review escalation queue, a bearer-token gate, remote hosting on Vercel + Neon Postgres, synthetic seeded data.
Out (by design, not oversight): no frontend, no real payment-provider integration (provider data is simulated via seed data), no multi-tenant support, no accounts/login system (bearer token only — see Assumptions), and no tool that mutates payment/order state. This is the direction confirmed by the team: diagnosis and evidence gathering are in scope, automatic correction is not.
Architecture
Next.js 16 App Router, single route:
app/api/mcp/route.ts.mcp-handlerv2 (createMcpHandler) +experimental_withMcpAuthfor a bearer-token gate (Authorization: Bearer <MCP_BEARER_TOKEN>).Neon Postgres via
@neondatabase/serverless(HTTP-based driver, avoids connection-pool exhaustion in serverless functions).The diagnostic decisions live in Postgres, not in application code.
order_diagnoses(a view defined inschema.sql) computes the 5-way drift classification (clean,missed_capture,missed_refund,false_capture,false_refund) directly via aCASEexpression overorders,webhook_events, andprovider_charges, and pre-shapes the JSON bothget_order_snapshotandscan_fleet_for_driftreturn. Both toolsSELECTfrom this one view rather than each re-deriving the classification, and rather than pulling raw rows into the app to classify in TypeScript.Duplicate-charge detection is the same story:
lib/queries.tsholds the raw, parameterized SQL (DUPLICATE_CANDIDATES_FOR_ORDER_SQLfor the single-order case,DUPLICATE_PAIRS_SINCE_SQLfor the fleet-wide self-join) as the one place the 10-minute rule is enforced. Every row either query returns is already a confirmed match — same customer/amount/currency, distinct orders, withinDUPLICATE_CHARGE_WINDOW_MS— so there's no re-verification step left in application code, only presentation (toDuplicateEvidence, inlib/reconciliation.ts) and pair-level dedup.Because the diagnostic and duplicate-matching decisions now live in SQL, they can't be unit-tested as pure functions anymore — verifying them requires a real Postgres. See "Automated tests" below for how that's done without depending on the live deployed database.
lib/reconciliation.tskeeps only what's still genuinely pure: shared types, theDUPLICATE_CHARGE_WINDOW_MSconstant (imported by both the app and, transitively, the SQL call sites, so there's exactly one number rather than independently hardcoded copies), evidence formatting (toDuplicateEvidence), and idempotency-key construction (buildIdempotencyKey) — none of which is a diagnostic decision, so these stay unit-tested.lib/queries.tsexists specifically so the duplicate-charge SQL text is identical between production (executed via the Neon driver'ssql.query(text, params)) and the integration test suite (executed viapgagainst a local Postgres) — one string, two drivers, rather than the test suite re-typing an equivalent query that could silently drift from what actually runs in production.
Data model
Five tables (schema.sql):
orders— internal order/payment record (status:unpaid | paid | refunded).webhook_events— what our system actually recorded from provider webhooks. Can be missing an event even when the provider-side fact exists.provider_charges— the provider's ground truth, independent of whether a webhook for it was ever processed.reconciliation_actions— the human-review escalation queue, holding only the current state of each escalation (one row peridempotency_key, overwritten in place on re-escalation). Nothing here executes a payment operation.idempotency_keyis unique, so re-scanning and re-escalating the same drift updates the existing row instead of duplicating it.reconciliation_action_history— append-only, never updated or deleted. Beforeescalate_for_reviewreopens a previously-acknowledged escalation (overwritingstatus/acknowledged_at/evidenceon the row above), it archives that acknowledge+reopen cycle here first, so the fact that an escalation was acknowledged before — when, on what evidence, and when it was reopened — is never lost to a later re-diagnosis. Seeget_escalation_history.
These two tables are the only ones the MCP ever writes to — nothing in either executes a payment operation; both are pure record-keeping around the human-review process.
seed.sql seeds 7 orders covering 5 distinct drift types plus one clean
control case (see comments in the file for the exact scenario each order
represents): missed_capture, missed_refund, false_capture,
false_refund, and a duplicate_charge pair (two orders, same
customer/amount/currency, provider charges 5 minutes apart).
schema.sql also defines order_diagnoses, a view over these four tables
that computes the drift diagnosis and pre-shapes the JSON the MCP tools
return — see Architecture above.
MCP tools
Tool | Type | Description |
| read-only | Investigate one order: internal record, webhook events, provider charges, and a computed diagnosis ( |
| read-only | Given one |
| read-only | Triage orders from the last |
| write (idempotent) | Upserts a human-review record for a diagnosed drift. Never touches |
| read-only | Shows the current human-review queue, most recent first, capped by an optional |
| write (idempotent) | Marks an open escalation |
| read-only | Shows every past acknowledge+reopen cycle for an escalation from |
Setup
Create a Neon Postgres database and copy its connection string.
Copy
.env.exampleto.env.localand fill inDATABASE_URLandMCP_BEARER_TOKEN(any long random string you control).Apply the schema and seed data:
psql "$DATABASE_URL" -f schema.sql psql "$DATABASE_URL" -f seed.sqlnpm installnpm run dev(local) or deploy to Vercel with the same two environment variables set in the project settings.For the automated test suite, Docker is required (
npm testbrings up its own throwaway Postgres — see Automated tests below). No Neon credential is needed to run tests.
Connecting from Claude Code
Register the deployed server as a remote HTTP MCP server with a bearer-token header:
claude mcp add --transport http stuck-order-mcp \
"https://stuck-order-mcp.vercel.app/api/mcp" \
--header "Authorization: Bearer $MCP_BEARER_TOKEN"(swap the URL for http://localhost:3000/api/mcp to point at a local
npm run dev instance instead).
--scope local(the default) keeps the server private to you in this project; use--scope projectto check it into.mcp.jsonfor collaborators, or--scope userto make it available across all projects.Verify the connection with
claude mcp get stuck-order-mcp— it should reportStatus: ✔ Connected.MCP servers are loaded when a Claude Code session starts, so if you added it mid-session, start a new session (or restart) before its tools show up.
To remove it:
claude mcp remove stuck-order-mcp -s local.
Once connected, ask Claude Code to investigate an order (e.g. "check order ord_missed_capture for drift") or triage the fleet ("scan for drift in the last 24 hours") — see MCP tools below for the full list and Verifying the workflow end to end for the exact scenario each seeded order represents.
Verifying the workflow end to end
Against the deployed URL (or http://localhost:3000/api/mcp locally), using
any MCP-compatible client, or directly with curl against the Streamable
HTTP endpoint:
curl -X POST "$MCP_URL/api/mcp" \
-H "Authorization: Bearer $MCP_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_order_snapshot","arguments":{"order_id":"ord_missed_capture"}}}'Expected walkthrough:
get_order_snapshotonord_missed_capturereturns diagnosismissed_capture.scan_fleet_for_drift(defaultwindow_hours: 24) returns findings forord_missed_capture,ord_missed_refund,ord_false_capture,ord_false_refund, and theord_dup_1/ord_dup_2duplicate-charge pair — but nothing forord_clean. All seeded orders are timestamped within the last 20 hours specifically so they're visible under the default window without needing a widerwindow_hoursvalue.check_duplicate_chargeonord_dup_1returnsduplicate_found: trueagainstord_dup_2— the "customer says they were charged twice" workflow, answered directly for one order without a full fleet scan. It fetches candidate charges pre-filtered to the same customer/amount/ currency within a wide window and checks the 10-minute rule directly against them, so it doesn't need the fleet-wide scan either.get_order_snapshoton the same order returns its diagnosis only — duplicate-charge checking is deliberately not duplicated into that tool, sincecheck_duplicate_chargealready exists for it.escalate_for_reviewwith that evidence creates an open record; calling it again with the samedrift_type+order_idsupdates the same row rather than creating a second one (checklist_open_escalationsbefore and after — the count doesn't change on the second call).list_open_escalationsshows the escalation, closing the loop: diagnose → escalate → visible to ops.acknowledge_escalationwith that escalation'sidmarks itacknowledgedand stampsacknowledged_at; it then disappears fromlist_open_escalations(which only showsopenrecords). Callingacknowledge_escalationagain with the same id returns the existing record (already_acknowledged: true) instead of erroring or movingacknowledged_ata second time.If the same drift is diagnosed again later, calling
escalate_for_reviewwith the samedrift_type+order_idsreopens the acknowledged record (back toopen,acknowledged_atcleared) rather than leaving it invisible inlist_open_escalations— and archives the acknowledge+reopen cycle being overwritten intoreconciliation_action_historyfirst, soget_escalation_historyon that escalation'sidstill shows when it was acknowledged, on what evidence, and when it was reopened, even thoughreconciliation_actionsitself has already moved on to its new state.
Automated tests
npm testThis brings up a throwaway Postgres via docker-compose.test.yml, runs the
integration suite against it, runs the unit suite, then tears the container
down — self-contained, no live/deployed database credential required. (Docker
must be available; npm run test:db:up / npm run test:db:down manage the
container directly if you want it to stay up between runs, and
npm run test:integration / npm run test:unit run either suite alone.)
Integration tests (lib/reconciliation.integration.test.ts, run against
real Postgres via pg) are now the primary coverage, because the
diagnostic and duplicate-matching decisions live in SQL (see Architecture)
and can't be exercised as pure functions anymore. They apply schema.sql +
seed.sql plus a few extra boundary fixtures, then assert: all 5 drift types
classify correctly via order_diagnoses (plus the clean case), evidence
JSON references the right underlying charge, the duplicate-charge queries
find the seeded pair, a pair exactly 10 minutes apart still matches
(confirming the boundary is inclusive), a pair 10 minutes and 1 second apart
does not, and a currency mismatch never matches regardless of timing. Because
lib/queries.ts is imported directly rather than re-typed, these tests are
verifying the literal SQL text that runs in production, not an equivalent
reimplementation of it.
Unit tests (lib/reconciliation.test.ts) now only cover what's still
pure: toDuplicateEvidence's formatting, and that buildIdempotencyKey is
order-independent, which is what guarantees the database-level upsert
actually dedupes repeated escalations.
Key assumptions
The provider is simulated via seeded data rather than a real Stripe test account, to keep this self-contained and synthetic.
No accounts/user-management system: access is gated by a single shared bearer token, not per-user login. There is one actor (the ops engineer), so a single permission level is sufficient.
reconciliation_actionsrecords an escalation, never a correction — applying the actual fix (issuing a refund, re-triggering a capture, etc.) happens outside this system, by a human, using the provider's own tools.Duplicate-charge detection matches on customer + amount + currency within a 10-minute window.
scan_fleet_for_driftfinds it fleet-wide via a single indexed self-join directly in Postgres (idx_provider_charges_dup_lookup), independently of thelimit-bounded per-order diagnosis pass — the independence matters because a row-count cutoff, unlike a time-based one, can silently split a real duplicate pair across the boundary. Only rows that already match come back from the database; charges with no match are never pulled into app memory at all.check_duplicate_chargefinds it for one order the same way, by fetching only the charges that could plausibly match it, rather than requiring a fleet-wide scan —get_order_snapshotdeliberately doesn't duplicate that check, since a dedicated tool for it already exists. Both queries are a complete, exact implementation of the rule (not a wide prefilter re-verified in code) — see Architecture.The automated test suite never runs against the live/deployed Neon database. It uses a disposable local Postgres via
docker-compose.test.ymlinstead, so running tests never requires — or risks — a shared production credential.
Remaining risks / explicitly out of scope
No pagination on
scan_fleet_for_drift's per-order diagnosis pass or onlist_open_escalationsbeyond a simplelimit/window_hours— fine at this data volume, would need it at real scale.Putting the classification decision in a SQL view trades away pure-function unit testability for it (see Architecture) — that trade was made deliberately, but it does mean the
order_diagnosesCASE logic can now only be verified with a real Postgres connection (the integration suite), not with a fast, dependency-free unit test.acknowledge_escalationonly has two states (open→acknowledged); there is no "reopen" transition and no notion of who acknowledged it (no per-user auth exists to attribute it to — see Key assumptions). Fine for a single shared ops actor; would need an identity if this grew multi-user.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Alicense-qualityDmaintenanceMCP server for agent ecosystem analytics. Provides real-time health snapshots including transaction velocity and dispute rates, predictive analysis with confidence scoring, and pattern detection across multi-agent networks.Last updatedMIT
- Alicense-qualityDmaintenanceMCP server for monitoring, diagnosing, and auto-correcting observability issues across platform services, integrating with Sentry and PostgreSQL for error tracking and billing pipeline health.Last updatedMIT
- Flicense-qualityDmaintenanceMCP server for payment reliability tasks including retry strategy recommendation, payment failure simulation, billing guardrail validation, fee leakage detection, and incident summarization.Last updated
- Alicense-qualityBmaintenanceMCP server for verifying agent payment integrity on x402, providing tools to check payTo mismatches, market stats, and lookup endpoints.Last updated1MIT
Related MCP Connectors
A paid remote MCP for hosted MCP server, built to return verdicts, receipts, usage logs, and audit-r
A paid remote MCP for AI SDK data query MCP, built to return verdicts, receipts, usage logs, and aud
Remote MCP for A2A failure replay MCP, structured receipts, audit logs, and reviewer-ready evidence.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/SafalBhandari12/stuck-order-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server