Skip to main content
Glama

Gemini MCP Server

by lbds137
BUILD_YOUR_OWN_MCP_SERVER.md18.8 kB
# Building Your Own MCP Server for Claude Code This guide will walk you through creating a custom MCP (Model Context Protocol) server that integrates with Claude Code, allowing you to extend Claude's capabilities with external tools, APIs, or even other AI models. ## What is MCP? MCP (Model Context Protocol) is a protocol that allows Claude to communicate with external servers to access tools and capabilities beyond its built-in features. Think of it as a plugin system for Claude. ## Prerequisites - Python 3.8 or higher - Claude Code CLI installed (`npm install -g @anthropic-ai/claude-code`) - Basic understanding of JSON-RPC protocol ## ⚠️ IMPORTANT: MCP Configuration Scopes Before building your MCP server, understand Claude Code's configuration hierarchy to avoid common issues: ### Configuration Scope Types Claude Code supports three configuration scopes (in order of priority): 1. **Project Scope** (`.vscode/mcp.json`) - Highest priority, overrides everything 2. **Local Scope** (`claude mcp add` default) - Works only in current directory 3. **User Scope** (`claude mcp add --scope user`) - Global configuration ### Common Pitfall: Local vs Global Configuration **❌ WRONG (Local scope - only works in current directory):** ```bash claude mcp add my-server python3 /path/to/server.py ``` **✅ CORRECT (User scope - works globally):** ```bash claude mcp add --scope user my-server python3 /path/to/server.py ``` ### Recommended Setup for Global Access 1. **Always use `--scope user`** for global MCP servers 2. **Store servers in permanent location**: `~/.claude-mcp-servers/` 3. **Avoid project-local configs** unless specifically needed 4. **Remove conflicting `.vscode/mcp.json`** files ### Quick Fix for Broken MCP Access If your MCP only works in one directory: ```bash # Check current configuration claude mcp list # Remove local config claude mcp remove your-server # Re-add with user scope claude mcp add --scope user your-server python3 /path/to/server.py # Remove any project-local configs rm .vscode/mcp.json # if exists ``` ## Basic MCP Server Structure ### 1. Create Your Project Directory ```bash mkdir my-mcp-server cd my-mcp-server ``` ### 2. Basic Server Template Create a file named `server.py`: ```python #!/usr/bin/env python3 """ Basic MCP Server Template """ import json import sys import os from typing import Dict, Any, Optional # Ensure unbuffered output for proper communication sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1) sys.stderr = os.fdopen(sys.stderr.fileno(), 'w', 1) def send_response(response: Dict[str, Any]): """Send a JSON-RPC response""" print(json.dumps(response), flush=True) def handle_initialize(request_id: Any) -> Dict[str, Any]: """Handle initialization request""" return { "jsonrpc": "2.0", "id": request_id, "result": { "protocolVersion": "2024-11-05", "capabilities": { "tools": {} }, "serverInfo": { "name": "my-mcp-server", "version": "1.0.0" } } } def handle_tools_list(request_id: Any) -> Dict[str, Any]: """List available tools""" tools = [ { "name": "hello_world", "description": "A simple hello world tool", "inputSchema": { "type": "object", "properties": { "name": { "type": "string", "description": "Name to greet" } }, "required": ["name"] } } ] return { "jsonrpc": "2.0", "id": request_id, "result": { "tools": tools } } def handle_tool_call(request_id: Any, params: Dict[str, Any]) -> Dict[str, Any]: """Handle tool execution""" tool_name = params.get("name") arguments = params.get("arguments", {}) try: if tool_name == "hello_world": name = arguments.get("name", "World") result = f"Hello, {name}! This is a response from your MCP server." else: raise ValueError(f"Unknown tool: {tool_name}") return { "jsonrpc": "2.0", "id": request_id, "result": { "content": [ { "type": "text", "text": result } ] } } except Exception as e: return { "jsonrpc": "2.0", "id": request_id, "error": { "code": -32603, "message": str(e) } } def main(): """Main server loop""" while True: try: line = sys.stdin.readline() if not line: break request = json.loads(line.strip()) method = request.get("method") request_id = request.get("id") params = request.get("params", {}) if method == "initialize": response = handle_initialize(request_id) elif method == "tools/list": response = handle_tools_list(request_id) elif method == "tools/call": response = handle_tool_call(request_id, params) else: response = { "jsonrpc": "2.0", "id": request_id, "error": { "code": -32601, "message": f"Method not found: {method}" } } send_response(response) except json.JSONDecodeError: continue except EOFError: break except Exception as e: if 'request_id' in locals(): send_response({ "jsonrpc": "2.0", "id": request_id, "error": { "code": -32603, "message": f"Internal error: {str(e)}" } }) if __name__ == "__main__": main() ``` ### 3. Make It Executable ```bash chmod +x server.py ``` ### 4. Add to Claude Code ```bash claude mcp add my-server python3 /path/to/your/server.py ``` ## Understanding the MCP Protocol ### Required Methods Your MCP server must handle these JSON-RPC methods: 1. **`initialize`** - Called when Claude connects to your server - Must return protocol version and capabilities 2. **`tools/list`** - Lists all available tools - Returns array of tool definitions with schemas 3. **`tools/call`** - Executes a specific tool - Receives tool name and arguments - Returns results that Claude can use ### Message Format All communication uses JSON-RPC 2.0 over standard input/output: **Request from Claude:** ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "hello_world", "arguments": {"name": "Claude"} } } ``` **Response from your server:** ```json { "jsonrpc": "2.0", "id": 1, "result": { "content": [ { "type": "text", "text": "Hello, Claude!" } ] } } ``` ## Advanced Example: Weather API Server Here's a more practical example that fetches weather data: ```python #!/usr/bin/env python3 import json import sys import os import requests from typing import Dict, Any sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1) sys.stderr = os.fdopen(sys.stderr.fileno(), 'w', 1) # Your API key (store securely in production) WEATHER_API_KEY = "your-api-key-here" def get_weather(city: str) -> str: """Fetch weather data from API""" try: url = f"http://api.openweathermap.org/data/2.5/weather" params = { "q": city, "appid": WEATHER_API_KEY, "units": "metric" } response = requests.get(url, params=params) data = response.json() if response.status_code == 200: temp = data["main"]["temp"] desc = data["weather"][0]["description"] return f"Weather in {city}: {temp}°C, {desc}" else: return f"Error: {data.get('message', 'Unknown error')}" except Exception as e: return f"Error fetching weather: {str(e)}" # ... (include the same boilerplate as before) def handle_tools_list(request_id: Any) -> Dict[str, Any]: tools = [ { "name": "get_weather", "description": "Get current weather for a city", "inputSchema": { "type": "object", "properties": { "city": { "type": "string", "description": "City name" } }, "required": ["city"] } } ] # ... rest of implementation ``` ## Best Practices ### 1. Error Handling Always wrap tool execution in try-except blocks and return proper JSON-RPC errors: ```python try: # Your tool logic result = do_something() except Exception as e: return { "jsonrpc": "2.0", "id": request_id, "error": { "code": -32603, "message": str(e) } } ``` ### 2. Input Validation Validate all inputs from the arguments: ```python def validate_arguments(arguments: Dict[str, Any], required: List[str]): for field in required: if field not in arguments: raise ValueError(f"Missing required field: {field}") ``` ### 3. Logging Use stderr for logging to avoid interfering with JSON-RPC: ```python import logging logging.basicConfig(level=logging.INFO, stream=sys.stderr) ``` ### 4. Dependencies Create a `requirements.txt` file: ``` requests>=2.28.0 # Add other dependencies ``` Install with: `pip install -r requirements.txt` ## Testing Your MCP Server ### 1. Manual Testing Test individual methods: ```bash # Test initialize echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' | python3 server.py # Test tools/list echo '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' | python3 server.py # Test tool call echo '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"hello_world","arguments":{"name":"Test"}}}' | python3 server.py ``` ### 2. Integration Testing After adding to Claude Code: ```bash # List all MCP servers claude mcp list # In a Claude Code session, your tools will appear as: # mcp__<server-name>__<tool-name> ``` ## Debugging Tips 1. **Check configuration scope first**: ```bash # Check what's configured claude mcp list # Check if you're in a directory with local config ls .vscode/mcp.json # Test in different directories cd ~ && claude mcp list ``` 2. **Check logs**: ```bash ls ~/Library/Caches/claude-cli-nodejs/*/mcp-logs-<server-name>/ ``` 3. **Run with debug mode**: ```bash claude --debug ``` 4. **Common issues**: - **MCP only works in one directory**: Wrong scope, use `--scope user` - **MCP not found**: Check if `.vscode/mcp.json` exists and conflicts - Import errors: Ensure all dependencies are installed - Connection closed: Check for syntax errors or crashes - Tools not appearing: Verify tools/list returns valid schema 5. **Configuration conflicts**: ```bash # Remove project-local config rm .vscode/mcp.json # Remove local scope config claude mcp remove server-name # Re-add with user scope claude mcp add --scope user server-name python3 /path/to/server.py ``` ## Advanced Features ### 1. Stateful Conversations Store conversation context: ```python class MCPServer: def __init__(self): self.conversation_history = [] def add_to_history(self, role: str, content: str): self.conversation_history.append({ "role": role, "content": content }) ``` ### 2. File Handling Return different content types: ```python # Text content { "type": "text", "text": "Your response" } # Image content (base64) { "type": "image", "data": base64_encoded_image, "mimeType": "image/png" } ``` ### 3. Async Operations For long-running tasks, consider implementing progress updates or background processing. ## Safe Installation & Deployment ### Recommended Directory Structure Store your MCP servers in a permanent location: ```bash ~/.claude-mcp-servers/ ├── your-server-name/ │ ├── server.py │ ├── requirements.txt │ ├── setup.py │ └── README.md └── backup.sh ``` ### Create an Auto-Setup Script Create `setup.py` for easy installation: ```python #!/usr/bin/env python3 """Setup script for MCP server""" import subprocess import sys import os def check_python_version(): if sys.version_info < (3, 8): print("❌ Python 3.8+ required") sys.exit(1) def install_dependencies(): print("📦 Installing dependencies...") subprocess.check_call([sys.executable, "-m", "pip", "install", "-r", "requirements.txt"]) def add_to_claude(): server_path = os.path.join(os.path.dirname(__file__), "server.py") print(f"🔧 Adding to Claude MCP with global scope...") # IMPORTANT: Use --scope user for global access! subprocess.run(["claude", "mcp", "add", "--scope", "user", "your-server", "python3", server_path]) if __name__ == "__main__": check_python_version() install_dependencies() add_to_claude() print("✅ Setup complete!") ``` ### Version Management Add version tracking to your server: ```python __version__ = "1.0.0" __updated__ = "2025-06-11" # In your tools list, add: { "name": "server_info", "description": "Get server version and status", "inputSchema": {"type": "object", "properties": {}} } # In tool handler: if tool_name == "server_info": return f"Server v{__version__} (updated {__updated__})" ``` ### Dependency Checking Add automatic dependency checking: ```python def check_dependencies(): """Check if all required packages are installed""" required = ["requests", "other-package"] missing = [] for package in required: try: __import__(package) except ImportError: missing.append(package) if missing: return f"Missing packages: {', '.join(missing)}" return "All dependencies installed!" ``` ### Automatic Updates Create an update mechanism for your server: ```python # In your server.py def check_for_updates(): """Check if updates are available""" try: import requests response = requests.get("https://api.github.com/repos/YOUR_REPO/releases/latest") latest_version = response.json()["tag_name"] if latest_version > __version__: return f"Update available: {latest_version}" return "Server is up to date" except: return "Could not check for updates" # Add update tool to your tools list { "name": "update_server", "description": "Update the MCP server to latest version", "inputSchema": {"type": "object", "properties": {}} } ``` ### Environment Variables Support environment variables for configuration: ```python import os # API keys and sensitive data API_KEY = os.environ.get("YOUR_API_KEY", "default-key-if-any") # Configuration DEBUG = os.environ.get("MCP_DEBUG", "false").lower() == "true" LOG_LEVEL = os.environ.get("MCP_LOG_LEVEL", "ERROR") ``` ## Publishing Your MCP Server 1. **Package your server**: ```bash my-mcp-server/ ├── server.py ├── requirements.txt ├── setup.py ├── README.md └── LICENSE ``` 2. **Create one-line installer**: ```bash # In your README: curl -sSL https://your-repo/install.sh | bash ``` 3. **Share on GitHub** with clear documentation ## Example Use Cases 1. **Database Query Tool**: Allow Claude to query your database 2. **API Integration**: Connect to any REST API 3. **System Monitoring**: Check system stats, logs, etc. 4. **Custom AI Models**: Integrate other AI models (like we did with Gemini) 5. **Development Tools**: Linters, formatters, test runners 6. **Communication Tools**: Send emails, Slack messages, etc. ## Security Considerations 1. **API Keys**: Never hardcode sensitive keys ```python API_KEY = os.environ.get("MY_API_KEY") ``` 2. **Input Sanitization**: Always validate and sanitize inputs 3. **Access Control**: Limit what your MCP server can access 4. **Rate Limiting**: Implement rate limits for API calls ## Real-World Example: Claude-Gemini Collaboration MCP Here's a complete working example that enables Claude Code to collaborate with Google's Gemini AI: ### Quick Installation ```bash # 1. Create permanent directory mkdir -p ~/.claude-mcp-servers/gemini-collab # 2. Install Gemini SDK pip install google-generativeai # 3. Download server (simplified version) curl -o ~/.claude-mcp-servers/gemini-collab/server.py https://your-repo/server.py # 4. Add to Claude with USER SCOPE (crucial!) claude mcp add --scope user gemini-collab python3 ~/.claude-mcp-servers/gemini-collab/server.py # 5. Test from any directory claude /mcp # Should show gemini-collab connected ``` ### Available Tools Once installed, you'll have these tools globally: - `mcp__gemini-collab__ask_gemini` - Ask Gemini questions - `mcp__gemini-collab__gemini_code_review` - Code reviews - `mcp__gemini-collab__gemini_brainstorm` - Collaborative brainstorming ### Usage Example ```bash # In any directory, start Claude Code: claude # Use Gemini for code review: mcp__gemini-collab__gemini_code_review code: "function authenticate(user) { return user.password === 'admin'; }" focus: "security" # Gemini's response appears directly in Claude's context! ``` ### Key Learnings from This Setup 1. **Always use `--scope user`** for global access 2. **Store in `~/.claude-mcp-servers/`** for permanence 3. **Remove conflicting local configs** like `.vscode/mcp.json` 4. **Test in multiple directories** to verify global access 5. **Environment variables** for API keys when possible ## Conclusion MCP servers extend Claude Code's capabilities infinitely. You can integrate any API, tool, or service by following this protocol. The key points for success: 1. **Use proper configuration scope** (`--scope user` for global) 2. **Handle errors gracefully** with try-except blocks 3. **Provide clear tool descriptions** so Claude knows how to use them 4. **Test thoroughly** in multiple directories 5. **Store servers permanently** in `~/.claude-mcp-servers/` **Remember**: Configuration scope is the #1 source of MCP issues. When in doubt, use `--scope user`! Happy building! 🚀

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/lbds137/gemini-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server