# 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.

Category: Guide · Updated: 2026-06-17 · Tags: mcp, tools, protocols, agents, implementation
Canonical: https://changegamer.ai/resources/building-mcp-servers

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:

1. **initialize** — client sends `initialize` with `protocolVersion` and its capabilities; server responds with its `protocolVersion`, `capabilities` (which primitives it supports), and `serverInfo`. The negotiated version governs the session.
2. **initialized** — client sends the `notifications/initialized` notification; the session is now open for normal requests.
3. **discovery** — client may call `tools/list`, `resources/list`, `prompts/list` to enumerate what the server offers.
4. **invocation** — client sends `tools/call` (or equivalent) with the tool name and arguments; server runs the handler and returns a content array.
5. **shutdown** — for stdio, the host closes stdin; for Streamable HTTP, the client sends `HTTP DELETE` with the session ID (if the server issued one).

TypeScript minimal example (using `@modelcontextprotocol/sdk`):

```typescript
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:

```bash
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
