MCP 101: Build Your First Model Context Protocol Server — The 2026 Beginner's Guide
TL;DR: The Model Context Protocol (MCP) is an open standard from Anthropic that lets AI models plug into external tools, databases, and APIs — think of it as USB-C for AI. This step-by-step guide walks you through building your first MCP server in 15 minutes, connecting it to Claude Desktop, and understanding why every AI developer in 2026 should learn this protocol.
What Is the Model Context Protocol?
MCP is an open-source protocol that standardizes how AI applications connect to external data and tools. Before MCP, every AI integration was bespoke — you'd write custom function definitions for OpenAI, a different set for Anthropic, another for Google, and so on. MCP solves this by providing a universal interface layer.
If you've ever wished your AI assistant could read your database, search your documents, or send emails — MCP is how you make that happen without duct-taping together half a dozen APIs.
Why You Should Care
- Write once, use everywhere. An MCP server you build for Claude works with ChatGPT, Cursor, VS Code Copilot, and any other MCP-compatible client.
- No more custom glue code. Instead of writing provider-specific tool definitions for every LLM, you write one MCP server and it's instantly compatible.
- Your AI finally has hands. MCP gives LLMs access to real data and real actions — databases, APIs, files, calendars, you name it.
- Adopted everywhere. By mid-2026, MCP is supported by OpenAI, Google, AWS, Azure, and every major AI framework.
Core Architecture
MCP uses a simple three-component architecture:
| Component | Role | Example |
|---|---|---|
| MCP Host | The AI application you interact with | Claude Desktop, ChatGPT, Cursor |
| MCP Client | Protocol client inside the host, connects to servers | Built into the host application |
| MCP Server | Exposes resources, tools, and prompts | Your custom server, community npm packages |
Communication happens over JSON-RPC 2.0, typically via stdio (local) or SSE (remote).
The Three Primitives
MCP servers expose three types of capabilities:
1. Resources — Read-only data sources the AI can browse (files, database schemas, logs)
2. Tools — Functions the AI can call to take action (query a DB, send an email, search the web)
3. Prompts — Reusable prompt templates for common interactions
Most servers you'll build focus on tools — that's where the real power is.
Prerequisites
Before we start building, make sure you have:
- Node.js 18+ installed (node -v to check)
- npm or yarn package manager
- Claude Desktop (or any MCP-compatible client) for testing
- A text editor or IDE
Step 1:
Initialize Your Project
Create a new directory and initialize a Node.js project:
class="language-bash">mkdir my-first-mcp-server
cd my-first-mcp-server
npm init -yStep 2:
Install the MCP SDK
Install the official TypeScript SDK from Anthropic:
class="language-bash">npm install @modelcontextprotocol/sdkThis package handles all the protocol plumbing — JSON-RPC serialization, transport management, request routing — so you only need to write your actual tool logic.
Step 3:
Create the Server File
Create index.js with the following boilerplate:
class="language-javascript">import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
const server = new Server(
{
name: "mcp-tutorial-server",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("MCP server running on stdio");
}
main().catch((error) => {
console.error("Server error:", error);
process.exit(1);
});This creates an MCP server with no tools yet — let's add some.
Step 4:
Declare Available Tools
Add a handler that tells the AI client what tools are available. This handler runs when the client connects or refreshes the tool list:
class="language-javascript">server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "calculate",
description: "Evaluate a mathematical expression",
inputSchema: {
type: "object",
properties: {
expression: {
type: "string",
description:
"A mathematical expression to evaluate (e.g., '2 + 2', 'sqrt(144)')",
},
},
required: ["expression"],
},
},
{
name: "get_time",
description: "Get the current time in a timezone",
inputSchema: {
type: "object",
properties: {
timezone: {
type: "string",
description:
"IANA timezone (e.g., 'America/New_York', 'Europe/London')",
},
},
required: ["timezone"],
},
},
],
};
});Each tool declaration includes:
- name — What the AI calls to invoke this tool (kebab-case recommended)
- description — Tells the AI when to use this tool. Be descriptive — the LLM decides based on this.
- inputSchema — JSON Schema defining the expected arguments
Step 5:
Handle Tool Calls
Now add the handler that actually executes the tools when the AI calls them:
class="language-javascript">server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
switch (name) {
case "calculate": {
const { expression } = args;
try {
// Safe evaluation using native Math
const fn = new Function("return (" + expression + ")");
const result = fn();
return {
content: [
{
type: "text",
text: String(result),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: <code>Error evaluating expression: ${error.message}</code>,
},
],
isError: true,
};
}
}
case "get_time": {
const { timezone } = args;
try {
const now = new Date();
const timeString = now.toLocaleString("en-US", {
timeZone: timezone,
dateStyle: "full",
timeStyle: "long",
});
return {
content: [
{
type: "text",
text: <code>Current time in ${timezone}: ${timeString}</code>,
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: <code>Error: Unknown timezone "${timezone}"</code>,
},
],
isError: true,
};
}
}
default:
throw new Error(<code>Unknown tool: ${name}</code>);
}
});Two things to notice:
- Return structured content — Always return a content array with type: "text" objects. The LLM reads this and uses it to continue the conversation.
- Set isError: true on failures — This tells the LLM something went wrong, so it can handle it gracefully (retry, ask the user, or try a different approach).
Step 6:
Test Your Server
Before connecting to a client, test locally to make sure everything runs:
class="language-bash">node index.jsYou should see: MCP server running on stdio. Press Ctrl+C to stop.
For a more thorough test, pipe a JSON-RPC request directly:
class="language-bash">echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | node index.jsThis should output the tool definitions to stdout (you'll also see the log message on stderr).
Step 7:
Connect to Claude Desktop
This is where the magic happens. Configure your Claude Desktop to use your new MCP server:
macOS
Edit ~/Library/Application Support/Claude/claude_desktop_config.json:
class="language-json">{
"mcpServers": {
"my-first-server": {
"command": "node",
"args": ["/absolute/path/to/my-first-mcp-server/index.js"]
}
}
}Windows
Edit %APPDATA%\Claude\claude_desktop_config.json with the same format.
Linux
Edit ~/.config/Claude/claude_desktop_config.json:
class="language-json">{
"mcpServers": {
"my-first-server": {
"command": "node",
"args": ["/home/your-user/my-first-mcp-server/index.js"]
}
}
}Restart and Verify
1. Restart Claude Desktop completely
2. Look for the hammer icon 🔨 in the input area — that means MCP tools are active
3. Try asking: "What's the current time in Tokyo?"
4. Then: "What's 145 * 37?"
The AI will automatically call your MCP server tools instead of guessing or hallucinating answers.
MCP Best Practices for Production
Once you've built your first server, here's how to make it production-ready:
1.
Handle Errors Gracefully
Always return structured errors so the LLM can adapt:
class="language-javascript">return {
content: [
{
type: "text",
text: JSON.stringify({
error: "RATE_LIMITED",
message: "API limit reached. Try again in 60 seconds.",
retryAfter: 60,
}),
},
],
isError: true,
};2.
Use Environment Variables for Secrets
Never hardcode API keys:
class="language-javascript">const API_KEY = process.env.MY_API_KEY;
if (!API_KEY) {
throw new Error("MY_API_KEY environment variable is required");
}3.
Validate Inputs Thoroughly
The LLM can pass unexpected values. Validate everything:
class="language-javascript">const validTimezones = ["America/New_York", "Europe/London", "Asia/Tokyo"];
if (!validTimezones.includes(args.timezone)) {
return {
content: [{ type: "text", text: "Unsupported timezone" }],
isError: true,
};
}4.
Keep Responses Concise
The LLM's context window is limited. Return only what's needed — don't dump the entire database response if a summary will do.
The MCP Ecosystem in 2026
MCP has grown far beyond its Anthropic origins:
| Category | Players |
|---|---|
| AI Clients | Claude Desktop, ChatGPT, Cursor, VS Code Copilot, JetBrains AI, Cline |
| Cloud Platforms | AWS Bedrock, Azure AI, Google Vertex AI all offer managed MCP endpoints |
| Agent Frameworks | LangGraph, CrewAI, AutoGen all use MCP for tool calling |
| Community Servers | 2,000+ MCP servers on npm — GitHub, Slack, PostgreSQL, Filesystem, Web Search |
| SDKs | TypeScript, Python, Java, C#, Go — official SDKs for every major language |
Frequently Asked Questions
Does MCP work with any AI model?
Yes. MCP is model-agnostic. As long as the client application supports MCP (Claude Desktop, ChatGPT, Cursor, VS Code, etc.), any LLM behind it can use MCP servers. The protocol standardizes the connection layer.
Do I need to run MCP servers on the same machine?
For development, yes — stdio transport runs locally. For production, you can use SSE (Server-Sent Events) or WebSocket transports to connect to remote MCP servers. Cloud platforms like AWS Bedrock now offer managed MCP endpoints for remote servers.
How is MCP different from LangChain tools?
LangChain tools are framework-specific — they only work inside LangChain. MCP is a universal protocol that works across any compatible client, regardless of the framework behind it. Think of LangChain tools as a single-brand connector, while MCP is the universal standard.
Is MCP secure?
MCP servers run locally by default, so data never leaves your machine unless you configure remote access. The protocol supports OAuth for remote servers, and you control exactly what tools and data you expose. Client applications enforce user consent before calling tools.
Can I use Python instead of TypeScript?
Absolutely. Anthropic provides an official Python SDK with a FastMCP framework that makes building servers even simpler. The Python version supports the same features — resources, tools, prompts, and all transport types.
What if my AI client doesn't support MCP?
Most major AI tools now support MCP natively. If you're using an older or custom application, you can add MCP client support using the SDKs. The protocol is open-source and well-documented.
Start Building
MCP isn't just another protocol — it's the infrastructure layer that turns LLMs from text generators into actual tools that can read your data, run your queries, and automate your workflows. In 2026, learning MCP is like learning REST was in 2010: it's the standard, and it's not going away.
Your first server took 15 minutes to build. The only question now is: what will you connect next?
Built a cool MCP server? Share it on GitHub, tag it #mcp, and add it to the community registry.
← Back to all posts