Skip to content
Model Context Protocol (MCP)Building a server

Senior · 6 chapters

Building a server

Choosing the surface, keeping tools orthogonal, returning model-legible errors, testing it in isolation from any agent

A server with a bloated surface or opaque failures makes agents unreliable before prompting can compensate.

Read these first

What you will know

  • Build a working server with a small, orthogonal tool set

    Keep schemas narrow and non-overlapping so tool selection remains reliable as the model sees more capabilities.

  • Test it in isolation, with no agent involved

    Direct calls expose protocol, schema, serialization, and lifecycle defects without conflating them with planning or prompt behavior.

  • Return errors a model can act on

    Include a stable failure category, the relevant input problem, and a safe next action instead of stack traces or vague status messages.

  • Compare two candidate surfaces against representative tasks and record which tools are omitted and why

    Removing redundant operations reduces selection ambiguity, permission exposure, and the amount of tool documentation in context.

  • Hand it to someone else and have them use it without asking questions

    A fresh user should discover required inputs, constraints, and recovery paths from the exposed descriptions and responses alone.


Chapters

  1. 1

    Orthogonality in a tool surface

    An issue tracker exposing list_issues, search_issues, find_issues_by_assignee and get_sprint_issues has four names competing for one job, and sharpening all four descriptions cannot settle a tie the surface created. State the overlap precisely — some task in scope has two reasonable tools — by marking a table of real tasks against the tools a careful reader would consider defensible, then collapse the set into one query_issues with a filter object, which moves the decision from unvalidated ranking into a schema that validates. The surface you register on the server is the design artefact everything in this topic works on.

  2. 2

    Tool count and selection accuracy

    Nineteen tools, one per CLI subcommand, is how selection accuracy slides from 94% to 81% over two quarters: each addition can conflict with every existing one, so pairwise disambiguation grows faster than the surface does — 73% more tools between Q1 and Q3 and 211% more pairs. Run two candidate surfaces against the same representative tasks, read completion counts as weak evidence rather than a verdict, and record which operations you omitted and why. Deleting a tool two teams already call is a rollout: alias the old names onto the consolidated tool, keep a retired one callable but unlisted, and leave a reverse edge for the caller that cannot migrate.

  3. 3

    The blast radius of a misroute

    Better descriptions lower how often a selection goes wrong and change nothing about what a wrong selection can reach; 93% accuracy over 64 eval tasks is still four or five wrong selections a pass. Separate read surfaces from write ones so an ambiguous choice lands on a query, make destructive operations name their target explicitly and carry a precondition instead of defaulting to a current or last-used one, and spend confirmation only on the few operations whose cost is irreversible. None of these depend on how good your schemas are, which is the point: they are what you design for the misroute you will still get.

  4. 4

    Actionable tool errors

    A create_calendar_event that answers a non-IANA timezone with HTTP 400 and \"Invalid request\" leaves the agent guessing six times over 9.4 seconds and 11,200 tokens before telling the user the calendar is down. An actionable replacement carries a stable machine code such as invalid_timezone, the value received, the constraint as an accepted form with an example, and a signal that an unchanged retry cannot help. Splitting one 500 into 401, 404 and 429 corrects the transport and not the behaviour, because the model never sees a status; what shows the new responses are better is retries per failure category, success on the call after the first failure, and how often a run ends with the agent declaring the integration broken.

  5. 5

    Direct calls and agent runs

    Direct calls against the server catch protocol, schema, serialisation and lifecycle defects in seconds, with no planner muddying which one fired, and they fire error branches an agent reaches only by accident. What they structurally cannot catch is whether the description and schema make this the obvious tool for the job it exists for, because the script never chose — you did, when you wrote the call. An agent run is not a superset: it is a noisier instrument over a smaller sample, exercising 6 of 14 tools on a 20-task set. Work out what each one sees, and what you are accepting when a demo tomorrow leaves time for only one.

  6. 6

    The fresh-subject handoff

    Hand the server to an engineer on another team with nothing but what it exposes, and count the questions: what format since wants, whether limit has a ceiling, what to do when a call returns rate_limited. Each is a defect rather than a question, because the model has nobody to ask, and each is located in exactly one of three places — a schema, a description or an error body. This chapter turns the handoff into a repeatable acceptance test with a fixed task list and a logged record, and works the asymmetric cases: a tool a human uses fine and an agent cannot, and the reverse.

External resources