What are the best MCP server examples for beginners?
The Model Context Protocol (MCP) enables AI assistants to interact with external tools and data sources through standardized servers. For developers new to MCP, starting with well-documented examples is crucial for understanding how to build and integrate these servers. This guide explores the best MCP server examples for beginners, complete with implementation details and practical use cases.
Understanding MCP Server Basics
Before diving into examples, it's important to understand that MCP servers act as bridges between AI models and external resources. They expose tools, resources, and prompts that AI assistants can use to perform tasks like file operations, database queries, or web scraping.
An MCP server typically consists of:
- Resources: Data sources that can be read (files, database records, API responses)
- Tools: Functions that can be executed (file operations, calculations, web requests)
- Prompts: Pre-defined templates for common interactions
1. Filesystem MCP Server
The filesystem MCP server is one of the most beginner-friendly examples because it deals with familiar operations: reading, writing, and managing files.
Python Implementation
import asyncio
from mcp.server import Server
from mcp.types import Resource, Tool
import aiofiles
import os
app = Server("filesystem-server")
@app.list_resources()
async def list_files():
"""List all files in the allowed directory"""
files = []
for filename in os.listdir("./data"):
files.append(Resource(
uri=f"file://data/{filename}",
name=filename,
mimeType="text/plain"
))
return files
@app.read_resource()
async def read_file(uri: str):
"""Read file contents"""
filepath = uri.replace("file://", "")
async with aiofiles.open(filepath, 'r') as f:
content = await f.read()
return content
@app.call_tool()
async def write_file(name: str, content: str):
"""Write content to a file"""
async with aiofiles.open(f"./data/{name}", 'w') as f:
await f.write(content)
return {"success": True, "message": f"File {name} written successfully"}
if __name__ == "__main__":
asyncio.run(app.run())
JavaScript Implementation
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import fs from "fs/promises";
import path from "path";
const server = new Server({
name: "filesystem-server",
version: "1.0.0"
}, {
capabilities: {
resources: {},
tools: {}
}
});
// List available files
server.setRequestHandler("resources/list", async () => {
const files = await fs.readdir("./data");
return {
resources: files.map(file => ({
uri: `file://data/${file}`,
name: file,
mimeType: "text/plain"
}))
};
});
// Read file contents
server.setRequestHandler("resources/read", async (request) => {
const filepath = request.params.uri.replace("file://", "");
const content = await fs.readFile(filepath, "utf-8");
return {
contents: [{
uri: request.params.uri,
mimeType: "text/plain",
text: content
}]
};
});
// Write file tool
server.setRequestHandler("tools/call", async (request) => {
if (request.params.name === "write_file") {
const { filename, content } = request.params.arguments;
await fs.writeFile(`./data/${filename}`, content);
return {
content: [{
type: "text",
text: `File ${filename} written successfully`
}]
};
}
});
const transport = new StdioServerTransport();
await server.connect(transport);
This example is perfect for beginners because it demonstrates core MCP concepts while working with familiar file operations.
2. Web Scraping MCP Server
A web scraping MCP server showcases how to integrate external APIs and handle asynchronous operations, which are common requirements in real-world applications.
Python Implementation with WebScraping.AI
import asyncio
import httpx
from mcp.server import Server
from mcp.types import Tool, TextContent
app = Server("webscraping-server")
WEBSCRAPING_API_KEY = "your_api_key_here"
API_BASE_URL = "https://api.webscraping.ai"
@app.list_tools()
async def list_tools():
"""List available scraping tools"""
return [
Tool(
name="scrape_html",
description="Extract HTML content from a webpage",
inputSchema={
"type": "object",
"properties": {
"url": {"type": "string", "description": "URL to scrape"},
"js": {"type": "boolean", "description": "Execute JavaScript"}
},
"required": ["url"]
}
),
Tool(
name="scrape_text",
description="Extract text content from a webpage",
inputSchema={
"type": "object",
"properties": {
"url": {"type": "string", "description": "URL to scrape"}
},
"required": ["url"]
}
)
]
@app.call_tool()
async def scrape_page(name: str, arguments: dict):
"""Execute scraping operations"""
async with httpx.AsyncClient() as client:
if name == "scrape_html":
response = await client.get(
f"{API_BASE_URL}/html",
params={
"api_key": WEBSCRAPING_API_KEY,
"url": arguments["url"],
"js": arguments.get("js", True)
}
)
return [TextContent(type="text", text=response.text)]
elif name == "scrape_text":
response = await client.get(
f"{API_BASE_URL}/text",
params={
"api_key": WEBSCRAPING_API_KEY,
"url": arguments["url"]
}
)
return [TextContent(type="text", text=response.text)]
if __name__ == "__main__":
asyncio.run(app.run())
JavaScript Implementation
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import fetch from "node-fetch";
const API_KEY = "your_api_key_here";
const API_BASE_URL = "https://api.webscraping.ai";
const server = new Server({
name: "webscraping-server",
version: "1.0.0"
}, {
capabilities: {
tools: {}
}
});
server.setRequestHandler("tools/list", async () => {
return {
tools: [
{
name: "scrape_html",
description: "Extract HTML content from a webpage",
inputSchema: {
type: "object",
properties: {
url: { type: "string", description: "URL to scrape" },
js: { type: "boolean", description: "Execute JavaScript" }
},
required: ["url"]
}
},
{
name: "scrape_text",
description: "Extract text content from a webpage",
inputSchema: {
type: "object",
properties: {
url: { type: "string", description: "URL to scrape" }
},
required: ["url"]
}
}
]
};
});
server.setRequestHandler("tools/call", async (request) => {
const { name, arguments: args } = request.params;
if (name === "scrape_html") {
const response = await fetch(
`${API_BASE_URL}/html?api_key=${API_KEY}&url=${encodeURIComponent(args.url)}&js=${args.js || true}`
);
const html = await response.text();
return {
content: [{ type: "text", text: html }]
};
}
if (name === "scrape_text") {
const response = await fetch(
`${API_BASE_URL}/text?api_key=${API_KEY}&url=${encodeURIComponent(args.url)}`
);
const text = await response.text();
return {
content: [{ type: "text", text: text }]
};
}
});
const transport = new StdioServerTransport();
await server.connect(transport);
This example demonstrates how to integrate with external APIs, handle asynchronous requests, and expose multiple tools through a single MCP server.
3. Database Query MCP Server
A database MCP server teaches important concepts about data access, security, and structured queries.
Python Implementation with SQLite
import asyncio
import aiosqlite
from mcp.server import Server
from mcp.types import Resource, Tool, TextContent
import json
app = Server("database-server")
DATABASE_PATH = "./data/app.db"
@app.list_resources()
async def list_tables():
"""List all database tables as resources"""
async with aiosqlite.connect(DATABASE_PATH) as db:
cursor = await db.execute(
"SELECT name FROM sqlite_master WHERE type='table'"
)
tables = await cursor.fetchall()
return [
Resource(
uri=f"db://table/{table[0]}",
name=f"Table: {table[0]}",
mimeType="application/json"
)
for table in tables
]
@app.read_resource()
async def read_table(uri: str):
"""Read all records from a table"""
table_name = uri.split("/")[-1]
async with aiosqlite.connect(DATABASE_PATH) as db:
db.row_factory = aiosqlite.Row
cursor = await db.execute(f"SELECT * FROM {table_name} LIMIT 100")
rows = await cursor.fetchall()
data = [dict(row) for row in rows]
return json.dumps(data, indent=2)
@app.call_tool()
async def execute_query(name: str, arguments: dict):
"""Execute a SELECT query"""
if name == "query":
query = arguments["sql"]
# Security: Only allow SELECT statements
if not query.strip().upper().startswith("SELECT"):
return [TextContent(
type="text",
text="Error: Only SELECT queries are allowed"
)]
async with aiosqlite.connect(DATABASE_PATH) as db:
db.row_factory = aiosqlite.Row
cursor = await db.execute(query)
rows = await cursor.fetchall()
data = [dict(row) for row in rows]
return [TextContent(
type="text",
text=json.dumps(data, indent=2)
)]
if __name__ == "__main__":
asyncio.run(app.run())
4. Calculator MCP Server (Simplest Example)
For absolute beginners, a simple calculator server demonstrates the fundamentals without external dependencies.
Python Implementation
import asyncio
from mcp.server import Server
from mcp.types import Tool, TextContent
app = Server("calculator-server")
@app.list_tools()
async def list_tools():
return [
Tool(
name="add",
description="Add two numbers",
inputSchema={
"type": "object",
"properties": {
"a": {"type": "number"},
"b": {"type": "number"}
},
"required": ["a", "b"]
}
),
Tool(
name="multiply",
description="Multiply two numbers",
inputSchema={
"type": "object",
"properties": {
"a": {"type": "number"},
"b": {"type": "number"}
},
"required": ["a", "b"]
}
)
]
@app.call_tool()
async def calculate(name: str, arguments: dict):
a = arguments["a"]
b = arguments["b"]
if name == "add":
result = a + b
elif name == "multiply":
result = a * b
return [TextContent(type="text", text=str(result))]
if __name__ == "__main__":
asyncio.run(app.run())
Setting Up and Testing Your MCP Server
Once you've chosen an example to work with, follow these steps:
Installation
For Python:
pip install mcp anthropic-mcp-server
For JavaScript:
npm install @modelcontextprotocol/sdk
Running the Server
Python:
python your_mcp_server.py
JavaScript:
node your_mcp_server.js
Configuring Claude Desktop
Add your server to Claude Desktop's configuration file (claude_desktop_config.json
):
{
"mcpServers": {
"your-server-name": {
"command": "python",
"args": ["/path/to/your_mcp_server.py"]
}
}
}
For Node.js servers:
{
"mcpServers": {
"your-server-name": {
"command": "node",
"args": ["/path/to/your_mcp_server.js"]
}
}
}
Best Practices for MCP Server Development
- Start Simple: Begin with the calculator or filesystem example before moving to more complex integrations
- Error Handling: Always validate inputs and handle errors gracefully
- Security: Never expose sensitive operations without proper authentication
- Documentation: Use clear descriptions for tools and resources
- Testing: Test each tool independently before integration
- Logging: Add logging to debug issues during development
Learning Path for MCP Beginners
- Start with Calculator: Understand basic tool implementation
- Move to Filesystem: Learn about resources and state management
- Build Web Scraper: Master asynchronous operations and API integration
- Create Database Server: Handle complex data structures and security
Integration with Web Scraping Workflows
MCP servers can be particularly powerful when combined with web scraping tools. For example, you might use browser automation with Puppeteer alongside an MCP server that manages scraped data storage and processing. This allows AI assistants to orchestrate complex scraping workflows by handling AJAX requests and dynamically adjusting scraping strategies based on the collected data.
Common Pitfalls to Avoid
- Blocking Operations: Always use async/await for I/O operations
- Missing Error Handlers: Uncaught errors will crash your server
- Poor Schema Validation: Invalid tool schemas prevent proper AI integration
- Hardcoded Paths: Use environment variables for configuration
- Missing Dependencies: Document all required packages
Next Steps
After mastering these examples, consider:
- Building a custom MCP server for your specific use case
- Contributing to the MCP community with new examples
- Integrating multiple MCP servers for complex workflows
- Exploring advanced features like streaming responses
Conclusion
The best way to learn MCP server development is through hands-on practice with progressively complex examples. Start with the calculator server to understand the fundamentals, then move to the filesystem server to learn about resources. The web scraping and database examples will prepare you for real-world applications where MCP servers can significantly enhance AI assistant capabilities.
By following these examples and best practices, you'll be well-equipped to build custom MCP servers that extend AI capabilities in meaningful ways, whether for data processing, browser automation, or complex business logic integration.