Command Palette

Search for a command to run...

UnylyUnyly
Весь каталог

Reach Dispute

БесплатноНе проверен

Provides read-only MCP tools to diagnose customer billing disputes by analyzing billing records, identifying contradictions, citing evidence, and scoring confid

GitHubEmbed

Описание

Provides read-only MCP tools to diagnose customer billing disputes by analyzing billing records, identifying contradictions, citing evidence, and scoring confidence to auto-resolve or escalate.

README

Read-only tools over the REACH billing system of record that diagnose customer billing disputes: find the contradiction behind the complaint, cite the exact rows it rests on, score confidence, and decide auto-resolve or escalate.

It is not a chatbot. It is a set of tools that plugs into Claude — or anything else that speaks MCP, including the DCAL team's existing chatbot.

The division of labour is the point. Deterministic Python finds the contradiction and sets the confidence score. The language model only turns that finding into a sentence a person can read. The model can lower confidence; it can never raise it.


Quick start

cd ~/Documents/Project/reach-dispute-mcp

.venv/bin/python check.py             # data layer          — 15 checks
.venv/bin/python check_mcp.py         # live MCP handshake  — 18 checks
.venv/bin/python check_detectors.py   # verdicts are right  — 28 checks

All three should end with all checks passed. That's 61 assertions and takes about three seconds — run them before demoing.

The server is already registered in ~/.claude.json. In Claude Code:

/mcp

reach-dispute should appear with 7 tools. If it doesn't, see Troubleshooting.


The 60-second demo

Two customers. That's the whole show — one the system can answer, one it knows it can't.

Setup (10s)

"This is an MCP server. Seven read-only tools over our billing data. Claude is just the client — the same tools could plug into the DCAL chatbot."

Run /mcp and show reach-dispute · 7 tools.

Case 1 — it can answer (25s)

Type:

Customer CUS-4471 says they paid but they're still suspended. What does the data show?

Watch it call get_customer_snapshot, list_ledger_transactions, then diagnose_dispute. Point at three things in the answer:

Point at Say
The evidence rows "Every claim traces to a row — table, key, field, value. It physically cannot cite a record it didn't read."
Confidence 0.91 "Java-style deterministic rules produced that number, not the model. The model can't change it."
auto_resolve "Payment landed 22 July, suspension was 20 July, reconnection never ran. Two records disagreeing."

Case 2 — it knows it can't (25s)

Type:

CUS-6614 says you charged them $10.50 twice. Did you?

"This is RTJSM-41555, a real ticket that took three days and four people."

Point at Say
Confidence 0.40 · escalate "It does not tell the customer they're wrong."
The reason "The second charge, if it exists, is in IPpay — which this server cannot read."
HPY202607021852176 "It hands L2 the transaction ID we do hold. Half-solved ticket instead of a cold start."

Close (5s)

"Same system, two customers. It correctly knows which one it can answer. Knowing where its knowledge stops is the product."


The five scenarios

Synthetic data. Real scenarios — each reconstructs the contradiction its ticket turned on.

Customer Mobile Scenario Modelled on Verdict
CUS-4471 5550142201 Paid, still suspended RTJSM-41687 0.91 · auto-resolve
CUS-2210 5550163344 Charged twice ($10.50) REACHTS-12017 0.93 · auto-resolve
CUS-8802 5550178899 Bill jumped to $335.84 RTJSM-40005 0.90 · auto-resolve
CUS-3390 5550119055 Promo credit not applied REACHTS-12005 0.86 · auto-resolve
CUS-6614 5550188120 Gateway mismatch RTJSM-41555 0.40 · escalate

Look them up by customer ID, mobile number, or email. Ask Claude "what dispute scenarios are available?" to list them.

CUS-8802 is a good second-choice demo — the bill decomposes exactly: 83.85 plan + 251.88 carried forward + 0.11 tax = 335.84, and it names the three failed payments that created the carry-forward. In the real ticket, everything needed to work that out was already in the description and nobody did the subtraction for seven days.


Pasting a real ticket

Claude extracts the parameters from ticket text. It needs:

  • Required — something identifying the customer: ID, mobile number, or email.
  • Helps a lot — the claimed amount and how many times, if the customer named them ("$10.50 twice"). This is what lets the rules detect "you say two, I see one" rather than just reporting what we hold.
  • Ignored — everything else. Harmless, just not used.

Running on fixtures, only the five customers above resolve. A real ticket needs REACH_DATA_SOURCE=dev and that customer to exist in the dev database.


How it works

Claude Code ──stdio──▶ server.py ──▶ tools/diagnose.py ──▶ data/provider.py
                        7 tools       4 detectors           fixtures | dev
                                      + decision gate

Six read tools fetch data. Each returns {data, evidence[]}, where evidence is the actual rows read as {table, key, field, value}.

diagnose_dispute is the seventh and the one that decides. Four detectors, each a comparison between two records:

Detector The contradiction
paid_but_suspended successful payment dated after lastSuspendedDate, no reconnect recorded
duplicate_charge two ledger rows, same amount and category, < 24h apart, distinct transaction ids
credit_not_applied valid unapplied credit while the bill shows no adjustment
bill_increase_explained bill decomposes exactly into plan + carry-forward + tax
claim_exceeds_our_records customer claims more charges than the ledger holds → unverifiable

The decision gate

Auto-resolve requires all of:

  • confidence ≥ 0.80
  • nothing flagged unverifiable
  • if the remedy moves money (refund, apply credit), amount ≤ $25

The money cap applies only to money-moving remedies. CUS-4471 is a $52.40 dispute and still auto-resolves, because the remedy is reconnect a line we wrongly suspended — that costs nothing and can't be the wrong call. Explaining a bill is likewise always safe.

The one-way lock

diagnose_dispute accepts a concern parameter. Supplying one caps confidence at 0.50 and forces escalation:

without concern:  0.91  → auto_resolve
with concern:     0.50  → escalate

It clamps with min(). There is no path that raises confidence.


Say these unprompted

Getting caught not saying them is far worse than saying them.

  1. The data is synthetic. The scenarios are real tickets — numbers are in the table above.
  2. It is not wired into JSM. You give it a customer, not a ticket. Deliberate: wiring is a day of plumbing that proves nothing about whether the diagnosis is right — and posting a comment to a live ticket would be a write, which breaks the read-only guarantee.
  3. No blanket time claim. In a 100-ticket sample the median was 2.45 days and 29% closed same-day. The claim is hours back on the 71% that don't, plus knowing whether the refunds we grant blind were actually owed.
  4. The fixtures and the detectors were written together, so of course they agree. This proves the wiring and the logic, not that detectors fire correctly on production data. One dev-database run is what settles that.

Read-only guarantees

Three layers, none of them "we promise":

  1. The interface has no write method. DisputeDataProvider declares six methods, all reads. There is nothing to call.
  2. Every tool advertises readOnlyHint=true in its MCP annotations — the host can see it, not just take the README's word.
  3. Evidence is read from the row, never passed alongside it. Citing a field that doesn't exist raises rather than emitting a blank.

Switching to real data

REACH_DATA_SOURCE=dev .venv/bin/python server.py

Or change env in the ~/.claude.json entry.

Working as of 11 Aug 2026. data/dynamo.py is implemented and the dev path is live. Run check_dynamo.py first — it verifies credentials, table names and index names, and tells you which layer is broken instead of dumping a botocore traceback.

Credentials are temporary session keys in ~/.aws/credentials with no SSO cache, so when they lapse they have to be re-pasted from the console — there is no aws sso login path. Export REACH_DEV_ACCOUNT_ID if you want the check to assert it reached the intended account. A read-only scoped role would be better than running this under admin.

Before pointing at dev, check whether that data is seeded or copied from prod — if it's a prod copy, real customer records land on screen during the demo.


Troubleshooting

reach-dispute missing from /mcp — restart Claude Code; the config is only read at startup. Then verify the entry in ~/.claude.json points at the venv python by absolute path.

Server won't start — run it directly and read stderr:

.venv/bin/python server.py

reach-dispute MCP server starting (data source: fixtures) then a wait for input is correct. It's a stdio server — there is no port to curl.

"No customer matches that identifier" — you're on fixtures and used an identifier outside the five above.

Checks fail after an editcheck_detectors.py names the scenario and the assertion. The fixtures and detectors are coupled by design; changing a fixture value will correctly break the detector test.


Layout

server.py             MCP server — tool registration and descriptions
data/
  provider.py         read-only contract; fixtures/dev switch
  records.py          Row and Evidence — the citation machinery
  fixtures.py         the five scenarios (data only, no logic)
tools/
  read_tools.py       the six read tools
  diagnose.py         four detectors + the decision gate
check.py              data layer          — 15 checks
check_mcp.py          live MCP handshake  — 18 checks
check_detectors.py    verdicts are right  — 28 checks

data/records.py is the smallest file and the load-bearing one: sixty-odd lines are the reason the model cannot cite a row it never read.


Environment

  • Python 3.12, isolated .venv — system Python untouched
  • mcp==2.0.0 (note: 2.0 uses MCPServer, not the 1.x FastMCP)
  • boto3 only needed for REACH_DATA_SOURCE=dev
  • No ANTHROPIC_API_KEY — Claude Code is the host and drives the loop

from github.com/hritiksahu/reach-dispute-mcp

Установка Reach Dispute

У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.

▸ github.com/hritiksahu/reach-dispute-mcp

FAQ

Reach Dispute MCP бесплатный?

Да, Reach Dispute MCP бесплатный — установка в пару кликов через Unyly без оплаты.

Нужен ли API-ключ для Reach Dispute?

Нет, Reach Dispute работает без API-ключей и переменных окружения.

Reach Dispute — hosted или self-hosted?

Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.

Как установить Reach Dispute в Claude Desktop, Claude Code или Cursor?

Открой Reach Dispute на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.

Похожие MCP

$5

Stripe

Payments, customers, subscriptions

Stripeавтор: Stripe

malamutemayhem/unclick-agent-native-endpoints

110+ tools for AI agents spanning social media, finance, gaming, music, AU-specific services, and utilities. Zero-config local tools plus platform connectors. n

malamutemayhemавтор: malamutemayhem

whiteknightonhorse/APIbase

Unified API hub for AI agents with 56+ tools across travel (Amadeus, Sabre), prediction markets (Polymarket), crypto, and weather. Pay-per-call via x402 micropa

whiteknightonhorseавтор: whiteknightonhorse

trackerfitness729-jpg/sitelauncher-mcp-server

Deploy live HTTPS websites in seconds. Instant subdomains ($1 USDC) or custom .xyz domains ($10 USDC) on Base chain. Templates for crypto tokens and AI agent pr

trackerfitness729-jpgавтор: trackerfitness729-jpg

embeddedlayers/mcp-analytics

Statistical analysis, forecasting, and ML for business data (Shopify, Stripe, WooCommerce, eBay, GA4, Search Console). Upload a CSV or connect live data sources

embeddedlayersавтор: embeddedlayers

carrierone/verilexdata-mcp

20 structured datasets (NPI healthcare, SEC filings, OFAC sanctions, crypto whales, Polymarket signals, patents, economic indicators) via x402 pay-per-query wit

carrieroneавтор: carrierone

tipdotmd/tip-md-x402-mcp-server

MCP server for cryptocurrency tipping through AI interfaces using x402 payment protocol and CDP Wallet.

tipdotmdавтор: tipdotmd

laundromatic/shopgraph

Structured product data from the open web — Schema.org + AI extraction for e-commerce enrichment. Pay per call via Stripe. [shopgraph.dev](https://shopgraph.dev

laundromaticавтор: laundromatic

mrslbt/xendit-mcp

Xendit payment gateway for Southeast Asia. Invoices, disbursements, balance checks, and bank transfers across Indonesia, Philippines, Thailand, Vietnam, and Mal

mrslbtавтор: mrslbt

@arbitova/mcp-server

Non-custodial on-chain escrow + AI dispute arbitration for agent-to-agent USDC payments on Base. Seven tools covering the full EscrowV1 contract surface: create

jiayuanliang0716-maxавтор: jiayuanliang0716-max

Compare Reach Dispute with

Не уверен что выбрать?

Найди свой стек за 60 секунд

Автор?

Embed-бейдж для README

Похожее

Все в категории finance