This document provides a comprehensive overview of Open Edison's architecture, design decisions, and development patterns for contributors and maintainers.
- Architecture Overview
- Core Components
- Data Flow
- Configuration System
- API Design
- Development Workflow
- Testing Strategy
- Extension Points
- Code Organization
- Design Decisions
Open Edison is a single-user MCP (Model Context Protocol) proxy server designed for simplicity and ease of use. Unlike its commercial counterpart edison.watch, it focuses on local deployment without multi-user complexity.
graph TB
Client[MCP Client/Frontend] --> API[FastAPI Server]
API --> Auth[API Key Auth]
API --> MCP[SingleUserMCP]
MCP --> Servers[MCP Servers]
Config[JSON Config] --> API
Config --> MCP
subgraph "MCP Servers"
FS[Filesystem MCP]
GH[GitHub MCP]
Custom[Custom MCPs]
end
MCP --> FS
MCP --> GH
MCP --> Custom
- Simplicity First - No database, no complex auth, just JSON config
- Single User - No multi-tenancy concerns
- Local Deployment - Designed for self-hosting
- Extensible - Easy to add new MCP servers and features
- Development Friendly - Clear separation of concerns
The main HTTP server that provides the REST API interface.
Responsibilities:
- HTTP request handling
- API key authentication
- Route management
- Error handling and logging
- Server lifecycle management
Key Classes:
OpenEdisonProxy- Main server class
FastMCP-based manager that initializes and manages MCP servers and hosts the MCP protocol server.
Responsibilities:
- Server initialization and lifecycle
- Mount/unmount operations via API
- Hosting MCP protocol at port 3000 (
/mcp/)
JSON-based configuration management with dataclasses.
Responsibilities:
- Configuration loading and validation
- Default configuration generation
- Type-safe configuration access
- Configuration persistence
Key Classes:
Config- Main configuration containerServerConfig- Server-specific settingsLoggingConfig- Logging configurationMCPServerConfig- Individual MCP server configuration
sequenceDiagram
participant Main as main.py
participant Server as OpenEdisonProxy
participant Config as Configuration
participant MCP as SingleUserMCP
Main->>Config: Load config.json
Main->>Server: Create server instance
Server->>MCP: Initialize FastMCP server
Server->>Server: Create FastAPI app
Main->>Server: Start server
Server->>Config: Get enabled servers
Server->>MCP: Prepare configured servers
Server->>Server: Start uvicorn server
sequenceDiagram
participant Client as Client
participant API as FastAPI
participant MCP as MCP Server (FastMCP)
Client->>MCP: JSON-RPC over HTTP to /mcp/
MCP-->>Client: JSON-RPC response
{
"server": {
"host": "localhost",
"port": 3000,
"api_key": "your-secure-key"
},
"logging": {
"level": "INFO"
},
"mcp_servers": [
{
"name": "filesystem",
"command": "uvx",
"args": ["mcp-server-filesystem", "/path/to/dir"],
"env": {"CUSTOM_VAR": "value"},
"enabled": true
}
]
}- Default Location: Platform config dir or
OPEN_EDISON_CONFIG_DIR; fileconfig.json - Auto-generation: Creates default config if missing
- Type Safety: Uses dataclasses for validation
- Environment Support: Per-server environment variables
@dataclass
class ServerConfig:
host: str = "localhost"
port: int = 3000
api_key: str = "dev-api-key-change-me"
# Add new options here
max_connections: int = 100
timeout: int = 30| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| GET | /health |
Health check | No |
| GET | /mcp/status |
Configured servers (name, enabled) | No |
| GET | /sessions |
Recent session summaries | No |
| GET | /mcp/mounted |
Mounted servers | Yes |
| POST | /mcp/reinitialize |
Reinitialize servers | Yes |
| POST | /mcp/mount/{name} |
Mount server | Yes |
| DELETE | /mcp/mount/{name} |
Unmount server | Yes |
| GET/POST/... | /mcp/oauth/* |
OAuth status/management | Yes |
- Method: Bearer token authentication
- Header:
Authorization: Bearer <api_key> - Storage: API key stored in configuration
- Scope: Global (single-user system)
# Standard error response format
{
"detail": "Error message",
"status_code": 500
}# Health check
{
"status": "healthy",
"version": "0.1.0",
"mcp_servers": 2
}
# Server status
{
"servers": [
{"name": "filesystem", "enabled": true}
]
}# Install dependencies
make sync
# Create default configuration
make setup
# Run the server
make run# Lint code
make lint
# Format code
make format
# Run tests
make test
# Full CI check
make ci- Run:
make run - Logging: Structured logging with loguru
- Configuration: Edit
config.jsonand restart
tests/
├── test_config.py # Configuration tests
├── test_server.py # API endpoint tests
└── test_proxy.py # MCP proxy tests (future)
- Unit Tests - Individual component testing
- Integration Tests - API endpoint testing
- Configuration Tests - Config loading/saving
- Future: MCP Tests - End-to-end MCP communication
# All tests
make test
# Specific test file
pytest tests/test_config.py
# With coverage
pytest --cov=src tests/Update config.json:
{
"mcp_servers": [
{
"name": "my-custom-mcp",
"command": "python",
"args": ["-m", "my_mcp_package"],
"env": {"API_KEY": "secret"},
"enabled": true
}
]
}In src/server.py:
@app.get("/my/endpoint", dependencies=[Depends(verify_api_key)])
async def my_endpoint():
"""Custom endpoint"""
return {"message": "Custom response"}Extend the verify_api_key function:
def verify_api_key(credentials: HTTPAuthorizationCredentials = Depends(security)):
# Custom auth logic here
if not custom_auth_check(credentials.credentials):
raise HTTPException(status_code=401, detail="Custom auth failed")
return credentials.credentialssrc/
├── __init__.py # Package initialization
├── config.py # Configuration management
├── server.py # FastAPI management API + dashboard
├── single_user_mcp.py # FastMCP single-user manager
└── utils/ # Future utilities
├── logging.py # Session logging (future)
└── helpers.py # Common utilities (future)
# Standard library
import json
from pathlib import Path
# Third party
from fastapi import FastAPI
from loguru import logger as log
# Project imports (top-level; avoid local-in-function imports)
from src.config import Config
from src.single_user_mcp import SingleUserMCP- Simplicity - Easy to edit and understand
- No Database - Reduces complexity for single-user deployment
- Version Control Friendly - Can be committed to git
- Portable - Easy migration between environments
- Type Safety - Built-in request/response validation
- Documentation - Automatic OpenAPI docs
- Performance - Async support and high performance
- Developer Experience - Excellent tooling and IDE support
- Isolation - Each MCP server runs in its own process
- Reliability - Server crashes don't affect the proxy
- Resource Management - Easy to monitor and restart servers
- Compatibility - Works with any MCP server implementation
- Bearer Tokens - Simple and widely supported
- Single Key - Appropriate for single-user deployment
- Configurable - Easy to change via config file
- Use uv for formatting (
make format) and Ruff for linting (make lint) - Type hints for all function signatures
- Docstrings for all public functions/classes
feat: add new MCP server configuration
fix: resolve authentication issue
docs: update development guide
test: add proxy integration tests
- Create feature branch from main
- Implement changes with tests
- Run full CI check (
make ci) and tests (make test) - Update documentation if needed
- Submit PR with clear description
This development guide should be updated as the project evolves. For questions or clarifications, please refer to the source code or open an issue on GitHub.