havenware-support-mcp
by adwaitasathe
README.md
# havenware-support-mcp
Working code for the InfoQ article **Building Privacy-Aware MCP Servers: An
Engineering Runbook**.
A customer-facing support agent over a fictional home-goods retailer, built
twice. `before/` is a competent thin wrapper over an existing internal REST API.
`after/` is the same server with the article's six-stage runbook applied. The
internal API does not change between them, because in practice it can't.
Every number quoted in the article is produced by `harness/measure.py` against
these two servers, not estimated.
```
Rows returned 25 -> 2
Distinct fields in context 49 -> 9
Third-party subjects exposed 2 -> 0
Payload into model context 22,214 -> 453 bytes
Bytes written to logs 22,305 -> 445
Log contains response payloads yes -> no
Health disclosure in context yes -> yes
```
That last row is deliberate. The customer typed the disclosure herself, so no
server-side control prevents it crossing. What changes is that it stops
accumulating in logs and tickets.
## Quick start
```bash
pip install -r requirements.txt
make seed # build the deterministic fixture database
make classify # Stage 1: every reachable column is classified (62 of 62)
make demo # the before/after measurement above
make canary # Stage 6: the canary PII suite (11 tests)
```
Python 3.12. No accounts, no API keys, no network access required.
To point an MCP client at either server:
```bash
python -m before.server # the baseline
python -m after.server # after the runbook
```
Both bind one fixture customer as the session subject so the tools can be
exercised by hand. A real deployment resolves that from the chat widget's
verified session.
## Where each runbook stage lives
| Stage | Article section | Code |
|---|---|---|
| 1. Classify what your server can touch | 5 | `after/classification.py` |
| 2. Scope tools to purposes | 6 | `after/server.py`, `session.py` |
| 3. Minimize every response | 7 | `after/shaping.py` |
| 4. Guard the model boundary | 8 | `after/boundary.py` |
| 5. Sanitize logs, set retention | 9 | `after/audit.py` |
| 6. Verify with canary tests | 10 | `tests/test_canary.py` |
Other files:
- `internal_api.py` — the REST API that predates MCP. Identical for both
servers. It was a correct answer to the question it was built for.
- `before/server.py` — three entity-shaped tools and one line of payload
logging. Session identity is already correct here; the leak is not an
authorization bug.
- `harness/measure.py` — runs the same customer task against both and prints
the comparison.
- `figures/` — the article's diagrams as SVG.
## The task everything measures
One ticket, one sentence:
> Where's my order? It was supposed to arrive Friday and I need it before my
> mother's surgery on the 14th.
Answering it needs three things: the shipment status on one order, the promised
date, and the customer's first name.
## CI
`.github/workflows/ci.yml` runs the article's own argument as build gates. The
build fails if a reachable column has no classification entry, if a canary
marker reaches tool output, a log, or an exception message, or if a tool ships
without a declared output schema.
## A note on the data
Every customer, order, ticket and address in `fixtures/` is synthetic and
generated from a fixed seed. Canary records use addresses at `1 Canary Row`,
phone numbers in the reserved `555-01xx` range, and `example.invalid` email
addresses, so they cannot route anywhere. No real personal data appears in this
repository.
Havenware does not exist. The failure patterns do.
## Licence
MIT. See `LICENSE`.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues