Skip to main content
Glama
MayWei

mcp-server-personal

by MayWei
README.md
# mcp-server-personal

Personal-assistant MCP server for **personal data**. Part of the `personal-assistant`
project; built with FastMCP + Starlette + streamable-http, with per-domain
`register_tools_*` modules.

MCP endpoint: `http://localhost:8002/mcp`

## Tools

| Tool | Backend | Config needed |
|---|---|---|
| `get_calendar_events(start, end)` / `add_calendar_event(...)` | local `data/calendar.ics` | no |
| `list_emails(n, unread_only)` / `read_email(id)` / `label_email(id, label)` | Gmail IMAP + app password | IMAP creds |
| `add_contact` / `mark_contacted` / `list_contacts_dates()` | local SQLite | no |

Todos and long-term preference memory live in the **main app**, not in this
server. Email is **read + label only** — sending is deliberately not an MCP
tool; drafts go through the assistant's approval card and a separate gated
send path.

## Run

```bash
cp .env.example .env   # fill IMAP creds if you want email tools
uv sync
uv run src/run.py
```

Inspect tools:

```bash
npx @modelcontextprotocol/inspector
# connect to http://localhost:8002/mcp (streamable-http)
# if ENABLE_AUTH=true, add header: Authorization: Bearer <MCP_AUTH_TOKEN>
```

Test with the AI agent (LangChain agent over this server's tools):

```bash
export OPENAI_API_KEY=sk-...   # or set it in .env
uv run ai_agent.py                                    # example question
uv run ai_agent.py "add lunch with Alex to my calendar this Saturday at noon"
```

`ai_agent.py` automatically sends the bearer token from `MCP_AUTH_TOKEN` when set.

## Authentication

The MCP endpoint is protected by a static bearer token (Starlette
`AuthenticationMiddleware` + `PersonalAuthBackend` in `src/auth/auth.py`):

- `ENABLE_AUTH=true` + `MCP_AUTH_TOKEN=<random>` in `.env` — clients must send
  `Authorization: Bearer <token>`; anything else gets `401`.
- `ENABLE_AUTH=false` — open access (local development).

Generate a token with `openssl rand -hex 24`.

## Observability

- **Tracing**: OpenTelemetry spans per request (`ENABLE_TRACE`;
  `TRACE_SPAN_TYPE=local` prints to console, `otlp` exports to a collector).
- **Logging**: console + rotating file (`app.log`, 1 MB x 3) via `LOG_LEVEL`
  and `LOG_PATH`. All tool failures are logged with tracebacks — start with
  `grep WARNING app.log` / `grep ERROR app.log` when a tool misbehaves.

## Tests

```bash
uv run pytest tests -v
```

## Docker

```bash
docker build -t mcp-server-personal .
docker run --rm -p 8002:8002 --env-file .env mcp-server-personal
```

Multi-stage build, non-root user, prod dependencies only; state
(`calendar.ics`, `personal.db`, logs) lives in `/app/data`.

## CI/CD

- `.github/workflows/ci.yaml` — on PRs and pushes to `main`: `uv sync` +
  `pytest`, then a Docker build (no push).
- `.github/workflows/cd.yaml` — manual (`workflow_dispatch`): builds and pushes
  `ghcr.io/<owner>/mcp-server-personal:<version>` using the built-in
  `GITHUB_TOKEN`, with a `test`/`prod` environment choice.