0 XP
L1
?
Lessons
MCP Servers
build ⏱ 30m
2/3

MCP Servers

MCP (Model Context Protocol) is how you extend what an LLM can do. Instead of building custom API integrations for every tool, MCP provides a standard protocol. You already use 10+ MCP servers. Now learn to build your own.

MCP Architecture

┌──────────┐     stdio/HTTP     ┌──────────┐
│  Client   │◄──────────────────►│  Server   │
│(Claude    │                    │(Your code)│
│  Code)    │                    │           │
└──────────┘                    └──────────┘
     │                               │
     │ Uses tools,                   │ Exposes tools,
     │ reads resources               │ resources, prompts
     │                               │

An MCP server exposes three things:

  • Tools: Functions the model can call (like search_meetings, create_task)
  • Resources: Data the model can read (like a database schema, config files)
  • Prompts: Pre-built prompt templates (like "summarize this meeting")

Building a Server

Using @modelcontextprotocol/sdk:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const server = new McpServer({
  name: "my-project-server",
  version: "1.0.0"
});

// Define a tool
server.tool(
  "search_notes",
  "Search Obsidian meeting notes by keyword and date range",
  {
    query: z.string().describe("Search keywords"),
    project: z.string().optional().describe("Filter by project name"),
    since: z.string().optional().describe("ISO date, only notes after this date")
  },
  async ({ query, project, since }) => {
    // Your implementation: search files, filter, return results
    const results = await searchObsidianVault(query, project, since);
    return {
      content: [{ type: "text", text: JSON.stringify(results) }]
    };
  }
);

// Start the server
const transport = new StdioServerTransport();
await server.connect(transport);

Tool Design Principles

From your experience with 10+ MCP servers, you know what makes a good tool:

1. Do one thing well. search_notes searches. create_note creates. Don't combine them.

2. Clear descriptions. The model reads the description to decide when to use the tool. Be explicit about what it does, what inputs it expects, and what it returns.

3. Return structured data. JSON is easier for the model to process than free text. Include metadata (dates, IDs) that the model can use for follow-up actions.

4. Handle errors gracefully. Return error messages the model can understand and act on. "Note not found" is better than a stack trace.

5. Minimize parameters. Every parameter is a decision the model has to make. Only require what's essential, make the rest optional with sensible defaults.

Connecting to Claude Code

Add your server to .claude/settings.json:

{
  "mcpServers": {
    "my-project-server": {
      "command": "npx",
      "args": ["tsx", "/path/to/your/server.ts"]
    }
  }
}

Restart Claude Code, and your tools appear alongside the built-in ones.

What to Build

The highest-value MCP servers bridge your existing tools:

  • Obsidian + Linear: Search meeting notes for action items, auto-create Linear tickets
  • PostHog + Slack: Fetch analytics, generate weekly report, post to client channel
  • Calendar + Obsidian: Check tomorrow's meetings, pull relevant notes, create prep brief

These bridges are where the real productivity gains are — connecting data that currently lives in silos.

❓ Quiz 1
What are the three things an MCP server can expose?
MCP servers expose tools (callable functions), resources (readable data), and prompts (pre-built templates). This is the protocol's core abstraction.
Answer to continue ↓
⚖ Decision 1
You're building an MCP server that connects Obsidian meeting notes to Linear. When Claude finds action items in a meeting, it should create Linear tickets. Should the MCP server expose this as tools or resources?
A Works, but the model has to orchestrate the full flow: search meetings, parse action items, call create_ticket for each. That's a lot of tool calls and the model might miss items.
B ★ Resources let the model READ meeting notes directly (they appear in context). Tools let it WRITE to Linear. Clean separation: reading is passive, writing is active. The model sees the data first, then decides what to create.
C Convenient but opaque — the model can't see intermediate results. What if the meeting has 5 action items but only 3 should be tickets? No human-in-the-loop control.
Make your choice to continue ↓
🛠 Exercise 1
Design an MCP server for one of your workflows. Choose a bridge between two tools you currently use (e.g., Obsidian + Linear, PostHog + Slack). Define 3 tools with: name, description, parameters (with types), and what each returns. Be specific enough that you could implement this.
✓ Saved
advance · ? shortcuts 05.02
Claude — Tutor
select text for context
Ask me anything about this lesson.
I can see your quiz answers and decisions.

💡 Select text in the lesson to use it as context.
CONTEXT