BestMCPTools.org
Back to blog

What Is an MCP Server? A Developer's Guide to the Protocol

What is an MCP server? A technical guide to Model Context Protocol primitives, transports, lifecycle, and the implementation details that trip developers up.

If you've been building with LLMs for any length of time, you've written the same glue code more than once: a wrapper around an API, a schema describing it to the model, a dispatch layer that turns tool calls back into HTTP requests. Then you did it again for a different model, with a different tool format.

The Model Context Protocol exists to kill that duplication. So what is an MCP server, concretely? It's a process that exposes tools, resources, and prompts over a standardized JSON-RPC interface, so any MCP-compatible client — Claude Desktop, an IDE, your own agent loop — can discover and call them without knowing anything about your implementation.

The analogy people reach for is LSP. Before the Language Server Protocol, every editor needed a bespoke integration for every language. After it, you wrote one server and every editor got support. MCP is the same bet applied to model context.

The Protocol Surface

The clearest way to answer what is an MCP server is to look at what it exposes. An MCP server offers three primitive types, and the distinction between them matters more than it first appears.

Tools are model-controlled. They're functions the model can decide to invoke, each with a name, description, and JSON Schema for its input. create_issue, run_query, send_message. The model chooses when to call them, which means your description text is effectively prompt engineering — a vague description produces a tool that never gets used or gets used wrongly.

Resources are application-controlled. They're readable context identified by URI: a file, a database row, a document. The client decides what to pull in. Resources are for things the model should be able to read, not act on, and they support subscription for change notifications.

Prompts are user-controlled. Parameterized templates the client surfaces as commands — the slash-command pattern. Useful for encoding a workflow that a user invokes deliberately rather than one the model triggers on its own.

Getting this mapping right is most of the design work. A common mistake is exposing everything as tools, which floods the model's context with function definitions it rarely needs. If the client should decide when to include something, it's a resource.

Transports and Lifecycle

Communication is JSON-RPC 2.0 over one of two transports.

stdio is the simplest: the client spawns your server as a subprocess and speaks over stdin/stdout. No ports, no auth, no network surface. This is the right default for anything running on the same machine as the client — local filesystem access, a git wrapper, a database on localhost.

Streamable HTTP is for remote servers. A single endpoint handles POSTed requests, with Server-Sent Events for streaming responses and server-initiated messages. This is what you want for a hosted service multiple users connect to, and it's where authorization comes in — the spec builds on OAuth 2.1, treating the server as a resource server with the client obtaining tokens from an authorization server.

The connection lifecycle is straightforward: the client sends initialize with its protocol version and capabilities, the server responds with its own, and the client confirms with notifications/initialized. After that, tools/list, tools/call, resources/list, resources/read and friends are available. Capability negotiation during initialize is what lets the protocol evolve without breaking older implementations — check what the peer actually declared rather than assuming.

Building One

If the question behind what is an MCP server is really "how much work is it to write one" — not much. The SDKs handle the protocol plumbing, so a minimal server is genuinely small. In TypeScript or Python you register a tool with a name, a description, an input schema, and a handler, then connect a transport. That's it.

Where implementations go wrong is in the details:

Descriptions are the interface. The model sees only the name, description, and schema. If your parameter is a date, say what format. If two tools are similar, say explicitly when to use each. Treat this text as seriously as you'd treat API documentation for a human.

Return errors as content, not exceptions. A failed tool call should return isError: true with a message the model can read and act on. Throwing a protocol-level error hides the reason and gives the model nothing to recover from. "Repository not found — check the owner/repo format" is useful. A stack trace is not.

Bound your output. A tool that returns 50,000 rows will blow through the context window and produce a worse answer than one that returns 20 rows and a count. Paginate, truncate, and say what you truncated.

Keep tool surfaces small. Twenty granular tools compete for the model's attention. Consider consolidating into fewer tools with a mode or action parameter, especially when the operations share most of their inputs.

Validate every input. The model can and will produce arguments that don't match your schema's intent. Path traversal, SQL injection, unbounded ranges — all the usual server-side concerns apply, because the caller is nondeterministic.

Security Considerations Worth Front-Loading

MCP servers frequently sit on top of privileged systems, and the calling party is a language model that can be influenced by content it reads. Two consequences follow.

First, tool descriptions and returned content are untrusted input from the model's perspective, and content your server returns can contain injected instructions. If you're proxying arbitrary web pages or user-generated data, assume it may try to redirect the model's behavior. Keep destructive operations behind explicit confirmation rather than making them a single tool call away.

Second, scope credentials tightly. A server given a full-access token grants that access to anything the model decides to do. Prefer narrow, per-resource permissions, and don't pass through tokens issued for a different audience.

Go Deeper

Once you can answer what is an MCP server from the protocol level down, the useful next step is seeing how other people have built one — the tool granularity they chose, how they handled auth, what they exposed as resources versus tools.

Best MCP Tools is a directory of production MCP servers and clients, organized by what they connect to and how they're built. Browse it to find an implementation close to what you're building, or to find a server that already does what you were about to write yourself.

If you've shipped an MCP server, submit it to the directory — or leave a review on one you've integrated. Notes on what actually held up in production are the most useful thing in this ecosystem right now.