Designing MCP tools that earn their keep
Most MCP servers expose a database. The useful ones expose the operations a business actually performs — and that distinction decides whether an assistant is trustworthy.
The quickest way to build a Model Context Protocol server is to wrap your tables:
list_orders, update_order, delete_order. It demos well and it is almost
always the wrong shape.
Model operations, not rows
A support agent does not "update an order". They resolve a refund, which means
checking eligibility, writing a ledger entry, notifying the customer, and leaving
an audit trail. If the tool is update_order, the model has to invent that
sequence every time, and it will invent it differently every time.
// Wrong shape: the model has to know your invariants.
updateOrder({ id, status: 'refunded', refundAmount: 4200 });
// Right shape: the invariants live in the tool.
refundOrder({ id, amount: 4200, reason: 'damaged_on_arrival' });
The second version can enforce the refund window, reject an amount above what was captured, and be idempotent on retry. None of that is achievable when the tool is a thin passthrough.
Scope every tool separately
A single "read-write access to production" token is not a permission model. Each tool gets its own scope, and the transport carries a short-lived credential:
catalog.search— read only, no auth beyond tenancycontent.publish— write, requires an editor roleorder.refund— write, capped by amount, always audited
Measure before and after
Once a workflow is live, a model upgrade is a production change. Keep an evaluation set of real requests with known-good outcomes so you can answer the only question that matters: did this get better or worse?