The MCP server that can't see who you are | Building Products  

 [Mischa Sigtermans](https://mischa.sigtermans.me)

   Menu Close        [Thoughts](https://mischa.sigtermans.me/thoughts) [Books](https://mischa.sigtermans.me/books) [Consultancy](https://mischa.sigtermans.me/consultancy) [About](https://mischa.sigtermans.me/about)  

 [← Thoughts](https://mischa.sigtermans.me/thoughts)   June 24th, 2026  · AI 

The MCP server that can't see who you are
=========================================

I gave a case file with thousands of participants its own MCP server. The interesting design question wasn't which tools to expose. It was what the model must never see.

In June I gave [HW3 Claim](https://hw3claim.nl) its own MCP server. Five tools, one bearer token, and a rule that shaped everything else: the model doesn't get to know who anyone is.

The site collects Tesla owners with Hardware 3 who paid for Full Self-Driving. Thousands of people have signed up. Every one of them handed over a name, an email address, a country, a car and, if they had it, a purchase agreement. Working with that data through a chat window is exactly as unwise as it sounds. Working with it through a tool that decides what leaves the database is a different proposition.

The question everyone skips
---------------------------

Most MCP tutorials answer 'which tools should I expose?' That's the easy half. The models are good at using whatever you give them, so the tool list mostly writes itself: the things you already look up by clicking around.

The hard half is what the tool returns. An MCP server is a boundary, and a boundary is only worth having if something is on the other side of it.

For a case file, the thing on the other side is every participant's personal data.

Anonymous by construction
-------------------------

Every participant record passes through a presenter before it becomes a tool response. The comment at the top of that file is the whole policy:

> No personal data ever leaves here: no name, email, postal code, phone, VIN, IP, user agent, free-text notes or document filenames. The user\_id is the identifier.

So a participant looks like a numeric id, whether their email is confirmed, a country, a car and its build year, what they paid for FSD, how many documents they uploaded. Enough to answer 'how many participants in Germany bought FSD before 2020 and have a purchase agreement on file?'. Not enough to know which person that is.

If you need the person, you look the id up in the backend, where access is logged and the lookup is a deliberate act by a human. That extra step is the feature.

The server's instructions say it to the model directly, so it doesn't go hunting:

> The participant tools never expose personal data. Records are identified by a numeric user\_id only. To reach the real person, look the user\_id up in the backend.

One tool that breaks the rule, loudly
-------------------------------------

There's an exception, and hiding it would be worse than having it. Sometimes you have to read the actual document someone uploaded.

`get-document` returns the raw contents of one uploaded file: PDF text, an image, plain text. The purchase agreement has a name on it. There's no way to read evidence without reading evidence.

So the exception is declared in the same breath as the rule:

> The one exception is get-document, which returns the raw contents of an uploaded file including the personal data inside it. Use it only when you must read a specific piece of evidence.

One document at a time, by id, never in bulk. The tools that scale are blind. The tool that sees costs you a deliberate call.

Writing, but only one kind
--------------------------

Four tools read. One writes, and it writes exactly one thing: an internal note on a user, a vehicle or a document.

Notes are an append-only log, visible only in the backend and never to the participant. The instruction to the model is specific about how to treat it: read the current note first, append a new dated entry, and only replace text to correct an earlier mistake.

That's a small rule with a large effect. An agent that can rewrite history is a liability in anything that might one day be evidence. An agent that can only add a dated line is a colleague taking notes.

Nothing else writes. Nothing deletes. The model can annotate the case, and it cannot change a single fact anyone submitted.

The boring parts that matter
----------------------------

The rest is unglamorous and does most of the work.

The route sits behind a middleware that checks one bearer token from the environment. No token, no server. It's a private tool for one operator, not a public API.

Responses are encoded with [Laravel TOON](/thought/why-i-built-laravel-toon), the package I built for exactly this: a uniform list of records becomes a table with the column names written once instead of repeated per row. On a list of participants that's the difference between a comfortable answer and a context window full of repeated keys.

And the server tells the model how to read that format, in its own instructions, because a model that has to guess your encoding will guess wrong at the worst possible moment.

What a boundary is for
----------------------

I've written before that [an MCP server lets a model work with a system that already encodes the domain](/thought/generic-ai-is-a-commodity), instead of teaching it the domain in a prompt. That's still the reason to build one.

But when the data is people, the server is doing a second job. It's deciding, in code, reviewable and testable, what an AI is allowed to know. Not a policy in a document. Not a line in a system prompt asking nicely.

The tools are the easy part. The answer to 'what can it never see?' is the design.

 *thanks for reading*

Hi, I'm [Mischa](https://mischa.sigtermans.me/about). I've been *shipping products* and *building ventures* for over a decade. First exit at 25, second at 30. Now Partner &amp; CPO at [Ryde Ventures](https://ryde.ventures), an AI venture studio in Amsterdam. Currently shipping [Stagent](https://stagent.com), [TAP](https://tap.fm) and [Steddle](https://steddle.com). I [write](https://mischa.sigtermans.me/thoughts) about what I learn along the way. [More about me](https://mischa.sigtermans.me/about).

Keep reading: [Taylor is still immortal, but now he remembers](https://mischa.sigtermans.me/thought/taylor-is-still-immortal-but-now-he-remembers).

  [← Thoughts](https://mischa.sigtermans.me/thoughts) Connect
-------

  [X](https://x.com/mischamartijn) [LinkedIn](https://linkedin.com/in/mischasigtermans) [GitHub](https://github.com/mischasigtermans)  

 © 2026 Hold My Beer B.V. · [RSS](feed:https://mischa.sigtermans.me/feed)
