- Published on
Model Context Protocol (MCP) — The HTTP of AI Agent Communication
- Authors

- Name
- Sanjeev Sharma
- @webcoderspeed1
Introduction
Model Context Protocol (MCP) is Anthropic's open standard for enabling AI agents to securely connect with tools, data sources, and context. It''s the HTTP of AI agent communication—a standardised interface that lets LLMs access external resources without needing to know implementation details. This post explores MCP architecture, how to build an MCP server in TypeScript, and why it matters for production AI systems.
- What is Model Context Protocol?
- MCP Server Architecture
- Building an MCP Server in TypeScript
- Connecting MCP to Claude Desktop
- MCP vs Function Calling
- Real-World MCP Server Examples
- Security Considerations
- Checklist
- Conclusion
What is Model Context Protocol?
MCP solves a fundamental problem: how do you let an AI model safely access external tools and data? Function calling lets models trigger actions, but MCP goes further by standardising the entire interface.
Think of HTTP. It''s a protocol that separates clients from servers. Whether you''re accessing a banking API, a weather service, or a healthcare database, you use the same protocol. MCP does this for AI agents.
The MCP specification defines three core primitives:
Resources: Files, databases, APIs that the model can read Tools: Functions the model can invoke Prompts: Pre-written instructions that guide the model''s reasoning
MCP operates over JSON-RPC 2.0, making it language-agnostic and easy to implement across any stack.
MCP Server Architecture
An MCP server exposes capabilities to an MCP client (Claude, Cursor, or any LLM). The server implements:
import {
Server,
StdioServerTransport,
CallToolRequest,
ListResourcesRequest,
ReadResourceRequest,
ListToolsRequest,
} from '@modelcontextprotocol/sdk/server/index.js';
import { TextContent } from '@modelcontextprotocol/sdk/types.js';
const server = new Server({
name: 'example-server',
version: '1.0.0',
});
// Register tools
server.setRequestHandler(ListToolsRequest, async () => ({
tools: [
{
name: 'query_database',
description: 'Execute SQL queries against the production database',
inputSchema: {
type: 'object',
properties: {
sql: {
type: 'string',
description: 'SQL query to execute',
},
},
required: ['sql'],
},
},
],
}));
// Handle tool calls
server.setRequestHandler(CallToolRequest, async (request) => {
const { name, arguments: args } = request.params;
if (name === 'query_database') {
try {
const result = await executeQuery((args as { sql: string }).sql);
return {
content: [
{
type: 'text',
text: JSON.stringify(result),
} as TextContent,
],
};
} catch (error) {
return {
content: [
{
type: 'text',
text: `Error: ${(error as Error).message}`,
} as TextContent,
],
isError: true,
};
}
}
throw new Error(`Unknown tool: ${name}`);
});
// Register resources
server.setRequestHandler(ListResourcesRequest, async () => ({
resources: [
{
uri: 'file:///database/schema',
name: 'database_schema',
description: 'Current database schema',
mimeType: 'application/json',
},
],
}));
// Handle resource reads
server.setRequestHandler(ReadResourceRequest, async (request) => {
const { uri } = request.params;
if (uri === 'file:///database/schema') {
const schema = await getSchemaDefinition();
return {
contents: [
{
uri,
mimeType: 'application/json',
text: JSON.stringify(schema),
},
],
};
}
throw new Error(`Unknown resource: ${uri}`);
});
const transport = new StdioServerTransport();
await server.connect(transport);
This server exposes a database query tool and a schema resource. The client connects via stdio, and the server responds to requests.
Building an MCP Server in TypeScript
Let''s build a complete MCP server that provides GitHub integration:
import {
Server,
StdioServerTransport,
CallToolRequest,
ListToolsRequest,
} from '@modelcontextprotocol/sdk/server/index.js';
import { Octokit } = require('@octokit/rest');
const server = new Server({
name: 'github-mcp-server',
version: '1.0.0',
});
const octokit = new Octokit({
auth: process.env.GITHUB_TOKEN,
});
server.setRequestHandler(ListToolsRequest, async () => ({
tools: [
{
name: 'list_repositories',
description: 'List user''s repositories',
inputSchema: {
type: 'object',
properties: {
per_page: { type: 'number', description: 'Results per page' },
},
},
},
{
name: 'create_issue',
description: 'Create a GitHub issue',
inputSchema: {
type: 'object',
properties: {
owner: { type: 'string' },
repo: { type: 'string' },
title: { type: 'string' },
body: { type: 'string' },
},
required: ['owner', 'repo', 'title', 'body'],
},
},
],
}));
server.setRequestHandler(CallToolRequest, async (request) => {
const { name, arguments: args } = request.params;
if (name === 'list_repositories') {
const response = await octokit.repos.listForAuthenticatedUser({
per_page: (args as { per_page?: number }).per_page || 30,
});
return {
content: [
{
type: 'text',
text: JSON.stringify(
response.data.map((r) => ({
name: r.name,
url: r.html_url,
description: r.description,
}))
),
},
],
};
}
if (name === 'create_issue') {
const { owner, repo, title, body } = args as {
owner: string;
repo: string;
title: string;
body: string;
};
const response = await octokit.issues.create({
owner,
repo,
title,
body,
});
return {
content: [
{
type: 'text',
text: `Issue #${response.data.number} created: ${response.data.html_url}`,
},
],
};
}
throw new Error(`Unknown tool: ${name}`);
});
const transport = new StdioServerTransport();
await server.connect(transport);
Connecting MCP to Claude Desktop
Claude Desktop reads MCP configurations from ~/.claude/config.json:
{
"mcpServers": {
"github": {
"command": "node",
"args": ["/path/to/github-mcp-server.js"],
"env": {
"GITHUB_TOKEN": "your-token-here"
}
},
"database": {
"command": "node",
"args": ["/path/to/db-server.js"],
"env": {
"DATABASE_URL": "postgres://..."
}
}
}
}
Claude automatically loads these servers on startup and exposes their tools to the model.
MCP vs Function Calling
Both enable models to call external code, but they differ significantly:
Function Calling (OpenAI, Anthropic): Tools are embedded in API requests. The model sees the available tools and returns function calls. The client must handle retries, validation, and context management.
MCP: Tools live on a server. The model doesn''t see the implementation—only the schema. The MCP server handles execution, logging, and resource management. MCP works across multiple clients and integrates deeper context.
Use function calling for simple, request-scoped tools. Use MCP when you need stateful server-side logic, resource management, or plan to connect multiple AI clients.
Real-World MCP Server Examples
Here''s an MCP server for filesystem access with security boundaries:
import path from 'path';
import fs from 'fs/promises';
const ALLOWED_ROOT = '/home/user/documents';
server.setRequestHandler(CallToolRequest, async (request) => {
const { name, arguments: args } = request.params;
if (name === 'read_file') {
const filePath = (args as { path: string }).path;
const resolvedPath = path.resolve(ALLOWED_ROOT, filePath);
if (!resolvedPath.startsWith(ALLOWED_ROOT)) {
return {
content: [
{
type: 'text',
text: 'Access denied: path outside allowed root',
},
],
isError: true,
};
}
const content = await fs.readFile(resolvedPath, 'utf-8');
return {
content: [
{
type: 'text',
text: content,
},
],
};
}
throw new Error(`Unknown tool: ${name}`);
});
This demonstrates permission checks—a critical concern for production MCP servers.
Security Considerations
MCP servers should implement:
- Authentication (API keys, OAuth tokens)
- Authorization (role-based access control)
- Rate limiting (throttle model requests)
- Audit logging (track all tool calls)
- Input validation (sanitise model-provided arguments)
- Resource quotas (prevent runaway operations)
Always run MCP servers in isolated processes with minimal permissions. Never expose sensitive credentials in logs or error messages.
Checklist
- Understand MCP primitives (Resources, Tools, Prompts)
- Build an MCP server with
@modelcontextprotocol/sdk - Connect to Claude Desktop via
config.json - Implement authentication and authorisation
- Add rate limiting and audit logging
- Test tool invocation and error handling
Conclusion
Model Context Protocol standardises how AI agents access tools and data. By separating tool implementation from the model, MCP enables secure, scalable agent systems. Start by building a simple MCP server for your existing APIs, connect it to Claude Desktop, and gradually expand to multiple specialized servers. The flexibility and safety of MCP make it the foundation for production-grade AI agents.