October 14, 2026
Agentic AI

Building Your First MCP Server: A 12-Step Developer Tutorial

Building Your First MCP Server A 12-Step Developer Tutorial

An MCP server is an ordinary program that declares what it can do and executes requests, with no model inside it. The engineering is straightforward. The difficulty is deciding which tools to expose and describing them so a model chooses correctly, which is writing work rather than coding work.

Developers approaching their first server usually expect the hard part to be the protocol. It is not. Reference implementations handle the plumbing, and a working server is genuinely a short afternoon.

What takes real thought is scoping: which operations belong in the interface, how coarse they should be, and what to say about each one.

At a glance

  • A server needs no machine learning knowledge and can be written in any language
  • Tool granularity matters more than tool count
  • Descriptions are runtime instructions, not documentation
  • Errors should tell the agent what to do next
  • Test the server with ordinary tests before involving a model
  • Read-only first is the safest way to ship

Steps 1-4: decide what to expose

  1. Name the tasks, not the endpoints. Write down what someone would want to accomplish through this system. “Find an order and issue a refund” is a task; four API calls are not.
  2. Map each task to one tool where possible. A tool that internally does lookup, validation and write removes sequencing decisions the agent would otherwise get wrong.
  3. Split read from write deliberately. Read tools are safe to expose broadly. Write tools need approval design, and mixing them in one tool forces the strictest policy onto both.
  4. Cap the tool count. Every declaration consumes context and adds a selection decision. If you have listed more than a dozen, look for tasks that should merge.

Step 2 is where most server quality is decided. Endpoint-shaped tools push orchestration onto the model; task-shaped tools keep it in your code where it is testable.

Steps 5-8: define the interface

Figure 1 — Two ways to expose the same capability

Endpoint-shaped

get_customer, get_orders, get_order_items, check_refund_eligibility, create_refund

Five tools the agent must sequence correctly, with five chances to pass a wrong identifier and no guarantee it checks eligibility before writing.

Orchestration on the model

Task-shaped

find_customer, issue_refund

The refund tool internally fetches orders, checks eligibility and writes. The agent supplies intent; your code guarantees the sequence.

Orchestration in your code

The right granularity is the smallest unit a user would recognise as a complete action. Below that you are exporting your internal architecture; above it the tool becomes too vague to invoke confidently.

  1. Write the schema before the implementation. Use enums for closed value sets, precise types for identifiers and dates, and mark genuinely optional fields optional so the agent does not invent values for them.
  2. Write the description as decision criteria. State what it does, what it needs, what it returns, when to use it and explicitly when not to, naming the alternative tool.
  3. Add one worked example per tool. It does more than several paragraphs of prose.
  4. Decide what is a resource rather than a tool. Data the application should place in context is a resource; actions the model should choose to take are tools.

Watch for this

Never expose a tool that takes an identifier without also exposing the tool that produces valid ones, and say so in the description. Agents generate plausible identifiers with real confidence, matching your format exactly while referring to nothing. Sequencing the tools structurally is more reliable than any instruction telling the model not to guess.

Steps 9-12: implement, test, ship

  1. Implement against the reference SDK. The protocol layer is solved; write the handlers and let the library do the transport.
  2. Return actionable errors. Not a status code, but what was wrong with the specific input and what the caller should do next, including whether retrying is worthwhile.
  3. Test with ordinary tests. A server is a normal program, so unit-test the handlers, validate schemas, and check error paths without any model involved.
  4. Ship read-only first. Run it against real usage, watch which tools get called and how, then add write capability once selection behaviour is understood.

Step 11 is the seam that makes debugging tractable. When something misbehaves later, being able to exercise the server independently answers “is it the model or the integration” in minutes.

How do you know the descriptions are good?

Test selection separately from execution.

Write twenty realistic requests, run them against a client connected only to your server, and record which tool the model chose for each before any execution happens. Wrong choices point at descriptions, not at the model.

SymptomLikely causeFix
Two tools chosen interchangeablyOverlapping descriptionsAdd a “do not use for X, use Y instead” clause to each
Tool never chosenDescription does not match how users phrase the needRewrite using the user’s vocabulary, not the system’s
Right tool, invented argumentsLoose schemaEnums, precise types, worked example
Agent asks the user for data a tool could fetchLookup tool missing or poorly describedExpose it and reference it from the action tool
Agent stops after an errorError message gave no next stepRewrite the error as an instruction

What about security?

The server is a boundary, so treat every incoming call as untrusted regardless of what the host promises.

  • Validate arguments server-side even though the schema constrains them, because schema conformance is not business validity.
  • Scope credentials narrowly. A server should hold the minimum access its tools require, not a general-purpose key.
  • Log every invocation with arguments and outcome, since this is your audit trail.
  • Never accept an operation as an argument. A tool taking a raw query or command string hands control of your system to whatever produced it.

That last point is the one worth being firm about. A tool called run_sql with a free-text argument is an injection surface with extra steps, no matter how carefully the description asks for well-formed input.

Where do developers go wrong?

Mirroring the API one-to-one

Generating a tool per endpoint is fast and produces a server that technically works and behaves badly. The API was designed for a developer with documentation open; the agent has neither.

Writing descriptions last

Descriptions written as an afterthought get the least attention and determine the most behaviour. Write them first, as part of interface design, and treat edits to them as changes requiring re-testing.

Shipping write access on day one

Read-only servers fail harmlessly. Write-capable servers fail expensively. Understanding selection behaviour before granting side effects costs a few days and prevents the incident category that matters.

What do experienced developers do differently?

They version the tool interface like a public API.

Tool names, argument shapes and descriptions are a contract clients depend on. Changing them alters model behaviour in ways that surface as odd decisions rather than errors, so they add rather than mutate, and deprecate on a schedule.

They also include cost and latency hints in descriptions. An agent told that a tool is slow and expensive uses it more judiciously, and that information exists nowhere else in what it can see.

A short glossary

Tool
An invocable action declared by the server, selected by the model at decision time.
Resource
Readable data the application chooses to place into context, not selected by the model.
Task-shaped tool
A tool designed around a complete user-level action rather than one API endpoint.
Actionable error
An error naming both what went wrong and what the caller should do next.
Selection test
Checking which tool a model picks for a set of realistic requests, before any execution.

Key takeaways

  • A server is an ordinary program with no model inside it, writable in any language.
  • Tool granularity decides quality: task-shaped tools keep orchestration in testable code.
  • Descriptions are runtime decision criteria and should be written first, not last.
  • Never expose an identifier-taking tool without the lookup tool that produces valid ones.
  • Errors should be instructions naming the next step, not status codes.
  • Test the server with ordinary unit tests before any model is involved.
  • Ship read-only first, then add writes once selection behaviour is understood.

Frequently asked questions

Do I need machine learning experience to build an MCP server?

No. A server contains no model. It declares what it can do and executes requests against a database, API or filesystem, so it is ordinary application code in whatever language that system already uses.

How many tools should one server expose?

Fewer than instinct suggests, typically under a dozen. Each declaration consumes context and adds a selection decision the model must get right. If your list is long, look for several endpoint-shaped tools that should merge into one task-shaped tool.

Should tools mirror my API endpoints?

No. APIs assume a developer with documentation open who can sequence calls correctly. Agents have neither. Design tools around complete user-level tasks, letting your code handle the internal sequencing that the agent would otherwise have to infer.

How do I test whether my descriptions work?

Run a selection test. Write twenty realistic requests, connect a client to only your server, and record which tool the model picks for each before executing anything. Wrong picks indicate description problems rather than model problems.

What is the difference between a tool and a resource?

Tools are actions the model chooses to invoke. Resources are data the application decides to place into context. If the model should decide when something happens, make it a tool; if the application should supply it, make it a resource.

Should I expose a general query tool?

No. A tool accepting a raw SQL string or shell command hands control of your system to whatever generated that string, which is an injection surface regardless of how the description is worded. Expose specific parameterised operations instead.

How should server errors be written?

As instructions. Return what was wrong with the specific input and what the caller should do next, including whether retrying helps. A status code leaves an agent to guess, and it will often retry identically or proceed as though nothing failed.

Is it safe to ship write access immediately?

Safer not to. Read-only servers fail harmlessly while you learn which tools get chosen and how arguments are constructed. Adding write capability after that behaviour is understood costs a few days and avoids the failure category with real consequences.

References

    Avatar photo
    Laura Bradley graduated with a first- class Bachelor's degree in software engineering from the University of Southampton and holds a Master's degree in human-computer interaction from University College London. With more than 7 years of professional experience, Laura specializes in UX design, product development, and emerging technologies including virtual reality (VR) and augmented reality (AR). Starting her career as a UX designer for a top London-based tech consulting, she supervised projects aiming at creating basic user interfaces for AR applications in education and healthcare.Later on Laura entered the startup scene helping early-stage companies to refine their technology solutions and scale their user base by means of contribution to product strategy and invention teams. Driven by the junction of technology and human behavior, Laura regularly writes on how new technologies are transforming daily life, especially in areas of access and immersive experiences.Regular trade show and conference speaker, she promotes ethical technology development and user-centered design. Outside of the office Laura enjoys painting, riding through the English countryside, and experimenting with digital art and 3D modeling.

      Leave a Reply

      Your email address will not be published. Required fields are marked *