docs: Add documentation site and API reference
This commit is contained in:
168
docs/architecture.md
Normal file
168
docs/architecture.md
Normal file
@@ -0,0 +1,168 @@
|
||||
# Architecture
|
||||
|
||||
## Overview
|
||||
|
||||
AegisGitea MCP is a Python 3.10+ application built on **FastAPI**. It acts as a bridge between an AI client (such as ChatGPT) and a self-hosted Gitea instance, implementing the [Model Context Protocol (MCP)](https://modelcontextprotocol.io).
|
||||
|
||||
```
|
||||
AI Client (ChatGPT)
|
||||
│
|
||||
│ HTTP (Authorization: Bearer <key>)
|
||||
▼
|
||||
┌────────────────────────────────────────────┐
|
||||
│ FastAPI Server │
|
||||
│ server.py │
|
||||
│ - Route: POST /mcp/tool/call │
|
||||
│ - Route: GET /mcp/tools │
|
||||
│ - Route: GET /health │
|
||||
│ - SSE support (GET/POST /mcp/sse) │
|
||||
└───────┬───────────────────┬────────────────┘
|
||||
│ │
|
||||
┌────▼────┐ ┌────▼──────────────┐
|
||||
│ auth │ │ Tool dispatcher │
|
||||
│ auth.py│ │ (server.py) │
|
||||
└────┬────┘ └────────┬──────────┘
|
||||
│ │
|
||||
│ ┌────────▼──────────┐
|
||||
│ │ Tool handlers │
|
||||
│ │ tools/repo.py │
|
||||
│ └────────┬──────────┘
|
||||
│ │
|
||||
│ ┌────────▼──────────┐
|
||||
│ │ GiteaClient │
|
||||
│ │ gitea_client.py │
|
||||
│ └────────┬──────────┘
|
||||
│ │ HTTPS
|
||||
│ ▼
|
||||
│ Gitea instance
|
||||
│
|
||||
┌────▼────────────────────┐
|
||||
│ AuditLogger │
|
||||
│ audit.py │
|
||||
│ /var/log/aegis-mcp/ │
|
||||
│ audit.log │
|
||||
└─────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Source Modules
|
||||
|
||||
### `server.py`
|
||||
|
||||
The entry point and FastAPI application. Responsibilities:
|
||||
|
||||
- Defines all HTTP routes
|
||||
- Reads configuration on startup and initialises `GiteaClient`
|
||||
- Applies authentication middleware to protected routes
|
||||
- Dispatches tool calls to the appropriate handler function
|
||||
- Handles CORS
|
||||
|
||||
### `auth.py`
|
||||
|
||||
API key validation. Responsibilities:
|
||||
|
||||
- `APIKeyValidator` class: holds the set of valid keys, tracks failed attempts per IP
|
||||
- Constant-time comparison to prevent timing side-channels
|
||||
- Rate limiting: blocks IPs that exceed `MAX_AUTH_FAILURES` within `AUTH_FAILURE_WINDOW`
|
||||
- Helper functions for key generation and hashing
|
||||
- Singleton pattern (`get_validator()`) with test-friendly reset (`reset_validator()`)
|
||||
|
||||
### `config.py`
|
||||
|
||||
Pydantic `BaseSettings` model. Responsibilities:
|
||||
|
||||
- Loads all configuration from environment variables or `.env`
|
||||
- Validates values (log level enum, token format, key minimum length)
|
||||
- Parses comma-separated `MCP_API_KEYS` into a list
|
||||
- Exposes computed properties (e.g. base URL for Gitea API)
|
||||
|
||||
### `gitea_client.py`
|
||||
|
||||
Async HTTP client for the Gitea API. Responsibilities:
|
||||
|
||||
- Wraps `httpx.AsyncClient` with bearer token authentication
|
||||
- Maps HTTP status codes to typed exceptions (`GiteaAuthenticationError`, `GiteaNotFoundError`, etc.)
|
||||
- Enforces file size limit before returning file contents
|
||||
- Logs all API calls to the audit logger
|
||||
|
||||
Key methods:
|
||||
|
||||
| Method | Gitea endpoint |
|
||||
|---|---|
|
||||
| `get_current_user()` | `GET /api/v1/user` |
|
||||
| `list_repositories()` | `GET /api/v1/repos/search` |
|
||||
| `get_repository()` | `GET /api/v1/repos/{owner}/{repo}` |
|
||||
| `get_file_contents()` | `GET /api/v1/repos/{owner}/{repo}/contents/{path}` |
|
||||
| `get_tree()` | `GET /api/v1/repos/{owner}/{repo}/git/trees/{ref}` |
|
||||
|
||||
### `audit.py`
|
||||
|
||||
Structured audit logging using `structlog`. Responsibilities:
|
||||
|
||||
- Initialises a `structlog` logger writing JSON to the configured log file
|
||||
- `log_tool_invocation()`: records tool calls with result and correlation ID
|
||||
- `log_access_denied()`: records failed authentication
|
||||
- `log_security_event()`: records rate limit triggers and other security events
|
||||
- Auto-generates UUID correlation IDs when none is provided
|
||||
|
||||
### `mcp_protocol.py`
|
||||
|
||||
MCP data models and tool registry. Responsibilities:
|
||||
|
||||
- Pydantic models: `MCPTool`, `MCPToolCallRequest`, `MCPToolCallResponse`, `MCPListToolsResponse`
|
||||
- `AVAILABLE_TOOLS` list: the canonical list of tools exposed to clients
|
||||
- `get_tool_by_name()`: lookup helper used by the dispatcher
|
||||
|
||||
### `tools/repository.py`
|
||||
|
||||
Concrete tool handler functions. Responsibilities:
|
||||
|
||||
- `list_repositories_tool()`: calls `GiteaClient.list_repositories()`, formats the result
|
||||
- `get_repository_info_tool()`: calls `GiteaClient.get_repository()`, formats metadata
|
||||
- `get_file_tree_tool()`: calls `GiteaClient.get_tree()`, flattens to a list of paths
|
||||
- `get_file_contents_tool()`: calls `GiteaClient.get_file_contents()`, decodes base64
|
||||
|
||||
All handlers return a plain string. `server.py` wraps this in an `MCPToolCallResponse`.
|
||||
|
||||
---
|
||||
|
||||
## Request Lifecycle
|
||||
|
||||
```
|
||||
1. Client sends POST /mcp/tool/call
|
||||
│
|
||||
2. FastAPI routes the request to the tool-call handler in server.py
|
||||
│
|
||||
3. auth.validate_api_key() checks the Authorization header
|
||||
├── Fail → AuditLogger.log_access_denied() → HTTP 401 / 429
|
||||
└── Pass → continue
|
||||
│
|
||||
4. AuditLogger.log_tool_invocation(status="pending")
|
||||
│
|
||||
5. Tool dispatcher looks up the tool by name (mcp_protocol.get_tool_by_name)
|
||||
│
|
||||
6. Tool handler function (tools/repository.py) is called
|
||||
│
|
||||
7. GiteaClient makes an async HTTP call to the Gitea API
|
||||
│
|
||||
8. Result (or error) is returned to server.py
|
||||
│
|
||||
9. AuditLogger.log_tool_invocation(status="success" | "error")
|
||||
│
|
||||
10. MCPToolCallResponse is returned to the client
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Design Decisions
|
||||
|
||||
**Read-only by design.** The MCP tools only read data from Gitea. No write operations are implemented.
|
||||
|
||||
**Gitea controls access.** The server does not maintain its own repository ACL. The Gitea bot user's permissions are the source of truth. If the bot cannot access a repo, the server cannot either.
|
||||
|
||||
**Public tool discovery.** `GET /mcp/tools` requires no authentication so that ChatGPT's plugin system can discover the available tools without credentials. All other endpoints require authentication.
|
||||
|
||||
**Stateless server.** No database or persistent state beyond the audit log file. Rate limit counters are in-memory and reset on restart.
|
||||
|
||||
**Async throughout.** FastAPI + `httpx.AsyncClient` means all Gitea API calls are non-blocking, allowing the server to handle concurrent requests efficiently.
|
||||
Reference in New Issue
Block a user