forked from cardosofelipe/fast-next-template
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>
156 lines
4.2 KiB
Python
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")
|