Skip to content
AI & Automation

How to Build an MCP Server: A Practical Development Guide

A practical guide to building an MCP server: choosing an SDK, defining tools with schemas, implementing handlers, stdio and Streamable HTTP transports, authorization, testing with the Inspector and deployment.

Quick answer

To build an MCP server: pick an official SDK in your team's language, define a small set of narrow tools with clear descriptions and strict input schemas, implement handlers that call your existing APIs with validation and permission checks, and choose a transport (stdio for local use, Streamable HTTP for remote use with OAuth-based authorization). Test with unit tests, the MCP Inspector and a real client, then deploy like any production service with logging, tracing, rate limits and versioning. Target the current 2026-07-28 specification.

Where This Fits

Read the MCP guide for concepts first. Security requirements are in MCP security, and when MCP is the right interface at all is in MCP vs API.

Step 1: Decide What to Expose

Start from the questions and actions users need, not from your whole API. List candidate tools, mark each as read or write, and choose a first set of three to five read tools. Decide whether some data is better exposed as resources (readable documents or records by URI) and whether reusable prompts help users.

CapabilityUse forExample
ToolActions and queries the model initiatesget_order_status, create_ticket
ResourceData the application can read by URIorders://ORD-123456, a policy document
PromptTemplates a user can choose'Summarize this account for a QBR'

Step 2: Choose an SDK and Transport

Use an official SDK so protocol details (discovery, message formats, the stateless request metadata introduced in 2026-07-28) are handled for you. The official server tutorial covers TypeScript, Python, Java, Kotlin and C#. For transport, stdio suits local desktop and IDE use; Streamable HTTP suits remote servers. The older HTTP+SSE transport is deprecated.

Validation is the part of the server that protects your systems from bad inputs.

Step 3: Define Tools and Implement Handlers

The example below follows the official TypeScript tutorial's API: the server package is @modelcontextprotocol/server, tools are registered with registerTool and a Zod input schema, and a stdio transport connects the server. It wraps an existing internal API rather than querying a database directly, and returns a helpful message on failure instead of throwing raw errors.

Example: a minimal TypeScript MCP server with one read tool (stdio)
import { McpServer } from "@modelcontextprotocol/server";
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
import { z } from "zod";

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

server.registerTool(
  "get_order_status",
  {
    description: "Get the status, items and latest shipment event for one order by its ID.",
    inputSchema: z.object({
      orderId: z.string().regex(/^ORD-[0-9]{6}$/).describe("Order ID, e.g. ORD-123456"),
    }),
  },
  async ({ orderId }) => {
    const res = await fetch(`${process.env.ORDERS_API}/orders/${orderId}`, {
      headers: { Authorization: `Bearer ${process.env.ORDERS_API_TOKEN}` },
    });
    if (!res.ok) {
      return { content: [{ type: "text", text: `Order ${orderId} could not be retrieved (${res.status}).` }] };
    }
    const order = await res.json();
    return { content: [{ type: "text", text: JSON.stringify({ status: order.status, items: order.items, lastEvent: order.lastEvent }) }] };
  }
);

async function main() {
  await server.connect(new StdioServerTransport());
  console.error("orders MCP server running on stdio"); // stderr, never stdout
}

main().catch((err) => { console.error(err); process.exit(1); });

Writing Good Tool Definitions

  • Name tools with verbs and objects: get_order_status, create_support_ticket
  • Describe when to use the tool and what it returns, in one or two sentences
  • Constrain inputs with patterns, enums, ranges and required fields
  • Return concise, structured results; avoid dumping whole API responses
  • Return clear error messages the model can act on
  • Keep write tools separate from read tools, and make them idempotent
  • Return tools in a deterministic order, as the 2026-07-28 specification recommends

Building an MCP server for your product or internal systems?

ZSpace Labs builds MCP servers with narrow tools, validation, authorization and tests, on top of your existing APIs.

Start a Project

Step 4: Add Authorization for Remote Servers

A remote server over Streamable HTTP needs authorization. Following the specification: publish OAuth protected resource metadata so clients can find your authorization server, require access tokens with each request, validate that each token was issued for your server (audience) and has the right scopes, and use separate credentials when calling upstream APIs; never pass the client's token through. Prefer Client ID Metadata Documents for client registration, as Dynamic Client Registration is now deprecated in MCP. Details are in MCP security.

Step 5: Test

  • Unit-test each handler, including upstream failures
  • Use the MCP Inspector to list and call tools interactively
  • Connect a real client and try realistic requests
  • Test invalid inputs, unauthorized users and rate limits
  • Check tool descriptions by watching which tools the model picks
  • Add regression tests before changing tool schemas

Step 6: Deploy and Operate

Package local servers so users can install them easily and pin versions. Deploy remote servers as web services behind HTTPS with secrets management, rate limits, structured logging and tracing (the specification documents OpenTelemetry trace context in request metadata), health checks and alerts. Version your server and tools, communicate breaking changes and keep old versions during migrations.

Advantages and Limitations

A well-built MCP server makes your systems usable from many AI applications without bespoke integrations, and centralizes validation and authorization in one place. The limits: the specification and SDKs are moving quickly, clients support features unevenly, and an MCP server adds another service to secure and maintain. Keep business logic in your APIs so the server stays a thin, safe adapter.

The Same Tool in Python

In the official Python SDK, the current tutorial uses the MCPServer class from mcp.server, with tools defined by decorators and type hints. The equivalent of the TypeScript example looks like this (check the SDK documentation for the version you install):

Example: Python MCP server with one read tool (stdio)
import os
import logging
import httpx2
from mcp.server import MCPServer

logging.basicConfig(level=logging.INFO)  # logs go to stderr, never stdout
mcp = MCPServer("orders")

@mcp.tool()
async def get_order_status(order_id: str) -> str:
    """Get the status, items and latest shipment event for one order.

    Args:
        order_id: Order ID in the form ORD-123456
    """
    if not (order_id.startswith("ORD-") and order_id[4:].isdigit() and len(order_id) == 10):
        return "Invalid order ID format. Expected ORD-123456."
    async with httpx2.AsyncClient(timeout=15) as client:
        res = await client.get(
            f"{os.environ['ORDERS_API']}/orders/{order_id}",
            headers={"Authorization": f"Bearer {os.environ['ORDERS_API_TOKEN']}"},
        )
    if res.status_code != 200:
        return f"Order {order_id} could not be retrieved ({res.status_code})."
    order = res.json()
    return f"Status: {order['status']}; last event: {order['lastEvent']}"

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

Remote Server Deployment Checklist

  • Streamable HTTP over HTTPS only
  • Protected resource metadata published; authorization server configured with PKCE and resource indicators
  • Audience and scope validation on every request
  • Separate credentials for upstream APIs; no token passthrough
  • Per-user and per-tool rate limits
  • Structured logs and traces, with sensitive values redacted
  • Health checks, alerts and an on-call owner
  • Versioned releases with a changelog for tool changes

Worked Example

An illustrative scenario, not a client case: an internal operations team builds a stdio server with three read tools over its warehouse API so analysts can ask an AI assistant about stock and shipments. After a month, a remote version adds a create_transfer_request write tool behind OAuth with a scope only supervisors receive, and every call is logged with the user and arguments.

Common Mistakes

  • Copying older examples that use deprecated transports or the removed handshake
  • Generic tools that accept arbitrary queries
  • Logging to stdout in stdio servers
  • Token passthrough to upstream APIs
  • Dumping entire API responses into tool results
  • No tests for failure paths

Need help taking an MCP server to production?

Talk to ZSpace Labs about MCP development and API, auth and deployment work.

Start a Project

Conclusion

A good MCP server is a thin, well-validated adapter over your APIs: few narrow tools, clear descriptions, the right transport, proper authorization and solid tests. Related: MCP guide, MCP security and MCP vs API.

FAQ

Common questions

An official MCP SDK for your language, a clear set of tools or resources to expose, the API or data source they will use, a transport choice (stdio or Streamable HTTP), and for remote servers an authorization setup.

Get in touch

Have a project in mind?

Whether you're building a new digital product, improving an existing website, or looking to automate part of your business — let's talk.

Keep exploring
AI & Automation
9 min read

Model Context Protocol (MCP): A Complete Guide for Developers and Businesses

What the Model Context Protocol is and how it works in 2026: hosts, clients and servers, tools, resources and prompts, transports, the stateless 2026-07-28 specification, authorization and practical use cases.

Read article
AI & Automation
8 min read

MCP Security: How to Secure AI Tools, Servers and Data Access

How to secure Model Context Protocol deployments: OAuth-based authorization, audience-bound tokens, no token passthrough, least-privilege tools, consent, tool poisoning, prompt injection, local server risks and audit trails.

Read article
AI & Automation
6 min read

MCP vs API: What's the Difference and When Should You Use Each?

How the Model Context Protocol differs from traditional APIs: audience, discovery, schemas and descriptions, authorization and interoperability, why MCP usually wraps APIs, and when to build each.

Read article