ยท18 min read

MCP Servers Are the Glue Between Claude Code and the Real World

claude-codemcptutorial

A few months ago I wrote a post called MCP Is the USB of AI where I argued that Anthropic's Model Context Protocol is the most important infrastructure standard in the AI agent space. That post was about the protocol itself -- what MCP is, why standardization matters, and the strategic implications of an open standard for tool integration.

This post is different. This one is about how MCP actually works inside Claude Code on a daily basis. Not the theory. The practice. What servers I run, how they connect, what lazy loading looks like, and why MCP is the reason Claude Code feels less like a code assistant and more like a general-purpose agent.

If the last post was "here's why USB matters," this one is "here's what I've plugged into the USB ports."

How MCP acts as a universal connector between Claude Code and external services. Claude speaks one protocol; each server translates to the native API.

MCP in Claude Code, Specifically

Let me start with the mechanics. MCP servers are external processes that expose tools, resources, and prompts to Claude Code via a standard protocol. Each server is a standalone program that speaks JSON-RPC over stdio. Claude Code talks to these servers using the MCP protocol. The servers talk to the outside world using whatever APIs, SDKs, or browser automation they need.

When Claude needs to read a Slack channel, it doesn't call the Slack API directly. It calls a tool exposed by the Slack MCP server, which handles authentication, rate limiting, pagination, and all the details of the Slack API. Claude speaks MCP. The server speaks Slack.

When Claude needs to navigate a webpage, it doesn't somehow open a browser by itself. It calls tools exposed by the Playwright MCP server, which controls a real Chromium browser. Claude speaks MCP. The server speaks browser automation.

This separation is the whole point. Claude doesn't need to know how to authenticate with Slack or how to drive a browser. It just needs to know what tools are available and what inputs they expect. The MCP server handles everything else.

The result is that adding a new capability to Claude Code is as simple as running a new MCP server. No model fine-tuning. No prompt engineering for API specifics. Just a new process that exposes new tools through the standard protocol. You can see all three MCP servers in my stack and how they fit together in my exact setup walkthrough.

My MCP Stack

I run three MCP servers daily. Each one opens up a category of capability that would be impossible without it.

Slack

The Slack MCP server gives Claude Code full access to my Slack workspace. Read channels. Send messages. Search across public and private channels. Read user profiles. Schedule messages. Search for users by name.

The use cases sound simple, and they are. That's what makes them valuable.

I check team channels without context-switching out of the terminal. When I'm deep in a coding session and someone mentions a design decision in a Slack thread, I ask Claude to pull up the conversation rather than opening the Slack app, losing my mental context, getting distracted by twelve other channels with unread messages, and eventually finding the thread five minutes later. In the terminal, it takes about ten seconds.

I send quick status updates without leaving my workflow. "Tell the team in the engineering channel that the PR is up for review." Done. No app switch. No notification rabbit hole.

The search is the most useful feature. "Find the conversation from last week where Alex mentioned the database migration timeline." Slack's built-in search is fine. Slack search through Claude Code is better because Claude understands what I'm looking for even when I describe it vaguely. I don't need to remember the exact keywords. I describe the conversation and Claude figures out the right search terms.

Playwright

This is the most versatile MCP server in my stack. Playwright gives Claude Code control over a real Chromium browser. Navigate to URLs. Click elements. Fill forms. Take screenshots. Read page content through the accessibility tree. Run arbitrary JavaScript on the page.

This turns any website into an API.

My grocery planning skill runs on Playwright. It navigates to the Wegmans website, checks item availability, reads prices, and builds a shopping list organized by store section. There's no Wegmans API. There doesn't need to be. The browser is the API.

My NotebookLM skill uses Playwright to interact with Google's NotebookLM web interface. Again, no public API exists. Doesn't matter. Claude controls the browser, clicks the buttons, reads the responses.

Any workflow that involves a website without a programmatic API becomes possible with Playwright. And that's most websites. The majority of useful services on the internet have web interfaces but no developer API, or have APIs that are locked behind enterprise pricing tiers. Playwright sidesteps all of that.

I'll go deeper on Playwright later in this post because there's a lot to unpack about how it works.

Context7

Context7 is the least flashy MCP server I use and one of the most consistently useful. It provides up-to-date documentation lookup for libraries and frameworks. React, Next.js, Tailwind, whatever you're working with.

The problem it solves is subtle but real. Claude's training data has a cutoff. Library APIs change. A function signature that was correct in React 18 might be different in React 19. Context7 provides the current docs directly to Claude, so when it writes code that references a library, it's working from the actual documentation, not from potentially stale training data.

I don't interact with Context7 directly very often. It works in the background. Claude reaches for it when it needs to verify an API or check documentation for a library feature. The effect is that the code Claude writes is more likely to be correct for the current version of whatever library I'm using. Fewer "this function doesn't exist" moments. Fewer deprecation warnings. It's the kind of improvement that's hard to notice because it manifests as the absence of problems.

Tool Search and Lazy Loading

Here's a detail about MCP in Claude Code that most people don't know about: the tools don't all load at startup.

If they did, it would be a disaster. My three MCP servers expose dozens of tools between them. The Slack server alone has tools for reading channels, sending messages, searching, reading profiles, scheduling, creating canvases, reading threads, and more. The Playwright server has tools for navigating, clicking, filling forms, taking screenshots, reading the accessibility tree, evaluating JavaScript, handling dialogs, uploading files, hovering, dragging, pressing keys, and waiting for elements. Context7 adds its documentation tools on top of that.

Loading every tool description into the context window at the start of every session would waste thousands of tokens. Most sessions don't need most tools. A session where I'm refactoring a Python module doesn't need Slack tools or browser automation.

Claude Code solves this with lazy loading through a mechanism called ToolSearch. Tools from MCP servers are registered as "deferred" -- Claude Code knows they exist, but their full descriptions aren't loaded into context until they're needed.

The flow works like this. Claude encounters a task that requires Slack. It searches for "slack" tools using ToolSearch. The search returns matching tools -- slack_read_channel, slack_send_message, slack_search_public, and so on -- with their full descriptions and parameter schemas. Those tools are now loaded and available for the rest of the session. Claude picks the right one and uses it.

This is an elegant design. The context window stays lean. Tools load on demand. And Claude only pays the token cost for tools it actually uses. In practice, a typical session might load three or four MCP tools out of the forty-plus that are available. That's a significant token savings.

The first time I noticed this happening, it clicked why my Claude Code setup with multiple MCP servers didn't feel slower or more confused than a bare installation. The lazy loading means the agent's effective context stays focused on what it needs for the current task.

How MCP Servers Connect

Setting up an MCP server is straightforward once you understand the pattern. Each server is configured in your Claude Code settings with a few pieces of information: the command to run the server, any arguments it needs, and environment variables for authentication.

The configuration lives in your Claude Code settings file. A typical MCP server entry looks something like this:

"mcpServers": [
  "slack": [
    "type": "stdio",
    "command": "npx",
    "args": ["-y", "@anthropic/slack-mcp-server"],
    "env": [
      "SLACK_BOT_TOKEN": "xoxb-your-token-here",
      "SLACK_TEAM_ID": "T12345678"
    ]
  ]
]

(I'm using square brackets instead of the standard object notation in the example above for MDX compatibility, but you get the idea.)

The type field is almost always stdio, meaning Claude Code communicates with the server over standard input/output. The command and args fields tell Claude Code how to start the server process. The env field passes in any API keys or tokens the server needs.

When Claude Code starts a session, it launches each configured MCP server as a child process. The server stays running for the duration of the session. Communication happens over stdin/stdout using JSON-RPC messages. Claude Code sends tool invocation requests. The server processes them and sends back results.

Adding a new MCP server is a three-step process. First, find or build the server. The MCP ecosystem has grown quickly and there are servers for most popular services. Second, add the configuration entry with the right command and credentials. Third, restart Claude Code. The new tools show up in ToolSearch immediately.

The hardest part is usually getting the credentials right. Each server needs its own authentication -- a Slack bot token for Slack, no explicit auth for Playwright since it runs locally, an API key for Context7. The credentials are standard for each service. MCP doesn't add any authentication complexity on top of what the underlying service already requires.

The Playwright Deep Dive

Playwright deserves its own section because it's the MCP server that most dramatically expands what Claude Code can do. The other servers connect Claude to specific services. Playwright connects Claude to the entire web.

Here are the tools it exposes:

browser_navigate -- Go to a URL. This is the starting point for every web interaction. Claude tells the browser where to go, and the browser goes there.

browser_snapshot -- Get the page's accessibility tree. This is how Claude "reads" a webpage. Instead of parsing raw HTML (which would be enormous and noisy), it gets the accessibility tree -- a structured representation of the page's interactive elements, text content, and layout. This is the same tree that screen readers use, which means it captures the semantic structure of the page, not just the visual layout.

browser_click -- Click an element. Claude identifies elements by their role and text from the accessibility tree, then clicks them. "Click the button labeled Submit." "Click the link that says Dashboard."

browser_fill_form -- Fill in form fields. Claude can type into text inputs, select dropdown options, check checkboxes. Combined with browser_click, this means Claude can interact with any form on any website.

browser_take_screenshot -- Capture the visual state of the page. This is useful for verification. After performing a series of actions, Claude can screenshot the page to confirm the result looks correct. It's also useful for debugging when the accessibility tree doesn't tell the whole story.

browser_evaluate -- Run arbitrary JavaScript on the page. This is the escape hatch. If the accessibility tree and click/fill tools aren't enough, Claude can inject JavaScript directly. Read a value from a specific DOM element. Trigger an event. Modify the page state. Anything JavaScript can do, Claude can do through this tool.

browser_press_key -- Send keyboard events. Enter, Tab, Escape, arrow keys. Some web interfaces require keyboard interaction that goes beyond clicking and typing.

browser_hover -- Hover over an element. Some UI elements only appear on hover -- tooltips, dropdown menus, preview panels. Claude can trigger those interactions.

browser_wait_for -- Wait for an element to appear or a condition to be met. Web pages are asynchronous. Content loads dynamically. This tool lets Claude wait for the page to be in the right state before proceeding.

The composition of these tools is what makes Playwright powerful. A typical web automation sequence looks like: navigate to the site, snapshot to understand the page structure, click a button, wait for the new content to load, snapshot again, fill in a form, click submit, take a screenshot to verify. Each step is a separate tool call, and Claude orchestrates them based on what it sees in the accessibility snapshots.

What makes this different from traditional browser automation scripts is that Claude adapts to what it sees. A Selenium script breaks when the page layout changes. Claude reads the accessibility tree, understands the page structure semantically, and figures out what to click even if the layout has shifted. The element might have moved from the top of the page to a sidebar. As long as it's still labeled "Submit" in the accessibility tree, Claude finds it.

This robustness is why my grocery planning skill has kept working for months without maintenance. The Wegmans website has probably been updated multiple times. The Playwright automation doesn't care because Claude isn't looking for elements at specific CSS selectors. It's reading the page like a human would and acting on what it finds.

Building Your Own MCP Server

If the existing MCP servers don't cover a service you need, building one is surprisingly accessible. The pattern is consistent across all MCP servers.

An MCP server is a process that communicates via JSON-RPC over stdio. It responds to a standard set of protocol messages: initialize, list tools, call tool, and a few others. When it starts, the client (Claude Code) sends an initialization handshake. Then it can request the list of available tools. Each tool has a name, a description, and an input schema (JSON Schema). When Claude decides to use a tool, the client sends a call_tool message with the tool name and arguments. The server executes the request and returns the result.

You don't need to implement the protocol from scratch. There are SDKs in Python and TypeScript that handle all the protocol boilerplate. In Python, it looks something like this:

from mcp import Server
 
server = Server("my-custom-server")
 
@server.tool(
    name="get_weather",
    description="Get current weather for a city",
    input_schema=...
)
async def get_weather(city: str) -> str:
    # Call a weather API, format the result
    return f"Weather in (city): 72F, sunny"
 
server.run()

The TypeScript SDK follows the same pattern. Define your tools with names, descriptions, and schemas. Implement the handler functions. Let the SDK manage the protocol.

The input schema matters a lot. It's what Claude uses to understand what arguments the tool expects. A well-written schema with clear descriptions and examples means Claude will call the tool correctly. A vague schema means Claude will guess at the arguments and sometimes get them wrong.

Tool descriptions are equally important. Claude reads the description to decide when to use the tool. "Get current weather for a city" tells Claude exactly when this tool is relevant. "Weather utility" is too vague and might cause Claude to use the tool in situations where it doesn't apply.

The barrier to building a custom MCP server is low. If you can write a function that calls an API, you can wrap it in an MCP server. The protocol layer is handled by the SDK. Your job is just the business logic.

What MCP Enables That Wasn't Possible Before

The real power of MCP isn't any individual server. It's the composition.

In a single Claude Code session, I have access to my entire codebase through Claude's native file and search tools. I have Slack for team communication and context. I have Playwright for interacting with any website. I have Context7 for up-to-date library documentation.

These capabilities compose. I demonstrate this composition in my end-to-end feature workflow, where a single feature build uses memory, brainstorming, coding, and review across multiple tools. A realistic workflow looks like this: I'm working on a feature. I ask Claude to check the Slack channel where the product team discussed the requirements. Claude reads the relevant Slack thread and extracts the key decisions. Then I ask it to check the React docs for a specific API I want to use. Context7 provides the current documentation. Then Claude implements the feature using that context. Then I ask it to test the feature by navigating to the local development server in a browser, filling in the form, and taking a screenshot of the result.

Codebase knowledge plus team communication context plus current documentation plus browser-based testing. All in one session. All without leaving the terminal. Each MCP server contributed one piece, and together they covered a workflow that would otherwise require four different apps and significant context-switching.

This is what "agent" means in practice. Not a language model that generates code. A system that can gather information from multiple sources, reason about it, take actions across multiple platforms, and verify the results. MCP is the infrastructure that makes this composition possible.

Before MCP, you could build some of this with custom scripts and glue code. I know because I tried. The integration was brittle. Every new tool required custom plumbing. The tools couldn't discover each other or compose naturally. MCP replaced all of that with a standard protocol, and the difference in how it feels to use is night and day.

The Ecosystem Effect

One thing I've noticed over the past few months is how quickly the MCP ecosystem is growing. When I first set up MCP servers, the options were limited. Now there are servers for databases, cloud providers, monitoring tools, project management platforms, design tools, and dozens of other services.

This is the network effect I predicted in the earlier MCP post, playing out in real time. Every new MCP server makes Claude Code more capable. Every Claude Code user who benefits from those servers creates demand for more servers. The flywheel is spinning.

For developers building tools and services, MCP is becoming the obvious integration point. Build one MCP server and you're compatible with Claude Code, with any other MCP-compatible client, and with future platforms that adopt the protocol. That's a better investment than building custom integrations for each AI platform separately.

Closing

MCP is the reason Claude Code feels less like a code editor with AI autocomplete and more like a general-purpose agent that happens to be very good at code.

Without MCP, Claude Code can read and write files, run commands, and search your codebase. That's useful. It's also limited to the boundaries of your local filesystem and terminal.

With MCP, Claude Code can read your team's Slack conversations for context. It can check the latest library documentation before writing code. It can open a browser, navigate to your staging environment, and visually verify that the feature works. It can interact with any service that has an MCP server, and if a server doesn't exist yet, you can build one in an afternoon.

The protocol is the USB port. The MCP servers are the peripherals. The more you plug in, the more Claude can do. And unlike actual USB peripherals, MCP servers are free, composable, and you can build your own.

If you're running Claude Code without any MCP servers, you're leaving most of its capability on the table. Start with Playwright. It's the one that most dramatically changes what's possible. Then add whatever services are relevant to your workflow -- Slack, database, cloud provider, whatever you interact with daily. Each server you add is another dimension of capability.

The setup takes minutes. The impact is permanent.