DEV Community

Cover image for What is the Model Context Protocol (MCP)?
Konstantin Konovalov
Konstantin Konovalov

Posted on

What is the Model Context Protocol (MCP)?

The Model Context Protocol (MCP) is an open standard for connecting language models to tools and data through one consistent interface, so an integration you write once keeps working across different models and host applications.

Every time you wire an LLM into a real system, you hit the same wall. The model can reason about your data, but it cannot reach it. Your Postgres rows, your ticketing system, the file on disk that the answer actually depends on. So you write glue. A function here, a JSON schema there, a bespoke adapter for each model vendor. Then a new model ships, or a new data source appears, and you write the glue again.

MCP exists to stop that loop.

What problem does MCP actually solve?

Before MCP, "give the model access to X" meant N times M work. N models, M data sources, and a custom bridge for every pair. Each bridge had its own auth story, its own way of describing what a tool does, its own error handling.

MCP collapses that into N plus M. You write one MCP server that exposes your data source. Any MCP-capable client can talk to it. Swap the model, keep the server. Add a second app, point it at the same server. The protocol is the contract in the middle, and both sides only have to speak that contract.

If you have used the Language Server Protocol, the shape is familiar. LSP let one language server serve many editors instead of every editor reimplementing support for every language. MCP does the same move for model context.

How MCP's client/server model works

MCP has three roles worth naming clearly.

The host is the application the user interacts with. A desktop assistant, an IDE plugin, a chat UI, your own agent.

The client lives inside the host and manages one connection to one server. If the host talks to four servers, it runs four clients.

The server is a small program you write that exposes capabilities. A GitHub server, a filesystem server, a company-database server. A server does not track which model is on the other end. It answers protocol messages, and the same server works no matter which LLM the host is driving.

Transport is either stdio (the host launches the server as a subprocess and talks over stdin/stdout, good for local tools) or HTTP with server-sent events for remote servers. Messages are JSON-RPC 2.0 in both cases.

The important property: the server declares what it can do, and the host discovers those capabilities at connection time. Nothing is hardcoded on the model side.

MCP vs raw function calling

Function calling is a model feature. The model can emit a structured call against a schema you pass in. MCP is the transport, discovery, and reuse layer built around that feature, so the same capability works across hosts without rewiring.

Concern Raw function calling MCP
Where tools are defined In your app code, per model vendor In a standalone server, once
Reuse across apps Copy the code into each app Point each app at the same server
Discovery You hardcode the tool list Host lists tools at connect time
Isolation Runs in your app process Server runs as a separate process with its own permissions
Transport Vendor SDK specific JSON-RPC over stdio or HTTP/SSE
Beyond actions Tools only Tools, resources, and prompts

Tools, resources, and prompts

An MCP server exposes three kinds of things, and each has a distinct job when you design a server.

Tools: actions the model can invoke

Tools are actions the model can invoke. create_issue, run_query, send_email. They can have side effects. Each tool ships a name, a description, and a JSON Schema for its inputs. The model reads that schema and decides when and how to call.

A tool definition looks roughly like this:

{
  "name": "search_orders",
  "description": "Find orders by customer email or order id.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "email": { "type": "string" },
      "order_id": { "type": "string" }
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

The model never touches your database directly. It emits a structured call, the host relays it to your server, your server runs the real query and returns the result.

Resources: data the model can read

Resources are data the model can read. A file, a database record, an API response, addressed by URI. Think file:///logs/today.txt or db://customers/4821. Resources are meant to be read-only context, closer to a GET than a POST. The host decides how to surface them, whether to inline them or let the user pick.

Prompts: reusable interaction templates

Prompts are reusable templates the server offers, often surfaced as slash commands or menu items in the host. "Summarize this incident," "review this diff." They let a server ship a known-good interaction pattern instead of making every user reinvent the wording.

The split is practical. Tools carry actions with side effects, resources carry read-only context, and prompts carry canned interactions. Sort each capability into the right bucket when you design a server, and the host can render each one the way it was meant to be used.

A minimal MCP server you can run

Here is a full server you can copy, run, and connect to. It uses the official Python SDK, which handles the JSON-RPC plumbing so you only write the tool body.

First install the SDK:

pip install "mcp[cli]"

Enter fullscreen mode Exit fullscreen mode

Then save this as server.py:

from mcp.server.fastmcp import FastMCP

server = FastMCP("notes")

# a stand-in for your real data source
NOTES = [
    "We decided to keep the caching layer in Redis for now.",
    "Postgres upgrade is scheduled after the launch freeze.",
    "Auth service owns rate limiting, not the gateway.",
]

@server.tool()
def search_notes(query: str) -> list[str]:
    """Return note snippets matching the query."""
    q = query.lower()
    return [n for n in NOTES if q in n.lower()][:5]

if __name__ == "__main__":
    # runs over stdio; a host launches this file as a subprocess
    server.run()

Enter fullscreen mode Exit fullscreen mode

Run it locally to confirm it starts:

python server.py

Enter fullscreen mode Exit fullscreen mode

That is the whole integration surface. At connect time the host asks the server to list its tools, sees search_notes and its schema, and hands that description to the model. When a user asks "what did we decide about the caching layer," the model calls search_notes("caching layer"), your function runs, snippets come back, and the model answers grounded in real notes.

You did not touch the model vendor's SDK. You did not write a parser for the model's output format. You described a capability and let the protocol carry it.

Why this matters for your integrations

Three practical payoffs.

Reuse. The server you write for your IDE assistant also works in your CI bot and your customer support agent, unchanged.

Isolation. Servers run as separate processes with their own permissions. You can give a filesystem server access to exactly one directory and nothing else. The model's reach is bounded by what each server chooses to expose.

Composability. A host can connect to several servers at once and the model sees a merged toolset. Filesystem plus GitHub plus your database, all discovered dynamically, no central registry to maintain.

The cost is that you now think in terms of a protocol boundary. Tool descriptions become part of your product surface, because the model reads them like documentation. Vague descriptions produce vague tool use, so writing crisp tool contracts and testing how a model actually calls them is a real skill.

Mini-FAQ

What is MCP in one sentence?
An open protocol that lets language models call tools and read data through one standard interface, so one integration works across many models and hosts.

How is MCP different from function calling?
Function calling is the model-side ability to emit a structured call. MCP wraps that in a transport, a discovery step, and a reuse boundary, so the tool lives in a standalone server and any compliant host can use it without vendor-specific glue. If you only ever call one API from one script, plain function calling is enough. MCP earns its keep when the same tools are shared across hosts or when you want process isolation.

What are MCP tools vs resources vs prompts?
Tools are actions with possible side effects and an input schema. Resources are read-only data addressed by URI, closer to a GET. Prompts are reusable interaction templates the server offers to the host. Tools do, resources inform, prompts guide.

What transports does MCP support (stdio vs HTTP/SSE)?
Two. With stdio the host launches the server as a subprocess and they talk over stdin/stdout, which suits local tools. With HTTP and server-sent events the server runs remotely. Both carry JSON-RPC 2.0 messages, so your tool code does not change between them.

Is MCP tied to one model vendor?
No. It is an open protocol. Any host and any model that implement it can participate.

Is a server hard to write?
A minimal server is a few dozen lines, as shown above. Official SDKs in Python and TypeScript handle the JSON-RPC plumbing so you focus on the tool bodies.

Start with one server and one tool

Describe your data and actions once, behind a stable protocol, and stop rewriting glue every time the model or the host changes. Start with one server exposing one tool against something you already have. Once you see the model call it correctly, add the next tool.

Top comments (0)