AI Agents
Model Context Protocol (MCP) explained - what it is, how it works, how MCP servers expose tools and resources, and why it replaces custom LLM integrations.
Every time you want to connect a large language model to an external tool - a database, a file system, a REST API, a code executor - you write a custom integration from scratch. You decide on the input format, parse the output, handle errors, and format everything for the model. Then you do it again for the next tool. Model Context Protocol (MCP) was designed to make this the last time anyone has to solve that problem. It defines a standard protocol so any AI client can connect to any tool server using the same interface, the same message format, and the same primitives - regardless of what the tool does or which model is running the agent.
MCP is a protocol specification, not a library or a framework. It defines three things: the message format (JSON-RPC 2.0 - request/response envelopes with method names and parameters), the transport options (stdio for local process communication, HTTP/SSE or Streamable HTTP for remote servers), and the three primitives each server can expose - tools, resources, and prompts. Any client that implements the MCP spec can connect to any server that implements it, without either side knowing implementation details about the other.
The analogy that makes this concrete: MCP is to AI tool integrations what USB is to hardware peripherals. Before USB, every device needed a custom port and a custom driver. USB defined a standard connector and protocol, and suddenly any device could plug into any computer. MCP does the same for AI agent integrations.
An MCP session has two roles: the client (your AI agent or Claude Code) and the server (the process that exposes tools). The flow on every connection:
initialize request with its protocol version; the server responds with its capabilitiestools/list, resources/list, and prompts/list to learn what the server exposestools/call request with the tool name and arguments; the server executes and returns the resultHere is a minimal MCP server using @modelcontextprotocol/sdk v1.0+ that exposes one tool:
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
const server = new McpServer({
name: 'my-first-server',
version: '1.0.0',
});
// Register a tool
server.tool(
'get_weather',
'Get current weather for a city. Returns temperature and conditions.',
{
city: {
type: 'string',
description: 'City name, e.g. "Paris" or "Tokyo".',
},
units: {
type: 'string',
enum: ['celsius', 'fahrenheit'],
description: 'Temperature unit. Defaults to celsius.',
},
},
async ({ city, units = 'celsius' }) => {
// Your implementation - call a weather API, query a database, anything
const weather = await fetchWeatherData(city, units);
return {
content: [{
type: 'text',
text: JSON.stringify(weather),
}],
};
}
);
// Connect via stdio - Claude Code spawns this as a child process
const transport = new StdioServerTransport();
await server.connect(transport);
Claude Code reads the tool list on startup, and from that point the agent can call get_weather as naturally as any built-in tool - no custom integration code in the agent, no model-specific formatting, no manual error handling at the agent layer.
Every MCP server can expose up to three types of content. Understanding the distinction determines which one to reach for:
The trade-off: custom integrations are simpler for one-off, single-agent needs. MCP servers add a protocol layer that is worth the overhead when: multiple agents or users need the same tools, you want Claude Code's built-in MCP support, or you are building shared infrastructure for a team.
Add the server to your project's .claude/mcp.json or to ~/.claude/mcp.json for user-level access:
{
"mcpServers": {
"my-first-server": {
"command": "node",
"args": ["./mcp-servers/weather.js"],
"env": {
"WEATHER_API_KEY": "${WEATHER_API_KEY}"
}
}
}
}
Claude Code spawns the server as a child process on startup, reads its tool list, and makes those tools available to every agent in the session - including any subagents that inherit the session's tool access. For remote HTTP servers instead of local stdio processes, replace command/args with a url field pointing to your server's endpoint.
Build an MCP server when:
Do not build an MCP server when:
For a step-by-step tutorial building a complete MCP server with authentication and multiple tools, see building a custom MCP server. For adding OAuth and API key validation to an MCP server you plan to share across a team, see MCP server authentication.