Building an MCP Server
Implementation guide for MCP servers: architecture roles, the three server primitives, stdio vs Streamable HTTP transports, official SDKs, server lifecycle, remote-server concerns, testing with MCP Inspector, and publishing to the official registry.
The Model Context Protocol (MCP) gives a host application a uniform way to connect an AI model to external tools, data, and prompt templates. This guide covers what you need to implement a server — from wire format to deployment. For finding and evaluating existing servers see /resources/mcp-server-discovery; for OAuth 2.1 auth on remote servers see /resources/mcp-server-authentication.
Architecture: host, client, server
Three roles exist in every MCP deployment:
- Host — the application that owns the model interaction (e.g. Claude for Desktop, a custom agent). It spawns or connects to MCP servers and mediates access.
- Client — a component inside the host that manages one connection to one MCP server, running the JSON-RPC 2.0 message exchange.
- Server — the process or endpoint you build. It declares capabilities during initialization and handles requests for tools, resources, and prompts.
All messages between client and server are JSON-RPC 2.0 objects: requests carry an id, method, and optional params; responses carry the same id plus result or error. Notifications are one-way (no id, no response expected).
The three server primitives
Tools are model-invoked functions. The client calls tools/list to discover them; the model selects and calls one via tools/call. Each tool has a name, description, and an inputSchema (JSON Schema). Results are returned as typed content blocks (text, image, audio, resource link). Use tools when the agent needs to do something — query an API, run a calculation, write to a database.
Resources are read-only data the client can subscribe to: files, database rows, live feeds. They are identified by URI. Use resources when the agent needs to read structured data that may change over time.
Prompts are named, parameterised prompt templates served by the server. The client fetches them via prompts/get and injects them into the conversation. Use prompts to share curated instruction fragments without embedding them in the client.
Transports: stdio and Streamable HTTP
The current MCP spec (stable revision: 2025-11-25; treat as a snapshot, verify at modelcontextprotocol.io) defines two standard transports.
stdio (local) — the host launches the server as a child process. JSON-RPC messages are exchanged over stdin/stdout, delimited by newlines. The server MUST NOT write anything to stdout except valid MCP messages; use stderr for logs. stdio carries zero network exposure and is the right choice for local tools running on the same machine.
Streamable HTTP (remote) — the server runs as an independent network process. All client messages are HTTP POST requests to a single MCP endpoint; the server may respond with application/json (single response) or text/event-stream (SSE stream for multi-message responses). An optional Mcp-Session-Id header supports stateful sessions. The server MUST validate the Origin header on every incoming connection to prevent DNS rebinding attacks.
Official SDKs
All SDKs are under the modelcontextprotocol GitHub org. Treat version numbers as a snapshot; verify current releases before pinning.
| Language | Package | Install |
|---|---|---|
| TypeScript | @modelcontextprotocol/sdk |
npm install @modelcontextprotocol/sdk |
| Python | mcp |
pip install mcp (or uv add "mcp[cli]") |
| Kotlin | modelcontextprotocol/kotlin-sdk |
Maven/Gradle — maintained with JetBrains |
| Go | modelcontextprotocol/go-sdk |
go get — maintained with Google |
| Swift | modelcontextprotocol/swift-sdk |
Swift Package Manager |
Minimal server lifecycle
Regardless of transport, every session follows this sequence:
- initialize — client sends
initializewithprotocolVersionand its capabilities; server responds with itsprotocolVersion,capabilities(which primitives it supports), andserverInfo. The negotiated version governs the session. - initialized — client sends the
notifications/initializednotification; the session is now open for normal requests. - discovery — client may call
tools/list,resources/list,prompts/listto enumerate what the server offers. - invocation — client sends
tools/call(or equivalent) with the tool name and arguments; server runs the handler and returns a content array. - shutdown — for stdio, the host closes stdin; for Streamable HTTP, the client sends
HTTP DELETEwith the session ID (if the server issued one).
TypeScript minimal example (using @modelcontextprotocol/sdk):
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: "example", version: "1.0.0" });
server.registerTool("ping", {
description: "Return a pong",
inputSchema: { message: z.string().optional() },
}, async ({ message }) => ({
content: [{ type: "text", text: `pong: ${message ?? ""}` }],
}));
const transport = new StdioServerTransport();
await server.connect(transport);
Remote-server concerns
Authentication — remote Streamable HTTP servers should implement OAuth 2.1 with PKCE. Full detail is in /resources/mcp-server-authentication.
Statelessness — servers that do not issue an Mcp-Session-Id are effectively stateless: each POST is independent. This simplifies horizontal scaling but means the server cannot hold in-memory state across requests.
CORS — if the server will be called from browser-hosted clients, set Access-Control-Allow-Origin appropriately. Restrict origins to known client domains where possible.
Session handling — if issuing session IDs, use cryptographically random values (UUID v4 or equivalent). Clients that receive HTTP 404 on a session-bearing request must start a new initialize exchange.
Testing: MCP Inspector
The official interactive testing tool is MCP Inspector (@modelcontextprotocol/inspector on npm). Run it with:
npx @modelcontextprotocol/inspector
It provides a UI to connect to any MCP server, browse its tools/resources/prompts, send calls manually, and inspect the raw JSON-RPC messages. Source: github.com/modelcontextprotocol/inspector.
Publishing and discovery
The official registry is at registry.modelcontextprotocol.io. To publish, add a server.json file to your repository (schema: https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json) describing your server's name, version, transport type, and endpoint URL, then use the mcp-publisher CLI (mcp-publisher login github → mcp-publisher publish). Publishing to the registry seeds downstream aggregators and client-vendor marketplaces. For evaluation criteria and security checks to apply before connecting to third-party servers, see /resources/mcp-server-discovery.
ChangeGamer's /mcp endpoint
ChangeGamer runs a remote MCP server at https://changegamer.ai/mcp using the Streamable HTTP transport. It is unauthenticated — no OAuth flow is required to connect. It is open and read-only. Premium resource bodies still require an api_key argument passed to the get_resource tool; this is an application-layer key check, not OAuth. ChangeGamer does not implement RFC 9728 Protected Resource Metadata.
Verified sources
- MCP spec (stable 2025-11-25): https://modelcontextprotocol.io/specification/2025-11-25
- MCP spec changelog (2025-11-25 vs 2025-06-18): https://modelcontextprotocol.io/specification/2025-11-25/changelog
- MCP transports spec: https://modelcontextprotocol.io/specification/2025-11-25/basic/transports
- MCP tools concept: https://modelcontextprotocol.io/docs/concepts/tools
- MCP quickstart (server): https://modelcontextprotocol.io/quickstart/server
- TypeScript SDK (npm: @modelcontextprotocol/sdk): https://github.com/modelcontextprotocol/typescript-sdk
- Python SDK (PyPI: mcp): https://github.com/modelcontextprotocol/python-sdk
- MCP Inspector (npm: @modelcontextprotocol/inspector): https://github.com/modelcontextprotocol/inspector
- Official MCP registry: https://registry.modelcontextprotocol.io/
- MCP registry GitHub (server.json spec): https://github.com/modelcontextprotocol/registry