Skip to main content

The Complete MCP Server Handbook for Developers (April 2026 Edition)

· 16 min read
Yassine El Haddad
Software Developer & Automation Specialist

I build production AI agents, web scrapers, and automation pipelines. Most of what I publish here comes from the actual problems they run into: proxies that get banned, anti-bot stacks that fingerprint your client, RAG that drifts when the underlying data moves. Stack: Python, TypeScript, Go, FastAPI, LangChain, Crawlee, Playwright, deployed on AWS, GCP, and Cloudflare.

The Model Context Protocol (MCP) is the USB-C for AI: a single open standard that connects AI applications to tools, data sources, and workflows. Originated by Anthropic in late 2024 and now an open, community-maintained standard, MCP is supported across a wide range of clients (Claude Desktop and Claude Code, ChatGPT, VS Code, Cursor, and others). The specification covers two transports (local stdio and remote Streamable HTTP, which supersedes the older HTTP+SSE transport), server-side tools, resources, and prompts, and an OAuth 2.1-based authorization flow for remote servers. The public server ecosystem is large and growing fast, spanning official Anthropic servers, vendor servers, and community projects.

If you do not already use Claude, you can try Claude free for a week and follow along with every example in this handbook. The guide covers MCP server development end-to-end, from your first server to production deployment with authentication.

TL;DR:

What you'll learnSection
MCP architecture and how it differs from REST/GraphQL§1–2
Build your first server in TypeScript§3
Build your first server in Python§4
Connect to Claude Desktop and Claude Code§5–6
OAuth 2.1 security and production auth§7
Top 20 production MCP servers§8
MCP for web scraping§9
Enterprise deployment patterns§10

Prerequisites:

  • Node.js 18+ or Python 3.10+
  • Claude Desktop or Claude Code installed
  • Basic understanding of APIs and JSON

What MCP is (and isn't)

MCP is a JSON-RPC 2.0 protocol that defines how AI hosts (Claude, Cursor, VS Code) communicate with tool servers. It replaces ad-hoc function calling implementations with a standardized contract.

┌─────────────────┐ JSON-RPC ┌─────────────────┐
│ MCP Host │◄────────────────────►│ MCP Server │
│ (Claude, │ tools/list │ (your code) │
│ Cursor, etc.) │ tools/call │ │
│ │ resources/read │ Exposes: │
│ │ prompts/list │ - Tools │
│ │ │ - Resources │
│ │ │ - Prompts │
└─────────────────┘ └─────────────────┘

MCP vs REST vs LangChain tools

MCPREST APILangChain tools
DiscoveryAutomatic (tools/list)Manual (OpenAPI spec)Hardcoded in code
Transportstdio, Streamable HTTPHTTPIn-process
AuthOAuth 2.1 (spec-native)Varies (API keys, JWT)App-level
Multi-modelSame server works with Claude, Cursor, CopilotRequires per-client integrationLangChain only
EcosystemLarge, fast-growing server catalogBillions of APIsThousands of tools
Stateful sessionsBuilt-in (session tokens)Stateless by defaultFramework-managed

When to use MCP: You want your tool to work across multiple AI hosts (Claude Desktop, Claude Code, Cursor, VS Code Copilot) without building separate integrations for each.

When to use REST: You need high-throughput, low-latency API access for non-AI clients, or your tool is consumed by traditional web/mobile apps.


The 2026 spec landscape

MCP has matured well beyond simple local tool calling. The areas below matter most when you build a production server. Always confirm the exact shapes against the current official spec for your installed SDK version, since the protocol is actively evolving.

Streamable HTTP transport

Streamable HTTP supersedes the older HTTP+Server-Sent Events (SSE) transport. A single HTTP endpoint handles both request-response and streaming via Server-Sent Events:

POST /mcp → Request (JSON-RPC)
← Response (JSON-RPC or SSE stream)

Advantages over stdio: works across networks, supports Docker/Kubernetes deployments, enables cloud-hosted MCP servers.

Async, long-running operations

Most real MCP work (web scraping, data processing, multi-step jobs) takes longer than a single synchronous response. The common pattern is to accept the request, return quickly with a task or job identifier, and let the client poll or subscribe for completion rather than blocking the connection. A request might look like:

{
"method": "tools/call",
"params": {
"name": "web-scrape",
"arguments": { "url": "https://example.com", "depth": 3 }
}
}

Check the current spec and your SDK for the exact long-running-task and progress-notification APIs, since these have changed over time.

Server-initiated notifications

MCP supports server-to-client notifications, which lets a server tell the client about progress or state changes during a call instead of only replying once at the end. This is what makes streaming progress updates and reactive workflows possible: Claude can react to what the server reports, not just to the initial prompt.

OAuth 2.1 authorization

For remote (HTTP) servers, MCP defines an authorization flow based on OAuth 2.1 with PKCE. A server can require authorization before exposing its tools, and gate sensitive operations behind scoped permissions. Local stdio servers typically rely on the host process and local credentials instead.


Build your first MCP server in TypeScript

A minimal MCP server that exposes a single calculator tool:

mkdir my-mcp-server && cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod

Create src/index.ts:

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: "calculator",
version: "1.0.0",
description: "A simple calculator MCP server",
});

// Define a tool
server.tool(
"calculate",
"Perform basic arithmetic",
{
expression: z.string().describe("Math expression like '2 + 3 * 4'"),
},
async ({ expression }) => {
// WARNING: eval-style execution is used for simplicity. Never do this in production.
const result = Function(`"use strict"; return (${expression})`)();
return {
content: [{ type: "text", text: `Result: ${result}` }],
};
}
);

// Define a resource
server.resource(
"help",
"calculator://help",
async (uri) => ({
contents: [{
uri: uri.href,
mimeType: "text/plain",
text: "Supported operations: +, -, *, /, **, %, ()",
}],
})
);

// Start the server
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Calculator MCP server running on stdio");

Build and test:

# Recommended: use a tsconfig.json for multi-file projects
# npx tsc (with tsconfig.json in root)
# Or for quick single-file compile:
npx tsc src/index.ts --outDir dist --esModuleInterop --moduleResolution node
node dist/index.js

Expected output:

Calculator MCP server running on stdio

For a Streamable HTTP transport version (production deployments):

import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import express from "express";

const app = express();
app.use(express.json());

app.post("/mcp", async (req, res) => {
const transport = new StreamableHTTPServerTransport("/mcp");
await server.connect(transport);
await transport.handleRequest(req, res);
});

app.listen(3001, () => {
console.log("MCP server listening on http://localhost:3001/mcp");
});

Production note: The snippet above is illustrative only. Real Streamable HTTP deployments need a GET handler for optional SSE streams, session IDs (e.g. MCP-Session-Id), Accept / protocol version headers, and Origin validation per the current MCP transport spec. Match your code to the TypeScript SDK examples for your installed SDK version.

For the complete TypeScript walkthrough, see How to Build an MCP Server in TypeScript.


Build your first MCP server in Python

pip install mcp

Create server.py:

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("weather", description="Weather data server")

@mcp.tool()
async def get_weather(city: str) -> str:
"""Get current weather for a city.

Args:
city: City name (e.g., 'London', 'Tokyo')
"""
# Replace with real API call in production
return f"Weather in {city}: 22°C, partly cloudy"

@mcp.resource("weather://cities")
async def list_cities() -> str:
"""List supported cities."""
return "London, Tokyo, New York, Berlin, Sydney"

if __name__ == "__main__":
mcp.run(transport="stdio")

Run:

python server.py

For HTTP transport:

python server.py --transport streamable-http --port 3001

Connect to Claude Desktop

Edit claude_desktop_config.json:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json
  • Linux: ~/.config/Claude/claude_desktop_config.json
{
"mcpServers": {
"calculator": {
"command": "node",
"args": ["/path/to/dist/index.js"]
},
"weather": {
"command": "python",
"args": ["/path/to/server.py"]
}
}
}

Restart Claude Desktop after every config change. Verify servers loaded by clicking the tools icon in the chat input. Your tools should appear there.


Connect to Claude Code

Claude Code loads MCP servers from its global config or project-level .mcp.json:

# Add a server globally
claude mcp add calculator -- node /path/to/dist/index.js

# Or add to project .mcp.json
cat > .mcp.json << 'EOF'
{
"mcpServers": {
"calculator": {
"command": "node",
"args": ["./dist/index.js"]
}
}
}
EOF

Then use it in a Claude Code session:

> Use the calculator tool to compute 2^10 + 15 * 3

Security deep dive: OAuth 2.1, Server-Side Request Forgery (SSRF) prevention, and least privilege

OAuth 2.1 authentication

The MCP spec includes a native auth flow. Server implementation with TypeScript SDK:

// ILLUSTRATIVE ONLY. The OAuth constructor options and requiredScopes
// parameter shown here are not part of the stable @modelcontextprotocol/sdk
// API as of this writing. Verify against the current SDK source at
// https://github.com/modelcontextprotocol/typescript-sdk before using in production.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";

const server = new McpServer({
name: "secure-server",
version: "1.0.0",
auth: {
type: "oauth2",
authorizationUrl: "https://auth.example.com/authorize",
tokenUrl: "https://auth.example.com/token",
scopes: ["read:data", "write:data"],
pkce: true, // Required by OAuth 2.1
},
});

// Tools can require specific scopes
server.tool(
"read-data",
"Read sensitive data",
{ query: z.string() },
async ({ query }, { auth }) => {
// auth.scopes contains granted scopes
if (!auth?.scopes.includes("read:data")) {
throw new Error("Insufficient permissions");
}
// ... handle request
},
{ requiredScopes: ["read:data"] }
);

SSRF prevention

MCP servers that accept URLs from AI clients must validate inputs:

function isAllowedUrl(url: string): boolean {
const parsed = new URL(url);
// Block internal networks
const blocked = ["localhost", "127.0.0.1", "0.0.0.0", "169.254.169.254"];
if (blocked.includes(parsed.hostname)) return false;
// Block private IP ranges
if (parsed.hostname.match(/^(10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.)/)) return false;
// Allow only HTTPS
if (parsed.protocol !== "https:") return false;
return true;
}

Least-privilege scoping

Production MCP servers should expose the minimum viable toolset:

PrincipleImplementation
Read-only by defaultSeparate read and write tools; require explicit opt-in for mutations
Scoped permissionsUse OAuth scopes to gate destructive operations
Rate limitingImplement per-session rate limits to prevent runaway agent loops
Audit loggingLog every tool call with timestamps, inputs, and outputs
Input validationValidate all parameters with Zod/Pydantic before processing

Top 20 production MCP servers

Curated from production deployments. Sources: MCP registry, GitHub, vendor documentation.

Web Data

ServerMaintainerWhat it doesInstall
ApifyApify30,000+ web scraping Actorsnpx -y @apify/actors-mcp-server
FirecrawlFirecrawlURL → Markdown conversionnpx -y firecrawl-mcp
Bright DataBright DataAnti-bot web scrapingnpx -y @brightdata/mcp
Brave SearchBraveWeb search with AI summariesnpx -y @modelcontextprotocol/server-brave-search
Jina ReaderJina AIURL → clean textnpx -y mcp-jinaai-reader, or hosted https://mcp.jina.ai/sse (Jina MCP)

Developer Productivity

ServerMaintainerWhat it doesInstall
GitHubAnthropic (official)Repos, issues, PRs, filesnpx -y @modelcontextprotocol/server-github
FilesystemAnthropic (official)Local file read/write/searchnpx -y @modelcontextprotocol/server-filesystem
PostgreSQLAnthropic (official)Database queriesnpx -y @modelcontextprotocol/server-postgres
SQLiteAnthropic (official)Embedded databasenpx -y @modelcontextprotocol/server-sqlite
GitAnthropic (official)Git operationsnpx -y @modelcontextprotocol/server-git

Communication

ServerMaintainerWhat it doesInstall
SlackAnthropic (official)Channels, messages, searchnpx -y @modelcontextprotocol/server-slack
GmailCommunityEmail read/writenpx -y @gongrzhe/server-gmail-autoauth-mcp (OAuth; run package auth flow per README)
Google DriveCommunityFile managementnpx -y @piotr-agier/google-drive-mcp (set GOOGLE_DRIVE_OAUTH_CREDENTIALS; see npm README)

Business & CRM

ServerMaintainerWhat it doesInstall
HubSpotHubSpot (official)CRM contacts, deals, companiesnpx -y @hubspot/mcp-server
NotionNotion (official npm)Pages, databases, wikisnpx -y @notionhq/notion-mcp-server, or remote https://mcp.notion.com/mcp in clients that support HTTP MCP
Google SheetsCommunitySpreadsheet read/writenpx -y mcp-gsheets@latest (needs GOOGLE_PROJECT_ID + GOOGLE_APPLICATION_CREDENTIALS)
StripeStripe (official)Payments, customers, invoicesnpx -y @stripe/mcp -- --api-key=sk_live_... (see Stripe MCP docs)

Orchestration

ServerMaintainerWhat it doesInstall
PuppeteerAnthropic (official)Browser automationnpx -y @modelcontextprotocol/server-puppeteer
n8nCommunity ecosystemExpose n8n to MCP clientsnpx -y n8n-mcp (verify README for env vars; not the same as the self-hosted n8n UI)
Make.comMakeVisual automationVia Make MCP connector

For web-scraping-specific MCP servers, see MCP Servers for Web Scraping. For business-focused servers, see Top 10 MCP Servers for Marketing and Business.


MCP for web scraping

MCP servers transform AI assistants into autonomous web scrapers. Three approaches:

Approach 1: Apify MCP (structured extraction from 30,000+ Actors)

{
"mcpServers": {
"apify": {
"command": "npx",
"args": ["-y", "@apify/actors-mcp-server"],
"env": { "APIFY_TOKEN": "your_token" }
}
}
}

Then ask Claude: "Use Apify to scrape the top 50 coffee shops from Google Maps in Austin, TX and return as a table."

Approach 2: Firecrawl MCP (URL → Markdown for any page)

{
"mcpServers": {
"firecrawl": {
"command": "npx",
"args": ["-y", "firecrawl-mcp"],
"env": { "FIRECRAWL_API_KEY": "your_key" }
}
}
}

Then ask Claude: "Use Firecrawl to read the pricing page at https://competitor.com/pricing and summarize the plans."

Approach 3: Bright Data MCP (anti-bot bypass for protected sites)

{
"mcpServers": {
"brightdata": {
"command": "npx",
"args": ["-y", "@brightdata/mcp"],
"env": {
"API_TOKEN": "your_token",
"WEB_UNLOCKER_ZONE": "your_zone"
}
}
}
}

For detailed comparisons, see AI Agent Web Tools Comparison 2026 and Apify MCP Server Setup.


Enterprise deployment patterns

Multi-agent coordination

MCP enables agent-to-agent communication where one agent's output becomes another's input:

┌──────────────┐ MCP ┌──────────────┐ MCP ┌──────────────┐
│ Research │────────────►│ Analysis │────────────►│ Report │
│ Agent │ │ Agent │ │ Agent │
│ (Apify MCP) │ │ (DB MCP) │ │ (Slack MCP) │
└──────────────┘ └──────────────┘ └──────────────┘

Production checklist

ConcernSolution
High availabilityDeploy MCP servers behind a load balancer with health checks
ObservabilityStructured logging for every tool call; export to Datadog/Grafana
Secret managementUse Vault or cloud secret managers; never env vars in config files
Version pinningPin MCP SDK versions; test upgrades in staging
Rate limitingImplement per-client quotas to prevent runaway agent loops
Network isolationRun MCP servers in private VPCs; whitelist outbound domains

Common mistakes

MistakeWhy it's badFix
Exposing write APIs without authAny AI client can mutate your dataStart read-only; add OAuth 2.1 for write operations
Missing SSRF protectionAI can be tricked into hitting internal servicesValidate and allowlist all URLs
Loading 10+ MCP serversContext pollution; Claude gets confused about which tool to useLimit to 4–5 active servers; use claude mcp list (Claude Code) and the MCP server directory to pick tools deliberately
Not restarting after config changesClaude Desktop loads MCP configs at startup onlyAlways restart after editing claude_desktop_config.json
Hardcoding API keys in configSecurity risk in shared reposUse environment variables or secret managers
Blocking operations in tool handlersHangs the MCP connection; client times outUse async operations; return task IDs for long-running work

Frequently Asked Questions

MCP is an open standard, originated by Anthropic in late 2024 and now community-maintained, that defines how AI applications (like Claude, ChatGPT, Cursor, and VS Code) communicate with external tools and data sources. Think of it as USB-C for AI: one protocol that connects any compatible AI model to any tool, replacing custom one-off integrations.

The public ecosystem is large and growing quickly, spanning official Anthropic servers (GitHub, Filesystem, PostgreSQL), vendor servers (Apify, Firecrawl, Stripe, HubSpot), and many community-built servers. Rather than chasing a headcount, browse the registry and directory at modelcontextprotocol.io to find current, maintained servers for your use case.

Yes. MCP is model-agnostic. ChatGPT, Cursor, VS Code, and other AI tools support MCP. The protocol is standardized, so any server works with any compatible client. Tool-calling quality does vary by model: current frontier models (for example Claude Sonnet 4.6 and Claude Opus 4.7) tend to handle multi-tool MCP setups most reliably.

The MCP specification includes OAuth 2.1 with PKCE for authentication, scoped tool permissions, and session management. The protocol itself is production-ready. Security depends on implementation: validate inputs, prevent SSRF, use least-privilege scoping, and audit every tool call.

Function calling is model-specific (OpenAI functions, Claude tool use, Gemini function declarations). MCP is a protocol that sits between the model and the tool, providing standardized discovery, invocation, authentication, and streaming. One MCP server serves all compatible AI clients.

Use stdio for local development and Claude Desktop (the simplest setup, where the server runs as a local subprocess). Use Streamable HTTP for production deployments where the MCP server runs on a remote machine, in Docker, or needs to serve multiple clients simultaneously.

Yes. The standard async pattern is: the server accepts the request, returns quickly with a task or job ID, and the client polls or subscribes for completion instead of blocking. This is essential for web scraping, data processing, and any operation that takes more than a few seconds. Check the current spec and your SDK for the exact long-running-task API.

Apify MCP for structured extraction from specific platforms (Google Maps, Amazon, LinkedIn). Firecrawl MCP for converting any URL to clean Markdown. Bright Data MCP for sites with heavy anti-bot protection. See our MCP servers for web scraping guide for detailed comparisons.

Check the MCP logs at ~/Library/Logs/Claude/mcp-*.log (macOS). Common issues: wrong command path, missing environment variables, port conflicts for HTTP servers, and forgetting to restart Claude Desktop after config changes.

Yes. MCP is an open standard with open-source SDKs, and many vendors (Apify, Firecrawl, Stripe, HubSpot) already ship commercial MCP servers. The protocol is designed for ecosystem growth, so building and distributing your own MCP servers is encouraged. Check the license of each SDK and reference server you build on.


MCP is the infrastructure layer that makes AI agents useful beyond chat. The protocol is mature (OAuth 2.1, Streamable HTTP, async job patterns), the ecosystem is deep (official, vendor, and community servers), and it is an open standard with broad client support. Build your first server today, connect it to Claude, and start giving your AI tools access to real data and real systems.

For web scraping MCP servers, browse the Apify Store and connect via @apify/actors-mcp-server. For URL-to-Markdown conversion, get a Firecrawl API key. For visual automation orchestration, sign up on Make.com.

Common mistakes and fixes

MCP server connects but Claude doesn't see any tools.

Restart Claude Desktop after editing claude_desktop_config.json. MCP servers load only at startup. Check ~/Library/Logs/Claude/mcp-*.log for connection errors.

Streamable HTTP transport returns 'connection refused' errors.

Ensure the server is listening on the correct port and host. For Docker deployments, bind to 0.0.0.0, not 127.0.0.1. Check firewall rules and CORS headers.

OAuth 2.1 flow hangs during authorization.

Verify redirect_uri matches exactly (including trailing slashes). Ensure the authorization server supports PKCE. Check that token endpoint returns proper JSON content-type.