feat(mcp): implement Git Operations MCP server with Gitea provider
Implements the Git Operations MCP server (Issue #58) providing: Core features: - GitPython wrapper for local repository operations (clone, commit, push, pull, diff, log) - Branch management (create, delete, list, checkout) - Workspace isolation per project with file-based locking - Gitea provider for remote PR operations MCP Tools (17 registered): - clone_repository, git_status, create_branch, list_branches - checkout, commit, push, pull, diff, log - create_pull_request, get_pull_request, list_pull_requests - merge_pull_request, get_workspace, lock_workspace, unlock_workspace Technical details: - FastMCP + FastAPI with JSON-RPC 2.0 protocol - pydantic-settings for configuration (env prefix: GIT_OPS_) - Comprehensive error hierarchy with structured codes - 131 tests passing with 67% coverage - Async operations via ThreadPoolExecutor Closes: #105, #106, #107, #108, #109 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
155
mcp-servers/git-ops/config.py
Normal file
155
mcp-servers/git-ops/config.py
Normal file
@@ -0,0 +1,155 @@
|
||||
"""
|
||||
Configuration for Git Operations MCP Server.
|
||||
|
||||
Uses pydantic-settings for environment variable loading.
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from pydantic import Field
|
||||
from pydantic_settings import BaseSettings
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
"""Application settings loaded from environment."""
|
||||
|
||||
# Server settings
|
||||
host: str = Field(default="0.0.0.0", description="Server host")
|
||||
port: int = Field(default=8003, description="Server port")
|
||||
debug: bool = Field(default=False, description="Debug mode")
|
||||
|
||||
# Workspace settings
|
||||
workspace_base_path: Path = Field(
|
||||
default=Path("/var/syndarix/workspaces"),
|
||||
description="Base path for git workspaces",
|
||||
)
|
||||
workspace_max_size_gb: float = Field(
|
||||
default=10.0,
|
||||
description="Maximum size per workspace in GB",
|
||||
)
|
||||
workspace_stale_days: int = Field(
|
||||
default=7,
|
||||
description="Days after which unused workspace is considered stale",
|
||||
)
|
||||
workspace_lock_timeout: int = Field(
|
||||
default=300,
|
||||
description="Workspace lock timeout in seconds",
|
||||
)
|
||||
|
||||
# Git settings
|
||||
git_timeout: int = Field(
|
||||
default=120,
|
||||
description="Default timeout for git operations in seconds",
|
||||
)
|
||||
git_clone_timeout: int = Field(
|
||||
default=600,
|
||||
description="Timeout for clone operations in seconds",
|
||||
)
|
||||
git_author_name: str = Field(
|
||||
default="Syndarix Agent",
|
||||
description="Default author name for commits",
|
||||
)
|
||||
git_author_email: str = Field(
|
||||
default="agent@syndarix.ai",
|
||||
description="Default author email for commits",
|
||||
)
|
||||
git_max_diff_lines: int = Field(
|
||||
default=10000,
|
||||
description="Maximum lines in diff output",
|
||||
)
|
||||
|
||||
# Redis settings (for distributed locking)
|
||||
redis_url: str = Field(
|
||||
default="redis://localhost:6379/0",
|
||||
description="Redis connection URL",
|
||||
)
|
||||
|
||||
# Provider settings
|
||||
gitea_base_url: str = Field(
|
||||
default="",
|
||||
description="Gitea API base URL (e.g., https://gitea.example.com)",
|
||||
)
|
||||
gitea_token: str = Field(
|
||||
default="",
|
||||
description="Gitea API token",
|
||||
)
|
||||
github_token: str = Field(
|
||||
default="",
|
||||
description="GitHub API token",
|
||||
)
|
||||
github_api_url: str = Field(
|
||||
default="https://api.github.com",
|
||||
description="GitHub API URL (for Enterprise)",
|
||||
)
|
||||
gitlab_token: str = Field(
|
||||
default="",
|
||||
description="GitLab API token",
|
||||
)
|
||||
gitlab_url: str = Field(
|
||||
default="https://gitlab.com",
|
||||
description="GitLab URL (for self-hosted)",
|
||||
)
|
||||
|
||||
# Rate limiting
|
||||
rate_limit_requests: int = Field(
|
||||
default=100,
|
||||
description="Max API requests per minute per provider",
|
||||
)
|
||||
rate_limit_window: int = Field(
|
||||
default=60,
|
||||
description="Rate limit window in seconds",
|
||||
)
|
||||
|
||||
# Retry settings
|
||||
retry_attempts: int = Field(
|
||||
default=3,
|
||||
description="Number of retry attempts for failed operations",
|
||||
)
|
||||
retry_delay: float = Field(
|
||||
default=1.0,
|
||||
description="Initial retry delay in seconds",
|
||||
)
|
||||
retry_max_delay: float = Field(
|
||||
default=30.0,
|
||||
description="Maximum retry delay in seconds",
|
||||
)
|
||||
|
||||
# Security settings
|
||||
allowed_hosts: list[str] = Field(
|
||||
default_factory=list,
|
||||
description="Allowed git host domains (empty = all)",
|
||||
)
|
||||
max_clone_size_mb: int = Field(
|
||||
default=500,
|
||||
description="Maximum repository size for clone in MB",
|
||||
)
|
||||
enable_force_push: bool = Field(
|
||||
default=False,
|
||||
description="Allow force push operations",
|
||||
)
|
||||
|
||||
model_config = {"env_prefix": "GIT_OPS_", "env_file": ".env", "extra": "ignore"}
|
||||
|
||||
|
||||
# Global settings instance (lazy initialization)
|
||||
_settings: Settings | None = None
|
||||
|
||||
|
||||
def get_settings() -> Settings:
|
||||
"""Get the global settings instance."""
|
||||
global _settings
|
||||
if _settings is None:
|
||||
_settings = Settings()
|
||||
return _settings
|
||||
|
||||
|
||||
def reset_settings() -> None:
|
||||
"""Reset the global settings (for testing)."""
|
||||
global _settings
|
||||
_settings = None
|
||||
|
||||
|
||||
def is_test_mode() -> bool:
|
||||
"""Check if running in test mode."""
|
||||
return os.getenv("IS_TEST", "").lower() in ("true", "1", "yes")
|
||||
Reference in New Issue
Block a user