Skip to content

MCP (Model Context Protocol) for AI Agents: The Complete Guide

Purpose

I spent months fighting LangChain. I wrote thousands of lines of code, debugged through multiple abstraction layers, and still couldn’t get my AI agent to do what I wanted. Then I discovered MCP.

This post explains what MCP (Model Context Protocol) is and why it changed how I build AI agents. The key insight: MCP lets you give any AI model new capabilities without a framework abstracting everything away.

What Problem Does MCP Solve?

When I tried to build an AI agent, I ran into these problems:

  1. Framework bloat: LangChain has thousands of files. I spent more time fighting the framework than building features.
  2. Hidden complexity: When something went wrong, I had to debug through multiple layers of abstractions.
  3. Limited customization: The framework made assumptions about how things should work. My use case didn’t fit.
  4. Vendor lock-in: Learning LangChain meant learning LangChain-specific patterns, not transferable skills.

A Reddit user (Deep_Ad1959) described my exact experience:

“I wrote maybe 500 lines of Swift for my MCP server and got more out of it than months of fighting LangChain.”

What is MCP?

MCP (Model Context Protocol) is an open protocol that defines how AI models communicate with external tools and resources. Think of it as a universal adapter.

+-------------+ +-------------+ +-------------+
| AI Model | | MCP Server | | External |
| (Client) |<-------->| (Your Code) |<-------->| Systems |
| | JSON | | API | |
| Claude | RPC | Tools | Calls | Files |
| GPT | | Resources | | Databases |
+-------------+ +-------------+ +-------------+

MCP gives your AI four capabilities:

CapabilityWhat It Does
ToolsFunctions the AI can call
ResourcesData the AI can access
PromptsTemplates the AI can use
SamplingAI requests specific data

MCP vs Traditional Frameworks

I compared MCP to my experience with LangChain:

AspectMCPLangChain
Lines of code~500 for full serverThousands of framework code
Learning curveUnderstand the protocolLearn framework idioms
DebuggingDirect, you see everythingThrough abstraction layers
CustomizationFull controlLimited by framework design
Transferable skillsProtocol knowledgeFramework-specific knowledge

How MCP Works

The protocol uses JSON-RPC for communication. Here’s the flow:

1. AI sends a tool call request
|
v
2. MCP Server receives and validates
|
v
3. Server executes the tool
|
v
4. Server returns the result
|
v
5. AI uses the result in its response

Building an MCP Server

I started with a simple weather server. Here’s how I built it.

Step 1: Initialize the Project

Terminal
# Create a new MCP server
npx @modelcontextprotocol/create-server weather-server
# Navigate to the project
cd weather-server
# Install dependencies
npm install

Step 2: Define Tools

The server needs to tell the AI what tools it provides. I defined a weather tool:

src/index.ts
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: 'weather-server',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
}
);
// Tell the AI what tools exist
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [{
name: 'get_weather',
description: 'Get current weather for a city',
inputSchema: {
type: 'object',
properties: {
city: { type: 'string' },
},
required: ['city'],
},
}],
}));

Step 3: Implement Tool Logic

Next, I handled the actual tool execution:

src/index.ts
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === 'get_weather') {
const city = args?.city as string;
if (!city) {
throw new Error('city is required');
}
// In real code, call a weather API here
const weatherData = await fetchWeather(city);
return {
content: [{
type: 'text',
text: JSON.stringify(weatherData),
}],
};
}
throw new Error(`Unknown tool: ${name}`);
});
// Start the server
const transport = new StdioServerTransport();
await server.connect(transport);

Step 4: Configure Claude Desktop

I added my server to Claude Desktop’s config:

claude_desktop_config.json
{
"mcpServers": {
"weather": {
"command": "node",
"args": ["/path/to/weather-server/build/index.js"]
}
}
}

Step 5: Test with MCP Inspector

Before connecting to Claude, I tested locally:

Terminal
# Install the inspector
npm install -g @modelcontextprotocol/inspector
# Run with inspector
npx mcp-inspector node build/index.js

Output:

Inspector Output
MCP Inspector running at http://localhost:5173
Server connected: weather-server v1.0.0
Available tools:
- get_weather

Real-World Example: Screen Capture Server

The Reddit success story mentioned a macOS screen capture server. Here’s a simplified Swift version:

ScreenCaptureServer.swift
import Cocoa
import MCP
class ScreenCaptureTool: Tool {
let name = "capture_screen"
let description = "Capture the current screen and return as base64"
func execute() async throws -> ToolResult {
// Capture screen
guard let display = CGMainDisplayID() as CGDirectDisplayID? else {
throw ToolError.noDisplay
}
let image = CGDisplayCreateImage(display)!
let bitmap = NSBitmapImageRep(cgImage: image)
let data = bitmap.representation(using: .png, properties: [:])!
let base64 = data.base64EncodedString()
return ToolResult(content: [
.image(data: base64, mimeType: "image/png")
])
}
}

The developer wrote ~500 lines total. That included:

  • Accessibility API integration
  • Screen capture functionality
  • Browser control
  • Full MCP protocol implementation

Why MCP Beats Frameworks

After using both approaches, I found these advantages:

1. You Understand Everything

With LangChain, I didn’t know what was happening behind the scenes. With MCP, I see every line of code.

2. Debugging is Direct

When my MCP server fails, I know exactly where to look. The error is in my code, not hidden in framework internals.

3. Only Write What You Need

I don’t need to import 50 modules to use one feature. My weather server is ~100 lines. My entire project is ~500 lines including tests.

4. Language Agnostic

MCP is a protocol, not a library. I can write servers in TypeScript, Python, Swift, or any language that speaks JSON-RPC.

Common Issues I Encountered

Issue 1: Tools Not Appearing

When my tools didn’t show up, I forgot to return from the handler:

Wrong - tools don't appear
server.setRequestHandler(ListToolsRequestSchema, async () => {
const tools = [{ name: 'get_weather' }];
// Forgot to return!
});
// Correct
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [{ name: 'get_weather', ... }]
};
});

Issue 2: Input Validation Errors

I got undefined errors when I didn’t validate inputs:

Wrong - can crash
const city = request.params.arguments.city;
// Correct
const args = request.params.arguments;
if (!args?.city) {
throw new Error('city is required');
}
const city = args.city as string;

Issue 3: Connection Refused

When Claude Desktop couldn’t connect, I had the wrong path:

Wrong path
{
"mcpServers": {
"weather": {
"command": "node",
"args": ["src/index.ts"] // Wrong - need compiled JS
}
}
}
// Correct
{
"mcpServers": {
"weather": {
"command": "node",
"args": ["/full/path/to/build/index.js"]
}
}
}

Best Practices

DO

  • Start small - one tool at a time
  • Test with MCP Inspector before connecting to Claude
  • Provide clear tool descriptions
  • Handle errors gracefully
  • Use TypeScript for type safety

DON’T

  • Over-engineer your first server
  • Skip input validation
  • Hardcode API keys
  • Create tools without clear purposes
  • Try to replicate framework features

MCP Ecosystem

Official Servers Available

  • Filesystem: File operations
  • PostgreSQL: Database queries
  • Git: Repository operations
  • Google Drive: Cloud storage
  • Slack: Team messaging

Community Servers

The community is building servers for:

  • Notion integration
  • Linear project management
  • GitHub operations
  • Database connectors
  • API wrappers

My Recommendation

If you’re building AI agents:

  1. Skip the frameworks initially
  2. Learn MCP directly
  3. Build one simple server
  4. Understand tool calling
  5. Add complexity only when needed

As one Reddit user said:

“You don’t have to LEARN. Just go install Claude and talk to it. Understand tool calling, MCP and skills. You will hit token limits and many issues, just ask Claude. Repeat. That is learning!”

Summary

MCP represents a simpler approach to building AI agents. Instead of learning complex frameworks, you learn a protocol. Instead of fighting abstractions, you write direct code.

The evidence is clear: 500 lines of MCP server code can deliver more value than months with traditional frameworks. It’s the part most people overlook - but those who try it find a more direct path to giving AI models custom capabilities.

The bottom line: If you’re building AI agents, understanding MCP is more valuable than learning any specific framework. It’s the protocol that lets you give AI models exactly the capabilities they need.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments