My Brain CellsMy Brain Cells
HomeBlogAbout

© 2026 My Brain Cells

XGitHubLinkedIn
MCP Deep Dive: A Simple (but Detailed) Guide

MCP Deep Dive: A Simple (but Detailed) Guide

AS
Anthony Sandesh
If you’ve ever tried to connect an LLM to tools (APIs, databases, file systems, internal services), you’ve probably felt the pain:
  • Every app invents its own “tool calling” format
  • Every integration is bespoke
  • Switching models/clients breaks everything
  • Security and permissions get messy fast
Model Context Protocol (MCP) fixes that by standardizing how apps provide tools + context to LLMs. Think: one protocol that lets you plug in many servers (tools/data) into many clients (ChatGPT apps, Claude Desktop, custom agents) without rewriting adapters every time. (OpenAI GitHub)
This post explains MCP end-to-end—simple to understand, but deep enough to build with confidence—and ends with a complete project you can run locally.

1) MCP in one picture (the mental model)

MCP is a client ↔ server protocol.
  • MCP Server: exposes capabilities (tools/resources/prompts)
  • MCP Client: connects to a server and calls those capabilities
  • Host app: the UX that wraps the client (Claude Desktop, ChatGPT app, your CLI, your IDE plugin)
Data flow:
The LLM doesn’t “magically access your DB.” It asks the host/client to call MCP tools/resources, and the host decides what to allow and how to present it. (Model Context Protocol)

2) The 3 core building blocks: Tools, Resources, Prompts

MCP servers can expose three “primitives”:

A) Tools = actions (like POST endpoints)

Tools do work: query an API, run code, write a file, create a ticket, etc.
  • Input: structured JSON schema
  • Output: structured content back to the client/LLM
(You’ll implement tools in our project.)

B) Resources = read-only context (like GET endpoints)

Resources are chunks of context that clients can add into the model’s working set: files, docs, DB schemas, config, etc.
Key point: resources are app-driven—the host decides how resources are surfaced/selected/included. (Model Context Protocol)

C) Prompts = reusable workflows/templates

Prompts let servers publish “prompt templates” (like slash commands) that users can explicitly choose and run. Prompts are designed to be user-controlled. (MCP Protocol)

3) Under the hood: how MCP actually talks

MCP messages are JSON-RPC

MCP uses JSON-RPC messages (UTF-8) between client and server. (Model Context Protocol)
A JSON-RPC request looks like:
And a response:

Two standard transports

MCP defines two standard ways to move these JSON-RPC messages: (Model Context Protocol)
  1. stdio
      • Client launches server as a subprocess
      • Server reads from stdin, writes MCP messages to stdout
      • Messages are newline-delimited JSON and must not contain embedded newlines (Model Context Protocol)
  1. Streamable HTTP
      • Server is an independent HTTP service
      • Client sends JSON-RPC via POST, may receive streaming responses via SSE
      • Includes critical security guidance (Origin validation, localhost binding, auth) (Model Context Protocol)
Rule that bites beginners:
If you use stdio, your server must not print random logs to stdout. Logs should go to stderr. (Model Context Protocol)

4) Designing MCP tools that LLMs won’t mess up

When an LLM picks tools, it’s guessing based on your metadata. So your tool UX matters.

Tool naming

Bad:
  • doThing
  • run
  • process
Good:
  • notes.search
  • notes.read
  • notes.append
  • notes.create

Tool schemas

Use clear schemas (required vs optional) and tight validation. Most MCP SDKs use Zod for schemas. (Model Context Protocol)

Side effects

If a tool writes/deletes/charges money:
  • require explicit user approval in the host (human-in-the-loop)
  • log actions to an audit trail
  • implement allow-lists and path-safety

5) Why Python + FastMCP is the easiest path

The MCP “Build a server” guide uses FastMCP in Python because it:
  • uses Python type hints and docstrings to automatically generate tool definitions (schemas)
  • keeps server code minimal and readable
You typically:
  1. Create mcp = FastMCP("name")
  1. Decorate functions with @mcp.tool(), @mcp.resource(...), @mcp.prompt()
  1. Run via mcp.run(transport="stdio")

5a) A tiny “hello tool” example (Python)

That’s enough to show up in an MCP host as a callable tool.

✅ Project: “Smart Notes” MCP Server (Python) + Agent Client

You’ll build:
  1. An MCP server that manages notes in SQLite:
  • Tools: add/list/get/search/delete/export
  • Resources: read-only views like data://notes/index and data://notes/{id}
  • Prompts: “turn this into a clean note”, “create a study plan from notes”, etc.
  1. A Python agent (OpenAI Agents SDK) that connects via stdio MCP and uses the tools.
The Agents SDK supports stdio MCP servers using MCPServerStdio.

Setup (uv + MCP SDK)

From the MCP tutorial (Python 3.10+, MCP SDK 1.2.0+), the recommended setup is:
We’ll also use SQLite (built-in) and a little JSON handling (built-in), so no extra deps required.

Project structure


The MCP server: notes_server.py

Important: logging must go to stderr (no print()), because we’re using stdio.
What you just built:
  • Tools for CRUD + export
  • Resources for “read-only context snapshots” (index, per-note, tag views, stats)
  • Prompts to standardize how the host asks the model to transform text

Test the server (2 easy ways)

Option A: Run in an MCP host (Claude Desktop config style)

The MCP tutorial shows adding a server entry that runs via uv --directory ... run your_script.py.
Example config pattern (adapted):

Option B: Use MCP Inspector (best for debugging)

MCP Inspector can be launched via npx and used to list tools/resources/prompts and test calls.
A common pattern is:
In the Inspector UI you can:
  • open Tools tab and call add_note, search_notes, etc.
  • open Resources tab and fetch data://notes/index, data://notes/1
  • open Prompts tab and test clean_note(...)

(Optional but awesome) Build the Python Agent that uses your MCP server

This uses the OpenAI Agents SDK, installed via:
PyPI lists that as the installation command.
The SDK can connect to a local stdio MCP server via MCPServerStdio and will spawn the process for you.

agent_cli.py

Run it:
Now you can say things like:
  • “Create a note titled ‘GPU profiling checklist’ with bullet points about Vulkan/CUDA profiling”
  • “Search my notes for ‘profiling’ and summarize what I’ve written”
  • “Export notes tagged ‘interview’ as markdown”

6) Practical tips that make MCP projects feel “production-grade”

Design tools like APIs

  • Use clear names: add_note, search_notes, export_notes_markdown
  • Keep inputs small and explicit
  • Prefer structured return values (dict/list) when possible

Put “read-only context” in resources

Resources are a clean way to expose “state views” like:
  • data://notes/index
  • data://notes/stats
  • data://notes/tag/{tag}
That reduces tool calls and helps the model ground itself.

Use prompts to standardize behavior

Instead of re-explaining “how to write notes” in every agent instruction, prompts let you reuse templates reliably.

7) Security and production notes (don’t skip)

If you stay on stdio (local):

  • protect the filesystem (allow-list paths, sanitize filenames)
  • log to stderr, never to stdout (Model Context Protocol)

If you move to Streamable HTTP:

Follow the transport spec’s warnings:
  • validate Origin (DNS rebinding prevention)
  • bind to 127.0.0.1 for local servers
  • implement authentication (Model Context Protocol)

If you integrate into OpenAI agents/apps:

OpenAI’s Agents SDK supports hosted MCP tools, Streamable HTTP, and stdio (and notes SSE is deprecated—prefer Streamable HTTP / stdio). (OpenAI GitHub)
ChatGPT Apps also use an MCP server as the backend that defines tools + UI wiring. (OpenAI Developers)

8) Where to take this next (easy upgrades)

Once your notes server works, you can evolve it into something serious:
  • Add tags/frontmatter parsing
  • Add embeddings + semantic search (tool: notes.semantic_search)
  • Add “read-only mode” vs “write mode”
  • Add per-note ACL (only allow writes to /notes/drafts/*)
  • Switch transport to Streamable HTTP when you need remote access

 

More posts

8-Stage Lifecycle of Modern LLM Applications

8-Stage Lifecycle of Modern LLM Applications

“Verl" for LLM Reinforcement Learning (Beyond Pre-training)

“Verl" for LLM Reinforcement Learning (Beyond Pre-training)

OLMo

OLMo

The Practical Guide to RAG: Types, Techniques, and How (and When) to Use Each

Older

The Practical Guide to RAG: Types, Techniques, and How (and When) to Use Each

On this page

  1. 1) MCP in one picture (the mental model)
  2. 2) The 3 core building blocks: Tools, Resources, Prompts
  3. A) Tools = actions (like POST endpoints)
  4. B) Resources = read-only context (like GET endpoints)
  5. C) Prompts = reusable workflows/templates
  6. 3) Under the hood: how MCP actually talks
  7. MCP messages are JSON-RPC
  8. Two standard transports
  9. 4) Designing MCP tools that LLMs won’t mess up
  10. Tool naming
  11. Tool schemas
  12. Side effects
  13. 5) Why Python + FastMCP is the easiest path
  14. 5a) A tiny “hello tool” example (Python)
  15. ✅ Project: “Smart Notes” MCP Server (Python) + Agent Client
  16. Setup (uv + MCP SDK)
  17. Project structure
  18. The MCP server: notes_server.py
  19. Test the server (2 easy ways)
  20. Option A: Run in an MCP host (Claude Desktop config style)
  21. Option B: Use MCP Inspector (best for debugging)
  22. (Optional but awesome) Build the Python Agent that uses your MCP server
  23. agent_cli.py
  24. 6) Practical tips that make MCP projects feel “production-grade”
  25. Design tools like APIs
  26. Put “read-only context” in resources
  27. Use prompts to standardize behavior
  28. 7) Security and production notes (don’t skip)
  29. If you stay on stdio (local):
  30. If you move to Streamable HTTP:
  31. If you integrate into OpenAI agents/apps:
  32. 8) Where to take this next (easy upgrades)