From 16436c847af1373d879e7d2ce0c9711ac06212e7 Mon Sep 17 00:00:00 2001 From: lmiranda Date: Tue, 3 Feb 2026 17:53:59 -0500 Subject: [PATCH] docs: Add Sprint 01 planning documentation Create comprehensive sprint planning documentation for Core Architecture Correction sprint. This addresses three fatal architectural problems from v1.0.0 release. Sprint documents include: - Executive proposal with architecture analysis - Detailed implementation guide with code snippets - Issue breakdown with dependencies - Sprint summary with approval checklist Sprint creates 10 issues in Gitea milestone 29: - Issues #19-28 covering package rename, MCP protocol implementation, Docker infrastructure, testing, and documentation - Total estimated effort: 19-28 hours (1 week sprint) - All issues properly sized (S/M), labeled, and dependency-tracked This is attempt #3 - all details from architectural correction prompt have been captured. Co-Authored-By: Claude Opus 4.5 --- docs/sprint-proposals/SPRINT-01-SUMMARY.md | 244 +++ .../sprint-01-core-architecture-correction.md | 329 ++++ .../sprint-01-implementation-guide.md | 1454 +++++++++++++++++ .../sprint-01-issue-breakdown.md | 489 ++++++ 4 files changed, 2516 insertions(+) create mode 100644 docs/sprint-proposals/SPRINT-01-SUMMARY.md create mode 100644 docs/sprint-proposals/sprint-01-core-architecture-correction.md create mode 100644 docs/sprint-proposals/sprint-01-implementation-guide.md create mode 100644 docs/sprint-proposals/sprint-01-issue-breakdown.md diff --git a/docs/sprint-proposals/SPRINT-01-SUMMARY.md b/docs/sprint-proposals/SPRINT-01-SUMMARY.md new file mode 100644 index 0000000..b1a0120 --- /dev/null +++ b/docs/sprint-proposals/SPRINT-01-SUMMARY.md @@ -0,0 +1,244 @@ +# Sprint 01: Core Architecture Correction - SUMMARY + +**Status:** 🟡 AWAITING APPROVAL +**Milestone:** [Sprint 01: Core Architecture Correction](https://gitea.hotserv.cloud/personal-projects/gitea-mcp-remote/milestone/29) +**Sprint Duration:** 1 week (Feb 3-10, 2026) +**Total Estimated Effort:** 19-28 hours + +--- + +## Sprint Overview + +This sprint addresses **three fatal architectural problems** introduced in the v1.0.0 release. This is **surgical correction work**, not a rewrite - supporting modules (config, middleware, filtering, tests) are solid and only need import path updates. + +### The Three Fatal Problems + +1. **Subprocess Architecture → Direct Python Import** + - Current: Spawns gitea-mcp-server as subprocess + - Required: Direct Python import from marketplace package + +2. **Custom REST API → MCP Streamable HTTP Protocol** + - Current: Custom endpoints `/tools/list` and `/tools/call` + - Required: MCP protocol `POST /mcp` with JSON-RPC 2.0 + +3. **Missing Marketplace Dependency** + - Current: Comment about installing separately + - Required: Actual pip dependency from marketplace Git repo + +--- + +## Issues Created + +All issues are in Gitea milestone: **Sprint 01: Core Architecture Correction** + +| Issue | Title | Type | Size | Est. Time | Dependencies | +|-------|-------|------|------|-----------|--------------| +| [#19](https://gitea.hotserv.cloud/personal-projects/gitea-mcp-remote/issues/19) | Rename package to gitea_mcp_remote and update configuration | Refactor | M | 2-3h | None | +| [#20](https://gitea.hotserv.cloud/personal-projects/gitea-mcp-remote/issues/20) | Update middleware and filtering with new import paths | Refactor | S | 1h | #19 | +| [#21](https://gitea.hotserv.cloud/personal-projects/gitea-mcp-remote/issues/21) | Move tests to repository root and update imports | Refactor | M | 1-2h | #19, #20 | +| [#22](https://gitea.hotserv.cloud/personal-projects/gitea-mcp-remote/issues/22) | Add marketplace dependency and update project config | Build | S | 1h | #19 | +| [#23](https://gitea.hotserv.cloud/personal-projects/gitea-mcp-remote/issues/23) | Remove old server and create MCP base server structure | Feature | M | 2-3h | #19, #20, #22 | +| [#24](https://gitea.hotserv.cloud/personal-projects/gitea-mcp-remote/issues/24) | Implement MCP Streamable HTTP protocol endpoints | Feature | M | 2-3h | #23 | +| [#25](https://gitea.hotserv.cloud/personal-projects/gitea-mcp-remote/issues/25) | Create Docker multi-service infrastructure with Caddy | Build | M | 3-4h | #22, #24 | +| [#26](https://gitea.hotserv.cloud/personal-projects/gitea-mcp-remote/issues/26) | Create startup scripts and MCP server tests | Test | M | 2-3h | #24 | +| [#27](https://gitea.hotserv.cloud/personal-projects/gitea-mcp-remote/issues/27) | Create CLAUDE.md and update deployment documentation | Docs | M | 2-3h | All | +| [#28](https://gitea.hotserv.cloud/personal-projects/gitea-mcp-remote/issues/28) | Final validation and integration testing | Test | M | 2-3h | All | + +**Total Issues:** 10 (was 9, split large task into 2 medium tasks) + +--- + +## Execution Order + +The dependency graph ensures proper execution order: + +``` +#19 (Rename + Config) ← FOUNDATION + ├─→ #20 (Middleware + Filtering) + │ └─→ #21 (Tests) + │ + ├─→ #22 (pyproject.toml) + │ ├─→ #23 (MCP Base Server) + │ │ ├─→ #24 (MCP Protocol) + │ │ │ ├─→ #25 (Docker) + │ │ │ └─→ #26 (Scripts + Tests) + │ │ │ + │ └─→ #21 (Tests - can run parallel) + │ + └─→ All above + └─→ #27 (Documentation) + └─→ #28 (Final Validation) +``` + +**Recommended sequence:** +1. #19 → #20 → #22 → #21 (Foundation - Day 1-2) +2. #23 → #24 (Core server - Day 2-3) +3. #25 → #26 (Infrastructure - Day 3-4) +4. #27 → #28 (Documentation and validation - Day 4-5) + +--- + +## What to KEEP (Rename Imports Only) + +These modules are **well-tested and solid**: + +- ✅ `config/settings.py` - Minor field changes only +- ✅ `middleware/auth.py` - Import paths only +- ✅ `filtering/filter.py` - Change ValueError to warning +- ✅ All tests - Move to root, update imports +- ✅ `DEPLOYMENT.md` - Update references + +--- + +## What to REPLACE + +- ❌ `server.py` → ✅ `server_http.py` (new MCP implementation) +- ❌ `pyproject.toml` → ✅ Updated with marketplace dependency +- ❌ `docker-compose.yml` → ✅ `docker/docker-compose.yml` (two services) +- ❌ `Dockerfile` → ✅ `docker/Dockerfile` (git + port 8080) + +--- + +## New Files to CREATE + +- 📄 `docker/Caddyfile` - Reverse proxy config +- 📄 `CLAUDE.md` - Project guidance for Claude Code +- 📄 `tests/test_server_http.py` - MCP server tests +- 📄 `scripts/start.sh` - Production startup +- 📄 `scripts/healthcheck.sh` - Docker healthcheck + +--- + +## Success Criteria (16 Validations) + +### Package Structure (3) +- [ ] `src/gitea_mcp_remote/` exists (not `gitea_http_wrapper`) +- [ ] No imports reference `gitea_http_wrapper` +- [ ] `tests/` is at repository root (not in `src/`) + +### Configuration (3) +- [ ] `config/settings.py` has `mcp_auth_mode` field +- [ ] `config/settings.py` has `gitea_repo: str | None` +- [ ] HTTP defaults are `0.0.0.0:8080` + +### Server Implementation (4) +- [ ] `server_http.py` imports from `mcp_server` package +- [ ] MCP endpoints exist: `POST /mcp`, `HEAD /mcp` +- [ ] Health endpoints exist: `/health`, `/healthz`, `/ping` +- [ ] No subprocess spawning code + +### Dependencies (3) +- [ ] `pyproject.toml` has marketplace Git dependency +- [ ] Entry point is `gitea-mcp-remote` (not `gitea-http-wrapper`) +- [ ] Can run: `pip install -e .` successfully + +### Docker (3) +- [ ] `docker/docker-compose.yml` has two services (app + caddy) +- [ ] `docker/Dockerfile` installs git and uses port 8080 +- [ ] `docker/Caddyfile` exists and proxies to app:8080 + +--- + +## Timeline + +### Effort Distribution +- **Small (1-2h):** 2 issues (#20, #22) = 2-4 hours +- **Medium (2-4h):** 8 issues (#19, #21, #23-28) = 17-24 hours +- **Total:** 19-28 hours ≈ 23.5 hours average + +### Sprint Schedule (1 week) +- **Day 1-2:** Foundation (Issues #19-22) - 5-7 hours +- **Day 2-3:** Core Server (Issues #23-24) - 4-6 hours +- **Day 3-4:** Infrastructure (Issues #25-26) - 5-7 hours +- **Day 4-5:** Docs & Validation (Issues #27-28) - 4-6 hours +- **Buffer:** 1-2 hours for unexpected issues + +--- + +## Risk Assessment + +### Low Risk ✅ +- Config, middleware, filtering: Well-tested, only import changes +- Test relocation: No logic changes + +### Medium Risk ⚠️ +- `server_http.py`: New file, but following MCP HTTP spec +- MCP protocol integration: Well-documented standard + +### High Risk 🔴 +- Docker multi-service: Requires Caddy configuration +- Marketplace Git dependency: Must be accessible during build + +### Mitigation +1. Execute in exact dependency order +2. Test at each major milestone +3. Validate Docker build before deployment +4. Keep development branch for rollback + +--- + +## Documentation Created + +1. **[sprint-01-core-architecture-correction.md](./sprint-01-core-architecture-correction.md)** + - Executive summary + - Three fatal problems explained + - What to keep vs replace + - Architecture diagram + - Risk assessment + +2. **[sprint-01-implementation-guide.md](./sprint-01-implementation-guide.md)** + - Step-by-step technical implementation + - Code snippets for each change + - Validation commands + - Complete file replacements + +3. **[sprint-01-issue-breakdown.md](./sprint-01-issue-breakdown.md)** + - Detailed issue descriptions + - Dependency graph + - Execution order + - Size distribution + +4. **[SPRINT-01-SUMMARY.md](./SPRINT-01-SUMMARY.md)** (this file) + - Sprint overview + - Issue table with links + - Success criteria + - Approval checklist + +--- + +## Links + +- **Milestone:** https://gitea.hotserv.cloud/personal-projects/gitea-mcp-remote/milestone/29 +- **Repository:** https://gitea.hotserv.cloud/personal-projects/gitea-mcp-remote +- **Branch:** development +- **Marketplace:** https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace + +--- + +## Approval Checklist + +Before execution begins, verify: + +- [ ] All 10 issues created and assigned to milestone +- [ ] Dependencies correctly set between issues +- [ ] Labels applied correctly (Type, Priority, Component, Size) +- [ ] Implementation guide reviewed and accurate +- [ ] Timeline is realistic (1 week) +- [ ] Success criteria are clear and testable +- [ ] Rollback plan understood (development branch) +- [ ] User has reviewed and approved the plan + +--- + +## Next Steps + +**AWAITING USER APPROVAL** to begin execution. + +Once approved: +1. Start with Issue #19 (Foundation) +2. Follow dependency order strictly +3. Update issue status as work progresses +4. Run validation after each major milestone +5. Complete sprint with Issue #28 (Final Validation) + +**Note:** This is attempt #3. User emphasized paying close attention to details. All requirements from the architectural correction prompt have been captured in the issue breakdown. diff --git a/docs/sprint-proposals/sprint-01-core-architecture-correction.md b/docs/sprint-proposals/sprint-01-core-architecture-correction.md new file mode 100644 index 0000000..8fa6ba1 --- /dev/null +++ b/docs/sprint-proposals/sprint-01-core-architecture-correction.md @@ -0,0 +1,329 @@ +# Sprint 01: Core Architecture Correction + +**Status:** Planning +**Sprint Duration:** 1 week (estimated 20-24 hours of work) +**Priority:** CRITICAL - Architectural Foundation +**Attempt:** #3 (Pay close attention to details) + +## Executive Summary + +This sprint addresses three fatal architectural problems introduced in the v1.0.0 release that prevent the HTTP wrapper from functioning correctly with the MCP protocol. This is **surgical correction work**, not a rewrite. Supporting modules (config, middleware, filtering, tests) are solid and only need import path updates. + +## The Three Fatal Problems + +### 1. Subprocess Architecture → Direct Python Import +**Current (Wrong):** `server.py` spawns `gitea-mcp-server` as a subprocess +**Required (Correct):** Direct Python import from marketplace package + +```python +# WRONG (current) +self.process = await asyncio.create_subprocess_exec("gitea-mcp-server", ...) + +# CORRECT (target) +from mcp_server import get_tool_definitions, create_tool_dispatcher, GiteaClient, GiteaConfig +``` + +**Why this is fatal:** Cannot access marketplace code as subprocess, breaks MCP protocol contract. + +### 2. Custom REST API → MCP Streamable HTTP Protocol +**Current (Wrong):** Custom endpoints `/tools/list` and `/tools/call` +**Required (Correct):** MCP Streamable HTTP protocol + +```python +# WRONG (current) +POST /tools/list +POST /tools/call + +# CORRECT (target) +POST /mcp # JSON-RPC 2.0 messages +HEAD /mcp # Protocol version header +``` + +**Why this is fatal:** Not compatible with Claude Desktop's MCP client implementation. + +### 3. Missing Marketplace Dependency +**Current (Wrong):** Comment in pyproject.toml about installing separately +**Required (Correct):** Actual pip dependency from marketplace Git repository + +```toml +# WRONG (current) +# gitea-mcp-server - installed separately (not on PyPI yet) + +# CORRECT (target) +dependencies = [ + "gitea-mcp-server @ git+https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git#subdirectory=mcp-servers/gitea", + ... +] +``` + +**Why this is fatal:** Dependency not installable, breaks Docker builds and deployment. + +## What to KEEP (Rename Imports Only) + +These modules are **solid and well-tested**. Only update import paths from `gitea_http_wrapper` to `gitea_mcp_remote`: + +### config/settings.py +- **Keep:** Overall structure, Pydantic settings, validation logic +- **Minor changes:** + - Make `gitea_repo` optional (allow None) + - Add `mcp_auth_mode: str = "optional"` field + - Change HTTP defaults: `http_host="0.0.0.0"`, `http_port=8080` + - Remove `get_gitea_mcp_env()` method (no longer needed for subprocess) + +### middleware/auth.py +- **Keep:** Entire file logic unchanged +- **Change:** Import paths only (`gitea_http_wrapper` → `gitea_mcp_remote`) + +### filtering/filter.py +- **Keep:** Entire filtering logic +- **Changes:** + - Line 30: Change `raise ValueError(...)` to `logger.warning(...)` (non-fatal) + - Import paths: `gitea_http_wrapper` → `gitea_mcp_remote` + +### Tests (all files) +- **Keep:** All test logic and fixtures +- **Move:** `src/gitea_http_wrapper/tests/` → `tests/` (top-level) +- **Change:** Import paths to reflect new structure + +### DEPLOYMENT.md +- **Keep:** Overall deployment guide structure +- **Update:** References to new MCP endpoints, Docker structure, marketplace dependency + +## What to REPLACE + +### server.py → server_http.py +**Complete replacement** with: +- Direct Python imports from marketplace `mcp_server` +- MCP Streamable HTTP transport (`POST /mcp`, `HEAD /mcp`) +- JSON-RPC 2.0 message handling +- GiteaClient instantiation with GiteaConfig +- Tool dispatcher integration +- Keep health endpoints: `/health`, `/healthz`, `/ping` + +### pyproject.toml +**Full replacement** with: +- Marketplace Git dependency +- Updated package name: `gitea-mcp-remote` +- New entry point: `gitea-mcp-remote = "gitea_mcp_remote.server_http:main"` +- Updated test paths: `testpaths = ["tests"]` + +### docker-compose.yml → docker/docker-compose.yml +**Move and restructure** with: +- Two services: `app` (Python server) and `caddy` (reverse proxy) +- App listens on port 8080 (internal) +- Caddy exposes port 443 (external HTTPS) +- Volume for Caddy certs persistence + +### Dockerfile → docker/Dockerfile +**Replace** with: +- Install `git` package (for Git dependency install) +- Expose port 8080 (not 8000) +- Use `curl` for healthcheck (not wget) +- Install from `requirements.txt` first, then marketplace dependency + +## New Files to CREATE + +### docker/Caddyfile +Reverse proxy configuration: +- HTTPS termination +- Proxy to app:8080 +- MCP endpoint routing + +### CLAUDE.md +Project guidance for Claude Code: +- Architecture explanation +- Development workflows +- Deployment procedures +- MCP protocol notes + +### scripts/start.sh +Production startup script: +- Environment validation +- Graceful startup +- Logging configuration + +### scripts/healthcheck.sh +Docker healthcheck script: +- Check `/health` endpoint +- Validate MCP endpoint +- Exit codes for Docker + +### tests/test_server_http.py +New test file for HTTP server: +- MCP endpoint tests +- JSON-RPC 2.0 validation +- Protocol version tests + +## Package Rename + +**From:** `src/gitea_http_wrapper/` +**To:** `src/gitea_mcp_remote/` + +All imports throughout codebase must be updated: +```python +# OLD +from gitea_http_wrapper.config import GiteaSettings + +# NEW +from gitea_mcp_remote.config import GiteaSettings +``` + +## Execution Order (18 Steps) + +This is the **exact sequence** that must be followed: + +1. Rename package directory: `gitea_http_wrapper` → `gitea_mcp_remote` +2. Update `config/settings.py` (fields + imports) +3. Update `middleware/auth.py` (imports only) +4. Update `filtering/filter.py` (warning + imports) +5. Move tests: `src/gitea_mcp_remote/tests/` → `tests/` +6. Update all test imports +7. Delete old `server.py` +8. Create new `server_http.py` with MCP protocol +9. Replace `pyproject.toml` with marketplace dependency +10. Update `pytest.ini` test paths +11. Create `docker/` directory +12. Move and update `docker-compose.yml` → `docker/docker-compose.yml` +13. Replace `Dockerfile` → `docker/Dockerfile` +14. Create `docker/Caddyfile` +15. Create `scripts/start.sh` and `scripts/healthcheck.sh` +16. Create `tests/test_server_http.py` +17. Create `CLAUDE.md` +18. Update `DEPLOYMENT.md` references + +## Validation Checklist (16 Items) + +After implementation, ALL must pass: + +### Package Structure +- [ ] `src/gitea_mcp_remote/` exists (not `gitea_http_wrapper`) +- [ ] No imports reference `gitea_http_wrapper` +- [ ] `tests/` is at repository root (not in `src/`) + +### Configuration +- [ ] `config/settings.py` has `mcp_auth_mode` field +- [ ] `config/settings.py` has `gitea_repo: str | None` +- [ ] HTTP defaults are `0.0.0.0:8080` + +### Server Implementation +- [ ] `server_http.py` imports from `mcp_server` package +- [ ] MCP endpoints exist: `POST /mcp`, `HEAD /mcp` +- [ ] Health endpoints exist: `/health`, `/healthz`, `/ping` +- [ ] No subprocess spawning code + +### Dependencies +- [ ] `pyproject.toml` has marketplace Git dependency +- [ ] Entry point is `gitea-mcp-remote` (not `gitea-http-wrapper`) +- [ ] Can run: `pip install -e .` successfully + +### Docker +- [ ] `docker/docker-compose.yml` has two services (app + caddy) +- [ ] `docker/Dockerfile` installs git and uses port 8080 +- [ ] `docker/Caddyfile` exists and proxies to app:8080 + +### Tests +- [ ] All tests pass: `pytest tests/` + +## Architecture Diagram + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Claude Desktop (MCP Client) │ +└───────────────────────┬─────────────────────────────────────┘ + │ JSON-RPC 2.0 over HTTP + │ POST /mcp + ↓ +┌─────────────────────────────────────────────────────────────┐ +│ Caddy (HTTPS Termination) │ +│ - TLS/SSL │ +│ - Reverse proxy to :8080 │ +└───────────────────────┬─────────────────────────────────────┘ + │ + ↓ +┌─────────────────────────────────────────────────────────────┐ +│ server_http.py (MCP HTTP Transport) │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ Starlette App │ │ +│ │ - POST /mcp (JSON-RPC handler) │ │ +│ │ - HEAD /mcp (protocol version) │ │ +│ │ - /health endpoints │ │ +│ └────────────────────┬────────────────────────────────────┘ │ +│ │ │ +│ ↓ │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ Middleware Stack │ │ +│ │ - BearerAuthMiddleware (auth.py) ✓ Keep │ │ +│ │ - HealthCheckBypassMiddleware ✓ Keep │ │ +│ └────────────────────┬────────────────────────────────────┘ │ +│ │ │ +│ ↓ │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ Tool Dispatcher │ │ +│ │ - create_tool_dispatcher() from mcp_server │ │ +│ │ - Tool filtering (filter.py) ✓ Keep │ │ +│ └────────────────────┬────────────────────────────────────┘ │ +└──────────────────────┼──────────────────────────────────────┘ + │ Direct Python calls + ↓ +┌─────────────────────────────────────────────────────────────┐ +│ Marketplace: mcp_server (gitea-mcp-server) │ +│ - GiteaClient │ +│ - GiteaConfig │ +│ - get_tool_definitions() │ +│ - create_tool_dispatcher() │ +└───────────────────────┬─────────────────────────────────────┘ + │ HTTPS API calls + ↓ +┌─────────────────────────────────────────────────────────────┐ +│ Gitea Instance (gitea.hotserv.cloud) │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Risk Assessment + +### Low Risk (Supporting Modules) +- Config, middleware, filtering: Well-tested, only import changes +- Tests: Moving location, no logic changes + +### Medium Risk (New Server Implementation) +- `server_http.py`: New file, but following MCP HTTP spec closely +- MCP protocol integration: Well-documented standard + +### High Risk (Deployment Changes) +- Docker multi-service setup: Requires Caddy configuration +- Marketplace Git dependency: Must be accessible during build + +### Mitigation Strategy +1. Execute in exact order (dependencies first, server last) +2. Test at each major milestone (config → middleware → server) +3. Validate Docker build before final deployment +4. Keep development branch for rollback if needed + +## Success Criteria + +1. ✅ All 16 validation items pass +2. ✅ Can install via `pip install -e .` +3. ✅ Can build Docker image successfully +4. ✅ Can start via `docker-compose up` +5. ✅ MCP endpoint responds to `POST /mcp` with protocol version +6. ✅ Claude Desktop can connect and list tools +7. ✅ Can create Gitea issue via MCP protocol +8. ✅ All tests pass + +## Timeline Estimate + +- **Setup & Config Changes:** 2-3 hours +- **Server Rewrite:** 4-6 hours +- **Docker Restructure:** 3-4 hours +- **Testing & Validation:** 4-5 hours +- **Documentation:** 2-3 hours +- **Buffer for Issues:** 4-5 hours + +**Total:** 19-26 hours → 1 week sprint + +## References + +- MCP Streamable HTTP Spec: https://spec.modelcontextprotocol.io/specification/basic/transports/ +- JSON-RPC 2.0 Spec: https://www.jsonrpc.org/specification +- Marketplace Repository: https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace +- Original Issue: (To be created in this sprint) diff --git a/docs/sprint-proposals/sprint-01-implementation-guide.md b/docs/sprint-proposals/sprint-01-implementation-guide.md new file mode 100644 index 0000000..b96da8f --- /dev/null +++ b/docs/sprint-proposals/sprint-01-implementation-guide.md @@ -0,0 +1,1454 @@ +# Sprint 01: Implementation Guide - Core Architecture Correction + +**Related Proposal:** [sprint-01-core-architecture-correction.md](./sprint-01-core-architecture-correction.md) + +This guide provides step-by-step technical implementation details for each file change. + +--- + +## Phase 1: Package Restructuring (Issues #1-2) + +### Issue #1: Rename Package Directory + +**Estimated Time:** 15 minutes +**Dependencies:** None +**Type:** Refactor + +**Steps:** +```bash +cd /home/lmiranda/gitea-mcp-remote +git mv src/gitea_http_wrapper src/gitea_mcp_remote +``` + +**Validation:** +```bash +# Should exist +ls -la src/gitea_mcp_remote/ + +# Should NOT exist +ls -la src/gitea_http_wrapper/ 2>&1 | grep "No such file" +``` + +--- + +### Issue #2: Update Configuration Module + +**Estimated Time:** 1-2 hours +**Dependencies:** Issue #1 +**Files:** `src/gitea_mcp_remote/config/settings.py`, `src/gitea_mcp_remote/config/__init__.py` + +**Changes to `settings.py`:** + +```python +# Line 1: Update docstring +"""Configuration settings for Gitea MCP HTTP transport.""" + +# Lines 33-36: Make gitea_repo optional +gitea_repo: str | None = Field( + default=None, + description="Default repository name (optional)", +) + +# Lines 39-45: Update HTTP defaults +http_host: str = Field( + default="0.0.0.0", + description="HTTP server bind address", +) +http_port: int = Field( + default=8080, + ge=1, + le=65535, + description="HTTP server port", +) + +# After line 54 (after auth_token): Add new field +mcp_auth_mode: str = Field( + default="optional", + description="MCP authentication mode: 'required', 'optional', or 'none'", +) + +# Delete lines 88-95: Remove get_gitea_mcp_env() method +# (No longer needed - we use direct Python imports, not subprocess) +``` + +**No import changes needed in this file** (it doesn't import from gitea_http_wrapper). + +**Update `__init__.py`:** +```python +"""Configuration module for Gitea MCP HTTP transport.""" + +from gitea_mcp_remote.config.settings import GiteaSettings, load_settings + +__all__ = ["GiteaSettings", "load_settings"] +``` + +**Validation:** +```python +# Test in Python REPL +from gitea_mcp_remote.config import GiteaSettings + +# Should have new field +assert hasattr(GiteaSettings, 'mcp_auth_mode') + +# Should have optional gitea_repo +settings = GiteaSettings( + gitea_url="https://test.com", + gitea_token="test", + gitea_owner="test" + # gitea_repo is optional now +) +``` + +--- + +## Phase 2: Update Supporting Modules (Issues #3-4) + +### Issue #3: Update Middleware Module + +**Estimated Time:** 30 minutes +**Dependencies:** Issue #1 +**Files:** `src/gitea_mcp_remote/middleware/auth.py`, `src/gitea_mcp_remote/middleware/__init__.py` + +**Changes to `auth.py`:** +- Keep ALL logic unchanged +- Only update imports + +**Changes to `__init__.py`:** +```python +"""Middleware components for MCP HTTP transport.""" + +from gitea_mcp_remote.middleware.auth import ( + BearerAuthMiddleware, + HealthCheckBypassMiddleware, +) + +__all__ = [ + "BearerAuthMiddleware", + "HealthCheckBypassMiddleware", +] +``` + +**Validation:** +```python +from gitea_mcp_remote.middleware import BearerAuthMiddleware +assert BearerAuthMiddleware is not None +``` + +--- + +### Issue #4: Update Filtering Module + +**Estimated Time:** 45 minutes +**Dependencies:** Issue #1 +**Files:** `src/gitea_mcp_remote/filtering/filter.py`, `src/gitea_mcp_remote/filtering/__init__.py` + +**Changes to `filter.py`:** + +```python +# Line 1: Update docstring +"""Tool filtering for MCP client compatibility.""" + +# Add import at top +import logging + +logger = logging.getLogger(__name__) + +# Lines 29-32: Change ValueError to warning +if enabled_tools is not None and disabled_tools is not None: + logger.warning( + "Both enabled_tools and disabled_tools specified. " + "Using disabled_tools (blacklist mode). " + "Recommendation: Choose one filtering mode." + ) + # Continue with disabled_tools taking precedence +``` + +**Changes to `__init__.py`:** +```python +"""Tool filtering module for MCP HTTP transport.""" + +from gitea_mcp_remote.filtering.filter import ToolFilter + +__all__ = ["ToolFilter"] +``` + +**Validation:** +```python +from gitea_mcp_remote.filtering import ToolFilter + +# Should log warning, not raise +filter = ToolFilter( + enabled_tools=["tool1"], + disabled_tools=["tool2"] +) +# Should use disabled_tools (blacklist mode) +assert filter.disabled_tools == {"tool2"} +``` + +--- + +## Phase 3: Relocate and Update Tests (Issues #5-6) + +### Issue #5: Move Tests to Root + +**Estimated Time:** 30 minutes +**Dependencies:** Issue #1 +**Type:** Refactor + +**Steps:** +```bash +cd /home/lmiranda/gitea-mcp-remote +git mv src/gitea_mcp_remote/tests tests +``` + +**Validation:** +```bash +# Should exist +ls -la tests/test_config.py +ls -la tests/test_middleware.py +ls -la tests/test_filtering.py +ls -la tests/conftest.py + +# Should NOT exist +ls -la src/gitea_mcp_remote/tests/ 2>&1 | grep "No such file" +``` + +--- + +### Issue #6: Update Test Imports + +**Estimated Time:** 1 hour +**Dependencies:** Issue #5 +**Files:** All files in `tests/` directory + +**Global search-replace in all test files:** +```python +# OLD +from gitea_http_wrapper.config import ... +from gitea_http_wrapper.middleware import ... +from gitea_http_wrapper.filtering import ... + +# NEW +from gitea_mcp_remote.config import ... +from gitea_mcp_remote.middleware import ... +from gitea_mcp_remote.filtering import ... +``` + +**Specific files to update:** +- `tests/conftest.py` +- `tests/test_config.py` +- `tests/test_middleware.py` +- `tests/test_filtering.py` + +**Validation:** +```bash +pytest tests/ -v +# All existing tests should pass +``` + +--- + +## Phase 4: Core Server Replacement (Issues #7-8) + +### Issue #7: Remove Old Server + +**Estimated Time:** 5 minutes +**Dependencies:** Issues #2-6 (ensure all imports work first) +**Type:** Deletion + +**Steps:** +```bash +git rm src/gitea_mcp_remote/server.py +``` + +**Validation:** +```bash +ls -la src/gitea_mcp_remote/server.py 2>&1 | grep "No such file" +``` + +--- + +### Issue #8: Create New MCP HTTP Server + +**Estimated Time:** 4-6 hours +**Dependencies:** Issue #7 +**Files:** `src/gitea_mcp_remote/server_http.py` + +**Complete new file:** + +```python +"""MCP HTTP transport server for Gitea operations. + +This server implements the MCP Streamable HTTP protocol, providing +JSON-RPC 2.0 communication with Claude Desktop clients. +""" + +import asyncio +import json +import logging +from typing import Any + +import uvicorn +from starlette.applications import Starlette +from starlette.requests import Request +from starlette.responses import JSONResponse, Response +from starlette.routing import Route + +# Import from marketplace gitea-mcp-server +from mcp_server import ( + GiteaClient, + GiteaConfig, + create_tool_dispatcher, + get_tool_definitions, +) + +from gitea_mcp_remote.config import GiteaSettings, load_settings +from gitea_mcp_remote.filtering import ToolFilter +from gitea_mcp_remote.middleware import ( + BearerAuthMiddleware, + HealthCheckBypassMiddleware, +) + +# Configure logging +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", +) +logger = logging.getLogger(__name__) + +# MCP Protocol version +MCP_VERSION = "2024-11-05" + + +class GiteaMCPServer: + """ + MCP HTTP transport server for Gitea. + + Implements MCP Streamable HTTP protocol with JSON-RPC 2.0. + """ + + def __init__(self, settings: GiteaSettings): + """Initialize MCP server with settings.""" + self.settings = settings + + # Initialize Gitea client + self.gitea_config = GiteaConfig( + base_url=settings.gitea_url, + api_token=settings.gitea_token, + default_owner=settings.gitea_owner, + default_repo=settings.gitea_repo, + ) + self.gitea_client = GiteaClient(self.gitea_config) + + # Initialize tool filtering + self.tool_filter = ToolFilter( + enabled_tools=settings.enabled_tools_list, + disabled_tools=settings.disabled_tools_list, + ) + + # Get tool definitions and create dispatcher + self.tool_definitions = get_tool_definitions() + self.tool_dispatcher = create_tool_dispatcher(self.gitea_client) + + logger.info(f"Initialized MCP server for {settings.gitea_url}") + logger.info(f"Tool filtering: {self.tool_filter.get_filter_stats()}") + + async def handle_list_tools(self, params: dict) -> dict: + """Handle tools/list MCP method.""" + # Get all tool definitions + tools = self.tool_definitions + + # Apply filtering + filtered_tools = self.tool_filter.filter_tools_list(tools) + + logger.info(f"Listed {len(filtered_tools)} tools (filtered from {len(tools)})") + + return { + "tools": filtered_tools + } + + async def handle_call_tool(self, params: dict) -> dict: + """Handle tools/call MCP method.""" + tool_name = params.get("name") + arguments = params.get("arguments", {}) + + # Check if tool is filtered + if not self.tool_filter.should_include_tool(tool_name): + logger.warning(f"Tool '{tool_name}' is filtered out") + raise ValueError(f"Tool '{tool_name}' is not available") + + logger.info(f"Calling tool: {tool_name}") + + # Dispatch to tool handler + result = await self.tool_dispatcher(tool_name, arguments) + + return { + "content": result + } + + async def handle_initialize(self, params: dict) -> dict: + """Handle initialize MCP method.""" + return { + "protocolVersion": MCP_VERSION, + "serverInfo": { + "name": "gitea-mcp-remote", + "version": "1.1.0", + }, + "capabilities": { + "tools": {} + } + } + + async def handle_jsonrpc_request(self, request_data: dict) -> dict: + """Handle JSON-RPC 2.0 request.""" + method = request_data.get("method") + params = request_data.get("params", {}) + request_id = request_data.get("id") + + try: + # Route to appropriate handler + if method == "initialize": + result = await self.handle_initialize(params) + elif method == "tools/list": + result = await self.handle_list_tools(params) + elif method == "tools/call": + result = await self.handle_call_tool(params) + else: + raise ValueError(f"Unknown method: {method}") + + # Success response + return { + "jsonrpc": "2.0", + "id": request_id, + "result": result, + } + + except Exception as e: + logger.exception(f"Error handling {method}") + + # Error response + return { + "jsonrpc": "2.0", + "id": request_id, + "error": { + "code": -32000, + "message": str(e), + } + } + + +# Global server instance +mcp_server: GiteaMCPServer | None = None + + +async def mcp_endpoint_head(request: Request) -> Response: + """ + Handle HEAD /mcp - Protocol version check. + + Returns MCP protocol version in X-MCP-Version header. + """ + return Response( + status_code=200, + headers={ + "X-MCP-Version": MCP_VERSION, + } + ) + + +async def mcp_endpoint_post(request: Request) -> JSONResponse: + """ + Handle POST /mcp - JSON-RPC 2.0 messages. + + Main MCP communication endpoint. + """ + try: + # Parse JSON-RPC request + request_data = await request.json() + + # Handle request + response_data = await mcp_server.handle_jsonrpc_request(request_data) + + return JSONResponse(response_data) + + except json.JSONDecodeError: + return JSONResponse( + { + "jsonrpc": "2.0", + "id": None, + "error": { + "code": -32700, + "message": "Parse error: Invalid JSON", + } + }, + status_code=400, + ) + + except Exception as e: + logger.exception("Error in MCP endpoint") + return JSONResponse( + { + "jsonrpc": "2.0", + "id": None, + "error": { + "code": -32603, + "message": f"Internal error: {str(e)}", + } + }, + status_code=500, + ) + + +async def health_check(request: Request) -> JSONResponse: + """Health check endpoint.""" + return JSONResponse({"status": "healthy"}) + + +async def startup() -> None: + """Application startup handler.""" + global mcp_server + settings = load_settings() + mcp_server = GiteaMCPServer(settings) + logger.info(f"MCP HTTP server starting on {settings.http_host}:{settings.http_port}") + + +# Define routes +routes = [ + # MCP protocol endpoints + Route("/mcp", mcp_endpoint_post, methods=["POST"]), + Route("/mcp", mcp_endpoint_head, methods=["HEAD"]), + + # Health check endpoints + Route("/health", health_check, methods=["GET"]), + Route("/healthz", health_check, methods=["GET"]), + Route("/ping", health_check, methods=["GET"]), +] + +# Create Starlette app +app = Starlette( + routes=routes, + on_startup=[startup], +) + + +def create_app(settings: GiteaSettings | None = None) -> Starlette: + """ + Create and configure the Starlette application. + + Args: + settings: Optional settings override for testing. + + Returns: + Configured Starlette application. + """ + if settings is None: + settings = load_settings() + + # Add middleware + app.add_middleware(HealthCheckBypassMiddleware) + + if settings.mcp_auth_mode == "required": + app.add_middleware(BearerAuthMiddleware, auth_token=settings.auth_token) + + return app + + +def main() -> None: + """Main entry point for the MCP HTTP server.""" + settings = load_settings() + + # Log configuration + logger.info(f"MCP Protocol Version: {MCP_VERSION}") + logger.info(f"Gitea URL: {settings.gitea_url}") + logger.info(f"Auth mode: {settings.mcp_auth_mode}") + + # Run server + uvicorn.run( + "gitea_mcp_remote.server_http:app", + host=settings.http_host, + port=settings.http_port, + log_level="info", + ) + + +if __name__ == "__main__": + main() +``` + +**Validation:** +```bash +# Should import successfully +python3 -c "from gitea_mcp_remote.server_http import GiteaMCPServer" + +# Check MCP endpoints exist +python3 -c "from gitea_mcp_remote.server_http import app; print([r.path for r in app.routes])" +# Should show: ['/mcp', '/mcp', '/health', '/healthz', '/ping'] +``` + +--- + +## Phase 5: Update Project Configuration (Issues #9-10) + +### Issue #9: Replace pyproject.toml + +**Estimated Time:** 30 minutes +**Dependencies:** Issue #8 +**File:** `pyproject.toml` + +**Complete replacement:** + +```toml +[build-system] +requires = ["setuptools>=61.0", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "gitea-mcp-remote" +version = "1.1.0" +description = "MCP HTTP transport for Gitea operations" +readme = "README.md" +requires-python = ">=3.10" +license = { text = "MIT" } +authors = [ + { name = "Leo Miranda", email = "lmiranda@example.com" } +] +keywords = ["mcp", "gitea", "model-context-protocol", "http-transport"] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", +] + +dependencies = [ + "gitea-mcp-server @ git+https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace.git#subdirectory=mcp-servers/gitea", + "mcp>=0.9.0", + "uvicorn>=0.27.0", + "pydantic>=2.0.0", + "pydantic-settings>=2.0.0", + "python-dotenv>=1.0.0", + "starlette>=0.36.0", +] + +[project.optional-dependencies] +dev = [ + "pytest>=7.0.0", + "pytest-asyncio>=0.21.0", + "pytest-cov>=4.0.0", + "httpx>=0.24.0", +] + +[project.scripts] +gitea-mcp-remote = "gitea_mcp_remote.server_http:main" + +[project.urls] +Homepage = "https://gitea.hotserv.cloud/personal-projects/gitea-mcp-remote" +Repository = "https://gitea.hotserv.cloud/personal-projects/gitea-mcp-remote" + +[tool.setuptools.packages.find] +where = ["src"] + +[tool.pytest.ini_options] +asyncio_mode = "auto" +testpaths = ["tests"] +python_files = ["test_*.py"] +python_classes = ["Test*"] +python_functions = ["test_*"] +``` + +**Validation:** +```bash +pip install -e . +# Should install successfully including marketplace dependency + +gitea-mcp-remote --help +# Should show help (if we add --help support) or start server +``` + +--- + +### Issue #10: Update pytest.ini + +**Estimated Time:** 5 minutes +**Dependencies:** Issue #9 +**File:** `pytest.ini` + +**Changes:** +```ini +[pytest] +asyncio_mode = auto +testpaths = tests +python_files = test_*.py +python_classes = Test* +python_functions = test_* +addopts = -v --strict-markers +``` + +**Validation:** +```bash +pytest --collect-only +# Should collect tests from tests/ directory +``` + +--- + +## Phase 6: Docker Infrastructure (Issues #11-14) + +### Issue #11: Create Docker Directory Structure + +**Estimated Time:** 15 minutes +**Dependencies:** None +**Type:** Setup + +**Steps:** +```bash +mkdir -p docker +``` + +**Validation:** +```bash +ls -la docker/ +``` + +--- + +### Issue #12: Create Docker Compose Configuration + +**Estimated Time:** 1-2 hours +**Dependencies:** Issue #11 +**File:** `docker/docker-compose.yml` + +**Complete file:** + +```yaml +version: '3.8' + +services: + app: + build: + context: .. + dockerfile: docker/Dockerfile + container_name: gitea-mcp-remote-app + restart: unless-stopped + environment: + # Gitea configuration + GITEA_URL: ${GITEA_URL} + GITEA_TOKEN: ${GITEA_TOKEN} + GITEA_OWNER: ${GITEA_OWNER} + GITEA_REPO: ${GITEA_REPO:-} + + # HTTP server + HTTP_HOST: 0.0.0.0 + HTTP_PORT: 8080 + + # Authentication + AUTH_TOKEN: ${AUTH_TOKEN:-} + MCP_AUTH_MODE: ${MCP_AUTH_MODE:-optional} + + # Tool filtering + ENABLED_TOOLS: ${ENABLED_TOOLS:-} + DISABLED_TOOLS: ${DISABLED_TOOLS:-} + ports: + - "8080:8080" + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + networks: + - mcp-network + + caddy: + image: caddy:2-alpine + container_name: gitea-mcp-remote-caddy + restart: unless-stopped + ports: + - "443:443" + - "80:80" + volumes: + - ./Caddyfile:/etc/caddy/Caddyfile:ro + - caddy-data:/data + - caddy-config:/config + depends_on: + app: + condition: service_healthy + networks: + - mcp-network + +networks: + mcp-network: + driver: bridge + +volumes: + caddy-data: + caddy-config: +``` + +**Validation:** +```bash +docker-compose -f docker/docker-compose.yml config +# Should validate without errors +``` + +--- + +### Issue #13: Create Dockerfile + +**Estimated Time:** 1 hour +**Dependencies:** Issue #11 +**File:** `docker/Dockerfile` + +**Complete file:** + +```dockerfile +FROM python:3.11-slim + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + git \ + curl \ + && rm -rf /var/lib/apt/lists/* + +# Set working directory +WORKDIR /app + +# Copy requirements first (for layer caching) +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy project files +COPY pyproject.toml . +COPY README.md . +COPY src/ src/ + +# Install project with marketplace dependency +RUN pip install --no-cache-dir -e . + +# Create non-root user +RUN useradd -m -u 1000 mcpuser && \ + chown -R mcpuser:mcpuser /app + +USER mcpuser + +# Expose port +EXPOSE 8080 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ + CMD curl -f http://localhost:8080/health || exit 1 + +# Run server +CMD ["gitea-mcp-remote"] +``` + +**Validation:** +```bash +docker build -f docker/Dockerfile -t gitea-mcp-remote:test . +# Should build successfully + +docker run --rm gitea-mcp-remote:test python3 -c "from gitea_mcp_remote.server_http import main" +# Should import successfully +``` + +--- + +### Issue #14: Create Caddyfile + +**Estimated Time:** 45 minutes +**Dependencies:** Issue #11 +**File:** `docker/Caddyfile` + +**Complete file:** + +```caddyfile +{ + # Global options + admin off + auto_https disable_redirects +} + +:443 { + # TLS configuration + tls internal + + # MCP endpoint + handle /mcp* { + reverse_proxy app:8080 + } + + # Health checks + handle /health* { + reverse_proxy app:8080 + } + + handle /ping { + reverse_proxy app:8080 + } + + # Default response + handle { + respond "Gitea MCP Remote - Use /mcp endpoint" 200 + } + + # Logging + log { + output stdout + format console + } +} +``` + +**Validation:** +```bash +# Validate Caddyfile syntax +docker run --rm -v $(pwd)/docker/Caddyfile:/etc/caddy/Caddyfile caddy:2-alpine caddy validate --config /etc/caddy/Caddyfile +``` + +--- + +## Phase 7: Utility Scripts (Issue #15) + +### Issue #15: Create Startup and Health Check Scripts + +**Estimated Time:** 1 hour +**Dependencies:** None +**Files:** `scripts/start.sh`, `scripts/healthcheck.sh` + +**`scripts/start.sh`:** + +```bash +#!/usr/bin/env bash +# Production startup script for gitea-mcp-remote + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" + +echo "=== Gitea MCP Remote Startup ===" + +# Validate required environment variables +required_vars=("GITEA_URL" "GITEA_TOKEN" "GITEA_OWNER") +for var in "${required_vars[@]}"; do + if [[ -z "${!var:-}" ]]; then + echo "ERROR: $var is not set" + exit 1 + fi +done + +echo "✓ Environment validated" + +# Optional: Load from .env if exists +if [[ -f "$PROJECT_ROOT/.env" ]]; then + echo "Loading environment from .env" + set -a + source "$PROJECT_ROOT/.env" + set +a +fi + +# Log configuration (sanitized) +echo "Configuration:" +echo " GITEA_URL: ${GITEA_URL}" +echo " GITEA_OWNER: ${GITEA_OWNER}" +echo " GITEA_REPO: ${GITEA_REPO:-}" +echo " HTTP_HOST: ${HTTP_HOST:-0.0.0.0}" +echo " HTTP_PORT: ${HTTP_PORT:-8080}" +echo " MCP_AUTH_MODE: ${MCP_AUTH_MODE:-optional}" + +# Start server +echo "Starting MCP HTTP server..." +cd "$PROJECT_ROOT" +exec gitea-mcp-remote +``` + +**`scripts/healthcheck.sh`:** + +```bash +#!/usr/bin/env bash +# Docker healthcheck script + +set -euo pipefail + +HOST="${HTTP_HOST:-0.0.0.0}" +PORT="${HTTP_PORT:-8080}" + +# Check health endpoint +if curl -f -s "http://${HOST}:${PORT}/health" > /dev/null; then + exit 0 +else + exit 1 +fi +``` + +**Make executable:** +```bash +chmod +x scripts/start.sh scripts/healthcheck.sh +``` + +**Validation:** +```bash +# Test start script (will fail without env vars - expected) +./scripts/start.sh 2>&1 | grep "ERROR: GITEA_URL" + +# Test healthcheck script (needs server running) +# export HTTP_HOST=localhost HTTP_PORT=8080 +# ./scripts/healthcheck.sh +``` + +--- + +## Phase 8: New Tests (Issue #16) + +### Issue #16: Create MCP Server Tests + +**Estimated Time:** 2-3 hours +**Dependencies:** Issue #8 +**File:** `tests/test_server_http.py` + +**Complete file:** + +```python +"""Tests for MCP HTTP server.""" + +import pytest +from starlette.testclient import TestClient + +from gitea_mcp_remote.config import GiteaSettings +from gitea_mcp_remote.server_http import create_app + + +@pytest.fixture +def settings(): + """Test settings.""" + return GiteaSettings( + gitea_url="https://gitea.test.com", + gitea_token="test_token", + gitea_owner="test_owner", + gitea_repo="test_repo", + http_host="127.0.0.1", + http_port=8080, + auth_token="test_auth_token", + mcp_auth_mode="optional", + ) + + +@pytest.fixture +def client(settings): + """Test client.""" + app = create_app(settings) + return TestClient(app) + + +def test_health_endpoints(client): + """Test health check endpoints.""" + for path in ["/health", "/healthz", "/ping"]: + response = client.get(path) + assert response.status_code == 200 + assert response.json() == {"status": "healthy"} + + +def test_mcp_head_endpoint(client): + """Test HEAD /mcp returns protocol version.""" + response = client.head("/mcp") + assert response.status_code == 200 + assert "X-MCP-Version" in response.headers + assert response.headers["X-MCP-Version"] == "2024-11-05" + + +def test_mcp_post_initialize(client): + """Test POST /mcp with initialize method.""" + request_data = { + "jsonrpc": "2.0", + "id": 1, + "method": "initialize", + "params": {} + } + + response = client.post("/mcp", json=request_data) + assert response.status_code == 200 + + data = response.json() + assert data["jsonrpc"] == "2.0" + assert data["id"] == 1 + assert "result" in data + assert data["result"]["protocolVersion"] == "2024-11-05" + + +def test_mcp_post_invalid_json(client): + """Test POST /mcp with invalid JSON.""" + response = client.post( + "/mcp", + content=b"not valid json", + headers={"Content-Type": "application/json"} + ) + + assert response.status_code == 400 + data = response.json() + assert "error" in data + assert data["error"]["code"] == -32700 + + +def test_mcp_post_unknown_method(client): + """Test POST /mcp with unknown method.""" + request_data = { + "jsonrpc": "2.0", + "id": 1, + "method": "unknown/method", + "params": {} + } + + response = client.post("/mcp", json=request_data) + assert response.status_code == 200 + + data = response.json() + assert "error" in data + assert data["error"]["code"] == -32000 + + +@pytest.mark.asyncio +async def test_tool_filtering(settings): + """Test tool filtering integration.""" + settings.disabled_tools = "create_issue,delete_issue" + + from gitea_mcp_remote.server_http import GiteaMCPServer + server = GiteaMCPServer(settings) + + # List tools should exclude disabled + result = await server.handle_list_tools({}) + tool_names = [tool["name"] for tool in result["tools"]] + + assert "create_issue" not in tool_names + assert "delete_issue" not in tool_names +``` + +**Validation:** +```bash +pytest tests/test_server_http.py -v +# All tests should pass +``` + +--- + +## Phase 9: Documentation (Issue #17-18) + +### Issue #17: Create CLAUDE.md + +**Estimated Time:** 1-2 hours +**Dependencies:** All previous issues +**File:** `CLAUDE.md` + +**Complete file:** + +```markdown +# CLAUDE.md - Gitea MCP Remote + +Project guidance for Claude Code when working with this repository. + +## Project Overview + +**Type:** Python MCP HTTP Transport Server +**Purpose:** Provide HTTP transport layer for Gitea MCP operations, enabling Claude Desktop integration +**Architecture:** MCP Streamable HTTP protocol with JSON-RPC 2.0 + +## Architecture + +### Component Stack + +``` +Claude Desktop + ↓ HTTP + JSON-RPC 2.0 +Caddy (HTTPS proxy) + ↓ +server_http.py (MCP HTTP transport) + ↓ Direct Python imports +mcp_server (from marketplace) + ↓ HTTPS API +Gitea Instance +``` + +### Key Components + +1. **server_http.py** - MCP HTTP transport server + - Implements MCP Streamable HTTP protocol + - JSON-RPC 2.0 message handling + - Routes: `POST /mcp`, `HEAD /mcp`, health endpoints + +2. **config/settings.py** - Configuration management + - Pydantic settings with environment variable loading + - Gitea connection parameters + - HTTP server configuration + - Authentication and filtering options + +3. **middleware/auth.py** - Authentication middleware + - Bearer token authentication + - Health check bypass + +4. **filtering/filter.py** - Tool filtering + - Whitelist/blacklist tool filtering + - Claude Desktop compatibility layer + +5. **mcp_server** (marketplace) - Core Gitea operations + - GiteaClient for API operations + - Tool definitions and dispatcher + +## Development Workflows + +### Local Development + +```bash +# Setup +python3 -m venv .venv +source .venv/bin/activate +pip install -e ".[dev]" + +# Run tests +pytest tests/ -v + +# Run server +export GITEA_URL=https://gitea.test.com +export GITEA_TOKEN=your_token +export GITEA_OWNER=your_org +gitea-mcp-remote +``` + +### Docker Development + +```bash +# Build and run +cd docker +docker-compose up --build + +# View logs +docker-compose logs -f app + +# Stop +docker-compose down +``` + +### Testing + +```bash +# All tests +pytest tests/ -v + +# With coverage +pytest tests/ --cov=gitea_mcp_remote --cov-report=html + +# Specific test file +pytest tests/test_server_http.py -v +``` + +## MCP Protocol Notes + +### Streamable HTTP Transport + +This server implements MCP Streamable HTTP protocol: + +- **HEAD /mcp** - Protocol version check + - Returns: `X-MCP-Version: 2024-11-05` header + +- **POST /mcp** - JSON-RPC 2.0 messages + - Content-Type: `application/json` + - Body: JSON-RPC 2.0 request/response + +### JSON-RPC Methods + +1. **initialize** - Client initialization + ```json + {"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {}} + ``` + +2. **tools/list** - List available tools + ```json + {"jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {}} + ``` + +3. **tools/call** - Execute a tool + ```json + { + "jsonrpc": "2.0", + "id": 3, + "method": "tools/call", + "params": { + "name": "list_issues", + "arguments": {"owner": "org", "repo": "repo"} + } + } + ``` + +## Configuration + +### Environment Variables + +**Required:** +- `GITEA_URL` - Gitea instance URL +- `GITEA_TOKEN` - Gitea API token +- `GITEA_OWNER` - Default repository owner + +**Optional:** +- `GITEA_REPO` - Default repository name +- `HTTP_HOST` - Server bind address (default: 0.0.0.0) +- `HTTP_PORT` - Server port (default: 8080) +- `AUTH_TOKEN` - Bearer authentication token +- `MCP_AUTH_MODE` - Auth mode: required/optional/none +- `ENABLED_TOOLS` - Comma-separated whitelist +- `DISABLED_TOOLS` - Comma-separated blacklist + +### Authentication Modes + +- **none** - No authentication required +- **optional** - Bearer token checked if provided +- **required** - Bearer token mandatory + +## Deployment + +### Docker Compose (Production) + +```bash +# Setup environment +cp .env.example .env +nano .env # Configure + +# Deploy +cd docker +docker-compose up -d + +# Check health +curl https://your-domain/health +``` + +### Claude Desktop Configuration + +```json +{ + "mcpServers": { + "gitea": { + "url": "https://your-domain/mcp", + "headers": { + "Authorization": "Bearer YOUR_TOKEN" + } + } + } +} +``` + +## Troubleshooting + +### Import Errors + +If you see import errors from `gitea_http_wrapper`: +- Package was renamed to `gitea_mcp_remote` +- All imports should use new name +- Check: `git grep -r gitea_http_wrapper` + +### Marketplace Dependency Issues + +If marketplace install fails: +- Check Git repository access: `https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace` +- Ensure `git` is installed in Docker image +- Check subdirectory path: `mcp-servers/gitea` + +### MCP Protocol Errors + +If Claude Desktop can't connect: +- Check protocol version: `curl -I https://your-domain/mcp` +- Test JSON-RPC: `curl -X POST https://your-domain/mcp -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'` +- Review server logs for errors + +## Important Notes + +1. **Do NOT use subprocess** - Import directly from `mcp_server` package +2. **Do NOT create custom REST API** - Use MCP protocol endpoints +3. **Do NOT skip marketplace dependency** - Must be in `pyproject.toml` +4. **Package name is gitea_mcp_remote** - Not gitea_http_wrapper +5. **Tests are at repo root** - Not in src/ directory + +## References + +- MCP Spec: https://spec.modelcontextprotocol.io +- Marketplace: https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace +- JSON-RPC 2.0: https://www.jsonrpc.org/specification +``` + +--- + +### Issue #18: Update DEPLOYMENT.md + +**Estimated Time:** 1 hour +**Dependencies:** All previous issues +**File:** `DEPLOYMENT.md` + +**Changes needed:** +- Update all references from `/tools/list` and `/tools/call` to `/mcp` +- Update Docker structure (two services, new directory) +- Update marketplace dependency installation +- Update Claude Desktop config example +- Add MCP protocol version checking +- Update health check endpoints + +**Key sections to update:** + +1. **Quick Start** - Reference `docker/docker-compose.yml` +2. **Configuration** - Add `MCP_AUTH_MODE` variable +3. **HTTP Endpoints** - Replace with MCP protocol endpoints +4. **Claude Desktop Config** - Update URL to `/mcp` +5. **Troubleshooting** - Add MCP protocol debugging + +--- + +## Final Validation (All Issues Complete) + +### Complete Validation Checklist + +```bash +# 1. Package structure +ls -la src/gitea_mcp_remote/ +ls -la tests/ +! ls -la src/gitea_http_wrapper/ 2>/dev/null + +# 2. No old imports +! git grep -r "gitea_http_wrapper" --include="*.py" + +# 3. Config fields +python3 -c "from gitea_mcp_remote.config import GiteaSettings; assert hasattr(GiteaSettings, 'mcp_auth_mode')" + +# 4. Server has MCP endpoints +python3 -c "from gitea_mcp_remote.server_http import app; paths = [r.path for r in app.routes]; assert '/mcp' in paths" + +# 5. Dependencies installable +pip install -e . + +# 6. Entry point works +which gitea-mcp-remote + +# 7. Tests pass +pytest tests/ -v + +# 8. Docker builds +docker build -f docker/Dockerfile -t gitea-mcp-remote:test . + +# 9. Docker compose validates +docker-compose -f docker/docker-compose.yml config + +# 10. Caddyfile validates +docker run --rm -v $(pwd)/docker/Caddyfile:/etc/caddy/Caddyfile caddy:2-alpine caddy validate --config /etc/caddy/Caddyfile +``` + +## Timeline Summary + +| Phase | Issues | Est. Time | +|-------|--------|-----------| +| 1. Package Restructuring | #1-2 | 2-3 hours | +| 2. Supporting Modules | #3-4 | 1-2 hours | +| 3. Test Relocation | #5-6 | 2 hours | +| 4. Core Server | #7-8 | 5-6 hours | +| 5. Project Config | #9-10 | 1 hour | +| 6. Docker Infrastructure | #11-14 | 4-5 hours | +| 7. Utility Scripts | #15 | 1 hour | +| 8. New Tests | #16 | 2-3 hours | +| 9. Documentation | #17-18 | 3-4 hours | + +**Total:** 21-30 hours (~1 week sprint) diff --git a/docs/sprint-proposals/sprint-01-issue-breakdown.md b/docs/sprint-proposals/sprint-01-issue-breakdown.md new file mode 100644 index 0000000..dfc4441 --- /dev/null +++ b/docs/sprint-proposals/sprint-01-issue-breakdown.md @@ -0,0 +1,489 @@ +# Sprint 01: Issue Breakdown + +## Issue Structure + +Each issue is sized for 1-4 hours of work and includes: +- Clear acceptance criteria +- Dependencies on other issues +- Reference to implementation guide +- Appropriate labels + +--- + +## Issue #1: Rename Package Directory and Update Config + +**Title:** `[Sprint 01] refactor: Rename package to gitea_mcp_remote and update configuration` + +**Estimated Time:** 2-3 hours + +**Labels:** +- `Type/Refactor` +- `Priority/High` +- `Component/Core` +- `Size/M` + +**Dependencies:** None + +**Description:** + +Rename the package directory from `gitea_http_wrapper` to `gitea_mcp_remote` and update the configuration module with new fields required for MCP protocol. + +**Tasks:** +- [ ] Rename `src/gitea_http_wrapper/` to `src/gitea_mcp_remote/` +- [ ] Update `config/settings.py`: + - Make `gitea_repo` optional (allow None) + - Add `mcp_auth_mode: str = "optional"` field + - Change HTTP defaults: `http_host="0.0.0.0"`, `http_port=8080` + - Remove `get_gitea_mcp_env()` method +- [ ] Update `config/__init__.py` imports +- [ ] Verify imports work: `from gitea_mcp_remote.config import GiteaSettings` + +**Acceptance Criteria:** +- Package directory is `src/gitea_mcp_remote/` +- Config has `mcp_auth_mode` field +- Config has optional `gitea_repo` field +- HTTP defaults are `0.0.0.0:8080` +- Can import: `from gitea_mcp_remote.config import GiteaSettings` + +**Implementation Reference:** +See `docs/sprint-proposals/sprint-01-implementation-guide.md` - Phase 1, Issues #1-2 + +--- + +## Issue #2: Update Middleware and Filtering Modules + +**Title:** `[Sprint 01] refactor: Update middleware and filtering with new import paths` + +**Estimated Time:** 1 hour + +**Labels:** +- `Type/Refactor` +- `Priority/High` +- `Component/Core` +- `Size/S` + +**Dependencies:** Issue #1 + +**Description:** + +Update middleware and filtering modules to use new package name. Middleware requires only import changes, filtering changes ValueError to warning. + +**Tasks:** +- [ ] Update `middleware/__init__.py` imports +- [ ] Update `middleware/auth.py` - imports only +- [ ] Update `filtering/__init__.py` imports +- [ ] Update `filtering/filter.py`: + - Add logging import + - Change line 29-32 ValueError to logger.warning +- [ ] Verify imports work + +**Acceptance Criteria:** +- Middleware imports from `gitea_mcp_remote.middleware` +- Filtering imports from `gitea_mcp_remote.filtering` +- ToolFilter logs warning instead of raising ValueError when both filter types specified +- Can import both modules successfully + +**Implementation Reference:** +See `docs/sprint-proposals/sprint-01-implementation-guide.md` - Phase 2, Issues #3-4 + +--- + +## Issue #3: Relocate Tests and Update Imports + +**Title:** `[Sprint 01] refactor: Move tests to repository root and update imports` + +**Estimated Time:** 1-2 hours + +**Labels:** +- `Type/Refactor` +- `Priority/High` +- `Component/Tests` +- `Size/M` + +**Dependencies:** Issue #1, Issue #2 + +**Description:** + +Move test suite from `src/gitea_mcp_remote/tests/` to repository root `tests/` directory and update all test imports to use new package name. + +**Tasks:** +- [ ] Move `src/gitea_mcp_remote/tests/` to `tests/` +- [ ] Update imports in `tests/conftest.py` +- [ ] Update imports in `tests/test_config.py` +- [ ] Update imports in `tests/test_middleware.py` +- [ ] Update imports in `tests/test_filtering.py` +- [ ] Update `pytest.ini` to use `testpaths = tests` +- [ ] Run pytest and verify all tests pass + +**Acceptance Criteria:** +- Tests located at repository root: `tests/` +- No tests in `src/gitea_mcp_remote/tests/` +- All test imports use `gitea_mcp_remote` package name +- All existing tests pass: `pytest tests/ -v` +- pytest.ini references `testpaths = tests` + +**Implementation Reference:** +See `docs/sprint-proposals/sprint-01-implementation-guide.md` - Phase 3, Issues #5-6 + +--- + +## Issue #4: Replace pyproject.toml with Marketplace Dependency + +**Title:** `[Sprint 01] build: Add marketplace dependency and update project configuration` + +**Estimated Time:** 1 hour + +**Labels:** +- `Type/Build` +- `Priority/Critical` +- `Component/Dependencies` +- `Size/S` + +**Dependencies:** Issue #1 + +**Description:** + +Replace pyproject.toml with new configuration including the marketplace Git dependency for gitea-mcp-server. + +**Tasks:** +- [ ] Update `pyproject.toml`: + - Add marketplace Git dependency + - Update package name to `gitea-mcp-remote` + - Change entry point to `gitea-mcp-remote` + - Update version to 1.1.0 + - Update test paths to `testpaths = ["tests"]` +- [ ] Test installation: `pip install -e .` +- [ ] Verify marketplace dependency installs +- [ ] Verify entry point exists: `which gitea-mcp-remote` + +**Acceptance Criteria:** +- pyproject.toml includes marketplace Git dependency +- Entry point is `gitea-mcp-remote` (not `gitea-http-wrapper`) +- Can run: `pip install -e .` successfully +- Marketplace dependency installs from Git repository +- Command `gitea-mcp-remote` is available + +**Implementation Reference:** +See `docs/sprint-proposals/sprint-01-implementation-guide.md` - Phase 5, Issue #9 + +--- + +## Issue #5: Implement MCP HTTP Server + +**Title:** `[Sprint 01] feat: Implement MCP Streamable HTTP protocol server` + +**Estimated Time:** 4-6 hours + +**Labels:** +- `Type/Feature` +- `Priority/Critical` +- `Component/Core` +- `Size/L` → **BREAKDOWN REQUIRED** + +**Dependencies:** Issue #1, Issue #2, Issue #4 + +**Description:** + +**NOTE:** This is a Large (L) task that should be broken down into Medium (M) subtasks: + +### Subtask 5.1: Remove Old Server and Create MCP Base Server (2-3 hours) +- Delete `src/gitea_mcp_remote/server.py` +- Create `src/gitea_mcp_remote/server_http.py` with: + - Imports from marketplace `mcp_server` + - GiteaMCPServer class with GiteaClient initialization + - Startup/shutdown handlers + - Basic route structure + +### Subtask 5.2: Implement MCP Protocol Endpoints (2-3 hours) +- Add HEAD /mcp endpoint (protocol version) +- Add POST /mcp endpoint (JSON-RPC 2.0 handler) +- Implement MCP methods: + - `initialize` + - `tools/list` + - `tools/call` +- Add error handling for JSON-RPC + +**Combined Tasks:** +- [ ] Delete old `server.py` +- [ ] Create new `server_http.py` +- [ ] Import from marketplace: `from mcp_server import ...` +- [ ] Implement GiteaMCPServer class +- [ ] Implement HEAD /mcp (protocol version) +- [ ] Implement POST /mcp (JSON-RPC handler) +- [ ] Implement initialize method +- [ ] Implement tools/list method with filtering +- [ ] Implement tools/call method with dispatcher +- [ ] Keep health endpoints: /health, /healthz, /ping +- [ ] Add JSON-RPC error handling +- [ ] Verify imports: `from gitea_mcp_remote.server_http import GiteaMCPServer` + +**Acceptance Criteria:** +- Old `server.py` deleted +- New `server_http.py` exists +- Imports from marketplace `mcp_server` package +- MCP endpoints exist: `POST /mcp`, `HEAD /mcp` +- Health endpoints exist: `/health`, `/healthz`, `/ping` +- No subprocess spawning code +- Can import server module successfully +- JSON-RPC 2.0 request/response handling works + +**Implementation Reference:** +See `docs/sprint-proposals/sprint-01-implementation-guide.md` - Phase 4, Issues #7-8 + +**Recommendation:** Create two separate issues (5.1 and 5.2) to keep within M size. + +--- + +## Issue #6: Create Docker Infrastructure + +**Title:** `[Sprint 01] build: Create Docker multi-service infrastructure with Caddy` + +**Estimated Time:** 3-4 hours + +**Labels:** +- `Type/Build` +- `Priority/High` +- `Component/Docker` +- `Size/M` + +**Dependencies:** Issue #4, Issue #5 + +**Description:** + +Create Docker infrastructure with two-service architecture: Python app and Caddy reverse proxy. + +**Tasks:** +- [ ] Create `docker/` directory +- [ ] Create `docker/docker-compose.yml` with two services (app + caddy) +- [ ] Create `docker/Dockerfile`: + - Install git package + - Expose port 8080 + - Use curl for healthcheck + - Install marketplace dependency +- [ ] Create `docker/Caddyfile`: + - HTTPS termination + - Proxy to app:8080 + - MCP endpoint routing +- [ ] Validate Dockerfile builds +- [ ] Validate docker-compose configuration +- [ ] Validate Caddyfile syntax + +**Acceptance Criteria:** +- `docker/docker-compose.yml` has two services (app + caddy) +- `docker/Dockerfile` installs git and uses port 8080 +- `docker/Caddyfile` exists and proxies to app:8080 +- Can build: `docker build -f docker/Dockerfile -t test .` +- Can validate: `docker-compose -f docker/docker-compose.yml config` +- Caddy config validates successfully + +**Implementation Reference:** +See `docs/sprint-proposals/sprint-01-implementation-guide.md` - Phase 6, Issues #11-14 + +--- + +## Issue #7: Create Utility Scripts and Server Tests + +**Title:** `[Sprint 01] test: Create startup scripts and MCP server tests` + +**Estimated Time:** 2-3 hours + +**Labels:** +- `Type/Test` +- `Priority/Medium` +- `Component/Tests` +- `Size/M` + +**Dependencies:** Issue #5 + +**Description:** + +Create production utility scripts and comprehensive tests for the new MCP HTTP server. + +**Tasks:** +- [ ] Create `scripts/start.sh` (production startup) +- [ ] Create `scripts/healthcheck.sh` (Docker healthcheck) +- [ ] Make scripts executable +- [ ] Create `tests/test_server_http.py`: + - Health endpoint tests + - MCP HEAD endpoint test (protocol version) + - MCP POST endpoint tests (initialize, tools/list, tools/call) + - JSON-RPC error handling tests + - Tool filtering integration test +- [ ] Run new tests and verify they pass + +**Acceptance Criteria:** +- `scripts/start.sh` validates environment and starts server +- `scripts/healthcheck.sh` checks health endpoint +- Both scripts are executable +- `tests/test_server_http.py` exists with comprehensive coverage +- All new tests pass: `pytest tests/test_server_http.py -v` +- All existing tests still pass: `pytest tests/ -v` + +**Implementation Reference:** +See `docs/sprint-proposals/sprint-01-implementation-guide.md` - Phase 7-8, Issues #15-16 + +--- + +## Issue #8: Create Documentation + +**Title:** `[Sprint 01] docs: Create CLAUDE.md and update deployment documentation` + +**Estimated Time:** 2-3 hours + +**Labels:** +- `Type/Documentation` +- `Priority/Medium` +- `Component/Documentation` +- `Size/M` + +**Dependencies:** All previous issues + +**Description:** + +Create comprehensive project documentation for Claude Code and update deployment guide with new MCP protocol and Docker structure. + +**Tasks:** +- [ ] Create `CLAUDE.md`: + - Project overview + - Architecture diagram + - Development workflows + - MCP protocol notes + - Configuration reference + - Deployment instructions + - Troubleshooting guide +- [ ] Update `DEPLOYMENT.md`: + - Replace custom REST API refs with MCP protocol + - Update Docker structure (docker/ directory, two services) + - Update marketplace dependency installation + - Update Claude Desktop config example + - Add MCP protocol debugging section +- [ ] Verify documentation accuracy + +**Acceptance Criteria:** +- `CLAUDE.md` exists with complete project guidance +- `DEPLOYMENT.md` updated with MCP protocol references +- No references to old `/tools/list` or `/tools/call` endpoints +- Docker paths reference `docker/docker-compose.yml` +- Claude Desktop config shows `/mcp` endpoint +- All code examples are accurate + +**Implementation Reference:** +See `docs/sprint-proposals/sprint-01-implementation-guide.md` - Phase 9, Issues #17-18 + +--- + +## Issue #9: Final Validation and Integration Testing + +**Title:** `[Sprint 01] test: Final validation and integration testing` + +**Estimated Time:** 2-3 hours + +**Labels:** +- `Type/Test` +- `Priority/Critical` +- `Component/Integration` +- `Size/M` + +**Dependencies:** All previous issues + +**Description:** + +Run complete validation checklist to ensure all architectural corrections are in place and working correctly. + +**Tasks:** +- [ ] Verify package structure (no gitea_http_wrapper) +- [ ] Verify no old imports remain +- [ ] Verify config has all new fields +- [ ] Verify server has MCP endpoints +- [ ] Run: `pip install -e .` successfully +- [ ] Run: `pytest tests/ -v` - all tests pass +- [ ] Build Docker image successfully +- [ ] Validate docker-compose configuration +- [ ] Validate Caddyfile syntax +- [ ] Test MCP endpoint responds to protocol version request +- [ ] Test MCP endpoint handles JSON-RPC messages +- [ ] Document any issues found +- [ ] Create follow-up issues if needed + +**Acceptance Criteria:** +All 16 validation items pass: + +**Package Structure:** +- [ ] `src/gitea_mcp_remote/` exists (not `gitea_http_wrapper`) +- [ ] No imports reference `gitea_http_wrapper` +- [ ] `tests/` is at repository root (not in `src/`) + +**Configuration:** +- [ ] `config/settings.py` has `mcp_auth_mode` field +- [ ] `config/settings.py` has `gitea_repo: str | None` +- [ ] HTTP defaults are `0.0.0.0:8080` + +**Server Implementation:** +- [ ] `server_http.py` imports from `mcp_server` package +- [ ] MCP endpoints exist: `POST /mcp`, `HEAD /mcp` +- [ ] Health endpoints exist: `/health`, `/healthz`, `/ping` +- [ ] No subprocess spawning code + +**Dependencies:** +- [ ] `pyproject.toml` has marketplace Git dependency +- [ ] Entry point is `gitea-mcp-remote` (not `gitea-http-wrapper`) +- [ ] Can run: `pip install -e .` successfully + +**Docker:** +- [ ] `docker/docker-compose.yml` has two services (app + caddy) +- [ ] `docker/Dockerfile` installs git and uses port 8080 +- [ ] `docker/Caddyfile` exists and proxies to app:8080 + +**Tests:** +- [ ] All tests pass: `pytest tests/` + +**Implementation Reference:** +See `docs/sprint-proposals/sprint-01-implementation-guide.md` - Final Validation section + +--- + +## Issue Dependencies Graph + +``` +Issue #1 (Rename + Config) + ├─→ Issue #2 (Middleware + Filtering) + │ └─→ Issue #3 (Tests) + │ + ├─→ Issue #4 (pyproject.toml) + │ ├─→ Issue #5 (MCP Server) + │ │ ├─→ Issue #6 (Docker) + │ │ └─→ Issue #7 (Scripts + Tests) + │ │ + │ └─→ Issue #3 (Tests) + │ + └─→ All above + └─→ Issue #8 (Documentation) + └─→ Issue #9 (Final Validation) +``` + +## Execution Order + +1. Issue #1 - Rename + Config (Foundation) +2. Issue #2 - Middleware + Filtering (Supporting modules) +3. Issue #4 - pyproject.toml (Dependencies before server) +4. Issue #3 - Tests (Can run in parallel with #4) +5. Issue #5 - MCP Server (Core implementation) **Consider splitting into 5.1 and 5.2** +6. Issue #6 - Docker (Deployment infrastructure) +7. Issue #7 - Scripts + Tests (Validation tools) +8. Issue #8 - Documentation (After implementation complete) +9. Issue #9 - Final Validation (Sprint completion) + +## Size Distribution + +- **Small (1-2h):** Issues #2, #4 (2 issues) +- **Medium (2-4h):** Issues #1, #3, #6, #7, #8, #9 (6 issues) +- **Large (4-6h):** Issue #5 (1 issue - SHOULD BE SPLIT) + +**Recommendation:** Split Issue #5 into two Medium issues for better tracking and clearer completion criteria. + +## Total Estimated Time + +- Minimum: 19 hours +- Maximum: 28 hours +- Average: 23.5 hours +- **Sprint Duration:** 1 week (5 working days)