From efe3ddf27ba915f53cddaa1af4ff51558875d4d0 Mon Sep 17 00:00:00 2001 From: Bill Date: Mon, 29 Dec 2025 19:35:54 -0500 Subject: [PATCH] docs: add Docker deployment design Design for containerizing grist-mcp with: - Multi-stage Dockerfile with Python 3.14 - SSE transport for remote server operation - Docker Compose for local deployment - Single adaptive CI workflow for Gitea and GitHub - Semantic version tagging --- .../2025-12-29-docker-deployment-design.md | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 docs/plans/2025-12-29-docker-deployment-design.md diff --git a/docs/plans/2025-12-29-docker-deployment-design.md b/docs/plans/2025-12-29-docker-deployment-design.md new file mode 100644 index 0000000..183421b --- /dev/null +++ b/docs/plans/2025-12-29-docker-deployment-design.md @@ -0,0 +1,107 @@ +# Docker Deployment Design + +## Overview + +Make grist-mcp deployable via Docker Compose with CI workflows for automated image builds on version tag pushes. + +## Requirements + +- Docker Compose for local deployment +- Single CI workflow that works on both Gitea and GitHub +- SSE transport (replacing stdio) for remote server operation +- Port 3000 default, configurable via environment variable +- Python 3.14 +- Semantic version tagging (1.2.3, 1.2, 1, latest) +- Config mounted at runtime (not baked into image) + +## Files to Create + +### Dockerfile + +Multi-stage build: + +**Stage 1 (builder):** +- Base: `python:3.14-slim` +- Install uv +- Copy `pyproject.toml` and `uv.lock` +- Install dependencies + +**Stage 2 (runtime):** +- Base: `python:3.14-slim` +- Copy virtual environment from builder +- Copy source code +- Non-root user (`appuser`) for security +- Expose port 3000 +- CMD: run server via uv + +### docker-compose.yaml + +```yaml +services: + grist-mcp: + build: . + ports: + - "${PORT:-3000}:3000" + volumes: + - ./config.yaml:/app/config.yaml:ro + env_file: + - .env + restart: unless-stopped +``` + +### .env.example + +``` +PORT=3000 +GRIST_MCP_TOKEN=your-agent-token-here +``` + +### .github/workflows/build.yaml + +Single workflow that detects platform (Gitea vs GitHub) at runtime: + +- **Trigger:** Push of version tags (`v*.*.*`) +- **Platform detection:** Check `GITEA_ACTIONS` environment variable +- **Registry:** + - Gitea: `${{ github.server_url }}/${{ github.repository }}` + - GitHub: `ghcr.io/${{ github.repository }}` +- **Authentication:** + - Gitea: `${{ secrets.REGISTRY_TOKEN }}` + - GitHub: `${{ secrets.GITHUB_TOKEN }}` +- **Tags generated:** `1.2.3`, `1.2`, `1`, `latest` + +## Files to Modify + +### pyproject.toml + +Add dependencies: +- `starlette` - ASGI framework for SSE +- `uvicorn` - ASGI server +- `sse-starlette` - SSE support + +### src/grist_mcp/main.py + +Replace stdio transport with SSE: + +1. Create Starlette ASGI app with routes: + - `GET /sse` - SSE connection endpoint + - `POST /messages` - Client message endpoint +2. Run with uvicorn on configurable port (default 3000) +3. Keep existing config/auth flow unchanged + +### .gitignore + +Ensure `.env` is ignored. + +## Implementation Order + +1. Update dependencies and main.py for SSE transport +2. Create Dockerfile +3. Create docker-compose.yaml and .env.example +4. Create CI workflow +5. Test locally with `docker compose up` + +## Secrets to Configure + +- **Gitea:** Create `REGISTRY_TOKEN` secret with registry push access +- **GitHub:** Uses automatic `GITHUB_TOKEN`