Build MCP (Model Context Protocol) servers that give Claude new capabilities. Use when user wants to create an MCP server, add tools to Claude, or integrate external services.
git clone https://github.com/shareAI-lab/learn-claude-code.git--- name: mcp-builder description: Build MCP (Model Context Protocol) servers that give Claude new capabilities. Use when user wants to create an MCP server, add tools to Claude, or integrate external services. --- # MCP Server Building Skill You now have expertise in building MCP (Model Context Protocol) servers. MCP enables Claude to interact with external services through a standardized protocol. ## What is MCP? MCP servers expose: - **Tools**: Functions Claude can call (like API endpoints) - **Resources**: Data Claude can read (like files or database records) - **Prompts**: Pre-built prompt templates ## Quick Start: Python MCP Server ### 1. Project Setup ```bash # Create project mkdir my-mcp-server && cd my-mcp-server python3 -m venv venv && source venv/bin/activate # Install MCP SDK pip install mcp ``` ### 2. Basic Server Template ```python #!/usr/bin/env python3 """my_server.py - A simple MCP server""" from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent # Create server instance server = Server("my-server") # Define a tool @server.tool() async def hello(name: str) -> str: """Say hello to someone. Args: name: The name to greet """ return f"Hello, {name}!" @server.tool() async def add_numbers(a: int, b: int) -> str: """Add two numbers together. Args: a: First number b: Second number """ return str(a + b) # Run server async def main(): async with stdio_server() as (read, write): await server.run(read, write) if __name__ == "__main__": import asyncio asyncio.run(main()) ``` ### 3. Register with Claude Add to `~/.claude/mcp.json`: ```json { "mcpServers": { "my-server": { "command": "python3", "args": ["/path/to/my_server.py"] } } } ``` ## TypeScript MCP Server ### 1. Setup ```bash mkdir my-mcp-server && cd my-mcp-server npm init -y npm install @modelcontextprotocol/sdk ``` ### 2. Template ```typescript // src/index.ts import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; const server = new Server({ name: "my-server", version: "1.0.0", }); // Define tools server.setRequestHandler("tools/list", async () => ({ tools: [ { name: "hello", description: "Say hello to someone", inputSchema: { type: "object", properties: { name: { type: "string", description: "Name to greet" }, }, required: ["name"], }, }, ], })); server.setRequestHandler("tools/call", async (request) => { if (request.params.name === "hello") { const name = request.params.arguments.name; return { content: [{ type: "text", text: `Hello, ${name}!` }] }; } throw new Error("Unknown tool"); }); // Start server const transport = new StdioServerTransport(); server.connect(transport); ``` ## Advanced Patterns ### External API Integration ```python import httpx from mcp.server import Server server = Server("weather-server") @server.tool() async def get_weather(city: str) -> str: """Get current weather for a city.""" async with httpx.AsyncClient() as client: resp = await client.get( f"https://api.weatherapi.com/v1/current.json", params={"key": "YOUR_API_KEY", "q": city} ) data = resp.json() return f"{city}: {data['current']['temp_c']}C, {data['current']['condition']['text']}" ``` ### Database Access ```python import sqlite3 from mcp.server import Server server = Server("db-server") @server.tool() async def query_db(sql: str) -> str: """Execute a read-only SQL query.""" if not sql.strip().upper().startswith("SELECT"): return "Error: Only SELECT queries allowed" conn = sqlite3.connect("data.db") cursor = conn.execute(sql) rows = cursor.fetchall() conn.close() return str(rows) ``` ### Resources (Read-only Data) ```python @server.resource("config://settings") async def get_settings() -> str: """Application settings.""" return open("settings.json").read() @server.resource("file://{path}") async def read_file(path: str) -> str: """Read a file from the workspace.""" return open(path).read() ``` ## Testing ```bash # Test with MCP Inspector npx @anthropics/mcp-inspector python3 my_server.py # Or send test messages directly echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | python3 my_server.py ``` ## Best Practices 1. **Clear tool descriptions**: Claude uses these to decide when to call tools 2. **Input validation**: Always validate and sanitize inputs 3. **Error handling**: Return meaningful error messages 4. **Async by default**: Use async/await for I/O operations 5. **Security**: Never expose sensitive operations without auth 6. **Idempotency**: Tools should be safe to retry
1. **Define Requirements**: Clearly list the tools/functions your MCP server needs to expose. Include parameters, descriptions, and any authentication requirements. 2. **Choose Implementation**: Select a programming language/framework (Python with `mcp` SDK, TypeScript with `@modelcontextprotocol/sdk`, etc.). 3. **Develop Server**: Write the server code with proper error handling and rate limiting. Use the `mcp` framework’s decorators for tool registration. 4. **Test Locally**: Run the server and test each tool with sample inputs. Use the MCP Inspector tool to verify functionality. 5. **Deploy**: Package the server (Docker recommended) and deploy to a cloud service. Configure Claude to use your MCP server by adding it to your MCP config file. **Tips:** - Start with a single tool and expand gradually. - Use environment variables for sensitive data like API keys. - Add logging to debug issues in production. - Follow MCP server best practices for security and performance.
No install command available. Check the GitHub repository for manual installation instructions.
git clone https://github.com/shareAI-lab/learn-claude-code/tree/main/skills/mcp-builderCopy the install command above and run it in your terminal.
Launch Claude Code, Cursor, or your preferred AI coding agent.
Use the prompt template or examples below to test the skill.
Adapt the skill to your specific use case and workflow.
Build an MCP server that exposes [SPECIFIC_TOOL_OR_SERVICE] as a tool for Claude. The server should include the following functionality: [LIST_FUNCTIONS_WITH_PARAMETERS]. Use [PROGRAMMING_LANGUAGE_FRAMEWORK] for implementation. Ensure the server follows MCP server best practices, including proper error handling, rate limiting, and authentication if required. Provide the complete code implementation, configuration files, and deployment instructions. Test the server locally with a sample query like '[SAMPLE_QUERY]' to verify functionality before deployment.
Here’s a complete MCP server implementation in Python using the `mcp` framework that exposes a Slack workspace management tool. The server provides three functions: `list_channels`, `post_message`, and `get_user_profile`.
```python
# mcp_server.py
from mcp.server import Server
from mcp.server.models import InitializationOptions
import os
import slack_sdk
server = Server("slack-workspace-mcp")
slack_token = os.getenv("SLACK_API_TOKEN")
client = slack_sdk.WebClient(token=slack_token)
@server.list_tools()
async def list_tools():
return [
{
"name": "list_channels",
"description": "List all public channels in the Slack workspace",
"inputSchema": {"type": "object", "properties": {}},
},
{
"name": "post_message",
"description": "Post a message to a specific channel",
"inputSchema": {
"type": "object",
"properties": {
"channel_id": {"type": "string", "description": "The Slack channel ID"},
"text": {"type": "string", "description": "The message text"},
},
"required": ["channel_id", "text"],
},
},
{
"name": "get_user_profile",
"description": "Get profile information for a specific user",
"inputSchema": {
"type": "object",
"properties": {
"user_id": {"type": "string", "description": "The Slack user ID"},
},
"required": ["user_id"],
},
},
]
@server.call_tool()
async def call_tool(name: str, arguments: dict):
if name == "list_channels":
result = client.conversations_list(types="public_channel")
return {
"content": [
{
"type": "text",
"text": f"Channel: {channel['name']} (ID: {channel['id']})"
}
for channel in result['channels']
]
}
elif name == "post_message":
result = client.chat_postMessage(
channel=arguments["channel_id"],
text=arguments["text"]
)
return {
"content": [
{
"type": "text",
"text": f"Message posted to {result['channel']} with timestamp {result['ts']}"
}
]
}
elif name == "get_user_profile":
result = client.users_profile_get(user=arguments["user_id"])
return {
"content": [
{
"type": "text",
"text": f"User: {result['profile']['real_name']} (@{result['profile']['display_name']})\nEmail: {result['profile']['email']}"
}
]
}
else:
raise ValueError(f"Unknown tool: {name}")
if __name__ == "__main__":
server.run()
```
**Configuration:**
Create a `.env` file with your Slack API token:
```
SLACK_API_TOKEN=xoxb-your-slack-token-here
```
**Deployment:**
1. Install dependencies: `pip install mcp-sdk slack-sdk python-dotenv`
2. Run locally: `python mcp_server.py`
3. Test with: `list_channels`
4. Deploy to a cloud service like Render or Railway for production use.
**Sample Output:**
When Claude calls `list_channels`, it receives:
```
Channel: general (ID: C1234567890)
Channel: random (ID: C0987654321)
Channel: project-alpha (ID: C1122334455)
```
When posting a message to channel C1234567890 with text "Hello team!", the response is:
```
Message posted to C1234567890 with timestamp 1712345678.123456
```skills-collection
Take a free 3-minute scan and get personalized AI skill recommendations.
Take free scan