Table of Contents
- 01. Why MCP exists
- 02. What a custom MCP server is
- 03. From M×N integrations to one protocol
- 04. Where custom servers help
- 05. Choosing a language
- 06. Tutorial 1: a Python web inspector
- 07. Tutorial 2: a TypeScript Hacker News feed
- 08. Tutorial 3: a Rust file integrity checker
- 09. How Claude uses MCP
- 10. Security risks and defenses
- 11. Getting started
- 12. References
01Why MCP exists
A language model on its own can reason about a contract or refactor a Rust crate, but it cannot query your database, open a GitHub issue, or check whether your website is up. It needs a connection to those systems, and before late 2024 each connection was a custom integration.
The Model Context Protocol (MCP) standardizes that connection. Anthropic open-sourced it on November 25, 2024, describing it in Anthropic's original MCP launch post as an open standard for connecting AI assistants to the systems where data lives. A year later, the official MCP blog's foundation announcement reported more than 97 million monthly SDK downloads, 10,000 active servers, and client support in ChatGPT, Claude, Cursor, Gemini, Microsoft Copilot and VS Code.
Many public servers wrap commercial products such as Notion, Sentry or GitHub. This article covers the other kind: a custom server you write for your own systems, in a language you already use, which Claude can call once you register it.
02What a custom MCP server is
An MCP server is a program that exposes three kinds of capability: tools (functions the model can call), resources (data it can read) and prompts (reusable templates). A host application such as Claude Desktop, Claude Code or an app built on the Claude API runs an MCP client, and that client can connect to many servers at once.
A custom server wraps something specific to you, such as a billing API, a Postgres replica, an internal wiki or an old SOAP service. Once it speaks MCP, any MCP-compatible client can use it without new integration code.
03From M×N integrations to one protocol
Connecting M AI applications to N systems used to take M×N integrations. Anthropic's launch post described the problem directly: every new data source needed its own custom implementation. With MCP, each application implements a client once and each system implements a server once, so the count drops to M+N.
The protocol also no longer belongs to one company. On December 9, 2025, Anthropic donated MCP to the Agentic AI Foundation, a directed fund under the Linux Foundation co-founded by Anthropic, Block and OpenAI, with support from Google, Microsoft, AWS, Cloudflare and Bloomberg.
04Where custom servers help
Anything with an API, a command line, a database driver or a file format can sit behind an MCP server. These are common starting points.
Engineering and DevOps
The Claude Code docs give this example: implement the feature described in a JIRA ticket and open a GitHub pull request, from one request.
Data and analytics
A server with read-only database credentials lets Claude answer questions such as monthly revenue against live tables.
Web monitoring
Uptime sweeps, title and metadata checks, price watching and public API polling. Tutorial 1 covers the first two.
Business apps
Notion, Asana, Stripe, HubSpot and Sentry already publish remote servers. Custom servers cover the in-house tools they don't.
Private and legacy systems
Claude's MCP tunnels documentation describes reaching servers inside a private network without opening inbound ports.
Personal automation
Notes, local files, home-lab scripts and research pipelines become tools Claude can call one after another.
SerpApi's first-half 2026 MCP review counts roughly 9,400 to 17,000+ public servers across the major registries and finds that fewer than 5% are monetized. It also reports momentum moving toward vendor-built connectors and private servers that companies run for their own systems.
05Choosing a language
The official MCP SDK tier list places TypeScript, Python, C#, Go, Rust and Ruby at Tier 1, Java at Tier 2, and Swift, PHP and Kotlin at Tier 3. Every SDK can build servers that expose tools, resources and prompts.
The tutorials target the 2026-07-28 specification, which became the stable 2026-07-28 MCP release on July 28, 2026. All three run over stdio, the transport Claude Desktop and Claude Code use when they start a local server as a child process.
06Tutorial 1: a Python web inspector
This server gives Claude two tools that report a public page's status code, response time and title. It uses the MCP Python SDK v2 repository, where the MCPServer class builds each tool's JSON Schema from its type hints.
# Create a project and add the MCP Python SDK v2 plus httpx for HTTP requests
uv init web-inspector && cd web-inspector
uv add "mcp[cli]" httpx
"""server.py - an MCP server that lets Claude inspect public web pages."""
import re
import time
import httpx # async HTTP client used to fetch pages
from mcp.server import MCPServer # high-level server class in SDK v2
# The name hosts such as Claude Code display for this server
mcp = MCPServer("WebInspector")
# Precompiled pattern that pulls the <title> text out of raw HTML
TITLE_RE = re.compile(r"<title[^>]*>(.*?)</title>", re.IGNORECASE | re.DOTALL)
async def _inspect(client: httpx.AsyncClient, url: str) -> dict:
# Shared helper: time one request and extract what Claude needs
start = time.perf_counter()
response = await client.get(url)
elapsed_ms = round((time.perf_counter() - start) * 1000)
match = TITLE_RE.search(response.text)
return {
"final_url": str(response.url), # after any redirects
"status": response.status_code,
"response_ms": elapsed_ms,
"title": match.group(1).strip() if match else "",
}
@mcp.tool()
async def inspect_page(url: str) -> dict:
"""Fetch a public URL and report HTTP status, response time and page title."""
# The docstring becomes the tool description Claude reads when choosing tools
async with httpx.AsyncClient(follow_redirects=True, timeout=15) as client:
return await _inspect(client, url)
@mcp.tool()
async def check_many(urls: list[str]) -> list[dict]:
"""Check several URLs in one call, reporting failures instead of raising."""
results = []
async with httpx.AsyncClient(follow_redirects=True, timeout=15) as client:
for url in urls:
try:
results.append(await _inspect(client, url))
except httpx.HTTPError as exc:
# A down site is data for Claude, not a crash for the server
results.append({"final_url": url, "error": str(exc)})
return results
if __name__ == "__main__":
# run() with no arguments starts a stdio server, the transport local hosts launch
mcp.run()
# Open the server in the MCP Inspector to call each tool by hand
uv run mcp dev server.py
# Register it with Claude Code; everything after -- is the launch command
claude mcp add web-inspector -- uv run --with "mcp[cli]" --with httpx mcp run /absolute/path/to/server.py
Run /mcp inside Claude Code to check that the server connected, then ask something like “Check thomascherickal.com and thomascherickal.github.io and tell me which responds faster.” For Claude Desktop, the Python SDK's connect-to-a-host guide shows uv run mcp install server.py writing the config entry for you.
07Tutorial 2: a TypeScript Hacker News feed
The official Hacker News Firebase API needs no key, so it works well as an example of wrapping a REST API. The server uses the @modelcontextprotocol/server package from the MCP TypeScript SDK v2 repository.
# ES module project with the v2 server package, Zod for schemas, tsx to run TypeScript directly
mkdir hn-feed && cd hn-feed
npm init -y && npm pkg set type=module
npm install @modelcontextprotocol/server zod
npm install -D tsx typescript
// index.ts - an MCP server that gives Claude a live Hacker News feed
import { McpServer } from '@modelcontextprotocol/server';
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
import * as z from 'zod/v4';
// Base URL of the public, key-free Hacker News API
const HN = 'https://hacker-news.firebaseio.com/v0';
// Name and version are what MCP clients display for this server
const server = new McpServer({ name: 'hn-feed', version: '1.0.0' });
// Register a tool; the Zod schema becomes the JSON Schema Claude sees
server.registerTool(
'top_stories',
{
description: 'Return the current top Hacker News stories with title, score and link',
inputSchema: z.object({
count: z.number().int().min(1).max(20).default(5) // cap output to protect context
})
},
async ({ count }) => {
// 1. Fetch the ranked list of top story IDs (up to 500)
const ids: number[] = await (await fetch(`${HN}/topstories.json`)).json();
// 2. Fetch the first `count` stories in parallel
const stories = await Promise.all(
ids.slice(0, count).map(async (id) => (await fetch(`${HN}/item/${id}.json`)).json())
);
// 3. One readable line per story; text content is what Claude reads back
const lines = stories.map((s: any, i: number) =>
`${i + 1}. ${s.title} (${s.score} points) ${s.url ?? `https://news.ycombinator.com/item?id=${s.id}`}`
);
return { content: [{ type: 'text', text: lines.join('
') }] };
}
);
// Serve over stdio so Claude Code can launch this file as a child process
async function main() {
await server.connect(new StdioServerTransport());
}
main();
# Claude Code launches the server with tsx whenever a session starts
claude mcp add hn-feed -- npx tsx /absolute/path/to/hn-feed/index.ts
You can then ask Claude for the current top stories with a short summary of each. The same structure works for a weather service, a CRM or an internal REST API.
08Tutorial 3: a Rust file integrity checker
Rust suits tools that need speed and memory safety and that you want to ship as a single binary. The official Rust SDK, the rmcp crate, implements the 2026-07-28 spec, stays compatible with 2025-11-25, and generates tool routing from macros.
Step 1 · Create The Crate# rmcp with server and stdio transport features, plus tokio, serde, anyhow and sha2
cargo new integrity-mcp && cd integrity-mcp
cargo add rmcp --features server,transport-io
cargo add tokio --features full
cargo add serde --features derive
cargo add anyhow sha2
// main.rs - an MCP server that lets Claude verify files with SHA-256
use rmcp::{handler::server::wrapper::Parameters, schemars, tool, tool_router, ServiceExt, transport::stdio};
use sha2::{Digest, Sha256};
// Tool input; field doc comments become descriptions in the generated JSON Schema
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
struct HashParams {
/// Absolute path of the file to hash
path: String,
}
// The server type; Clone lets rmcp hand a copy to each request
#[derive(Clone)]
struct Integrity;
// tool_router(server_handler) generates routing and the ServerHandler impl
#[tool_router(server_handler)]
impl Integrity {
#[tool(description = "Compute the SHA-256 checksum of a local file")]
fn sha256_file(&self, Parameters(HashParams { path }): Parameters<HashParams>) -> String {
// Read the file and return a readable message on failure instead of panicking
match std::fs::read(&path) {
Ok(bytes) => format!("{:x} {}", Sha256::digest(&bytes), path),
Err(e) => format!("error reading {path}: {e}"),
}
}
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Serve over stdin/stdout and keep running until the host disconnects
let service = Integrity.serve(stdio()).await?;
service.waiting().await?;
Ok(())
}
# Compile a release binary, then hand its absolute path to Claude Code
cargo build --release
claude mcp add integrity -- /absolute/path/to/integrity-mcp/target/release/integrity-mcp
With the server registered, you can ask Claude to hash the installers in a folder and compare the results with published checksums. rmcp feature flags change between releases, so check the crate README if the build fails.
09How Claude uses MCP
Claude reaches MCP servers through three surfaces.
Claude Desktop and Claude Code
Both start local stdio servers and connect to remote HTTP servers. The Claude Code MCP reference documents claude mcp add with local, project and user scopes, a .mcp.json file a team can commit, OAuth sign-in through /mcp, and tool search, which loads tool definitions only when needed so a large set of servers doesn't fill the context window. Claude Code also offers an mcp-server-dev plugin whose build skill scaffolds a new server.
Anthropic's launch post noted that Claude 3.5 Sonnet could build MCP server implementations quickly. With today's tooling, Claude can write a server, Claude Code can register it, and Claude can then call its tools.
The Claude API MCP connector
For your own applications, the Claude API MCP connector documentation describes how the Messages API calls a remote MCP server directly, with no client code of your own. It currently supports tool calls only, requires a server reachable over public HTTPS, and lets you allowlist or denylist individual tools.
import anthropic
client = anthropic.Anthropic() # reads ANTHROPIC_API_KEY from the environment
response = client.beta.messages.create(
model="claude-opus-5-5",
max_tokens=1000,
messages=[{"role": "user", "content": "Which of my monitored sites are down?"}],
# Where the remote server lives and how to authenticate to it
mcp_servers=[{
"type": "url",
"url": "https://mcp.example.com/mcp",
"name": "web-inspector",
"authorization_token": "YOUR_TOKEN",
}],
# Which of its tools Claude may use; here, all of them
tools=[{"type": "mcp_toolset", "mcp_server_name": "web-inspector"}],
betas=["mcp-client-2025-11-20"],
)
print(response.content)
What travels over the wire
The 2026-07-28 revision made the protocol stateless. WorkOS's breakdown of the stateless spec explains that the initialize handshake and the Mcp-Session-Id header were removed, so each request carries its own context. Cloudflare's next-generation MCP write-up adds that the new Mcp-Method and Mcp-Name headers let gateways route and rate-limit requests without parsing the JSON body.
10Security risks and defenses
A server that can reach your database, inbox and cloud console also gives an attacker more to work with. SerpApi's review records more than thirty MCP-related CVEs filed in January and February 2026 and describes tool poisoning, where a server hides instructions inside a tool description, as a mainstream concern. The Claude Code docs tell users to verify that they trust each server, because servers that fetch external content can expose them to prompt injection.
The defenses in the sources above are ordinary security practice:
- Read-only credentials: Give database servers read-only credentials, as the Claude Code docs do in their Postgres example.
- Granular toolsets: Use the API's
mcp_toolsetto disable every tool by default and enable only the ones you name, or to deny destructive tools. - Code auditability: Prefer servers whose code you have read, including the ones you write yourself.
- Idempotency keys: Add idempotency keys to tools that charge, send or provision, because WorkOS notes that the new spec removed stream resumability.
- Gateway-level headers: Apply per-tool authorization at the gateway with the new routing headers, which avoids inspecting request bodies.
11Getting started
Pick one system you use often, wrap it with the SDK that matches your stack, and register it with claude mcp add. The three tutorials share one structure, so most of the work goes into the tool functions themselves.
When a local server proves useful, serve it over Streamable HTTP with OAuth so it can run behind a load balancer and the Claude API connector can reach it.
12References
- Anthropic, “Introducing the Model Context Protocol,” November 25, 2024. https://www.anthropic.com/news/model-context-protocol
- Anthropic, “Donating the Model Context Protocol and establishing the Agentic AI Foundation,” December 9, 2025. https://www.anthropic.com/news/donating-the-model-context-protocol-and-establishing-of-the-agentic-ai-foundation
- Model Context Protocol Blog, “MCP joins the Agentic AI Foundation,” December 9, 2025. https://blog.modelcontextprotocol.io/posts/2025-12-09-mcp-joins-agentic-ai-foundation/
- Model Context Protocol Blog, “Introducing the MCP Registry,” September 8, 2025. https://blog.modelcontextprotocol.io/posts/2025-09-08-mcp-registry-preview/
- Model Context Protocol, “SDKs (official SDK tiers).” https://modelcontextprotocol.io/docs/sdk
- Model Context Protocol, “Release 2026-07-28 (stable specification),” GitHub, July 28, 2026. https://github.com/modelcontextprotocol/modelcontextprotocol/releases/tag/2026-07-28
- MCP Python SDK (v2), GitHub repository. “Python SDK v2 on GitHub.” https://github.com/modelcontextprotocol/python-sdk
- MCP Python SDK docs, “Connect to a real host.” https://py.sdk.modelcontextprotocol.io/get-started/real-host/
- MCP TypeScript SDK (v2), GitHub repository. “TypeScript SDK v2 on GitHub.” https://github.com/modelcontextprotocol/typescript-sdk
- MCP Rust SDK (rmcp), GitHub repository. “Rust SDK rmcp on GitHub.” https://github.com/modelcontextprotocol/rust-sdk
- Hacker News, “Official Hacker News API,” GitHub repository. https://github.com/HackerNews/API
- Claude Code Docs, “Connect Claude Code to tools via MCP.” https://code.claude.com/docs/en/mcp
- Claude Platform Docs, “MCP connector.” https://platform.claude.com/docs/en/agents-and-tools/mcp-connector
- Claude Platform Docs, “MCP tunnels.” https://platform.claude.com/docs/agents-and-tools/mcp-tunnels/overview
- WorkOS, “MCP went stateless: What changed in the 2026-07-28 spec,” September 16, 2026. https://workos.com/blog/mcp-stateless-spec-2026-07-28
- Cloudflare Blog, “The next generation of MCP,” August 6, 2026. https://blog.cloudflare.com/mcp-v2/
- SerpApi, “The State of MCP: Everything That Changed in H1 2026,” June 3, 2026. https://serpapi.com/blog/the-state-of-mcp-everything-that-changed-in-h1-2026/