What Is an MCP Server? A Developer's Guide to the Model Context Protocol in 2026
What is an MCP server? A developer's guide to Model Context Protocol primitives, transports, auth, and the design decisions that matter in production.
If you've spent any time building with LLMs, you've written the same code more than once: a wrapper around an API, a hand-rolled tool schema, some glue to feed results back into the model, and a pile of auth handling you'd rather not maintain. Then you switch models or frameworks and write it all again.
That repetition is the problem the Model Context Protocol was built to end. But if you're asking what is an MCP server in practical terms, the term gets used loosely enough that it's worth being precise about the architecture — because the answer determines how you design your integrations.
What Is an MCP Server, Exactly?
An MCP server is a process that exposes capabilities — tools, resources, and prompts — over the Model Context Protocol, a standard JSON-RPC based interface that any compliant client can consume.
The key word is standard. Before MCP, every integration between a model and an external system was bespoke: your Postgres connector for one framework didn't work in another. MCP defines the contract once. Write a server for your internal API, and it works with any MCP-compatible client — a desktop assistant, an IDE, an agent framework, your own application.
Structurally there are three roles:
- Host — the application the user interacts with.
- Client — the protocol connection the host maintains, one per server.
- Server — your process, exposing capabilities over stdio or streamable HTTP.
The critical design decision: the server never talks to the model. It exposes a typed interface; the host decides what to surface and when. That separation is what makes servers portable and what makes the security model tractable.
The Three Primitives
Tools are model-controlled functions. Each declares a name, description, and JSON Schema for its inputs. The model decides when to call them. create_issue, run_query, send_notification — anything with a side effect belongs here.
Resources are application-controlled data, addressed by URI. A file, a database row, a document. Resources are for context the host injects; they're read-oriented and don't imply model-initiated action.
Prompts are user-controlled templates — reusable workflows a user explicitly invokes, typically surfaced as slash commands.
The distinction matters more than it first appears. Getting it wrong is the most common design mistake: exposing everything as a tool floods the model's context and degrades tool selection accuracy. If the host should decide when data appears, it's a resource. If the user should trigger it, it's a prompt.
Servers also support sampling — asking the host to run a completion on the server's behalf — and elicitation for requesting user input mid-operation. Both keep the model relationship owned by the host.
Transports and What They Imply
Two transports matter in practice.
stdio — the server runs as a subprocess of the host, communicating over stdin/stdout. Simple, no network surface, inherits the local environment. This is the right default for anything touching local files or local credentials.
Streamable HTTP — the server runs remotely, with SSE for server-initiated messages. Necessary for multi-user or hosted deployments, and where OAuth 2.1 authorization comes in. MCP's auth spec treats the server as a resource server with a separate authorization server, which is more work but the correct shape for anything multi-tenant.
Rule of thumb: local integrations go stdio, shared services go HTTP. Don't reach for HTTP because it feels more "real" — a stdio server has a dramatically smaller attack surface.
Building One: What Actually Matters
Official SDKs exist for TypeScript, Python, Go, Java, Kotlin, C#, Rust, and more, and a minimal server is genuinely about thirty lines. The hard part isn't the protocol — it's the API design.
Descriptions are the interface. Your tool description is a prompt. "Search issues" is worse than "Search issues in the tracker by text query, status, and assignee. Returns up to 50 matches with ID, title, and status." Ambiguous descriptions produce wrong calls, and you'll debug it as a model problem when it's a docs problem.
Return errors as content, not exceptions. A tool that throws gives the model nothing to work with. A tool that returns "query failed: column 'usr_id' does not exist; did you mean 'user_id'?" gets a corrected retry.
Constrain the surface. Eight well-scoped tools beat forty thin wrappers around every endpoint. Model the workflows your users actually run.
Paginate and truncate. A tool returning 50,000 rows will blow the context window and take the whole session with it.
Treat every input as hostile. Tool arguments come from a model that may be acting on text from an untrusted source. Parameterize queries, validate paths, scope credentials to the minimum the server needs.
Find the Right Server — and Publish Yours
The ecosystem has moved fast enough that the hardest part of MCP is no longer writing a server. It's knowing which ones already exist, which are maintained, and which are production-ready versus a weekend proof of concept.
Best MCP Tools is a directory of MCP servers and tooling built for exactly that: browse by category, see what a server actually exposes, and skip the ones that haven't been touched in six months. If you came here asking what is an MCP server, the directory is the fastest way to see the answer in practice — real servers, real tool surfaces, real implementations to read.
If you've built one, submit it. And if you've run someone else's in production, leave a review — reliability, auth quirks, how it behaves under load. Reliability notes are the thing the README never tells you, and they're what makes this ecosystem usable for everyone building in it.