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:
- Framework bloat: LangChain has thousands of files. I spent more time fighting the framework than building features.
- Hidden complexity: When something went wrong, I had to debug through multiple layers of abstractions.
- Limited customization: The framework made assumptions about how things should work. My use case didn’t fit.
- 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:
| Capability | What It Does |
|---|---|
| Tools | Functions the AI can call |
| Resources | Data the AI can access |
| Prompts | Templates the AI can use |
| Sampling | AI requests specific data |
MCP vs Traditional Frameworks
I compared MCP to my experience with LangChain:
| Aspect | MCP | LangChain |
|---|---|---|
| Lines of code | ~500 for full server | Thousands of framework code |
| Learning curve | Understand the protocol | Learn framework idioms |
| Debugging | Direct, you see everything | Through abstraction layers |
| Customization | Full control | Limited by framework design |
| Transferable skills | Protocol knowledge | Framework-specific knowledge |
How MCP Works
The protocol uses JSON-RPC for communication. Here’s the flow:
1. AI sends a tool call request | v2. MCP Server receives and validates | v3. Server executes the tool | v4. Server returns the result | v5. AI uses the result in its responseBuilding an MCP Server
I started with a simple weather server. Here’s how I built it.
Step 1: Initialize the Project
# Create a new MCP servernpx @modelcontextprotocol/create-server weather-server
# Navigate to the projectcd weather-server
# Install dependenciesnpm installStep 2: Define Tools
The server needs to tell the AI what tools it provides. I defined a weather tool:
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 existserver.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:
// Handle tool callsserver.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 serverconst transport = new StdioServerTransport();await server.connect(transport);Step 4: Configure Claude Desktop
I added my server to Claude Desktop’s config:
{ "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:
# Install the inspectornpm install -g @modelcontextprotocol/inspector
# Run with inspectornpx mcp-inspector node build/index.jsOutput:
MCP Inspector running at http://localhost:5173Server connected: weather-server v1.0.0Available tools: - get_weatherReal-World Example: Screen Capture Server
The Reddit success story mentioned a macOS screen capture server. Here’s a simplified Swift version:
import Cocoaimport 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:
server.setRequestHandler(ListToolsRequestSchema, async () => { const tools = [{ name: 'get_weather' }]; // Forgot to return!});
// Correctserver.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [{ name: 'get_weather', ... }] };});Issue 2: Input Validation Errors
I got undefined errors when I didn’t validate inputs:
const city = request.params.arguments.city;
// Correctconst 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:
{ "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:
- Skip the frameworks initially
- Learn MCP directly
- Build one simple server
- Understand tool calling
- 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:
- 👨💻 Model Context Protocol Specification
- 👨💻 MCP SDK Documentation
- 👨💻 Claude Desktop Download
- 👨💻 Anthropic API Documentation
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments