← All posts
5 Oct 2026 · 3 min read

MCP in 10 minutes: one plug for every tool

What the Model Context Protocol actually standardises: hosts, clients and servers, the three things a server can offer, and what changed in the stateless 2026 spec.

mcpagentsllm

Before MCP, every AI app wired up its tools by hand. A Slack integration for one assistant had to be rewritten for the next. The Model Context Protocol fixes that with one standard way for an AI application to discover and use tools and data, the same way USB-C gave every device one port.

Three roles

  • Host: the AI application the user talks to, such as an IDE, a chat app or your own agent.
  • Client: a connection the host creates for each server. One host, many clients, one client per server.
  • Server: a program that offers context or actions. It can run locally next to the host, or remotely as a web service.

The protocol only defines how context is exchanged. It does not decide which model you use or how you prompt it.

What a server can offer

A server exposes three kinds of things, each with a */list method so the client can discover them:

  1. Tools: functions the model can call, like querying a database or creating a ticket (tools/list, tools/call).
  2. Resources: read-only context, like a file, a table schema or a record.
  3. Prompts: reusable templates, like a system prompt with few-shot examples.

Clients can offer something back. The main one today is elicitation: a server can ask the user for missing information or a confirmation. Sampling (a server asking the host's model for a completion) and logging are deprecated as of protocol version 2026-07-28.

On the wire

Every message is JSON-RPC 2.0. There are two transports:

  • stdio for local servers: the host starts the server as a child process and talks over standard input and output.
  • Streamable HTTP for remote servers: HTTP POST for requests, with optional server-sent events for streaming. OAuth is the recommended way to get tokens.

Calling a tool looks like this:

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": { "name": "get_equity_snapshot", "arguments": { "ticker": "HDFCBANK.NS" } }
}

What changed in 2026: stateless by default

Older versions started every session with an initialize handshake and kept state on the connection. The 2026-07-28 spec makes MCP stateless: every request carries the protocol version and the client's capabilities in its _meta field, so a server can handle each request on its own. A client that wants to know what a server supports up front sends server/discover, and the response can be cached.

For remote servers this matters a lot: any instance behind a load balancer can answer any request, with no sticky sessions.

A real server in a few lines

This is the shape of the MCP server in my Equity Research Command Center, using the official Python SDK. Each decorated function becomes a tool, and its docstring becomes the description the model reads when deciding whether to call it.

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("FinanceServer")

@mcp.tool()
async def get_equity_snapshot(ticker: str) -> str:
    """Fetch the latest internal metrics (price, market cap, P/E) for a ticker."""
    row = await db.latest_metrics(ticker.upper())
    if not row:
        return f"No internal data found for {ticker.upper()}."
    return f"P/E {row.pe_ratio}, market cap INR {row.market_cap}"

if __name__ == "__main__":
    mcp.run()  # stdio by default

My LangGraph "data gatherer" agent is the MCP client. It never touches the database directly: it lists the server's tools and calls get_equity_snapshot, which means a security layer can sit between the data and the model.

Takeaways

  • Write tool descriptions for the model, not for humans. They decide whether your tool gets called.
  • Keep tools small and named by what they do (get_equity_snapshot, not query).
  • Treat tool output as untrusted input to the model. Redact what it should not see before it leaves the server.