Building an AI Agent with Model Context Protocol (MCP): A Practical Guide
In the rapidly evolving landscape of artificial intelligence, LLMs (Large Language Models) have proven highly capable of reasoning, writing code, and solving complex problems. However, they traditionally lacked a standardized way to safely interact with local files, databases, APIs, and developer tools.
To solve this fragmentation, Anthropic introduced the Model Context Protocol (MCP)—an open-source standard designed to give LLMs secure, structured access to external data sources and tools.
As a Senior Full Stack Architect exploring the boundaries of AI integration, I have implemented MCP servers for various automation pipelines. In this article, I will share the architectural blueprints and a complete step-by-step guide to building your first custom MCP server.
1. Understanding the MCP Architecture
MCP separates concerns by implementing a client-server relationship between the AI host and the data/tool source:
- Host (e.g., Claude Desktop, Cursor, or Custom AI Agent): The application where the LLM runs and decides which tools to trigger.
- Client (MCP Client): The component inside the Host that establishes a connection (typically via stdio or SSE) to the server.
- Server (MCP Server): A lightweight process running locally or in the cloud that exposes resources, tools, and prompts.
graph LR
Host[AI Host / Desktop Client] <--> Client[MCP Client]
Client <-->|Protocol over Stdio/SSE| Server[MCP Server]
Server <--> Filesystem[(Local Files)]
Server <--> Database[(Databases)]
Server <--> APIs[External APIs]
2. Core Concepts: Resources, Tools, and Prompts
When building an MCP Server, you expose three main capabilities to the LLM:
- Resources: Read-only data sources (like database tables, system logs, or file contents) that the LLM can reference.
- Tools: Executable functions that the LLM can call to perform actions (e.g., writing a file, executing a database query, or making an API request). Tools require user approval or sandbox verification.
- Prompts: Pre-structured templates that guide the LLM's role, behavior, or input format.
3. Step-by-Step Implementation in TypeScript
Let's build a practical MCP Server using TypeScript and the official @modelcontextprotocol/sdk. This server will expose a tool to fetch real-time system metrics, allowing an AI agent to monitor your computer.
Step 1: Initialize the Project
Create a new directory and install the necessary dependencies:
mkdir my-mcp-server
cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk
npm install -D typescript @types/node tsx
npx tsc --init
Step 2: Write the MCP Server Code
Create an index.ts file and paste the following implementation:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import os from "os";
// Initialize the MCP Server
const server = new Server(
{
name: "system-monitor-mcp",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// Register Available Tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "get_system_metrics",
description: "Retrieve real-time CPU, memory, and uptime metrics from the local machine.",
inputSchema: {
type: "object",
properties: {},
},
},
],
};
});
// Handle Tool Execution
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name } = request.params;
if (name === "get_system_metrics") {
const freeMem = os.freemem();
const totalMem = os.totalmem();
const usagePercent = ((totalMem - freeMem) / totalMem) * 100;
const metrics = {
platform: os.platform(),
arch: os.arch(),
uptimeSeconds: os.uptime(),
cpuCount: os.cpus().length,
memoryUsagePercentage: usagePercent.toFixed(2) + "%",
freeMemoryGB: (freeMem / 1024 / 1024 / 1024).toFixed(2) + " GB",
};
return {
content: [
{
type: "text",
text: JSON.stringify(metrics, null, 2),
},
],
};
}
throw new Error(`Tool ${name} not found.`);
});
// Run the Server using standard input/output (stdio)
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("System Monitor MCP Server running on stdio");
}
main().catch((err) => {
console.error("Fatal error:", err);
process.exit(1);
});
4. Connecting the Server to Claude Desktop
To test and use this server inside your Claude Desktop app, edit your configuration file.
- Windows:
%APPDATA%\Claude\claude_desktop_config.json - macOS:
~/Library/Application Support/Claude/claude_desktop_config.json
Add your server configuration:
{
"mcpServers": {
"system-monitor": {
"command": "npx",
"args": [
"-y",
"tsx",
"C:/path/to/your/my-mcp-server/index.ts"
]
}
}
}
Restart Claude Desktop, and you will see a hammer icon indicating that your AI agent now has access to the local get_system_metrics tool.
5. Security & Production Best Practices
When transitioning custom MCP servers to production or cloud environments:
- Strict Sandbox Policies: Never allow tool commands to execute arbitrary shell scripts unless strictly sandboxed.
- Authentication & SSE: For cloud-deployed servers, transition from
stdiotransport to Server-Sent Events (SSE), securing endpoints with OAuth2 or API keys. - Least Privilege Access: Ensure the local user running the MCP process has highly restricted read/write permissions.
Conclusion
Model Context Protocol (MCP) bridges the gap between static LLM reasoning and active system execution. By structuring custom tools and resources with standard schemas, developers can create AI agents that act as true co-pilots.
As demonstrated by Ömer Özbay on his development showcase gucluyumhe.dev, leveraging MCP enables developers to build secure, robust local automation loops that dramatically elevate their full-stack engineering output.
