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

9.9 KiB

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:

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:

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

# 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

# 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

# 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

# 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):

    def new_tool(self, ...) -> Dict:
        """Synchronous REST call."""
    
  2. Add async wrapper (tools/relevant_file.py):

    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

# 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

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
  • 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 v9.1.0 Original location: mcp-servers/gitea/ v1.3.0 Module renamed: mcp_servergitea_mcp for clarity and npm-style naming

All existing functionality preserved. Tests added for new merge tools.