Initial commit: 5 MCP servers for Mail, Calendar, Contacts, Files, Notes

Self-hosted MCP servers with OAuth client_credentials auth.
Each server connects to a different backend:
- Mail: reads Maildir IMAP backups
- Calendar/Tasks: CalDAV against Radicale
- Contacts: CardDAV against Radicale
- Files: WebDAV against oCIS
- Notes: Joplin REST API

Credentials externalized to config.json (not in repo).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Stefan Lohmaier
2026-06-12 06:22:42 +02:00
commit fb642e47c8
11 changed files with 1199 additions and 0 deletions
+69
View File
@@ -0,0 +1,69 @@
# mcp-home
Self-hosted MCP (Model Context Protocol) servers for Claude. Provides access to personal data services via remote HTTP endpoints with OAuth client_credentials auth.
## Architecture
5 independent MCP servers, each on its own port, behind nginx reverse proxy at `mcp.home.slohmaier.de`:
- **Mail** (Port 5100) — searches IMAP backup Maildirs
- **Calendar** (Port 5101) — CalDAV against Radicale (events + tasks/reminders)
- **Contacts** (Port 5102) — CardDAV against Radicale
- **Files** (Port 5103) — WebDAV against oCIS
- **Notes** (Port 5104) — Joplin REST API
All servers share `common.py` for OAuth token handling and user resolution.
## Auth Flow
OAuth `client_credentials` grant — no browser redirect needed:
1. Client POSTs to `/<service>/token` with `client_id` + `client_secret`
2. Server returns `access_token` (valid 24h)
3. Client sends `Authorization: Bearer <token>` with MCP requests
4. User is identified by `client_id` (matches `tokens.json`)
OAuth metadata at `/<service>/.well-known/oauth-authorization-server`.
## User Separation
`tokens.json` (not in git, chmod 600) maps client_id → user. Each user only sees their own data. Shared CalDAV calendars are visible to both users.
## Files
- `common.py` — OAuth endpoints, Bearer middleware, user resolution via contextvars
- `tokens.json` — client_id/secret per user (generate with `python3 -c "import secrets; print(secrets.token_urlsafe(48))"`)
- `mail/server.py` — Maildir reader, search across subject/from/to/body
- `calendar/server.py` — CalDAV REPORT queries, event/task CRUD, vobject serialization
- `contacts/server.py` — CardDAV addressbook-query, vCard parsing, contact CRUD
- `files/server.py` — WebDAV PROPFIND/GET, recursive search, text file reading
- `notes/server.py` — Joplin REST API wrapper (needs per-user API token in code)
## Dependencies
```
pip install mcp[cli] httpx vobject python-dateutil
```
Python 3.12+, venv at `./venv/`.
## Deployment
Each server runs as systemd unit (`mcp-<name>.service`), managed by uvicorn. nginx at `mcp.home.slohmaier.de` proxies `/<service>/` to the right port. TLS via Certbot.
## Adding a New Service
1. Create `<name>/server.py` with `FastMCP` instance + `create_app()` using the same pattern
2. Import `get_current_user`, `OAUTH_ROUTES`, `BearerAuthMiddleware` from `common.py`
3. Add nginx location block for `/<name>/`
4. Create systemd unit `mcp-<name>.service`
5. Update `tokens.json` if the new service needs different user mapping
## Tool Design Guidelines
- Every tool docstring is the first thing Claude sees — make it actionable ("Call this first", "Use the UID from search results")
- Use `Annotated[str, Field(description="...")]` with example values for every parameter
- Indicate which parameters are optional ("Leave empty for all")
- Reference other tools in descriptions ("Use get_contact with the UID for full details")
- Return structured text, not JSON — Claude parses text faster
- Include UIDs/keys in output so Claude can chain to detail tools without asking the user