Understand MCP before you write code: evolution, architecture, and why it beats one-off integrations
Model Context Protocol is the open standard Anthropic published in November 2024 to unify how AI clients discover and invoke external capabilities. The bottom line: write one Server, connect it from Claude Desktop, Cursor, or any MCP-compatible client — swap the underlying LLM without rewriting your tool layer.
Function Calling era: Each LLM vendor shipped proprietary schemas. GPT, Claude, and Gemini each needed separate adapter code.
Plugins wave: ChatGPT Plugins proved demand for live data, but remained OpenAI-specific and closed.
MCP standard: Anthropic open-sourced a vendor-neutral protocol; by 2026 OpenAI, Google, and Microsoft all adopted it under Linux Foundation AAIF governance.
Design goal: Standardize AI-to-tool communication the way HTTP standardized web services — one wire format, runtime discovery, self-describing tools.
┌────────────────────┐ ┌─────────────────────┐
│ MCP Client │ ◄─────► │ MCP Server │
│ (Claude / Cursor) │ JSON │ (you build this) │
│ │ -RPC │ │
└────────────────────┘ └─────────────────────┘
│
┌─────────────┼─────────────┐
▼ ▼ ▼
Tools Resources Prompts
(actions) (data reads) (templates)
| Capability | Role | Example |
|---|---|---|
| Tools | Functions AI invokes | Search, calculate, SQL query, HTTP call |
| Resources | Data AI reads via URI | Config files, user profiles, note indexes |
| Prompts | Reusable prompt templates | Code review, interview simulation, debug assistant |
Wire format is JSON-RPC 2.0. Lifecycle: initialize handshake → capability negotiation → request/response loop → graceful shutdown. Two transport modes dominate: stdio (local subprocess, zero network deps) and HTTP + SSE (remote, multi-client, team-shared).
| Dimension | MCP | OpenAI Function Calling | LangChain Tools |
|---|---|---|---|
| Standardization | Open protocol | Vendor proprietary | Framework-bound |
| Transport | stdio / HTTP+SSE | HTTP only | HTTP only |
| Cross-model | Yes | No | Partial |
| Resources / Prompts | Native | Not supported | Not supported |
| Ecosystem (2026) | 10,000+ Servers | Mature but locked | Mature but coupled |
MCP is not another wrapper around Function Calling — it is the protocol layer that lets AI discover, describe, and invoke tools at runtime without hard-coded integration per model.
Environment setup and Hello World: from venv to a tool visible in Cursor
You can have a working MCP Server running in under ten minutes. Python with FastMCP is the recommended starting path; TypeScript mirrors the same concepts with @modelcontextprotocol/sdk.
| Language | SDK | Best for |
|---|---|---|
| Python | mcp (FastMCP) | Backend / AI engineers, fastest Hello World |
| TypeScript | @modelcontextprotocol/sdk | Frontend / full-stack teams on Node |
python -m venv .venv source .venv/bin/activate pip install mcp npm init -y npm install @modelcontextprotocol/sdk
Recommended project layout:
my-mcp-server/ ├── server.py ├── tools/ │ ├── calculator.py │ └── web_search.py ├── resources/ │ └── file_reader.py ├── prompts/ │ └── templates.py ├── tests/ │ └── test_tools.py └── pyproject.toml
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("my-first-server")
@mcp.tool()
def say_hello(name: str) -> str:
return f"Hello, {name}! This is your first MCP tool."
if __name__ == "__main__":
mcp.run()
Run and verify with the official MCP Inspector:
python server.py npx @modelcontextprotocol/inspector python server.py
Connect to clients: add the Server launch command in Claude Desktop claude_desktop_config.json or point Cursor mcp.json at the same command. After restart, confirm the tool appears in the client tool list and responds to a test invocation.
Note: STDIO Servers must stay running as a subprocess while the client is open. Use absolute paths in config to avoid working-directory mismatches — the most common Hello World failure.
Tools, Resources, and Prompts: the three capability layers your Server must expose
A production Server registers Tools for actions, Resources for readable data, and Prompts for reusable templates. Function signatures and docstrings become the AI-facing documentation — no separate OpenAPI doc required.
Tool structure: name from function name, parameters from type hints, description from docstring. Use Pydantic models for complex inputs:
from pydantic import BaseModel, Field
class SearchInput(BaseModel):
query: str = Field(description="Search keywords")
max_results: int = Field(default=5, description="Max results")
language: str = Field(default="en", description="Result language")
@mcp.tool()
def web_search(input: SearchInput) -> list[dict]:
...
| Practical tool | What it does | Key pattern |
|---|---|---|
| Calculator | Evaluate math expressions | Safe eval or AST parser; return string |
| File read/write | Local filesystem I/O | Whitelist allowed directories |
| HTTP request | Call external APIs | Async with httpx; timeout + status check |
| Database query | Run read-only SQL | Parameterized queries; block DDL |
| Time / timezone | Current time, conversion | Return ISO 8601 strings |
import httpx
@mcp.tool()
async def fetch_url(url: str) -> str:
async with httpx.AsyncClient(timeout=30.0) as client:
response = await client.get(url)
response.raise_for_status()
return response.text
Error handling: return structured error dicts for recoverable failures; raise only for unrecoverable states. Always set timeouts on I/O tools. Validate permissions before filesystem or database access.
Resources differ from Tools: Resources supply data via URI, Tools execute actions. Static resources use fixed URIs; dynamic resources accept path parameters.
@mcp.resource("config://app-settings")
def get_app_settings() -> str:
return json.dumps({"version": "1.0", "env": "production"})
@mcp.resource("user://{user_id}/profile")
def get_user_profile(user_id: str) -> str:
user = db.query_user(user_id)
return json.dumps(user)
| Resource type | MIME | Use case |
|---|---|---|
| Text | text/plain, application/json | Config, logs, structured data |
| Binary | image/*, application/pdf | Documents, screenshots |
| Streaming | Event streams | Live metrics, file watch feeds |
A filesystem resource Server lists directories, reads file contents, and can subscribe to change events. Prompts ship pre-built conversation templates with dynamic parameter injection:
from mcp.types import PromptMessage, TextContent
@mcp.prompt()
def code_review_prompt(language: str, code: str) -> list[PromptMessage]:
return [
PromptMessage(
role="user",
content=TextContent(
type="text",
text=f"Review this {language} code for bugs, security, and performance:\n```{language}\n{code}\n```"
)
)
]
Multi-turn Prompts mix user and assistant roles — useful for interview simulations, debug walkthroughs, and onboarding flows that need consistent structure across sessions.
HTTP transport, authentication, debugging, and automated testing
Local STDIO covers dev; team-shared or SaaS deployments need HTTP + SSE. FastMCP supports streamable-http transport with minimal code change.
| Trait | stdio | HTTP + SSE |
|---|---|---|
| Deployment | Local subprocess | Remote server |
| Latency | Near zero | Network-dependent |
| Multi-client | One client per process | Many concurrent clients |
| Best for | Personal dev tools | Team / production SaaS |
mcp = FastMCP("remote-server", transport="streamable-http")
if __name__ == "__main__":
mcp.run(host="0.0.0.0", port=8000)
Production HTTP Servers need auth and guardrails: Bearer Token or API Key middleware, CORS restricted to known client origins, and rate limiting on tool endpoints that hit external APIs or databases.
MCP Inspector remains the primary debug UI: launch it against your Server command, call Tools interactively, inspect JSON-RPC request/response pairs, and simulate error paths before wiring a client.
import pytest
from mcp.client.session import ClientSession
from mcp.client.stdio import StdioServerParameters, stdio_client
@pytest.mark.asyncio
async def test_calculator_tool():
server_params = StdioServerParameters(
command="python",
args=["server.py"]
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
result = await session.call_tool("calculate", {"expression": "2 + 2"})
assert result.content[0].text == "4"
| Error | Cause | Fix |
|---|---|---|
| Tool not visible in client | Wrong config path or command | Verify absolute path in mcp.json / claude_desktop_config.json |
| JSON serialization failure | Non-serializable return type | Return str or dict; avoid custom objects |
| Timeout disconnect | Blocking I/O in sync tool | Switch to async; add explicit timeout |
| Permission denied | Filesystem path outside whitelist | Configure allowed directories explicitly |
Production deployment, knowledge-base project, ecosystem picks, and six-step rollout
Moving from Hello World to production means containerization, observability, version negotiation, and a reference project that proves real value. The capstone below is a personal knowledge-base MCP Server — semantic search over local Markdown notes inside Cursor.
Inventory capabilities: List database queries, file I/O, internal APIs, and build triggers Agents need. Tag each as read-only or write.
Pick transport: STDIO for local dev; HTTP+SSE when multiple teammates or CI agents share one Server on a stable host.
Implement and Schema: Register tools via official SDKs. Write JSON Schema for every parameter and return shape.
Configure clients: Point Cursor mcp.json or Claude Desktop config at launch command or remote URL. Document required env vars.
Test discovery and invocation: Confirm tools/list returns full catalog. Run sample tools/call chains; verify multi-step context persists.
Deploy to production host: Containerize with Docker; ship to Railway/Render for quick starts, Cloud Run/Lambda for serverless, or a dedicated cloud Mac when you need Apple toolchain co-location and 24/7 uptime. See the pricing page.
Deployment options: Docker wraps Python deps cleanly. Railway and Render offer one-click deploys for personal projects. AWS Lambda and Google Cloud Run suit bursty serverless workloads. Self-hosted VPS plus Nginx reverse proxy fits teams with existing infra. Add structured logging, Prometheus metrics on tool call counts, Sentry for error alerts, and a /health endpoint. Declare MCP protocol version and negotiate capabilities at handshake so clients degrade gracefully on upgrades.
Knowledge-base project: Index local Markdown with ChromaDB or Qdrant, embed with text-embedding-3-small, watch file changes via watchfiles. Expose three Tools — index notes, semantic search, write/update note — plus a Resource listing indexed files. In Cursor, ask "What did I write about MCP last week?" and the Agent calls your search tool, returning relevant snippets.
| Community Server | Capability |
|---|---|
| mcp-server-filesystem | Directory listing, file read/write |
| mcp-server-github | Repo issues, PRs, code search |
| mcp-server-brave-search | Web search via Brave API |
| mcp-server-postgres | Parameterized SQL queries |
| mcp-server-slack | Channel messages and posting |
By 2026 every major AI client ships native MCP support. Marketplace directories list thousands of Servers; enterprise adoption is driving OAuth 2.0 auth standards and security audits on exposed endpoints. Next steps: read the full protocol spec at modelcontextprotocol.io, publish your first Server, combine MCP with Agent Skills for orchestrated workflows.
MCP ecosystem scale (2026): Public MCP Server count exceeds 10,000, with network effects comparable to early HTTP web growth (sources: openEuler / Jacar community aggregates).
Integration cost reduction: MCP standardization cuts AI tool integration development cost by 38–55% versus per-vendor adapters (sources: Atlan / WorkOS industry analysis).
Startup barrier drop: Standardized tool interfaces lower AI startup entry barriers by roughly 62%; traditional systems integrator custom work falls about 43% (sources: industry landscape surveys).
| Approach | Production MCP Server | Main gap |
|---|---|---|
| Laptop STDIO | Fast local dev | Lid close kills process; no 24/7 |
| Generic Linux VPS | HTTP+SSE deployable | No Apple toolchain; iOS CI mismatch |
| Serverless (Lambda/Cloud Run) | Low ops for bursty traffic | Cold start; stateful session limits |
| KVMNODE cloud Mac + MCP | Dedicated node, launchd, snapshot rollback | Requires rental planning |
Lay out the alternatives honestly. Running HTTP+SSE on a primary MacBook breaks on lid close and OS updates. Serverless without session affinity drops multi-step Agent context. Exposing MCP endpoints without auth invites prompt-injection and unauthorized tool calls — roughly 1,000 unprotected Servers were indexed in 2026 audits. For teams that need Apple Silicon, 24/7 uptime, and isolation between MCP Server, iOS CI, and Agent Gateway workloads, renting a dedicated KVMNODE Mac Mini M4 or M4 Pro is often the better answer: flexible daily, weekly, or monthly terms, launchd-managed processes, snapshot rollback. Compare tiers on the pricing page, setup details in the help center, and provision through the order page when your MCP stack needs a host that stays up after you close the laptop.