**feat(git-ops): enhance MCP server with Git provider updates and SSRF protection**

- Added `mcp-git-ops` service to `docker-compose.dev.yml` with health checks and configurations.
- Integrated SSRF protection in repository URL validation for enhanced security.
- Expanded `pyproject.toml` mypy settings and adjusted code to meet stricter type checking.
- Improved workspace management and GitWrapper operations with error handling refinements.
- Updated input validation, branching, and repository operations to align with new error structure.
- Shut down thread pool executor gracefully during server cleanup.
This commit is contained in:
2026-01-07 09:17:00 +01:00
parent 1779239c07
commit 76d7de5334
11 changed files with 781 additions and 181 deletions

View File

@@ -257,7 +257,9 @@ class WorkspaceInfo:
"last_accessed": self.last_accessed.isoformat(),
"size_bytes": self.size_bytes,
"lock_holder": self.lock_holder,
"lock_expires": self.lock_expires.isoformat() if self.lock_expires else None,
"lock_expires": self.lock_expires.isoformat()
if self.lock_expires
else None,
}
@@ -270,7 +272,9 @@ class CloneRequest(BaseModel):
project_id: str = Field(..., description="Project ID for scoping")
agent_id: str = Field(..., description="Agent ID making the request")
repo_url: str = Field(..., description="Repository URL to clone")
branch: str | None = Field(default=None, description="Branch to checkout after clone")
branch: str | None = Field(
default=None, description="Branch to checkout after clone"
)
depth: int | None = Field(
default=None, ge=1, description="Shallow clone depth (None = full clone)"
)
@@ -407,7 +411,9 @@ class PushRequest(BaseModel):
project_id: str = Field(..., description="Project ID for scoping")
agent_id: str = Field(..., description="Agent ID making the request")
branch: str | None = Field(default=None, description="Branch to push (None = current)")
branch: str | None = Field(
default=None, description="Branch to push (None = current)"
)
remote: str = Field(default="origin", description="Remote name")
force: bool = Field(default=False, description="Force push")
set_upstream: bool = Field(default=True, description="Set upstream tracking")
@@ -428,7 +434,9 @@ class PullRequest(BaseModel):
project_id: str = Field(..., description="Project ID for scoping")
agent_id: str = Field(..., description="Agent ID making the request")
branch: str | None = Field(default=None, description="Branch to pull (None = current)")
branch: str | None = Field(
default=None, description="Branch to pull (None = current)"
)
remote: str = Field(default="origin", description="Remote name")
rebase: bool = Field(default=False, description="Rebase instead of merge")
@@ -451,7 +459,9 @@ class DiffRequest(BaseModel):
project_id: str = Field(..., description="Project ID for scoping")
agent_id: str = Field(..., description="Agent ID making the request")
base: str | None = Field(default=None, description="Base reference (None = working tree)")
base: str | None = Field(
default=None, description="Base reference (None = working tree)"
)
head: str | None = Field(default=None, description="Head reference (None = HEAD)")
files: list[str] | None = Field(default=None, description="Specific files to diff")
context_lines: int = Field(default=3, ge=0, description="Context lines")
@@ -463,9 +473,7 @@ class DiffResult(BaseModel):
project_id: str = Field(..., description="Project ID")
base: str | None = Field(default=None, description="Base reference")
head: str | None = Field(default=None, description="Head reference")
files: list[dict[str, Any]] = Field(
default_factory=list, description="File diffs"
)
files: list[dict[str, Any]] = Field(default_factory=list, description="File diffs")
total_additions: int = Field(default=0, description="Total lines added")
total_deletions: int = Field(default=0, description="Total lines removed")
files_changed: int = Field(default=0, description="Number of files changed")
@@ -549,9 +557,7 @@ class ListPRsResult(BaseModel):
"""Result of listing pull requests."""
success: bool = Field(..., description="Whether list succeeded")
pull_requests: list[dict[str, Any]] = Field(
default_factory=list, description="PRs"
)
pull_requests: list[dict[str, Any]] = Field(default_factory=list, description="PRs")
total_count: int = Field(default=0, description="Total matching PRs")
error: str | None = Field(default=None, description="Error message if failed")
@@ -578,7 +584,9 @@ class MergePRResult(BaseModel):
success: bool = Field(..., description="Whether merge succeeded")
merge_commit_sha: str | None = Field(default=None, description="Merge commit SHA")
branch_deleted: bool = Field(default=False, description="Whether branch was deleted")
branch_deleted: bool = Field(
default=False, description="Whether branch was deleted"
)
error: str | None = Field(default=None, description="Error message if failed")
@@ -626,7 +634,9 @@ class LockWorkspaceRequest(BaseModel):
project_id: str = Field(..., description="Project ID")
agent_id: str = Field(..., description="Agent ID requesting lock")
timeout: int = Field(default=300, ge=10, le=3600, description="Lock timeout seconds")
timeout: int = Field(
default=300, ge=10, le=3600, description="Lock timeout seconds"
)
class LockWorkspaceResult(BaseModel):
@@ -634,7 +644,9 @@ class LockWorkspaceResult(BaseModel):
success: bool = Field(..., description="Whether lock acquired")
lock_holder: str | None = Field(default=None, description="Current lock holder")
lock_expires: str | None = Field(default=None, description="Lock expiry ISO timestamp")
lock_expires: str | None = Field(
default=None, description="Lock expiry ISO timestamp"
)
error: str | None = Field(default=None, description="Error message if failed")