Skip to main content
Glama
README.md
# claims-mcp-server

A secure, read-only Model Context Protocol (MCP) server over a synthetic health-insurance claims SQLite database.

## What and Why

Meridian Health TPA processes about 3,000 health insurance claims a day and internal operations teams want AI assistants to query claims data. Under strict security and compliance rules, **no AI agent is ever granted direct database access**, and every access requires an immutable audit trail. This repository provides the controlled layer in between: a read-only MCP server exposing five parameterized tools with authentication, per-tool authorization, rate limiting, untrusted text sanitization, and fail-closed audit logging.

## Quickstart

Get running in three simple commands:

```bash
# 1. Install dependencies
uv sync

# 2. Seed synthetic database and generate an API key
uv run python scripts/seed_db.py --out data/claims.db && uv run python scripts/make_key.py --id analyst-1 --tools all

# 3. Start the server over HTTP
uv run python -m claims_mcp --transport http
```

To run the demo client in a separate terminal:
```bash
uv run python scripts/demo_client.py --key <PLAINTEXT_KEY_FROM_STEP_2>
```

Alternatively, run locally with stdio (for local inspectors and dev):
```bash
uv run python -m claims_mcp --transport stdio
```

## Architecture

```
MCP Client (AI agent / demo_client.py / inspector)
        |  stdio  or  streamable HTTP (127.0.0.1, Bearer key)
        v
+-----------------------------------------------------------+
| server.py  (MCPServer, 5 tools, handlers are plain `def`)  |
|                                                           |
|  1 authenticate -> key_id        (security.py)            |
|  2 rate limit per key_id         (security.py)            |
|  3 authorize tool for key_id     (security.py)            |
|  4 validate inputs               (models.py)              |
|  5 open read-only connection     (db.py)  one per call    |
|  6 run fixed parameterized SQL   (queries.py)             |
|  7 build output, sanitize text   (models.py, security.py) |
|  8 write audit line, FAIL CLOSED (audit.py)               |
|  9 return result                                          |
+-----------------------------------------------------------+
        |                                  |
        v                                  v
   claims.db (read-only)           audit.jsonl (append-only)
```

## Tools Exposed

1. `get_claim(claim_id)`: Fetches full claim record joined with provider network status and member plan.
2. `list_member_claims(member_id, status=None, limit=20, offset=0)`: Paginated claim history ordered newest first.
3. `get_coverage_rule(plan_id, procedure_code)`: Policy coverage rules, prior authorization flag, and fee limits.
4. `get_member_ytd_summary(member_id, year)`: Aggregations by status, deductible, annual limit, and remaining limit.
5. `get_claim_notes(claim_id, limit=10)`: Clinical and administrative notes, sanitized and flagged `untrusted: true`.

## Security Summary

- **Read-Only Database Layer (SPEC S1):** Triple defense using SQLite URI `mode=ro`, `PRAGMA query_only = ON`, and a strict SQLite Authorizer that blocks all writes, mutations, table creation, and PRAGMA modifications.
- **Strict Parameterization (SPEC S2, S3):** 100% parameterized SQL with `?` placeholders. Zero string interpolation, verified by an automated AST scanner in CI.
- **Hashed Authentication (SPEC S4):** Static bearer keys hashed at rest with SHA-256 in `keys.json` and verified with constant-time comparison (`hmac.compare_digest`).
- **Tool Authorization & Rate Limiting (SPEC S5, S6):** Per-key allowlists and fixed-window rate limiting.
- **Untrusted Text Sanitization (SPEC S7):** Strips control characters, zero-width spaces, and bidi override tags; caps text at 1,000 characters; and marks output `untrusted: true`.
- **Fail-Closed Audit Logging (SPEC S9, S10):** Append-only JSONL log with zero payload or sensitive note leakage. If writing to the audit log fails, the request fails closed and returns no data.
- **Intercepted Validation (SPEC S13):** Eliminates raw input echoing on validation errors while maintaining complete typed JSON schemas for clients.

For detailed security mitigations and STRIDE threat analysis, read [THREAT_MODEL.md](THREAT_MODEL.md).

## Known Limitations

- **Adversarial Text Influence:** A model reading `get_claim_notes` can still be influenced by hostile text. Labeling data as untrusted is not enforcement; the real mitigations are read-only isolation, absence of write tools, and human-in-the-loop workflows in Project B.
- **Localhost HTTP Only:** Plaintext HTTP (no TLS) intended for localhost or container-mesh deployment behind an external TLS reverse proxy.
- **Single Process In-Memory Rate Limiting:** Rate limits are stored in memory and reset if the process restarts.
- **Manual Key Rotation:** API keys are rotated by updating `keys.json`.
- **Audit File Integrity:** The append-only JSONL log is not cryptographically signed or tamper-evident.