Why This Matters
The off-the-shelf MCP servers (Filesystem, GitHub, Postgres, Sentry) cover maybe ten percent of the tools your team actually uses. The other ninety percent are internal: a billing system, a deploy script, an in-house CRM, a feature-flag service. If you want an agent to touch those, you build the bridge yourself. The good news: a working custom MCP server is usually under 200 lines. The hard part is not the protocol — the SDK handles that. The hard part is tool design: picking what to expose, naming it so the agent picks the right tool without prompting, and writing failure messages the agent can act on. This guide is the full playbook, from the five-minute scaffold through auth, testing, and versioning.
The Problem
You have an internal tool. Maybe it's a billing system nobody outside finance touches. Maybe it's an on-prem CRM with an undocumented REST API. Maybe it's the in-house deploy script that ships every release. It works. The team uses it daily.
Now you want Claude to use it too. "Bump the staging tag for service X." "Look up customer 4471's invoice history." "Trigger a rollback on the prod cluster." These are exactly the operations an agent should automate — tedious, well-defined, expensive to context-switch into.
The off-the-shelf MCP servers don't help here. There's a Filesystem server, a GitHub server, a Sentry server, a Postgres server. There is no server for your billing system. There never will be. If you want an agent to touch your internal tool, you build the bridge yourself.
Most teams stall at this point. They either screen-scrape via Playwright (slow, brittle, no schema), wrap the tool in a generic "shell command" tool (terrifying), or just give up and copy-paste outputs by hand. None of these are the right answer. The right answer is a tiny custom MCP server — usually under 200 lines — that exposes exactly the operations you want, no more.
The Core Insight
An MCP server is just a JSON-RPC adapter that turns your internal API into agent-callable tools. The protocol is trivial. The hard part is tool design.
The SDK handles the wire format. It handles handshake, capability negotiation, stdio framing, error envelopes. You will not write a single byte of JSON-RPC by hand. What you will spend time on is:
- Picking which operations to expose (and which to deliberately withhold)
- Naming them so the agent picks the right one without prompting
- Shaping arguments so they mirror how a human would ask for the operation
- Writing tool descriptions that include what the tool does not do
- Designing failure messages the agent can act on, not just display
Get those right and the agent will use your server like a senior teammate. Get them wrong and it will hallucinate tool names, pass garbage arguments, and loop forever asking for things you never built.