Files
gitea_mcp/CLAUDE.md
l3ocho c34e06aa2b feat: add merge tools, tests, and documentation
Added 3 new PR merge tools to complete v1.0.0:
- merge_pull_request: Merge PR with 5 strategies (merge, rebase, rebase-merge, squash, fast-forward-only)
- get_pr_merge_status: Check if PR is mergeable
- cancel_auto_merge: Cancel scheduled auto-merge

Changes:
- New merge methods in GiteaClient (gitea_client.py)
- New async wrappers in PullRequestTools with branch checks (tools/pull_requests.py)
- Tool definitions and dispatch routing in tool_registry.py
- Boolean type coercion for force_merge and delete_branch parameters
- Comprehensive test suite with 18 tests (test_pull_requests.py)
- Full documentation: README.md, CHANGELOG.md, CLAUDE.md

Features:
- 5 merge strategies with full Gitea API support
- Branch-aware security enforcement
- Type coercion handles MCP string serialization
- 100% test coverage for merge operations

Result:
- Total tools: 39 (7 PR operations + 3 merge = 10 PR tools)
- All tests passing (18 new merge tests + 60 existing tests)
- Ready for v1.0.0 release

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-02-08 16:38:35 -05:00

342 lines
9.9 KiB
Markdown

# CLAUDE.md - gitea-mcp
## Project Overview
**Name:** gitea-mcp
**Type:** Python package — MCP server for Gitea integration
**Version:** 1.0.0
**Repository:** https://gitea.hotserv.cloud/personal-projects/gitea-mcp
**Distribution:** Gitea PyPI at gitea.hotserv.cloud
## Project Description
Standalone Python package providing 39 MCP (Model Context Protocol) tools for comprehensive Gitea operations. Designed for use with Claude Code and other MCP-compatible systems.
**Core Features:**
- 39 tools across 7 functional categories
- Dual-mode operation (project and PMO/company modes)
- Branch-aware security enforcement
- Transport-agnostic architecture for multiple deployment options
- Full type coercion and error handling
## Architecture
### Directory Structure
```
gitea_mcp/
├── __init__.py # Public API: __version__, get_tool_definitions, create_tool_dispatcher
├── server.py # MCP stdio server entry point
├── config.py # Configuration loader (env files, auto-detection)
├── gitea_client.py # Synchronous Gitea REST API client
├── tool_registry.py # Tool definitions + dispatcher (transport-agnostic)
└── tools/
├── __init__.py
├── issues.py # Issue CRUD + aggregation (6 tools)
├── labels.py # Label management + suggestions (5 tools)
├── wiki.py # Wiki pages + lessons learned + RFC (7 tools)
├── milestones.py # Milestone CRUD (5 tools)
├── dependencies.py # Issue dependency tracking (4 tools)
└── pull_requests.py # PR operations + merge (10 tools)
tests/
├── __init__.py
├── test_config.py
├── test_gitea_client.py
├── test_issues.py
├── test_labels.py
├── test_pull_requests.py # NEW: Merge tool tests
docs/ (future)
```
### Tool Categories
| Category | Count | Tools |
|----------|-------|-------|
| Issue Management | 6 | list_issues, get_issue, create_issue, update_issue, add_comment, aggregate_issues |
| Label Management | 5 | list_labels, get_labels, suggest_labels, create_label, create_label_smart |
| Wiki & Lessons | 7 | list_wiki_pages, get_wiki_page, create_wiki_page, update_wiki_page, create_lesson, search_lessons, allocate_rfc_number |
| Milestones | 5 | list_milestones, get_milestone, create_milestone, update_milestone, delete_milestone |
| Dependencies | 4 | list_issue_dependencies, create_issue_dependency, remove_issue_dependency, get_execution_order |
| Pull Requests | 7 | list_pull_requests, get_pull_request, get_pr_diff, get_pr_comments, create_pr_review, add_pr_comment, create_pull_request |
| **PR Merge (NEW)** | **3** | **merge_pull_request, get_pr_merge_status, cancel_auto_merge** |
| **Total** | **39** | — |
## Key Design Patterns
### Transport-Agnostic Architecture
- Tool definitions and dispatcher logic live in `tool_registry.py`, not `server.py`
- Can be used by multiple transports: stdio (Claude Code), HTTP (gitea-mcp-remote), direct import
- Single source of truth for tool schemas and dispatch logic
### Type Coercion
MCP serialization sometimes converts integers to strings and arrays to JSON strings. The `_coerce_types()` function in `tool_registry.py` handles:
- **Integer fields**: `pr_number`, `milestone_id`, `issue_number`, etc. (string → int)
- **Array fields**: `labels`, `tags`, `issue_numbers` (JSON string → list)
- **Boolean fields**: `force_merge`, `delete_branch` (string → bool)
### Async/Executor Pattern
All async wrappers in `tools/*.py` use:
```python
loop = asyncio.get_event_loop()
return await loop.run_in_executor(None, lambda: self.gitea.sync_method(...))
```
This allows synchronous REST calls (`requests`) to be awaited in async contexts without blocking.
### Branch-Aware Security
Checked in async wrappers in `tools/pull_requests.py`:
```python
if not self._check_branch_permissions('merge_pull_request'):
raise PermissionError(f"Cannot merge on branch '{branch}'...")
```
**Rules:**
- Read operations: All branches allowed
- main/master/prod/*: Read-only
- staging/stage/*: Comments only
- dev/feature/fix/*: Full access
- Unknown: Read-only (fail-safe)
### Configuration Auto-Detection
In `config.py`:
- Reads from env vars: `GITEA_API_URL`, `GITEA_API_TOKEN`, `GITEA_REPO`, `GITEA_MODE`
- Detects mode: Project if `GITEA_REPO` set, PMO if not set (org user)
- Supports `.env` files for local development
- Fallback defaults for local Gitea instances
## Development Commands
### Setup
```bash
# Create virtual environment
python3 -m venv .venv
source .venv/bin/activate # Linux/Mac
# or: .venv\Scripts\activate (Windows)
# Install in development mode
pip install -e ".[dev]"
```
### Testing
```bash
# Run all tests
pytest tests/ -v
# Run specific test file
pytest tests/test_pull_requests.py -v
# Quick test run
pytest tests/ -q
# With coverage
pytest tests/ --cov=gitea_mcp --cov-report=html
```
### Building
```bash
# Install build tools
pip install build
# Build distribution
python -m build
# Output: dist/gitea-mcp-1.0.0.tar.gz and dist/gitea_mcp-1.0.0-py3-none-any.whl
```
### Publishing
```bash
# Install Twine
pip install twine
# Upload to Gitea PyPI
twine upload \
--repository-url https://gitea.hotserv.cloud/api/packages/personal-projects/pypi \
--username your-gitea-username \
--password your-gitea-token \
dist/*
```
## Common Tasks
### Adding a New Tool
1. **Add method to client** (`gitea_client.py`):
```python
def new_tool(self, ...) -> Dict:
"""Synchronous REST call."""
```
2. **Add async wrapper** (`tools/relevant_file.py`):
```python
async def new_tool(self, ...) -> Dict:
"""Async wrapper with branch checks if write operation."""
if not self._check_branch_permissions('new_tool'):
raise PermissionError(...)
loop = asyncio.get_event_loop()
return await loop.run_in_executor(None, lambda: self.gitea.new_tool(...))
```
3. **Register tool** (`tool_registry.py`):
- Add `Tool()` to `_get_all_tool_definitions()`
- Add dispatch case in `create_tool_dispatcher()`
- Update `_coerce_types()` if new types added
4. **Write tests** (`tests/test_something.py`):
- Mock the client
- Test the method directly
- Test type coercion
- Test error cases
5. **Update docs**:
- Add to README.md tools table
- Update CHANGELOG.md
- Update CLAUDE.md if architecture changes
### Running Locally
```bash
# Set environment variables
export GITEA_API_TOKEN="your-token"
export GITEA_REPO="owner/repo"
# Test CLI
python -m gitea_mcp.server < /dev/null
# Or use in a script
python3 << 'EOF'
from gitea_mcp.gitea_client import GiteaClient
from gitea_mcp.tool_registry import get_tool_definitions
client = GiteaClient()
tools = get_tool_definitions()
print(f"Available tools: {len(tools)}")
for tool in tools[:5]:
print(f" - {tool.name}")
EOF
```
### Testing Type Coercion
```python
from gitea_mcp.tool_registry import _coerce_types
result = _coerce_types({
'pr_number': '42',
'force_merge': 'true',
'delete_branch': 'false'
})
assert result['pr_number'] == 42
assert result['force_merge'] is True
assert result['delete_branch'] is False
```
## Git Workflow
### Branches
- **main**: Stable releases only
- **development**: Feature integration (default)
- **feat/\***: Feature branches from development
- **fix/\***: Bug fix branches from development
### Commit Message Format
```
type(scope): short description
Longer explanation if needed.
Fixes: #123 (if applicable)
```
Types: `feat`, `fix`, `test`, `docs`, `refactor`, `perf`, `chore`
Example:
```
feat(merge): add merge_pull_request tool
- Support 5 merge strategies: merge, rebase, squash, etc.
- Add get_pr_merge_status for mergeable check
- Add cancel_auto_merge for scheduled merges
- Include type coercion for force_merge and delete_branch
- Add comprehensive tests
Closes: #10
```
## Dependencies
### Core
- `requests>=2.31.0` — HTTP client for Gitea API
- `mcp>=0.10.0` — Model Context Protocol
### Development
- `pytest>=7.0.0` — Testing framework
- `pytest-asyncio` — Async test support
- `build` — Package building
- `twine` — PyPI publishing
- `black` — Code formatting
- `flake8` — Linting
- `mypy` — Type checking (optional)
## Performance Considerations
- All API calls use `requests` Session with connection pooling
- Async wrappers use executor pattern (non-blocking)
- PMO mode caches org membership to reduce API calls
- Lesson searches use wiki page content for tags (linear scan, acceptable for <100 pages)
## Security
- **Authentication**: Token-based (GitHub-compatible Gitea API)
- **Branch checks**: Enforced in async wrappers
- **Input validation**: `_parse_repo()` validates owner/repo format
- **Error handling**: Comprehensive logging, HTTP errors propagated with context
## Known Limitations
1. **Wiki operations**: Linear scan of pages for lesson search (fine for <100 pages)
2. **Merge status**: Returns mergeable flag if available, not computed in real-time
3. **PMO mode**: Requires user to have org-level access (not repo-specific tokens)
4. **Execution order**: `get_execution_order()` uses simple topological sort (no cycle detection)
## Future Enhancements
- [ ] Webhook support for event-driven workflows
- [ ] Caching layer for frequently accessed data
- [ ] GraphQL support (if Gitea adds it)
- [ ] Batch operations for bulk issue/PR updates
- [ ] Custom field support for Gitea enterprise
## Related Projects
- **leo-claude-mktplace**: Marketplace of Claude integrations (includes gitea-mcp)
- **gitea-mcp-remote**: HTTP transport wrapper (uses gitea-mcp as library)
- **Gitea**: https://gitea.io
## Origin
Extracted from [leo-claude-mktplace](https://gitea.hotserv.cloud/personal-projects/leo-claude-mktplace) v9.1.0
Original location: `mcp-servers/gitea/` v1.3.0
Module renamed: `mcp_server` → `gitea_mcp` for clarity and npm-style naming
All existing functionality preserved. Tests added for new merge tools.