Home
Back to Blog
MCPIntermediate

The Complete Guide to MCP Servers: What They Are and How to Build One

Everything you need to know about the Model Context Protocol — what MCP servers are, why they matter, and a step-by-step walkthrough for building your own.

April 16, 202613 min readClaude Code Playbooks
MCP servermodel context protocolMCP tutorialClaude Codeagentstoolsintegration

If you've spent any time with AI agents in the last year, you've run into a frustrating pattern: every new tool integration means re-implementing the same plumbing. Auth for Gmail here, auth for Slack there, a custom Postgres adapter for one project, a different Postgres adapter for the next. It's the kind of drudgery that eats your afternoon and produces nothing durable.

The Model Context Protocol — MCP for short — is the fix. It's an open standard for exposing tools, data, and prompts to AI agents, the same way HTTP is an open standard for exposing content to browsers. Write the integration once, and any MCP-compatible agent can use it. This guide walks through exactly what MCP is, why it matters, and how to build your first MCP server end to end.

What Is MCP, Really?

MCP is a protocol. That's it. It's not a framework you have to adopt, a cloud service you have to pay for, or a library you have to import. It's a specification for how an AI agent (the client) talks to an external capability provider (the server) over JSON-RPC.

The easiest mental model: MCP is to AI agents what USB-C is to hardware. Before USB-C, every device had its own cable. Now one shape fits everything. MCP plays the same role for AI tooling — one protocol, any agent, any integration.

An MCP server can expose three kinds of things to an agent:

Tools

Functions the agent can call — "send_email", "query_database", "create_jira_ticket". Each tool has a name, a JSON Schema describing its inputs, and a return type.

Resources

Read-only data the agent can pull into its context — a file, a database table, a page from Notion. Resources are addressed by URI so the agent can reference them by name.

Prompts

Reusable prompt templates the server offers to the client — useful for standardizing common workflows ("summarize this ticket", "draft a release note") across agents and teams.

Why MCP Matters (More Than It Looks Like at First)

On the surface, MCP sounds like "another API standard." The reason it's a big deal is more subtle: it breaks the tight coupling between a model and its tools. Three downstream effects follow from that.

1. Your integrations become portable. Build an MCP server for your internal ticketing system once, and it works with Claude, any Claude Code project, and any other MCP-compatible client — now and in the future. When a better model comes out, you don't rebuild your tooling.

2. You can compose capabilities. An agent can connect to multiple MCP servers at once — your GitHub server, your Postgres server, your Slack server — and reason across all of them. You stop thinking in integrations and start thinking in capabilities.

3. The ecosystem compounds. Because MCP is open, the number of off-the-shelf servers grows every week. Filesystems, browsers, Git, databases, search engines — most of what an agent needs already exists as an MCP server someone else maintains. You don't have to build everything.

That third point is where MCP Hub comes in. It's a curated collection of the most useful MCP servers you can wire into Claude Code in one sitting — so before you build anything, it's worth checking whether the integration you need already exists.

How an MCP Conversation Actually Works

Under the hood, MCP is a JSON-RPC conversation between a client and a server over either stdio (for local servers) or HTTP with Server-Sent Events (for remote ones). The handshake has three phases:

  1. Initialize. The client says hello, declares which protocol version it speaks, and asks what capabilities the server supports.
  2. Discovery. The client callstools/list,resources/list, andprompts/listto find out what the server offers. Each returned entry includes a JSON Schema so the model knows how to call it.
  3. Invocation. When the agent decides to use a tool, the client sendstools/callwith the tool name and arguments. The server executes and returns the result.

Here's what a tool invocation looks like on the wire, stripped down:

// client → server
{
  "jsonrpc": "2.0",
  "id": 7,
  "method": "tools/call",
  "params": {
    "name": "create_issue",
    "arguments": {
      "repo": "acme/backend",
      "title": "Payments webhook retry logic is flaky",
      "body": "Fails on third retry..."
    }
  }
}

// server → client
{
  "jsonrpc": "2.0",
  "id": 7,
  "result": {
    "content": [
      { "type": "text", "text": "Created issue #4821" }
    ]
  }
}

That's the whole protocol in miniature. The SDKs hide the JSON-RPC boilerplate, so in practice you're writing Python or TypeScript functions and decorating them as tools.

Building Your First MCP Server (Step by Step)

Let's build a minimal MCP server from scratch. The example: a server that exposes one tool, get_weather, which takes a city name and returns a temperature. The same shape generalizes to any integration you'll ever build.

Step 1: Install the SDK

The official SDKs are published for Python and TypeScript. Pick whichever your team is more comfortable with — the protocol is identical.

# Python
pip install mcp

# TypeScript
npm install @modelcontextprotocol/sdk

Step 2: Write the Server

Here's a complete Python MCP server in twenty lines. It spins up over stdio, registers one tool, and returns a result:

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("weather-server")

@mcp.tool()
def get_weather(city: str) -> str:
    """Return the current temperature for a given city."""
    # Replace with a real API call
    return f"It is 18°C in {city} right now."

if __name__ == "__main__":
    mcp.run()

Three things are happening: FastMCP creates a server, the @mcp.tool() decorator registers the function (using the type hints and docstring to auto-generate the JSON Schema), and mcp.run() starts the JSON-RPC loop. That's a complete, valid MCP server.

Step 3: Register It with Claude Code

Add the server to your Claude Code config. The client will launch it as a subprocess and speak MCP over stdio.

{
  "mcpServers": {
    "weather": {
      "command": "python",
      "args": ["/absolute/path/to/server.py"]
    }
  }
}

Restart Claude Code. The get_weather tool is now available to any agent in that project, with the schema auto-derived from your type hints. Ask "what's the weather in Lisbon?" and watch the agent call your tool.

Step 4: Add Resources and Prompts

Tools are usually where the interesting behavior lives, but resources and prompts round out a useful server. Adding them follows the same pattern:

@mcp.resource("weather://history/{city}")
def weather_history(city: str) -> str:
    """Return a 7-day weather history for a city."""
    return load_history(city)

@mcp.prompt()
def weather_report(city: str) -> str:
    """Generate a weather report for a given city."""
    return f"Write a short weather report for {city} using the current data and the last 7 days."

Resources give the agent read-only data it can pull into context. Prompts give users a ready-made starting point they can invoke from the client UI. Neither is required — start with tools, add the others when they earn their keep.

Design Principles for Good MCP Servers

You can get a server running in twenty lines; you can't get a good server in twenty lines. The servers that actually hold up in production tend to share a few traits.

  • Name tools like verbs the agent would say.search_issues, not issuesSearchV2. The model chooses tools based on their names and descriptions — write them for the model, not for your internal API taxonomy.
  • Keep tool surface area small. A server with eight focused tools outperforms one with forty overlapping ones. The agent has to pick the right tool on every turn; fewer choices means fewer mistakes.
  • Return structured, summarizable results.Don't return a 50KB JSON blob when a five-line summary plus an ID to fetch more detail would do. Context is the agent's most expensive resource.
  • Fail loudly and usefully. Error messages are prompts — the agent reads them and decides what to do next. "Invalid date: expected YYYY-MM-DD, got 'last Friday'" is a good error. "400" is not.
  • Gate destructive actions. Require an explicitconfirm=true parameter for anything that sends, spends, or deletes. Agents confidently miswield new tools; guard rails catch most of those mistakes.

Local vs. Remote Servers

MCP servers come in two flavors. Local servers run as subprocesses on your machine and talk over stdio — they're the default and by far the easiest to get right. Remote servers run over HTTP + Server-Sent Events, letting you host shared tooling for a team or expose a public capability.

Default to local. You'll only want a remote server if: multiple people need to use the same tooling without setting it up each themselves, the server needs access to secrets or data that shouldn't live on laptops, or you're exposing the capability to third parties. Each of those cases adds deployment, auth, and observability concerns that a local subprocess simply doesn't have.

The Shortcut: Scaffold a Server in Minutes

Building an MCP server from scratch is valuable as a learning exercise — once. For every real project after that, starting from a scaffold is faster and less error-prone. The MCP Server Builder playbook takes a plain-English description of the tools you want ("let me search and create Linear issues") and generates a working server — protocol handlers, JSON schemas, auth, error handling, and the config snippet to wire it into Claude Code.

If you're not sure you need to build anything at all, start with the MCP Hub playbook first. It's a curated catalog of the servers most teams end up wanting — filesystem, Git, Postgres, browser automation, Slack, and more — with one-line install instructions. Most of the time, the integration you need is already a npm install away.

Where This Is All Going

A year from now, "does it have an MCP server?" will be the first question teams ask when evaluating a new tool, the same way "does it have an API?" is asked today. The tools that don't expose themselves via MCP will still work — but they'll be invisible to the agents that increasingly do the work.

The good news is how little effort it takes to be on the right side of that shift. A useful MCP server can be under a hundred lines of code. The protocol is stable, the SDKs are solid, and the ecosystem is growing fast enough that most of what you need already exists. Pick one integration you're tired of rebuilding, wrap it in MCP, and never think about it again.