Files
syndarix/mcp-servers/git-ops/config.py
Felipe Cardoso 9dfa76aa41 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>
2026-01-06 20:48:20 +01:00

156 lines
4.2 KiB
Python

"""
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")