Module 015 · Desk V · Build & Ship

MCP servers.

How Claude reaches your actual tools. Model Context Protocol, from concept to first working server in 90 minutes.

90 minutes · 9 sections · ~7,500 words · Prereq: Module 014
Written for
Rookie Manager Founder

Your agent can't reach your actual tools.

You built a working agent. It drafts, it summarizes, it classifies. It can't check your calendar, read your Slack, or open a ticket in Linear, because it has no way to talk to those systems. Every agent demo you've seen with "real tools" is using something called MCP. Model Context Protocol. A standard for connecting agents to the rest of your stack.

This module is 90 minutes of understanding MCP and shipping your first working server. By the end, you'll have:

  • A working mental model of client-server in the agent world.
  • A running MCP server on your laptop that Claude can call.
  • One custom tool exposed through that server.
  • A deploy path to put it in front of teammates.

Prereq: Module 003 (Tools). If you don't know what a tool call is, start there. MCP is "tools, generalized."

Thinker has the model.

MCP is a protocol, not a product. Three pieces:

  1. The client. Claude Desktop, Claude Code, your Agent SDK app. Whatever embodies the agent.
  2. The server. A process you run that exposes tools, resources, and prompts.
  3. The protocol. JSON-RPC over stdio (local) or SSE (remote). The client discovers what the server exposes, and can call it.

What MCP unlocks

  • Reusability. Build one "Gmail server." Every Claude client you run gets email tools for free.
  • Separation. The agent doesn't know (or care) how Gmail auth works. The server handles it.
  • Portability. Swap clients (Desktop to SDK) without rewriting your tool integrations.

What MCP is not

Not a workflow engine. Not a scheduler. Not memory. It's the plumbing between an agent and your systems, nothing more. Everything else is your job.

Talker has your first server.

The tool definition is where the agent meets your system. Get it right and the agent calls it naturally. Get it wrong and the agent avoids it.

Tool definition shape

{
  "name": "get_calendar_today",
  "description": "Returns today's calendar events for the user. Use this when the user asks about their schedule, meetings, or availability today.",
  "input_schema": {
    "type": "object",
    "properties": {
      "timezone": { "type": "string", "description": "IANA timezone, e.g. America/New_York" }
    },
    "required": ["timezone"]
  }
}

The description is the prompt. The agent reads it to decide whether to call your tool. Three rules:

  • Start with "Returns..." or "Does...". Verb first.
  • Name the trigger phrases. "Use this when the user asks about their schedule."
  • List what the tool does NOT do. "Does not modify the calendar."

The server skeleton (Python)

from mcp.server import Server
from mcp.server.stdio import stdio_server

app = Server("my-tools")

@app.list_tools()
async def list_tools():
    return [{
        "name": "get_calendar_today",
        "description": "...",
        "inputSchema": {...}
    }]

@app.call_tool()
async def call_tool(name, arguments):
    if name == "get_calendar_today":
        return [{"type": "text", "text": fetch_calendar(arguments["timezone"])}]

if __name__ == "__main__":
    import asyncio
    asyncio.run(stdio_server(app))

Rememberer on state.

MCP servers are stateful processes. They can hold resources, cache auth tokens, manage connections. How you handle state determines whether your server scales.

Two state shapes

  • Per-session. Client connects, server initializes a session, session dies when the client disconnects. Good for auth tokens, open connections.
  • Persistent. Server holds state across reconnects (SQLite, Redis, disk file). Good for caches, rate-limit counters, user preferences.

Where it lives

~/.mcp/
  servers/
    my-tools/
      server.py
      config.json
      state.db      (SQLite cache)
      .env          (secrets)

Never commit .env. Always commit config.json (sans secrets).

Claude Desktop config

Register the server in ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "my-tools": {
      "command": "python",
      "args": ["/Users/you/.mcp/servers/my-tools/server.py"]
    }
  }
}

Restart Claude Desktop. Your tool appears.

Doer ships one.

Twelve minutes. Ship a working MCP server that exposes one real tool.

Build block · 12 minutes
Ship an MCP server with one tool

Step 1. Scaffold (2 min)

mkdir -p ~/.mcp/servers/my-tools
cd ~/.mcp/servers/my-tools
python -m venv .venv
source .venv/bin/activate
pip install mcp

Step 2. Write the server (4 min)

Create server.py. Expose one tool that does something real. For this build, a "read the file at path" tool:

from mcp.server import Server
import asyncio, json

app = Server("my-tools")

@app.list_tools()
async def list_tools():
    return [{
        "name": "read_my_file",
        "description": "Returns the contents of a file at the given path on the local machine. Use when the user asks to read, show, or summarize a local file.",
        "inputSchema": {
            "type": "object",
            "properties": {"path": {"type": "string"}},
            "required": ["path"]
        }
    }]

@app.call_tool()
async def call_tool(name, args):
    if name == "read_my_file":
        with open(args["path"]) as f:
            return [{"type": "text", "text": f.read()}]

if __name__ == "__main__":
    from mcp.server.stdio import stdio_server
    asyncio.run(stdio_server(app))

Step 3. Register (2 min)

Edit claude_desktop_config.json with the JSON block from Rememberer. Restart Claude Desktop.

Step 4. Test (3 min)

In Claude Desktop, type: "Read /tmp/test.md using my tools." Claude should call read_my_file and return the contents.

Step 5. Commit (1 min)

cd ~/.mcp/servers/my-tools
git init
git add server.py config.json
git commit -m "my-tools: read_my_file"
Expected outcome

A working MCP server. One custom tool. Claude Desktop calls it on command.

If something's wrong
  • Tool doesn't appear: Claude Desktop didn't restart, or the config JSON is malformed.
  • Agent avoids your tool: description is too vague. Name the trigger phrases explicitly.
  • Server crashes on call: check that your Python path in args is absolute.

Rookie has the failures.

Failure 1. The bloated tool

You write one tool called do_everything. It takes a command string and dispatches. Agent calls it once, passes garbage, gets confused, gives up.

Fix: one tool per action. create_ticket, close_ticket, search_tickets. The agent reasons better over narrow tools.

Failure 2. The silent server

Your tool runs, returns nothing, and the agent has no idea what happened. It reruns. Twice. Three times.

Fix: every tool call returns a human-readable string, even on success. "Ticket 1234 created." Not empty. Not JSON the agent has to parse.

Failure 3. Leaked secrets

You commit .env with your API key. You push. Your key leaks to GitHub.

Fix: .gitignore everything sensitive before your first commit. Use .env.example with placeholders.

Manager on MCP in teams.

MCP servers in a team codebase work like microservices. They have owners, deploys, and SLAs.

One repo per server

Don't jam all your team's MCP tools in one mega-server. One repo per server (gmail, jira, calendar). Each has a named owner and its own CI.

Shared config

Keep team config in version control. .mcp/servers.json in the team repo, cloned to every laptop. Changes go through PR.

Server upgrades

When you change a tool's schema, it's a breaking change. Every agent that uses it needs to be re-tested. Treat MCP servers the same way you'd treat a shared library.

Chief on governance.

MCP servers expand your agent's reach into your business. Which means three new risk surfaces.

Risk 1. Authorization boundary

An MCP server acts on behalf of a user with that user's credentials. Audit which servers have which scopes. A gmail server with write access is a different risk than one with read-only.

Risk 2. Injection via tool output

A tool that returns user content (support tickets, emails) can feed adversarial instructions into your agent. Treat tool output as untrusted input. Escape, classify, or limit its reach.

Risk 3. Supply chain

Third-party MCP servers run on your machine with your credentials. Audit the source. Pin versions. Don't install "official" servers without checking the publisher.

Founder.

Solo operator running MCP: one server, five tools, all your personal workflows.

The solo stack

One server called personal-tools. Five tools:

  • Read any file on your laptop.
  • Append a line to your journal.
  • Search your notes.
  • Hit one of your frequent APIs (Notion, Linear, Airtable).
  • Run one cron job on demand.

That's it. Five tools cover 80% of what you ask Claude to do on a normal day.

The weekly polish

Once a week, add one tool if you found yourself manually doing something Claude could've done. After a year, your server has 30 tools and you're moving 10x faster than most developers.

The one thing to remember

MCP is tools, generalized.

Once you have a server, every Claude client you run gets those tools. One build, forever reach. The time to start is your first agent project, not your fifth.

Keep exploring
More from the library.
Browse the full catalog →