Read-only by default
At Kiin Intelligence we plug LinkedIn Ads data into Claude, so you can ask questions about your ad accounts instead of clicking through Campaign Manager for twenty minutes.
Every tool on that server is a read. There is no pause_campaign, no update_budget, no create_ad. Not because we couldn’t build them. Because nobody wants to find out their
assistant has permission to spend money.
What the constraint buys you
The obvious win is that the worst-case failure is a wrong answer instead of a wrong invoice. That alone is worth it.
The less obvious win is that it makes the product explainable. When someone asks what happens if the model hallucinates, the answer is one sentence: it says something incorrect and you disagree with it. No qualifiers. No “well, there’s a confirmation step.”
A confirmation step is a bet that a human reads a modal carefully at 5pm on a Friday. I would not take the other side of that bet about myself.
The hard part is underneath
People assume the difficulty of an MCP server is the MCP part. It isn’t. The protocol is JSON-RPC over HTTP with a schema for each tool. You can learn it in an afternoon.
The difficulty is that the data underneath has opinions. Fields that mean one thing in one endpoint and something subtly different in another. Metrics that only reconcile if you aggregate in a specific order. Rate limits that arrive exactly when a user is watching a demo. Date ranges where the API quietly disagrees with the dashboard about what “last 30 days” means, and the dashboard is what the client will compare against.
None of that is in the protocol. All of it is the job.
Tools are an interface, and interfaces need editing
The other thing I underestimated: tool descriptions are product copy. They’re read by a model that has to decide, with no other context, whether this is the right tool.
Vague descriptions produce a model that picks the wrong tool confidently. Overlapping descriptions produce a model that picks a different tool each time you ask the same question. Both look like the model being dumb and are actually me being unclear.
I now write descriptions as instructions to a competent new colleague on their first day. Use this when. Don’t use this for. Here’s what “account” means here specifically.
That rewrite fixed more bad answers than any change to the data layer.