2026 BUILD
MCP_SERVER_
FROM_
SCRATCH.

MCP Server development tutorial code and AI tool calling

A model, no matter how capable, cannot query your database, call your APIs, or read local notes without "hands and feet." Model Context Protocol (MCP) standardizes how AI clients connect to external capabilities over JSON-RPC—by 2026 the ecosystem exceeds 13,000 Servers, with OpenAI, Google, and Microsoft fully onboard. The pain point: Function Calling is vendor-specific; switch models and you rebuild integrations. The takeaway: after this guide you can independently develop, debug, and deploy production-ready MCP Servers. Structure: protocol fundamentals → environment setup → Hello World → Tools/Resources/Prompts → HTTP remote → debugging → Docker deployment → personal knowledge base project → ecosystem outlook.

1. What Is MCP? Understand the Protocol Before You Code

1.1 Why MCP Exists

Tool-calling capability evolved through three generations: Function Calling (OpenAI's proprietary format) → Plugins (ChatGPT plugin ecosystem, now fading) → MCP (open standard). Anthropic open-sourced MCP in November 2024 because every new AI client forced a rewrite of tool integrations—the classic N×M explosion. MCP solves standardized communication between AI and external tools: write one Server, use it in Cursor, Claude Desktop, and VS Code. By 2026 governance has moved to AAIF under the Linux Foundation.

1.2 MCP Protocol Architecture

┌────────────────────┐ ┌─────────────────────┐ │ MCP Client │ ◄─────► │ MCP Server │ │ (Claude / Cursor) │ JSON │ (what you build) │ │ │ -RPC │ │ └────────────────────┘ └─────────────────────┘ │ ┌─────────────┼─────────────┐ ▼ ▼ ▼ Tools Resources Prompts (tool calls) (data reads) (prompt templates)

Client: the AI-side host (Claude Desktop, Cursor, custom Agent). Server: the capability provider you develop. Three core primitives: Tools are functions the AI can invoke (search, compute, SQL); Resources are data the AI can read (files, configs, URLs); Prompts are predefined prompt templates with parameter injection.

1.3 Communication Mechanics

The wire format is JSON-RPC 2.0. Transport options: stdio (local subprocess, zero network config) and Streamable HTTP (spec dated 2025-06-18, replaces legacy HTTP+SSE—suited for remote/multi-client setups). Lifecycle: initialization handshake → capability negotiation (tools/list) → request/response → shutdown. Warning: in local stdio mode, never print non-protocol logs to stdout—it breaks JSON-RPC parsing.

1.4 MCP vs Other Approaches

DimensionMCPOpenAI Function CallingLangChain Tools
StandardizationOpen protocol standardVendor proprietaryFramework-bound
Transportstdio / Streamable HTTPHTTPHTTP
Cross-model supportYesNoPartial
Resources / PromptsNative supportNot supportedNot supported
Ecosystem13,000+ Servers (2026)MatureMature

2. Development Environment Setup

2.1 Choose a Language

Python (recommended for beginners): official SDK mcp + FastMCP decorators—concise and approachable. TypeScript (recommended for frontend/full-stack): @modelcontextprotocol/sdk + Zod validation; npm downloads exceed 150M. This guide focuses on Python, with TypeScript notes where useful.

2.2 Environment Setup

# Python environment python -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate pip install "mcp[cli]" # TypeScript environment (reference) npm init -y npm install @modelcontextprotocol/sdk zod

2.3 Project Structure

my-mcp-server/ ├── server.py # Main entry point ├── tools/ # Tool modules │ ├── calculator.py │ └── web_search.py ├── resources/ # Resource modules │ └── file_reader.py ├── prompts/ # Prompt templates │ └── templates.py ├── tests/ │ └── test_tools.py ├── pyproject.toml └── README.md

2.4 Debugging Tools

1) MCP Inspector: npx @modelcontextprotocol/inspector python server.py—browser UI at localhost:6274 for debugging Tools/Resources/Prompts. 2) Claude Desktop: edit ~/Library/Application Support/Claude/claude_desktop_config.json. 3) Cursor: Settings → MCP → add mcpServers config. See our MCP protocol deep dive for full setup details.

3. Your First MCP Server: Hello World

3.1 Minimal MCP Server

from mcp.server.fastmcp import FastMCP mcp = FastMCP("my-first-server") @mcp.tool() def say_hello(name: str) -> str: """Greet a person by name""" return f"Hello, {name}! This is your first MCP tool." if __name__ == "__main__": mcp.run()

3.2 Run and Verify

python server.py # Or debug with MCP Inspector npx @modelcontextprotocol/inspector python server.py

3.3 Connect in Cursor / Claude Desktop

// claude_desktop_config.json or Cursor MCP settings { "mcpServers": { "my-first-server": { "command": "/absolute/path/to/.venv/bin/python", "args": ["/absolute/path/to/server.py"] } } }

Warning: use absolute paths for both the Python interpreter and script. After restarting the client, the say_hello tool should appear in the conversation context.

4. Tools: Build Functions the AI Can Call

4.1 Tool Structure Basics

Function signatures are documentation: parameter types, return types, and docstrings are auto-converted to JSON Schema for the LLM. Naming: lowercase with underscores, semantically clear (web_search beats ws). Error handling: return structured error strings rather than uncaught exceptions that crash the entire Server.

4.2 Input Parameter Types

from pydantic import BaseModel, Field class SearchInput(BaseModel): query: str = Field(description="Search keywords") max_results: int = Field(default=5, description="Maximum results to return") language: str = Field(default="en", description="Result language") @mcp.tool() def web_search(input: SearchInput) -> list[dict]: """Run a web search and return matching results""" # Implement search logic return [{"title": "Example", "url": "https://example.com"}]

4.3 Practical Examples: Five Useful Tools

ToolPurposeKey Implementation
CalculatorEvaluate math expressionsSandbox eval or safe ast parsing
File I/ORead/write local filesRestrict allowed directories; prevent path traversal
HTTP requestsCall external APIshttpx + 30s timeout
Database queriesExecute read-only SQLParameterized queries; block DDL
Time utilitiesCurrent time / timezone conversionzoneinfo standard library

4.4 Async Tools

import httpx @mcp.tool() async def fetch_url(url: str) -> str: """Fetch content from a URL""" async with httpx.AsyncClient(timeout=30.0) as client: response = await client.get(url) return response.text[:10000] # Truncate to prevent token explosion

4.5 Tool Error Handling Best Practices

1) Structured errors: return {"error": "...", "code": "TIMEOUT"} strings. 2) Timeouts: cap all I/O at 30s. 3) Permission checks: whitelist directories and APIs at the Tool layer—do not rely on the LLM to "behave."

5. Resources: Let the AI Read Dynamic Content

5.1 Resource vs Tool

A Resource is a data provider (primarily read-only); a Tool is an action executor. URI addressing: file://, http://, or custom schemes like custom://.

5.2 Static vs Dynamic Resources

import json @mcp.resource("config://app-settings") def get_app_settings() -> str: """Return application configuration""" return json.dumps({"version": "1.0", "env": "production"}) @mcp.resource("user://{user_id}/profile") def get_user_profile(user_id: str) -> str: """Return user profile by ID""" return json.dumps({"user_id": user_id, "name": "Demo"})

5.3 Resource Types

Text resources (text/plain, application/json), binary resources (images/PDFs returned as base64), and streaming resources (live market data—requires Streamable HTTP).

5.4 Practical Example: Filesystem Resource Server

List directories, read file contents, optionally subscribe to file changes (resources/subscribe). In production, always restrict the root directory—reference the official mcp-server-filesystem allowlist design.

6. Prompts: Define Reusable Prompt Templates

6.1 What Is an MCP Prompt?

Predefined prompt fragments the AI can invoke directly; supports dynamic parameter injection; improves team prompt consistency and maintainability. Complements Cursor Agent Skills: MCP Prompt = protocol-layer template, Skill = operational playbook.

6.2 Create a Prompt Template

from mcp.types import PromptMessage, TextContent @mcp.prompt() def code_review_prompt(language: str, code: str) -> list[PromptMessage]: """Code review prompt template""" return [ PromptMessage( role="user", content=TextContent( type="text", text=f"""Review the following {language} code: 1. Code quality and readability 2. Potential bugs and security issues 3. Performance optimization suggestions ```{language} {code} ```""" ) ) ]

6.3 Multi-Turn Prompt Templates

Templates can include both user and assistant turns. Use cases: interview simulation (assistant plays interviewer), debugging assistant (assistant asks clarifying questions first).

7. Advanced: HTTP Transport (Remote MCP Server)

7.1 stdio vs Streamable HTTP

TraitstdioStreamable HTTP
DeploymentLocal processRemote server
LatencyVery low (<5ms)Network-dependent (50–200ms)
Multi-clientNot supportedSupported
Best forLocal tools, IDE pluginsSaaS / team sharing / 24×7

Warning: legacy HTTP+SSE was deprecated in the 2025-06-18 spec—use Streamable HTTP for new projects.

7.2 Implement HTTP Transport

from mcp.server.fastmcp import FastMCP mcp = FastMCP("remote-server") if __name__ == "__main__": mcp.run(transport="streamable-http", host="127.0.0.1", port=8000)

Production deployments can use uvicorn/gunicorn behind a reverse proxy. Serverless (Cloud Run/Lambda) needs stateless_http=True because in-memory sessions are lost on cold start.

7.3 Authentication and Security

Bearer Token auth, API Key middleware, CORS allowlists, rate limiting (recommended 100 req/min/IP). Bind to 127.0.0.1 in local dev—never expose 0.0.0.0 without authentication. Over 30 MCP-related CVEs were disclosed in 2026, including CVSS 9.6 RCE in mcp-remote.

8. Debugging and Testing

8.1 Debug with MCP Inspector

After launch, the UI lets you: list Tools → invoke manually → inspect raw JSON-RPC messages → simulate timeout/error scenarios. Roughly 10× faster than debugging through a live LLM connection.

8.2 Write Unit Tests

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 "4" in result.content[0].text

8.3 Common Errors and Fixes

ErrorCauseFix
Tool not visible in AIWrong config pathVerify absolute paths in config.json
JSON serialization failureUnsupported return typeConvert to str or dict
Timeout disconnectTool runs too longAsync + 30s timeout
Permission deniedFile path restrictedConfigure allowlist directories

9. Production Deployment

9.1 Docker Containerization

FROM python:3.12-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 8000 CMD ["python", "server.py"]

9.2 Deploy to Cloud Services

Railway / Render: one-click deploy, suited for personal projects (~$5–20/month). AWS Lambda / Google Cloud Run: serverless, pay per invocation. Self-hosted VPS: Nginx reverse proxy + Let's Encrypt + systemd/launchd keep-alive.

9.3 Monitoring and Observability

Structured logs (JSON Lines), Prometheus metrics (mcp_tool_calls_total), Sentry error alerts, /health endpoint. Recommended P99 latency alert threshold: 5s.

9.4 Version Management and Compatibility

Declare MCP protocol version at handshake; keep tool upgrades backward-compatible (add optional params, do not change required ones); use capabilities negotiation to prevent Clients from calling nonexistent Tools.

10. Project Walkthrough: Personal Knowledge Base MCP Server

10.1 Requirements

Let the AI search local Markdown notes, run semantic retrieval, and create/update notes. In Cursor, ask: "What did I write about MCP last week?"

10.2 Technology Choices

ComponentChoiceRationale
Vector databaseChromaDB / QdrantLightweight local, zero ops
Embedding modeltext-embedding-3-small1536 dimensions, low cost
File watcherwatchfilesAuto re-index on note changes

10.3 Core Implementation

Four modules: index_notes tool (scan directory and build index), semantic_search tool (vector retrieval Top-K), write_note tool (create/append Markdown), notes://{path} resource (read single file directly). Indexing ~1,000 notes takes 2–5 minutes on M4 Pro.

10.4 Demo

User asks in Cursor: "What did my notes say about MCP deployment last week?" → Agent calls semantic_search(query="MCP deployment", days=7) → returns 3 relevant snippets (similarity 0.82–0.91) → LLM synthesizes an answer with citations. The entire note library never enters the context window.

11. MCP Ecosystem and Outlook

11.1 Recommended MCP Servers

mcp-server-filesystem (filesystem), mcp-server-github (repo operations), mcp-server-brave-search (web search), mcp-server-postgres (database), mcp-server-slack (messaging). The official registry tracks 13,000+ Servers.

11.2 Ecosystem Trends (2026)

All four major vendors fully support MCP; MCP Marketplaces are emerging; enterprise OAuth 2.1 authentication is on the roadmap; complements Google A2A protocol (MCP = vertical tool layer, A2A = horizontal Agent orchestration).

11.3 Next Steps

① Read the modelcontextprotocol.io spec; ② publish your first public MCP Server; ③ explore MCP + Agent combinations; ④ contribute to the open-source ecosystem (Python/TS SDKs).

12. Five-Step Launch Checklist

Step 1 — Write Hello World with FastMCP, verify in Inspector. Step 2 — Register 3 business Tools + 1 Resource. Step 3 — Connect Cursor / Claude Desktop (absolute path config). Step 4 — Switch to Streamable HTTP for remote deployment as needed. Step 5 — Docker + monitoring + security audit for production.

13. Citable Numbers

MetricValue
MCP Server ecosystem (2026)13,000+
18-month growth multiplier7.8× (1,200 → 9,400+)
TS SDK cumulative downloads150M+
MCP-related CVEs in 202630+
Recommended Tool I/O timeout30s
Knowledge base index (1,000 notes)2–5 min (M4 Pro)

14. Case Study: From Local stdio to Mac Remote Compute Node

An AI engineer ran Cursor plus 5 stdio MCP Servers (filesystem, postgres, brave-search, custom knowledge base, browser tools) on a MacBook Air. Unified memory at 24GB sat at 19GB resident—the laptop throttled under heat and lost sessions on lid close. Migration: keep only the Cursor Host locally; deploy all 5 Servers via Streamable HTTP on a remote Mac mini (64GB unified memory) with launchd keep-alive; connect locally via url: http://node.macgpu.local:8000/mcp. Tool-call P99 latency went from local 180ms to 95ms (remote node, no throttling)—actually more stable.

Windows/Linux VPS can run MCP Servers, but for graphics/multimedia + AI toolchain workloads alongside Xcode, ComfyUI, and Final Cut, macOS still runs smoother. Local stdio suits development and validation; 24×7 production uptime fits remote Apple Silicon nodes better: unified memory handles concurrent Tool calls while the laptop stays an orchestrator.

If you need a stable, rentable environment to host MCP Server clusters and Agent Gateways, consider MACGPU remote Mac nodes: 24×7 uptime, HTTP reverse proxy pre-configured, unified memory that does not get eaten by subprocess sprawl—from "it runs" to "it runs reliably" is one node away.