The 400 Bad Request on second POST was caused by CORS not exposing
the mcp-session-id header, preventing MCP Inspector from reading it
and sending it back in subsequent requests.
Without the session ID, each request created a new transport session
instead of reusing the existing one, causing protocol errors.
Fix:
- Add expose_headers to CORS middleware configuration
- Expose mcp-session-id and mcp-protocol-version headers
- Allows browser clients to read and reuse session IDs
- Applied to both streamable-http and SSE transports
This fixes the session continuity issue and eliminates 400 errors.
The previous approach of running uvicorn.run(mcp_app) directly bypassed
MCP's internal session management, causing 400 Bad Request errors when
MCP Inspector tried to reuse sessions.
Solution:
- Use mcp.run() with middleware parameter instead of uvicorn.run()
- Build middleware list using starlette.middleware.Middleware wrapper
- Pass middleware list to mcp.run(transport, middleware=middleware_list)
- This preserves MCP's session handling while applying our middleware
Benefits:
- Proper MCP session continuity for streamable-http transport
- CORS middleware still applies correctly
- Authentication middleware works as expected
- Debug logging middleware available when LOG_LEVEL=DEBUG
This fixes the 400 Bad Request error on the second POST request.
Added RequestLoggingMiddleware to help diagnose 400 Bad Request errors:
- Logs all request methods and paths in DEBUG mode
- Logs request headers for debugging
- Warns on any 4xx/5xx responses with details
- Only enabled when LOG_LEVEL=DEBUG to avoid spam
This helps troubleshoot MCP protocol issues without modifying
the core request handling logic.
CORS preflight requests (OPTIONS) don't include Authorization headers
by browser design. The middleware was blocking these requests with 401.
Solution:
- Skip authentication check for OPTIONS requests
- OPTIONS requests are handled by CORS middleware only
- Actual requests (GET, POST) still require authentication
This fixes the "401 Unauthorized" error on OPTIONS /mcp when using
MCP Inspector or other browser-based clients with authentication enabled.
The previous fix added CORS middleware to the app, but mcp.run()
creates a new app instance that doesn't include our middleware.
Solution:
- Import uvicorn
- For streamable-http transport, run uvicorn directly with the
middleware-enhanced app (mcp_app)
- This ensures CORS middleware is actually applied
- For other transports (SSE), fall back to mcp.run() with a warning
This fixes the "OPTIONS /mcp HTTP/1.1 405 Method Not Allowed" error
by ensuring CORS middleware handles preflight requests properly.
This commit addresses two critical issues in the MCP server:
1. CORS Middleware Fix:
- Move CORS middleware outside the auth check so it's always enabled
- CORS is required for browser-based MCP clients, regardless of auth
- Fixes "OPTIONS /mcp HTTP/1.1 405 Method Not Allowed" error
2. Schema Cache Improvements:
- Add specific handling for corrupted JSON cache files
- Automatically remove corrupted cache files and log the action
- Prevents startup failures due to malformed JSON
- Better error messages to help diagnose cache issues
3. Configuration Updates:
- Change default SCHEMA_CACHE_DIR from /app/data/schemas to ./data/schemas
- Better default for local development (Docker still uses /app/data/schemas)
- Update .env.example with clearer documentation
These fixes improve robustness and make local development easier.
Implements comprehensive multi-user support allowing multiple users to
access the same MCP server instance with their own independent GeoGuessr
accounts. Each API key now has its own session storage and context.
## Multi-User Architecture
### New Components
**User Context System** (src/geoguessr_mcp/auth/user_context.py):
- UserContext dataclass tracks API key and associated GeoGuessr session
- Properties for user_id, username, ncfa_cookie, is_authenticated
- Automatically attached to each request
**Multi-User Session Manager** (src/geoguessr_mcp/auth/multi_user_session.py):
- MultiUserSessionManager manages separate SessionManager per API key
- Maps API keys to their own GeoGuessr sessions
- Methods: get_user_context, login_user, logout_user, set_user_cookie
- Global instance: multi_user_session_manager
**Request Context** (src/geoguessr_mcp/auth/request_context.py):
- ContextVar for accessing current user context in tools
- Functions: get_current_user_context, require_user_context, set_current_user_context
- Enables tools to access user-specific sessions automatically
### Updated Components
**Authentication Middleware** (src/geoguessr_mcp/middleware/auth.py):
- Now creates user context for each authenticated request
- Attaches context to both request.state and ContextVar
- Supports both authenticated and unauthenticated modes
- Default user context created when auth is disabled
**Authentication Tools** (src/geoguessr_mcp/tools/auth_tools.py):
- Completely rewritten for multi-user support
- login(): Creates session tied to caller's API key
- logout(): Logs out only the calling user's session
- set_ncfa_cookie(): Sets cookie for calling user only
- get_auth_status(): Returns calling user's auth status
- All tools use get_current_user_context() automatically
**GeoGuessr Client** (src/geoguessr_mcp/api/geoguessr_client.py):
- _get_authenticated_client() checks user context first
- Falls back to session_manager for backward compatibility
- Automatically uses caller's session when available
- No changes needed in services (profile, game, analysis)
## How It Works
1. User connects with API key in Authorization header
2. Middleware validates API key and creates/retrieves UserContext
3. UserContext attached to request.state and ContextVar
4. Tools call get_current_user_context() to access caller's session
5. Client automatically uses correct session for API calls
6. Each user's session is completely isolated
## Usage Example
```bash
# Configure multiple API keys
MCP_AUTH_ENABLED=true
MCP_API_KEYS=alice_key,bob_key,charlie_key
# Alice connects with: Authorization: Bearer alice_key
# Bob connects with: Authorization: Bearer bob_key
# Each can login to their own GeoGuessr account
# Sessions are completely independent
```
## Key Features
- **Zero Interference**: Users don't affect each other's sessions
- **Automatic Routing**: Requests automatically use correct user's session
- **Hot Reload**: Add new API keys and restart in ~2-3 seconds
- **Backward Compatible**: Still works with single-user mode
- **Fallback Support**: GEOGUESSR_NCFA_COOKIE still works as default
## Documentation Updates
- README.md: Added Multi-User Mode section with examples
- README.md: Updated authentication section with multi-user details
- README.md: Added "Adding New Users" workflow
- Key Features section now highlights multi-user support
## Technical Details
- Uses Python ContextVar for request-scoped user context
- Each API key gets its own SessionManager instance
- Session storage is in-memory (persists across requests, not restarts)
- Default cookie (GEOGUESSR_NCFA_COOKIE) used as fallback for all users
- Fully async/await compatible throughout
This commit implements several key improvements to the GeoGuessr MCP server:
## MCP Server Authentication
- Add Bearer token authentication for MCP server access control
- New middleware in src/geoguessr_mcp/middleware/auth.py
- Configuration via MCP_AUTH_ENABLED and MCP_API_KEYS environment variables
- Support for multiple API keys (comma-separated)
- Optional authentication - can be disabled for trusted deployments
- Clients connect using Authorization: Bearer YOUR_API_KEY header
## Docker Configuration Updates
- Update to use official pre-built image: nyxiumyuuki/geoguessr-mcp:latest
- Remove DOCKER_USERNAME and IMAGE_TAG from environment variables
- Simplify docker-compose.yml and docker-compose.prod.yml
- Remove healthcheck configuration (not necessary for the deployment)
## Deployment Improvements
- Move deploy.sh to scripts/deploy.sh for better organization
- Update deploy.sh to use official Docker image
- Add authentication validation in deployment script
- Improve deployment logging and error messages
## Documentation Updates
- Update README.md with authentication configuration examples
- Add MCP server authentication section with setup instructions
- Update environment variables table
- Simplify deployment instructions
- Update CLAUDE.md with new authentication architecture
- Add .env.example configuration for MCP authentication
## Technical Details
- Authentication middleware integrates with FastMCP's Starlette ASGI app
- Middleware validates Bearer tokens on all requests except /health
- Logs authentication attempts and failures
- Returns proper 401/403 HTTP status codes
- Validates configuration on startup to prevent misconfiguration
Resolves TODO items:
- [x] Fix Docker username in compose files and env vars
- [x] Add authentication to MCP server to allow access only to specific users
This commit adds comprehensive support for deploying the GeoGuessr MCP Server
to a VPS alongside existing nginx-proxy-manager setup (e.g., Firefly III).
Changes:
- Updated docker-compose.prod.yml to use existing firefly_network
- Removed standalone nginx service (uses nginx-proxy-manager instead)
- Changed from ports to expose for internal-only access
- Switched default to pre-built Docker Hub images
New files:
- DEPLOYMENT.md: Comprehensive deployment guide with SSL setup
- .env.production: Production-ready environment configuration template
- deploy.sh: Automated deployment script with health checks
Updated files:
- README.md: Added quick reference to VPS deployment with nginx-proxy-manager
- docker-compose.prod.yml: Simplified for proxy manager integration
Deployment features:
- Automatic SSL certificate management via nginx-proxy-manager
- Let's Encrypt integration for HTTPS
- Shared Docker network with existing services
- Persistent schema storage
- Health checks and logging
- Easy updates via deploy script
This setup allows users to deploy the MCP server on the same VPS as other
Docker services while using a single nginx-proxy-manager for SSL/HTTPS.
- Update Dockerfile to use pyproject.toml instead of requirements.txt
- Add support for Docker Hub image pulling in compose files
- Add comprehensive deployment documentation with multiple methods
- Create CLAUDE.md with development and architecture guide
- Add .dockerignore for optimized build context
- Update .env.example with Docker configuration variables
- Configure 24-hour monitoring interval by default
Changes:
- Dockerfile: Install from pyproject.toml, use main.py entry point
- docker-compose.yml: Add image option for registry deployment
- docker-compose.prod.yml: Add image option for VPS deployment
- README.md: Add Docker Hub push/pull workflows and examples
- CLAUDE.md: Comprehensive guide for AI assistants and developers
- .dockerignore: Exclude unnecessary files from Docker builds
- .env.example: Add DOCKER_USERNAME and IMAGE_TAG variables