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.
Key facts
- Every MCP deployment involves three roles — a host application, a client managing one connection per server, and the server itself — exchanging JSON-RPC 2.0 messages.
- Servers expose three distinct primitives suited to different jobs: tools for the agent to do something, resources for it to read subscribable URI-identified data, and prompts for distributing ready-made, parameterized instruction snippets.
- The two standard transports have very different footprints: stdio runs locally as a child process with zero network exposure, while Streamable HTTP runs as a remote endpoint that has to check where each incoming connection claims to originate from, guarding against DNS-rebinding attacks.
- As of the 2026-07-28 spec revision (superseding 2025-11-25, and the largest revision of the protocol since launch — see /resources/mcp-2026-spec-revision), MCP dropped the explicit
initialize/initializedhandshake: every request now carries its protocol version and capabilities in_meta, with an optionalserver/discoverRPC for up-front probing; discovery (listing tools/resources/prompts), invocation, and shutdown keep the same shape. - Remote Streamable HTTP servers carry extra concerns local stdio servers don't: PKCE-based OAuth belongs on that side, the 2026-07-28 revision removed the protocol-level session and
Mcp-Session-Idheader entirely (state now travels as an ordinary tool-argument handle the server mints itself), and CORS needs to be locked down to known client origins. - MCP Inspector is the official tool for manually connecting to a server, browsing its primitives, and inspecting the raw JSON-RPC traffic.
- Publishing to the official registry means adding a
server.jsonmanifest describing the server, then running themcp-publisherCLI to log in and publish.
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: 2026-07-28 — the largest revision of the protocol since launch, superseding 2025-11-25; treat as a snapshot, verify at modelcontextprotocol.io. See /resources/mcp-2026-spec-revision for the full changelog) 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). As of the 2026-07-28 spec revision, the protocol-level Mcp-Session-Id header and the session it carried were removed entirely (SEP-2567/SEP-2575) — a server needing cross-call state now mints its own handle and has the model pass it back as an ordinary tool argument, so any request can land on any server instance without sticky routing. Requests instead carry dedicated Mcp-Method and Mcp-Name headers (SEP-2243) so gateways can route on the operation without inspecting the body. 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/server / @modelcontextprotocol/client (v2.1.0, implements the current 2026-07-28 spec, confirmed via npm this session); legacy single-package @modelcontextprotocol/sdk (v1.30.1) still published for the pre-2026-07-28 protocol model |
npm install @modelcontextprotocol/server @modelcontextprotocol/client |
| Python | mcp (v2.2.0, implements the current 2026-07-28 spec, confirmed via PyPI this session) |
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 |
| C# | modelcontextprotocol/csharp-sdk |
NuGet (ModelContextProtocol, current release v2.2.0, confirmed via nuget.org this session) — maintained with Microsoft; past the v1.0 milestone previously cited |
Minimal server lifecycle
This is the lifecycle under the current 2026-07-28 spec revision, which dropped the explicit initialize/initialized handshake that pre-2026-07-28 servers used — see /resources/mcp-2026-spec-revision for the full before/after. Servers built against the older handshake (below, still common while the ecosystem migrates) continue to work against most deployed clients; new servers should target the current model:
- (optional) discover — a client MAY call
server/discoverbefore any other request to learn the server's supported protocol versions, capabilities, and identity up front, or use it as a backward-compatibility probe on stdio. - per-request version/capability negotiation — every request (not a one-time handshake) carries the client's protocol version and capabilities in
_meta(io.modelcontextprotocol/protocolVersion,io.modelcontextprotocol/clientCapabilities); a version the server does not support returnsUnsupportedProtocolVersionError. - discovery — client may call
tools/list,resources/list,prompts/listto enumerate what the server offers; list results are stateless (no per-connection variance) and carryttlMs/cacheScopemetadata for client-side caching. - invocation — client sends
tools/call(or equivalent) with the tool name and arguments; server runs the handler and returns a content array taggedresultType: "complete"(or"input_required"for a multi-round-trip interim result). - shutdown — for stdio, the host closes stdin; Streamable HTTP has no protocol-level session to close (there is no
Mcp-Session-Idto send anHTTP DELETEagainst) — the client simply stops sending requests.
The pre-2026-07-28 handshake model, for reference: client sends initialize with protocolVersion and capabilities, server responds with its own protocolVersion/capabilities/serverInfo, client sends notifications/initialized, then discovery/invocation/shutdown as above (shutdown over Streamable HTTP used HTTP DELETE with the now-removed session ID).
TypeScript minimal example, written against the legacy 1.x-line @modelcontextprotocol/sdk package (still published on npm at v1.30.1; implements the pre-2026-07-28 initialize/initialized handshake shown above) — the SDK implementing the current spec split into @modelcontextprotocol/server/@modelcontextprotocol/client (both v2.1.0); consult those packages' own docs for the current-model API surface, which is not reproduced here:
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
Re-verified 2026-09-28 (M221 corpus pass #14) — found and corrected a real drift: this page still described the 2025-11-25 spec as current, but 2026-07-28 shipped as final (confirmed by M178/M179 and its own dedicated resource, /resources/mcp-2026-spec-revision, back on 2026-07-31) and this implementation guide had not been updated to match. Directly fetched this session (via the spec repo's raw GitHub content, since modelcontextprotocol.io itself refused the connection under this session's egress policy): the repo README (confirms 2026-07-28 is the schema/spec directory referenced as current) and the 2026-07-28 changelog (confirms the initialize/initialized-handshake removal, Mcp-Session-Id removal, server/discover RPC, _meta-based per-request versioning, and resultType field — all now reflected in the "Minimal server lifecycle" section above). Also directly confirmed this session: the registry's server.schema.json at the 2025-12-11 path still resolves live (unchanged, no drift there), mcp on PyPI is at v2.2.0, @modelcontextprotocol/server/client on npm are at v2.1.0 (with the legacy single-package @modelcontextprotocol/sdk still published at v1.30.1), and ModelContextProtocol on NuGet is at v2.2.0 — well past the "reached v1.0" claim previously cited for the C# SDK, corrected in the table above.
- MCP spec (stable 2026-07-28, fetched directly via raw GitHub content this session): https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/main/docs/specification/2026-07-28/index.mdx (canonical URL, unreachable this session: https://modelcontextprotocol.io/specification/2026-07-28)
- MCP spec changelog (2026-07-28 vs 2025-11-25, fetched directly this session): https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/main/docs/specification/2026-07-28/changelog.mdx
- MCP tools concept: https://modelcontextprotocol.io/docs/concepts/tools
- MCP quickstart (server): https://modelcontextprotocol.io/quickstart/server
- Full detail on the 2026-07-28 revision: /resources/mcp-2026-spec-revision
- TypeScript SDK, current spec (npm: @modelcontextprotocol/server / @modelcontextprotocol/client, v2.1.0, confirmed via npm this session): https://github.com/modelcontextprotocol/typescript-sdk
- TypeScript SDK, legacy 1.x line (npm: @modelcontextprotocol/sdk, v1.30.1, confirmed via npm this session): https://github.com/modelcontextprotocol/typescript-sdk
- Python SDK (PyPI: mcp, v2.2.0, confirmed via PyPI this session): https://github.com/modelcontextprotocol/python-sdk
- C# SDK (NuGet: ModelContextProtocol, v2.2.0, confirmed via api.nuget.org this session): https://github.com/modelcontextprotocol/csharp-sdk
- MCP Inspector (npm: @modelcontextprotocol/inspector): https://github.com/modelcontextprotocol/inspector
- Official MCP registry (reachable, confirmed live this session): https://registry.modelcontextprotocol.io/
- MCP registry server.json schema, 2025-12-11 (reachable, confirmed live this session): https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json
- MCP registry GitHub (server.json spec): https://github.com/modelcontextprotocol/registry
Free to read, always. Want this whole reference corpus inside your own agents? €5 unlocks every premium reference for one agent; €25 licenses the full corpus as RAG / fine-tuning data with an AI-use grant (procurement one-pager: /corpus-license); €150 adds redistribution rights.