Skip to main content
Glama
jaredlang
by jaredlang
REMOTE_MCP_GUIDE.md13.6 kB
# Remote MCP Server Guide This guide explains how the weather agent works with the MCP server via HTTP/SSE transport. ## Quick Start **TL;DR - What took 3-4 hours to figure out:** 1. **MCP server must use SSE (Server-Sent Events) transport, not HTTP POST** 2. **Client must connect via `/sse` endpoint, not `/messages`** 3. **Audio data must be base64-encoded, not file paths** 4. **MCP client library handles the JSON-RPC protocol automatically** See [Implementation Summary](#implementation-summary) for detailed changes. ## Architecture Overview ```text Weather Agent → SSE/JSON-RPC → MCP Server (SSE/HTTP) → Cloud SQL ``` The weather agent **always** calls the MCP server remotely via SSE transport using the MCP protocol. This works for both: - **Local Development**: MCP server running on `localhost:8080` - **Production**: MCP server deployed on Cloud Run ## How It Works ### MCP Server (`forecast_storage_mcp/server.py`) The MCP server runs in HTTP mode and exposes endpoints: - `/sse` - Server-Sent Events endpoint for streaming - `/messages` - POST endpoint for tool calls Transport mode is controlled by the `MCP_TRANSPORT` environment variable: - `MCP_TRANSPORT=http` - HTTP mode (required) ### Client (`weather_agent/forecast_storage_client.py`) The client makes HTTP POST requests to the MCP server using JSON-RPC 2.0: - Sends tool name and arguments - Receives results as JSON - Handles timeouts and connection errors The MCP server URL is configured via `MCP_SERVER_URL` environment variable. ## Configuration ### For Local Development 1. Start MCP server on localhost: ```bash cd forecast_storage_mcp MCP_TRANSPORT=http PORT=8080 python server.py ``` 2. Configure weather agent to use localhost: ```bash # weather_agent/.env MCP_SERVER_URL=http://localhost:8080 ``` ### For Production (Cloud Run) 1. Deploy MCP server to Cloud Run (see [DEPLOYMENT.md](./DEPLOYMENT.md)) 2. Configure weather agent with Cloud Run URL: ```bash # weather_agent/.env MCP_SERVER_URL=https://forecast-mcp-server-xxxxx-uc.a.run.app ``` ## Testing ### Test Local HTTP Server ```bash # Terminal 1: Start MCP server cd forecast_storage_mcp MCP_TRANSPORT=http PORT=8080 python server.py # Terminal 2: Test with weather agent cd .. python test_remote_mcp.py local ``` ### Test Cloud Run Deployment ```bash export MCP_SERVER_URL=https://forecast-mcp-server-xxxxx-uc.a.run.app python test_remote_mcp.py remote ``` ### Test with curl ```bash # Local curl -X POST "http://localhost:8080/messages" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "test_connection", "arguments": {} } }' # Cloud Run curl -X POST "https://your-service.run.app/messages" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "test_connection", "arguments": {} } }' ``` ## MCP Protocol Details ### JSON-RPC 2.0 Request Format ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "tool_name", "arguments": { "param1": "value1", "param2": "value2" } } } ``` ### JSON-RPC 2.0 Response Format ```json { "jsonrpc": "2.0", "id": 1, "result": { "content": [ { "type": "text", "text": "{\"status\": \"success\", \"data\": \"...\"}" } ] } } ``` ## Available Tools All tools are accessed via HTTP: 1. **upload_forecast** - Store forecast text and audio in Cloud SQL 2. **get_cached_forecast** - Retrieve cached forecast if available 3. **get_storage_stats** - Get database statistics 4. **cleanup_expired_forecasts** - Remove expired forecasts 5. **list_forecasts** - List forecast history 6. **test_connection** - Verify database connectivity ## Development Workflow ### 1. Start MCP Server Locally ```bash cd forecast_storage_mcp MCP_TRANSPORT=http PORT=8080 python server.py ``` Keep this running in a dedicated terminal. ### 2. Develop Weather Agent ```bash # Ensure MCP_SERVER_URL points to localhost export MCP_SERVER_URL=http://localhost:8080 # Run your weather agent cd weather_agent python agent.py ``` ### 3. Deploy to Production ```bash # Deploy MCP server cd forecast_storage_mcp gcloud run deploy forecast-mcp-server --source . --region us-central1 # Get service URL SERVICE_URL=$(gcloud run services describe forecast-mcp-server \ --platform managed \ --region us-central1 \ --format 'value(status.url)') # Update weather agent configuration echo "MCP_SERVER_URL=$SERVICE_URL" >> ../weather_agent/.env # Deploy weather agent to your platform ``` ## Performance Considerations ### Local Development - **Latency**: ~10-50ms (localhost HTTP) - **Cold Start**: None (server always running) - **Best for**: Development, testing, debugging ### Cloud Run Production - **Latency**: ~100-500ms (network + cold start) - **Cold Start**: ~2-5s for first request after idle - **Scaling**: Automatic based on traffic - **Best for**: Production deployments ### Optimization Tips 1. **Reduce Cold Starts**: Set `--min-instances 1` in Cloud Run 2. **Increase Timeout**: Use 30-60s timeout for complex operations 3. **Monitor Performance**: Use Cloud Run metrics to track latency 4. **Connection Pooling**: MCP server reuses database connections ## Troubleshooting ### Cannot Connect to MCP Server **Error**: `Cannot connect to MCP server at http://localhost:8080` **Solutions**: 1. Check if MCP server is running: ```bash curl http://localhost:8080/messages ``` 2. Start MCP server: ```bash cd forecast_storage_mcp MCP_TRANSPORT=http PORT=8080 python server.py ``` 3. Check port availability: ```bash netstat -an | grep 8080 ``` ### MCP Server Request Timed Out **Error**: `MCP server request timed out` **Solutions**: 1. Check MCP server logs for errors 2. Verify database connectivity 3. Increase timeout in client code (default: 30s) 4. For Cloud Run, check if service is scaled to zero ### Database Connection Failed **Error**: `Connection to Cloud SQL failed` **Solutions**: 1. Verify Cloud SQL instance is running 2. Check database credentials in `.env` 3. Test connection: ```bash cd forecast_storage_mcp python -c "from tools.connection import test_connection; print(test_connection())" ``` ### Invalid MCP Response Format **Error**: `Invalid MCP response format` **Solutions**: 1. Check MCP server version compatibility 2. Verify JSON-RPC 2.0 response structure 3. Check server logs for errors 4. Ensure server is running in HTTP mode (not stdio) ## Best Practices ### For Development - Run MCP server in dedicated terminal - Use localhost URL for fastest iteration - Monitor server logs for errors - Test with `test_remote_mcp.py` script ### For Production - Deploy MCP server to Cloud Run - Enable Cloud Run authentication - Set appropriate timeout values - Monitor Cloud Run metrics - Configure min instances for low latency - Use Cloud SQL connection pooling ### For Security - Use HTTPS for production (Cloud Run provides this) - Enable authentication on Cloud Run endpoints - Rotate database credentials regularly - Use Secret Manager for sensitive values - Limit Cloud Run service account permissions ## Migration from Direct Imports If you previously used direct imports: 1. **Start MCP Server**: Run in HTTP mode on localhost 2. **Update Configuration**: Set `MCP_SERVER_URL=http://localhost:8080` 3. **Test**: Run `python test_remote_mcp.py local` 4. **Deploy**: MCP server to Cloud Run when ready Benefits: - Consistent behavior across environments - Better separation of concerns - Easier to scale and monitor - Simpler deployment architecture ## Implementation Summary This section documents the key changes made during the 3-4 hour debugging session to get remote MCP working. ### Problem 1: Transport Protocol Mismatch **Issue**: Initially tried using HTTP POST to `/messages` endpoint with manual JSON-RPC requests. **Root Cause**: MCP protocol uses Server-Sent Events (SSE) for bi-directional communication, not simple HTTP POST. **Solution**: - Changed server to use SSE transport: `MCP_TRANSPORT=http` (this enables SSE) - Client connects via `/sse` endpoint, not `/messages` - Use official MCP client library (`mcp.client.sse`) instead of manual HTTP requests **Files Changed**: - `forecast_storage_mcp/server.py`: Already configured for SSE - `weather_agent/forecast_storage_client.py`: Updated to use `sse_client()` ### Problem 2: Audio File Path vs Base64 Data **Issue**: Test script was passing `audio_file_path` parameter, but server expected `audio_data`. **Root Cause**: Remote MCP server cannot access local files. Audio must be transmitted as data. **Solution**: - Read audio file and encode as base64 in client - Pass `audio_data` parameter with base64 string - Server decodes base64 back to binary **Code Example**: ```python # Wrong - file path won't work for remote server result = await _call_mcp_tool_remote("upload_forecast", { "audio_file_path": "/path/to/file.wav" # ❌ }) # Correct - base64 encoded data with open(audio_path, 'rb') as f: audio_bytes = f.read() audio_base64 = base64.b64encode(audio_bytes).decode('utf-8') result = await _call_mcp_tool_remote("upload_forecast", { "audio_data": audio_base64 # ✅ }) ``` **Files Changed**: - `test_remote_mcp.py`: Added base64 encoding in both test functions - `weather_agent/forecast_storage_client.py`: Already uses base64 encoding ### Problem 3: MCP Client Library Integration **Issue**: Manual HTTP requests required complex JSON-RPC protocol handling. **Root Cause**: Tried to bypass MCP client library, which handles protocol details. **Solution**: - Use `mcp.client.sse.sse_client()` for SSE connection - Use `mcp.client.session.ClientSession` for session management - Let library handle JSON-RPC protocol automatically **Code Pattern**: ```python from mcp.client.sse import sse_client from mcp.client.session import ClientSession async def _call_mcp_tool_remote(tool_name: str, arguments: Dict[str, Any]): base_url = MCP_SERVER_URL.rstrip('/') sse_url = f"{base_url}/sse" # Connect to /sse endpoint async with sse_client(sse_url) as (read, write): async with ClientSession(read, write) as session: await session.initialize() result = await session.call_tool(tool_name, arguments) # Parse result from MCP response text_content = result.content[0].text return json.loads(text_content) ``` **Files Changed**: - `weather_agent/forecast_storage_client.py`: Implemented SSE client pattern ### Problem 4: Test Script Default Parameter **Issue**: Test script required command-line argument, making it harder to run. **Solution**: - Default to `"local"` mode when no argument provided - Makes testing faster: just run `python test_remote_mcp.py` **Files Changed**: - `test_remote_mcp.py`: Updated `main()` function with default parameter ### Key Lessons Learned 1. **SSE is not optional**: MCP protocol requires SSE for proper bi-directional communication 2. **Use official client libraries**: Don't try to implement MCP protocol manually 3. **Remote servers need data, not paths**: Always encode files as base64 for remote calls 4. **Test early and often**: The `test_remote_mcp.py` script was invaluable for debugging 5. **MCP_TRANSPORT=http means SSE**: Confusing naming, but "http" enables SSE transport ### Testing Checklist After making these changes, verify: - [ ] MCP server starts with `MCP_TRANSPORT=http` - [ ] Server logs show "Listening on http://localhost:8080" - [ ] `/sse` endpoint is accessible (check server logs) - [ ] Test script passes all 8 tests: `python test_remote_mcp.py` - [ ] Upload forecast test succeeds (base64 encoding works) - [ ] Weather agent can upload forecasts to storage - [ ] Cloud Run deployment works with same code ### File Summary **Modified Files**: 1. `weather_agent/forecast_storage_client.py` - SSE client implementation 2. `test_remote_mcp.py` - Base64 encoding + default parameter 3. `forecast_storage_mcp/server.py` - Already configured for SSE 4. `forecast_storage_mcp/REMOTE_MCP_GUIDE.md` - This documentation **Configuration Files**: 1. `forecast_storage_mcp/.env` - Database credentials 2. `weather_agent/.env` - MCP_SERVER_URL setting ### Environment Variables **MCP Server** (`forecast_storage_mcp/.env`): ```bash MCP_TRANSPORT=http # Enables SSE transport PORT=8080 # Server port DB_INSTANCE=project:region:instance DB_NAME=weather DB_USER=postgres DB_PASSWORD=your_password ``` **Weather Agent** (`weather_agent/.env`): ```bash # Local development MCP_SERVER_URL=http://localhost:8080 # Production MCP_SERVER_URL=https://forecast-mcp-server-xxxxx-uc.a.run.app ``` ### Debugging Tips 1. **Server not responding**: Check server logs for SSE connection establishment 2. **Invalid JSON errors**: Server might be returning HTML error page - check `/sse` endpoint 3. **Upload failures**: Verify base64 encoding and `audio_data` parameter name 4. **Connection timeouts**: Check firewall, Cloud Run authentication, and timeout settings ## Additional Resources - [MCP Protocol Specification](https://modelcontextprotocol.io/) - [Cloud Run Documentation](https://cloud.google.com/run/docs) - [Cloud SQL Connector](https://cloud.google.com/sql/docs/postgres/connect-run) - [JSON-RPC 2.0 Specification](https://www.jsonrpc.org/specification) - [Deployment Guide](./DEPLOYMENT.md)

Latest Blog Posts

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/jaredlang/forecast_storage_mcp'

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