Migrate to RunxBuild and earn up to $50 in hosting credit on your first deposit.

Calculate your savings
unxBuild

How to Build an MCP Server: The Eight-Item Checklist That Makes It Shippable

Sean

Platform Writer

Jun 20, 2026
10 min read

Building an MCP server is two distinct jobs: the SDK code (the easy part, with the official Python and TypeScript SDKs) and the shippable production (typed tools, good descriptions, scoped permissions, an SDK, deploy logs, an integration test, an error contract, and a deploy story). Most blog posts cover the first and stop. The result is an MCP server that demos well in a chat tab and fails the moment a real agent tries to use it in production. The eight-item checklist at the bottom of this post is the part that makes the difference between a demo and a real tool other teams depend on.

This post walks through both jobs, the Python and TypeScript SDK shapes, the description field (which is the API the agent reads), and the deploy story that makes the server reachable for other teams’ agents.

How to Build an MCP Server: The Eight-Item Checklist That Makes It Shippable

Table of contents

What an MCP server actually is

An MCP (Model Context Protocol) server is a process that exposes tools, resources, and prompts to an LLM agent through a standard JSON-RPC interface. The agent connects to the server, lists the available tools, and calls the tools when the agent’s reasoning decides the tool is the right one. The protocol is the same regardless of which LLM is on the other side, which is the part that makes MCP useful — the team writes the server once, and any agent that speaks MCP can use it.

The three things an MCP server can expose:

  • Tools. A tool is a function the agent can call with arguments. The tool returns a structured response. The tool description tells the agent when to use the tool.
  • Resources. A resource is a piece of data the agent can read. The resource URI is the address; the resource content is the data. The agent uses resources the way a developer uses a file system.
  • Prompts. A prompt is a template the agent can render with arguments. The prompt becomes the input to the model. The agent uses prompts for repeatable workflows.

The team’s mental model: an MCP server is an API for agents, with a description field that replaces the API documentation. The description is what the agent reads to decide which tool to call. The wrong description is the wrong tool call.

The Python SDK walkthrough (FastMCP)

The Python SDK is mcp (the official SDK) and fastmcp (the higher-level wrapper). The fastmcp wrapper is the right answer for most teams because it handles the boilerplate (the JSON-RPC parsing, the tool registration, the schema generation) and lets the team focus on the tool logic.

The minimal fastmcp server:

from fastmcp import FastMCP

mcp = FastMCP("my-server")

@mcp.tool
def add(a: int, b: int) -> int:
    """Add two numbers and return the result."""
    return a + b

if __name__ == "__main__":
    mcp.run()

The script defines a server named my-server, registers a tool called add that takes two integers and returns their sum, and runs the server on the default transport (stdio for local, HTTP+SSE for networked).

The gotcha: the @mcp.tool decorator generates the JSON schema for the tool’s arguments from the function signature. The schema is what the agent sees. The team that wants a more descriptive schema (with examples, ranges, or units) uses Pydantic models for the arguments.

The second gotcha: the tool’s docstring is the description the agent reads. The wrong docstring is the wrong tool call. The docstring is the API documentation, and the agent does not have access to any other documentation.

The third gotcha: the mcp.run() call uses stdio transport by default. The team that wants HTTP transport (so the server can be reached by other teams’ agents over the network) passes transport="http" and a host/port. The HTTP transport is the right answer for production.

The TypeScript SDK walkthrough

The TypeScript SDK is @modelcontextprotocol/sdk. The minimal server:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const server = new McpServer({ name: "my-server", version: "1.0.0" });

server.tool("add", { a: z.number(), b: z.number() }, async ({ a, b }) => ({
  content: [{ type: "text", text: String(a + b) }],
}));

const transport = new StdioServerTransport();
await server.connect(transport);

The script defines a server, registers a tool called add that takes two numbers and returns their sum as text, and connects over stdio transport.

The gotcha: the TypeScript SDK uses Zod for schema validation. The team that does not already use Zod needs to add it. The Zod schema is what the agent sees, and the Zod types are what the tool’s callback receives.

The second gotcha: the response shape is { content: [{ type: "text", text: "..." }] }, not a plain string. The shape is the protocol’s contract, and the agent’s parser expects it.

The third gotcha: the TypeScript SDK is async-first. The tool’s callback is async, and the return value is a Promise. The team that uses sync callbacks has to await them anyway.

The description field is the API the agent reads

The description field is the part most teams get wrong, and the part that determines whether the agent uses the tool correctly.

The right description tells the agent:

  1. What the tool does. One sentence, plain language, no marketing.
  2. When to use it. The trigger the agent uses to decide the tool is the right one.
  3. What the arguments mean. Each argument, the format, the units, the valid range.
  4. What the response looks like. The shape, the units, the error cases.

The wrong description is a marketing tagline (“Our world-class tool that empowers developers”) or a copy-paste from the docstring. The agent does not have access to the docstring; the agent has access to the description field.

The pattern: write the description for the agent, not for the human reading the source. The description is the API. The human reads the source; the agent reads the description.

The eight-item shippable checklist

The eight items that make an MCP server shippable:

  1. Typed tools. Every tool has a JSON schema for its arguments. No any, no untyped dicts.
  2. Good descriptions. Every tool has a description that tells the agent when to use it. The description is the API.
  3. Scoped permissions. Every tool has the minimum permissions it needs. The tool that reads does not also write; the tool that lists does not also delete.
  4. Error handling. Every tool returns a structured error response on failure. The error response includes the error message, the error code, and the recovery suggestion.
  5. Retry policy. Every tool has a defined retry policy. The tool that hits a rate limit retries with backoff; the tool that hits a permanent error fails fast.
  6. Deployment. The server has a Dockerfile, a build command, a start command, and a health check. The server is deployable to a managed platform in one push.
  7. Observability. The server has logs, metrics, and traces. The team can see what the tool calls looked like, how long they took, and where they failed.
  8. Integration test. The server has a test that proves an agent can call the tool correctly. The test runs the agent against the tool, asserts the response, and fails if the agent cannot use the tool.

A server with all eight is a server other teams can depend on. A server with three is a demo.

The deploy story

The deploy story for an MCP server is the same as for any other service. The server is a process that listens on a port, the platform provisions the process, the platform exposes the URL, and the agent connects to the URL.

The minimal Dockerfile for the Python SDK:

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["python", "server.py"]

The platform reads the Dockerfile, builds the image, deploys the image, and exposes the URL. The agent connects to the URL.

The gotcha: the server’s URL is the URL the team shares with other teams. The URL has to be stable across deploys. The platform’s URL is stable when the platform gives the team a fixed URL, which most platforms do.

The second gotcha: the server’s transport matters. Stdio transport is for local servers (the agent and the server on the same machine). HTTP transport is for networked servers (the agent and the server on different machines). The team’s deploy story depends on which transport.

The integration test that proves the agent can call the tool

The integration test is the part most teams skip, and the part that catches the description-field bug. The test runs an agent against the tool, asserts the response, and fails if the agent cannot use the tool.

The test pattern:

import asyncio
from fastmcp import FastMCP
from mcp_agent import Agent

async def test_add_tool():
    server = FastMCP("test-server")
    @server.tool
    def add(a: int, b: int) -> int:
        """Add two numbers and return the result."""
        return a + b

    agent = Agent(tools=[server])
    response = await agent.run("What is 2 + 2?")
    assert "4" in response

The test starts a server, creates an agent with the server’s tools, runs the agent with a prompt that should trigger the tool, and asserts the response includes the expected result. The test fails if the agent cannot use the tool (because the description is wrong, the schema is wrong, or the tool logic is wrong).

The gotcha: the test is a real test, not a unit test. The test runs an actual LLM against an actual tool. The cost is the LLM API call. The team’s pattern is to run the integration test in CI, with a cheap model, on a schedule.

How this fits the rest of the stack

The MCP server is also a hosting cost. The runtime, the memory, the bandwidth, the storage, and the egress each show up as a line item on the platform bill, and the team’s mental model for the project cost is the sum of those numbers. The right answer is to know the line items before the project ships, not after. The RunxBuild hosting calculator is the right place to do that exercise — pick the runtime size, the memory tier, the storage, the expected request volume, and the bandwidth, and the calculator shows what the MCP server actually costs to run at the team’s actual usage.

Useful related references:

FAQ

What is an MCP server?

An MCP (Model Context Protocol) server is a process that exposes tools, resources, and prompts to an LLM agent through a standard JSON-RPC interface. The team writes the server once, and any agent that speaks MCP can use it. The protocol is the same regardless of which LLM is on the other side.

Which SDK should I use to build an MCP server?

The Python SDK is mcp (official) or fastmcp (higher-level wrapper). The TypeScript SDK is @modelcontextprotocol/sdk. The Python SDK is the right answer for data engineering and scripting. The TypeScript SDK is the right answer for web and Node.js teams.

What transport should my MCP server use?

Stdio transport for local servers (the agent and the server on the same machine). HTTP transport for networked servers (the agent and the server on different machines). The deploy story depends on the transport.

What is the description field in an MCP tool?

The description is the API the agent reads. The wrong description is the wrong tool call. The right description tells the agent what the tool does, when to use it, what the arguments mean, and what the response looks like.

How do I test an MCP server?

The integration test is the test that proves an agent can call the tool correctly. The test runs the agent against the tool, asserts the response, and fails if the agent cannot use the tool. The test is a real test, not a unit test.

How do I deploy an MCP server?

The deploy story is the same as for any other service. The server is a process that listens on a port, the platform provisions the process, the platform exposes the URL, and the agent connects to the URL. The minimal Dockerfile is the standard pattern.

What makes an MCP server shippable?

The eight-item checklist: typed tools, good descriptions, scoped permissions, error handling, retry policy, deployment, observability, and integration tests. A server with all eight is a server other teams can depend on.

#MCP#Model Context Protocol#AI Tools#Agent Runtime#API