Claimbite
Redline illegal CPT unbundling, duplicate facility fees, and overlapping OEM labor on an Alexa+ tablet — then stop the billing portal's prompt-pay trap over MCP 2025-11-25 before a single dollar moves.
Quick Start · Live Screenshots & Video · The 3 Bills · Prompt-Pay Elicitation · Wire Protocol · Friction Log · 2-Min Demo Script
![]()
Live Walkthrough
Recorded demo video: docs/assets/demo.mp4 (2:30, narrated 1440×900). The first minute is the backstory: the $3,480 ER bill, the 15% prompt-pay waiver, and why an auto-click loses $768. The rest is the live tablet — MetroHealth redlines, the elicitation/create pause, confirmation DSP-90421, then Apex Auto and Northline.
1. Deterministic Redline ($3,480.00 → $2,190.00, −$1,290.00)

2. Mid-Stream elicitation/create on the 15% Prompt-Pay Trap ($2,958.00 vs $2,190.00)

3. Filed Clerical Dispute (DSP-90421) & Portal Receipt

The Problem
Hospital, auto-repair, and contractor invoices fail in the same clerical ways:
- Unbundled procedures — billing a routine blood draw (
CPT 36415) separately on the same claim as an emergency department evaluation (CPT 99284) that already includes it. - Duplicate revenue or call-out codes — stamping two emergency room facility fees (
Rev 0450) or two diagnostic call-outs (CALL-OUT) on the same visit. - Overlapping flat-rate labor — charging full book hours to replace a water pump and thermostat while the front of the engine is already torn down for a timing belt.
- Prompt-pay dark patterns — when you open the billing portal to dispute those lines, it offers an instant 15% "pay today" discount whose fine print waives your right to a coding audit.
Voice alone is terrible at reading a 12-line invoice aloud. An ungrounded LLM is terrible at arithmetic and statutory citations. And an autonomous agent that clicks "Accept 15% discount" without asking costs you $768.00.
Claimbite splits the job:
- Voice stays short — one sentence for the net overcharge, one sentence when the portal demands a human decision.
- The tablet holds the math — live line-by-line redlines, integer-cent totals, and tap-to-inspect statutory/OEM citations backed by
node:sqlite. - MCP
elicitation/createguards the money — when the portal presents a prompt-pay settlement, the server pauses mid-stream and waits for a signed JSON-RPC response from the tablet.
Quick Start
Requires Node.js 22+ (uses built-in node:sqlite — zero native C++ build steps).
git clone https://github.com/brian-mwirigi/claimbite.git
cd claimbite
npm install
npm test
npm run dev
npm run dev boots the Claimbite MCP server (:3001), the provider billing portal (:4000), and the Alexa+ tablet host (:5173) in one terminal.
| Surface | Local URL | What it does |
|---|---|---|
| Alexa+ Tablet & Wire Inspector | http://127.0.0.1:5173 |
Echo-style Alexa+ Dynamic Tab on the left; live MCP 2025-11-25 JSON-RPC / SSE inspector + embedded portal on the right |
| MCP Streamable HTTP Endpoint | http://127.0.0.1:3001/mcp |
Single POST /mcp endpoint (MCP-Session-Id, MCP-Protocol-Version: 2025-11-25, SSE progress + elicitation) + GET /.well-known/oauth-protected-resource |
| Provider Billing Portal | http://127.0.0.1:4000 |
Self-hosted MetroHealth, Apex Auto, and Northline portal serving the 15% prompt-pay trap and confirmation receipts |
Want to drop your own file onto the tablet? Use Drop receipt with examples/metrohealth.json, examples/apex-auto.json, or examples/northline.json.
Architecture
The highlighted slice is the product. MetroHealth bills $3,480. The portal's 15% offer is $2,958. After 57 integer-cent rules the real bill is $2,190. Accepting the discount still overpays by $768 and waives the audit. MCP 2025-11-25 elicitation/create holds that slice until a person answers.

| Choice | Confirmation | What you pay |
|---|---|---|
| Reject the trap and file the audit | DSP-90421 |
$2,190 |
| Accept the 15% offer | SET-22910 |
$2,958 |
The wire is five steps: the Alexa+ tab, POST /mcp with SSE progress, the rule engine (41 NCCI edits, 9 OEM labor overlaps, 7 padding policies), elicitation/create, then the portal. A client that does not advertise elicitation never reaches the portal. The same gate covers Apex Auto ($1,840 → $1,302) and Northline ($2,700 → $2,400).
sequenceDiagram
autonumber
participant User as Person at Tablet
participant Web as Alexa+ Tablet Host (apps/web)
participant MCP as Claimbite MCP Server (apps/mcp)
participant DB as Deterministic Rules + node:sqlite
participant Portal as Provider Billing Portal (apps/portal)
User->>Web: Tap "Hospital bill" (or drop metrohealth.json)
Web->>MCP: POST /mcp (initialize -> MCP-Session-Id)
Web->>MCP: POST /mcp (tools/call audit_itemized_invoice)
MCP->>DB: Run NCCI + duplicate + labor + stated-total checks
loop Each invoice line
MCP-->>Web: SSE notifications/progress (line status)
end
MCP-->>Web: Tool result ($3,480.00 -> $2,190.00, -$1,290.00)
User->>Web: Tap flagged row (CPT 36415)
Web->>MCP: POST /mcp (tools/call inspect_bundling_citation)
MCP->>DB: Lookup NCCI-PTP-99284-36415
MCP-->>Web: Statutory citation rendered under bill
User->>Web: Tap "Dispute $1,290.00 overcharge"
Web->>MCP: POST /mcp (tools/call execute_portal_dispute)
MCP->>Portal: GET /claims/8841/offer (15% prompt-pay = $2,958.00)
MCP-->>Web: SSE notifications/progress (open /claims/8841/settle)
MCP-->>Web: SSE request elicitation/create (Dispute $2,190 vs Pay $2,958)
Note over Web,MCP: Tool stream stays open while waiting for human choice
User->>Web: Tap "Reject trap & file $1,290.00 dispute"
Web->>MCP: POST /mcp (JSON-RPC response to elicitation id)
MCP->>Portal: POST /claims/8841/settle { decision: "dispute" }
Portal-->>MCP: { confirmationId: "DSP-90421" }
MCP-->>Web: Tool result (DSP-90421, saved $1,290.00, checkpoint written)
The Three Bills (Deterministic Math)
Every cent in Claimbite is an integer (348_000 = $3,480.00). An LLM never invents a price, a code, or a rule ID.
1. Hospital Emergency Visit — MetroHealth (Claim 8841)
| Line ID | Code / Rev | Description | Billed | Audit Status | Rule Triggered |
|---|---|---|---|---|---|
eval |
CPT 99284 |
Emergency department evaluation | $1,400.00 |
Kept | — |
draw |
CPT 36415 |
Routine venipuncture | $410.00 |
Flagged | NCCI-PTP-99284-36415 |
facility |
Rev 0450 |
Emergency room facility fee | $790.00 |
Kept | — |
facility-2 |
Rev 0450 |
Emergency room facility fee | $880.00 |
Flagged | DUP-REV-0450 |
| Total | $3,480.00 |
$2,190.00 recommended |
-$1,290.00 overcharge |
2. Timing-Belt Repair — Apex Auto (Claim RO-441)
| Line ID | Code | Description | Hours | Billed | Audit Status | Rule Triggered |
|---|---|---|---|---|---|---|
belt |
OEM-TB-45 |
Timing belt replacement | 4.5h |
$1,100.00 |
Kept | — |
pump |
OEM-WP-24 |
Water pump | 2.4h |
$146.00 |
Flagged | OEM-TIMING-WATER-PUMP |
stat |
OEM-TH-28 |
Thermostat housing | 2.8h |
$392.00 |
Flagged | OEM-TIMING-THERMOSTAT |
shop |
— | Shop supplies | — | $202.00 |
Kept | — |
| Total | $1,840.00 |
$1,302.00 recommended |
-$538.00 overcharge |
3. HVAC Condenser — Northline Mechanical (Claim Q-19)
| Line ID | Code | Description | Hours | Billed | Audit Status | Rule Triggered |
|---|---|---|---|---|---|---|
callout |
CALL-OUT |
Diagnostic call-out | 1.0h |
$150.00 |
Flagged | OEM-REPAIR-INCLUDES-DIAG |
unit |
— | Condenser replacement | 6.0h |
$2,400.00 |
Kept | — |
callout-2 |
CALL-OUT |
Diagnostic call-out | 1.0h |
$150.00 |
Flagged | DUP-CODE-CALL-OUT |
| Total | $2,700.00 |
$2,400.00 recommended |
-$300.00 overcharge |
More than the three demo bills
The tablet is not limited to the three fixtures.
- Enter bill accepts a pasted receipt, a dropped
.txt/.csv/.pdf/.jsonfile, a photo, ordescription | code | dollars. Photos are read from embedded text, Amazon Nova on Bedrock whenAWS_REGIONandAWS_ACCESS_KEY_IDare set (BEDROCK_MODEL_IDdefaults toamazon.nova-lite-v1:0), then local Tesseract. CPT codes, revenue codes, OEM codes, hours, and dollar amounts are read off the line. - Any audited bill can be filed on the portal. Claimbite registers the billed and recommended cents, then pauses on the same 15% prompt-pay elicitation. The three demo claims still confirm as
DSP-90421. - Click the cyan orb to speak: “hospital bill”, “auto repair”, “contractor”, “file the dispute”, or “take the offer”. The tablet reads the line aloud when speech recognition is missing.
- Prompt-pay math sits on the decision card: billed total, the 15% offer, and the audited balance, including how much the offer still overpays.
- Upcoding (
EM-UPCODE-LEVEL) reduces an emergency visit when the line names a lower level than the code billed. Only the extra levels are redlined. - Rule catalog seeded in
node:sqlite: 41 NCCI pairs (venipuncture, pulse oximetry, capillary puncture, specimen handling, surgical trays), 9 OEM overlaps (water pump, thermostat, diagnostic call-out, tensioner, idler, brake rotor, oil filter, refrigerant recovery), and 7 padding policies (shop-supply cap, parts markup, after-hours surcharge, one E/M level per visit, and emergency upcoding).list_rule_catalogreturns the live counts. - Dispute packet (
generate_dispute_packet) writes a plain-text demand letter from the latest audit.synthesize_appeal_briefis the second stage: Amazon Nova on Bedrock rewrites that letter whenAWS_REGIONandAWS_ACCESS_KEY_IDare set, and otherwise returns the same deterministic letter. - Visit note (
ingest_visit_summary, or Check 10-min doctor note (−$700) on the tablet) compares a spoken summary such as “level 2 emergency visit” with the billed E/M code. Only the extra levels are redlined. The three demo bills stay at their published totals until you run that step. - Household ledger persists each audit in
data/household.sqlite. A later MCP session can callget_household_ledger, and the tablet renders that ledger as a Household Guard carousel.ui://claimbite/audit-cardandui://claimbite/household-carouselare MCP App resources.
The three demo totals above do not move on their own. A visit note is a separate step, and it is the only path that redlines an emergency level already printed on those bills.
The 15% Prompt-Pay Trap & MCP elicitation/create
On Claim 8841, the provider portal offers to close the $3,480.00 bill immediately for $2,958.00 (a 15% discount of $522.00) if the patient waives a coding review.
Because the NCCI + duplicate dispute lowers the true balance to $2,190.00, accepting the portal's "deal" overpays by $768.00.
When execute_portal_dispute hits /claims/8841/offer, the MCP server emits a standard MCP 2025-11-25 server-to-client request on the active SSE stream:
{
"jsonrpc": "2.0",
"id": 1,
"method": "elicitation/create",
"params": {
"mode": "form",
"message": "MetroHealth offered $2,958.00 to close claim 8841. The coding dispute leaves $2,190.00.",
"requestedSchema": {
"type": "object",
"properties": {
"decision": {
"type": "string",
"title": "Decision",
"oneOf": [
{ "const": "dispute", "title": "File the full dispute" },
{ "const": "settlement", "title": "Take the prompt-pay offer" }
]
}
},
"required": ["decision"]
}
}
}
The browser client (packages/mcp-client) reads SSE chunks incrementally via ReadableStream.getReader(), renders the decision card on the tablet, and POSTs the JSON-RPC result back on the same MCP-Session-Id before the tool stream finishes:
- Reject trap & file $1,290.00 dispute → files the clerical dispute, returns confirmation
DSP-90421, saves$1,290.00, and writesdata/checkpoints/DSP-90421.json. - Take 15% offer ($2,958.00) → accepts the prompt-pay discount and returns confirmation
SET-22910. - Cancel → sends
action: "decline"and stops without touching the portal. - Fail-closed for non-eliciting clients → if
initializeomitscapabilities.elicitation,execute_portal_disputerefuses to touch the portal (isError: true).
MCP 2025-11-25 on the Wire
Because @alexa-ai/cli is currently partner-gated, Claimbite ships @mcp-apps/streamable-ui-kit — a clean-room MCP 2025-11-25 Streamable HTTP host built on @modelcontextprotocol/sdk's WebStandardStreamableHTTPServerTransport — and displays every JSON-RPC frame live next to the tablet. Object tool results include structuredContent, tools carry safety annotations (readOnlyHint on audits, destructiveHint on portal execution), and GET /.well-known/oauth-protected-resource serves RFC 9728 protected-resource metadata.
Registered MCP Tools, Prompts & Resources (apps/mcp/src/server.ts)
| Surface | Input | Behavior |
|---|---|---|
server_info |
{} |
Returns { name: "claimbite", protocolVersion: "2025-11-25" } |
stream_progress |
{ steps?: 1..5 } |
Streams notifications/progress frames over SSE before returning |
get_sample_invoice |
{ id: "metro-8841" | "apex-441" | "north-19" } |
Returns the typed sample invoice fixture |
audit_itemized_invoice |
{ invoice } |
Validates with Zod, runs deterministic rules (readOnlyHint: true), streams per-line progress, returns AuditReport |
inspect_bundling_citation |
{ ruleId } |
Queries the latest report + node:sqlite for the statutory or OEM citation |
list_rule_catalog |
{} |
Returns the seeded counts (41 NCCI edits, 9 OEM overlaps, 7 padding policies) |
generate_dispute_packet |
{} |
Builds a plain-text demand letter from the latest audit |
read_receipt_image |
{ dataBase64, mimeType, provider?, claimNumber?, kind? } |
Reads receipt text from PNG/JPEG metadata, Amazon Nova on Bedrock (amazon.nova-lite-v1:0), or local Tesseract |
execute_portal_dispute |
{ claimNumber, provider?, billedCents?, recommendedCents? } |
Fetches the portal offer (destructiveHint: true), pauses on elicitation/create (or fails closed), posts the choice, writes a checkpoint, and updates the household ledger |
get_household_ledger |
{} |
Returns audits and settlements stored in node:sqlite so a later MCP session still sees them |
ingest_visit_summary |
{ summary } |
Compares a visit note with the billed emergency level and redlines EM-UPCODE-LEVEL when the note names a lower level |
synthesize_appeal_brief |
{} |
Second Bedrock stage: Amazon Nova rewrites the dispute packet when AWS credentials are set; otherwise returns the deterministic letter |
audit-household-bill |
prompt ({ provider? }) |
Prompt template instructing the model to audit the bill and pause before accepting a prompt-pay settlement |
ui://claimbite/audit-card |
resource | Alexa+ card for the latest audit |
ui://claimbite/household-carousel |
resource | Alexa+ carousel of household claims remembered across sessions |
Try the MCP Server from a Terminal
With npm start (or npm run dev) running:
curl -i -X POST http://127.0.0.1:3001/mcp \
-H "Accept: application/json, text/event-stream" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{"elicitation":{"form":{}}},"clientInfo":{"name":"curl","version":"0.1.0"}}}'
The response headers include mcp-session-id, which subsequent requests pass alongside mcp-protocol-version: 2025-11-25.
Monorepo Layout
claimbite/
├── apps/
│ ├── mcp/ # Claimbite MCP server, dispute runner, JSON checkpoints
│ ├── portal/ # Self-hosted MetroHealth / Apex Auto / Northline billing portal
│ └── web/ # Alexa+ tablet UI, embedded portal pane, live JSON-RPC wire inspector
├── packages/
│ ├── streamable-ui-kit/ # Reusable MCP 2025-11-25 Streamable HTTP host + browser SSE parser
│ ├── rules/ # Integer-cent money, NCCI/duplicate/labor rules, Zod schema, node:sqlite store
│ └── mcp-client/ # Incremental SSE client capable of mid-stream elicitation/create replies
├── examples/ # Uploadable JSON invoices (metrohealth.json, apex-auto.json, northline.json)
├── docs/
│ ├── DEMO_SCRIPT.md # Timestamped walkthrough matching the live UI
│ ├── FRICTION_LOG.md # Real bugs hit building MCP 2025-11-25 Streamable HTTP (FL-1..FL-4)
│ └── TRY.md # Raw curl session handshake
└── .kiro/skills/ # Kiro skill spec for Alexa+ MCP 2025-11-25 Streamable HTTP
Verification
npm run typecheck # Root TypeScript + @claimbite/web TypeScript
npm test # 52 unit + integration tests across rules, portal, SSE kit, web, and MCP flows
npm run verify:mcp # MCP 2025-11-25 scorecard: session, Origin 403, elicitation pause, fail-closed gate, demo totals
npm run build -w @claimbite/web # Production Vite bundle
License
MIT © brian-mwirigi