# Notes on MCP servers

> The protocol is the easy part. The discipline is not rebuilding your product inside the server — keep it a thin proxy over the system you already run.

I've built a few MCP servers — two for an AI-visibility platform and one over a content pipeline. The protocol is the easy part. The discipline is not rebuilding your product inside the server.

## One tool, one job

A tool should do one thing, and its description should say exactly what. The model reads the description and little else before it decides to call you, so vagueness there comes back as wrong calls.

```ts
export const tool = {
  name: 'get_run',
  description: 'Fetch one agent run by id, with its steps and trace.',
  inputSchema: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },
};
```

## Reads cheap, writes explicit

Keep reads and writes separate and label them. A read can run on its own. Anything that spends money or changes state is a write: give it its own tool and mark it in the schema, not in a comment the model won't read.

| Kind  | Example          | Confirmation |
| ----- | ---------------- | ------------ |
| Read  | `get_run`        | none         |
| Write | `cancel_run`     | required     |

## A proxy, not a second implementation

The common mistake is rebuilding your logic inside the server. The one I trust is a thin proxy over the real service: the same endpoints, the same durable runs, the same auth and cost ceiling. The client gets the capability without a second copy of the product to keep in sync. Access control and anything that spends money stay on the service, where they already live.

## Closing

The protocol is small by design. The work is exposing a system you already run, honestly, and leaving that system as the one place the logic lives.
