How to Build an MCP Server
Shiplight AI Team
Updated on July 31, 2026
Shiplight AI Team
Updated on July 31, 2026

Writing a server that speaks the Model Context Protocol takes an afternoon. Writing one an agent uses correctly takes longer, and the difference is almost entirely in decisions that are not about the protocol at all.
This covers the minimum implementation, then the part that actually determines whether it works: tool design.
A server declares tools with names, descriptions and input schemas, then handles calls to them. Official SDKs exist for the common languages and handle the transport, so the code you write is close to the shape of a small RPC service.
Two transports matter. Stdio runs the server as a local subprocess of the client, which is the right default for developer tooling because it can reach the filesystem and the developer's running application without anything leaving the machine. HTTP is for remote servers shared across users.
Start with stdio. Ship the second when someone asks.
Everything below is what separates a server agents use from one they ignore.
The description is how the model decides whether your tool fits the task in front of it. It is not documentation for a human who already chose the tool. It is the pitch.
"Runs a check" gets ignored. Say what the tool does, when to reach for it, and what comes back. If your tool is not being called, the description is the first thing to fix, and it is usually the only thing.
Twelve granular tools that mirror your internal API is a common mistake. The model has to choose among all of them on every turn, and selection accuracy falls as the list grows.
Design for tasks, not for your object model. One tool that does the thing a user wants beats four that must be composed in the right order.
Every byte you return lands in the model's context and stays there. A tool that dumps a full API response spends context that other tools and the actual code need.
Return what is needed for the next decision. Structured beats prose, because the model parses it more reliably. If a result is genuinely large, return a summary plus a way to fetch detail on request.
This is the most common performance problem in practice, and it is invisible from the outside: the agent just gets slower and worse a few turns after using your tool.
An error saying what was expected and what arrived lets the model correct itself and retry. An error saying "failed" ends the attempt and the user sees "I was unable to do that."
A tool the model can call is a tool it can call at the wrong moment. Deletions, payments, production configuration: either keep them out, or require an explicit confirmation argument the model cannot supply on its own.
Debugging through an agent is miserable, because the model decides whether to call your tool, phrases the call, and reports back in prose that loses the actual error.
Connect MCP Inspector instead. It speaks the protocol directly, so you see the advertised tool list, call tools with parameters you choose, and read the raw response. Most problems resolve in a couple of minutes there and would take an hour through an agent.
The order that works: does the server start, does Inspector see the tools, does a tool work when called by hand, and only then look at the client config.
Worth building when the capability is specific to your organization, when several different agents need the same thing, or when there is no API to point at because the work is local.
Not worth building when a maintained server already does it. The ecosystem is large enough by 2026 that checking first is faster than writing.
For browser automation specifically, do not start from scratch. That problem has been solved carefully, including the part most people get wrong: returning structured accessibility snapshots rather than screenshots, which costs a fraction of the tokens and works with any reasoning model. See Playwright MCP.
Shiplight exposes browser verification and test generation over MCP, and the decision that mattered most was not a protocol one. It was that a verification which leaves no artifact is worth very little.
An agent can open a browser, confirm a flow works, and move on. That protects today. The tool is far more useful if the successful walkthrough is also written into the repository as a test that runs on every future change, because then today's check catches tomorrow's regression.
If you are building a server that verifies something, the design question worth asking early is what survives the session.
An SDK for your language, a transport (stdio for local developer tooling), and tool definitions with names, descriptions and input schemas. The protocol work is small.
Stdio for local developer tooling, since it can reach the filesystem and a locally running app without data leaving the machine. HTTP for remote servers shared across users.
Almost always the description. The model decides from that text whether the tool fits the task, so a vague description reads as irrelevant regardless of what the tool does.
As few as will do the job. Selection accuracy falls as the list grows, so design tools around tasks rather than mirroring your internal API.
With MCP Inspector, not through an agent. It shows the raw tool list, lets you call tools directly, and returns the actual error instead of a prose summary.