MCP Servers: Giving Language Models Hands and Eyes
A new protocol that lets a model actually do things, not just talk about them

A language model on its own is a brain in a jar. It can reason, summarise and write you a sonnet about your firewall rules, but it cannot read a file, query a database, or check whether your website is up. It only knows what was in its training data and whatever you paste into the chat. That gap — between knowing things and doing things — is the most interesting problem in applied AI right now, and the Model Context Protocol (MCP) is the most sensible attempt I’ve seen at closing it.
MCP arrived late last November and I’ve spent the past few weeks wiring it into my own homelab. It’s young, it’s a bit rough at the edges, and it’s already changed how I think about connecting models to the messy real world.
1 The problem MCP solves
Before MCP, every “give the model a tool” integration was bespoke. You’d hand-roll a function-calling shim for one model, glue it to your database, and then do the whole thing again — differently — for the next model and the next data source. It was an N-times-M mess: every model times every tool, each pairing reinvented by hand.
MCP turns that into N-plus-M. You write a server that exposes some capability — your filesystem, a Postgres database, a web search, the GitHub API — and any MCP-aware client can use it. The client (the thing hosting the model) and the server (the thing offering the capability) speak one common protocol. Write the server once; every compliant client benefits.
2 Hands and eyes
The protocol gives a model two broad kinds of power.
Tools are the hands. These are actions the model can invoke: run a query, write a file, create an issue, restart a container. The server advertises what each tool does and what arguments it takes, and the model decides when to call it.
Resources are the eyes. These are things the model can read for context: the contents of a file, a row from a table, the current weather. The model pulls them in when it needs them rather than you stuffing everything into the prompt up front.
There’s a third piece, prompts — reusable templates the server can offer — but tools and resources are where the action is.
3 What a server looks like
The thing that surprised me is how little code a useful server takes. Under the hood it’s JSON-RPC over a transport — stdio for local servers, or HTTP with server-sent events for remote ones. The SDKs hide most of that. Here’s the shape of a tiny Python server exposing one tool:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("homelab")
@mcp.tool()
def disk_free(mount: str = "/") -> str:
"""Report free disk space for a given mount point."""
import shutil
total, used, free = shutil.disk_usage(mount)
return f"{free // (2**30)} GiB free of {total // (2**30)} GiB"
if __name__ == "__main__":
mcp.run()That’s a complete, working server. Point a compatible client at it and the model can now ask your machine how much disk it has left — and, more usefully, reason about the answer. The docstring isn’t decoration; it’s how the model knows what the tool does and when to reach for it. Write vague docstrings and the model fumbles; write clear ones and it picks the right tool first time.
4 Wiring it into a client
A client is configured by telling it how to launch each server. For a stdio server that’s a command and its arguments:
{
"mcpServers": {
"homelab": {
"command": "python",
"args": ["/opt/mcp/homelab_server.py"]
},
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/srv/notes"]
}
}
}The client spawns each server as a subprocess, asks it what tools and resources it offers, and presents those to the model. There’s already a healthy crop of reference servers — filesystem, Git, fetch, several database connectors — so you can have a model poking around your notes folder or a read-only database within minutes.
5 The bit to be careful about
Here’s where my self-hosting paranoia kicks in. An MCP server is a thing that runs commands on your behalf, often with the same permissions as the process that launched it. A filesystem server pointed at the wrong directory, or a database server with write access it didn’t need, is a foot-gun. And a model can be talked into misusing a tool through cunning input — prompt injection isn’t theoretical once the model has hands.
So: least privilege, always. Give each server the narrowest scope that does the job. Read-only where you can. Never expose a remote MCP server to the open internet without authentication in front of it. The protocol gives the model power; you are responsible for fencing it in.
6 Is it worth it?
If you’re building anything where a model needs to touch real systems — your data, your files, your infrastructure — then yes, emphatically. MCP is the first integration approach that doesn’t feel like it’ll be obsolete the moment you swap models, precisely because it decouples the two. It’s new enough that documentation has gaps and some servers are flaky, but the core idea is right.
For a casual chatbot user, you’ll never notice it exists. For anyone building tools, it’s the standard worth betting on. I’ve replaced three hand-rolled integrations with MCP servers already, and I’m not going back to the N-times-M mess.




