feat(gitea-mcp): add create_pull_request tool
Add missing create_pull_request tool to Gitea MCP server. This completes the PR lifecycle - previously only had list/get/review/comment tools. - Add create_pull_request to GiteaClient - Add async wrapper to PullRequestTools with branch permissions - Register tool in server.py with proper schema - Parameters: title, body, head, base, labels (optional) - Branch-aware security: only allowed on development/feature branches Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
#### Gitea MCP Server - create_pull_request Tool
|
||||||
|
- **`create_pull_request`**: Create new pull requests via MCP
|
||||||
|
- Parameters: title, body, head (source branch), base (target branch), labels
|
||||||
|
- Branch-aware security: only allowed on development/feature branches
|
||||||
|
- Completes the PR lifecycle (was previously missing - only had list/get/review/comment)
|
||||||
|
|
||||||
#### cmdb-assistant v1.1.0 - Data Quality Validation
|
#### cmdb-assistant v1.1.0 - Data Quality Validation
|
||||||
- **SessionStart Hook**: Tests NetBox API connectivity at session start
|
- **SessionStart Hook**: Tests NetBox API connectivity at session start
|
||||||
- Warns if VMs exist without site assignment
|
- Warns if VMs exist without site assignment
|
||||||
|
|||||||
@@ -787,3 +787,42 @@ class GiteaClient:
|
|||||||
response = self.session.post(url, json=data)
|
response = self.session.post(url, json=data)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
return response.json()
|
return response.json()
|
||||||
|
|
||||||
|
def create_pull_request(
|
||||||
|
self,
|
||||||
|
title: str,
|
||||||
|
body: str,
|
||||||
|
head: str,
|
||||||
|
base: str,
|
||||||
|
labels: Optional[List[str]] = None,
|
||||||
|
repo: Optional[str] = None
|
||||||
|
) -> Dict:
|
||||||
|
"""
|
||||||
|
Create a new pull request.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
title: PR title
|
||||||
|
body: PR description/body
|
||||||
|
head: Source branch name (the branch with changes)
|
||||||
|
base: Target branch name (the branch to merge into)
|
||||||
|
labels: Optional list of label names
|
||||||
|
repo: Repository in 'owner/repo' format
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Created pull request dictionary
|
||||||
|
"""
|
||||||
|
owner, target_repo = self._parse_repo(repo)
|
||||||
|
url = f"{self.base_url}/repos/{owner}/{target_repo}/pulls"
|
||||||
|
data = {
|
||||||
|
'title': title,
|
||||||
|
'body': body,
|
||||||
|
'head': head,
|
||||||
|
'base': base
|
||||||
|
}
|
||||||
|
if labels:
|
||||||
|
label_ids = self._resolve_label_ids(labels, owner, target_repo)
|
||||||
|
data['labels'] = label_ids
|
||||||
|
logger.info(f"Creating PR '{title}' in {owner}/{target_repo}: {head} -> {base}")
|
||||||
|
response = self.session.post(url, json=data)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()
|
||||||
|
|||||||
@@ -844,6 +844,41 @@ class GiteaMCPServer:
|
|||||||
},
|
},
|
||||||
"required": ["pr_number", "body"]
|
"required": ["pr_number", "body"]
|
||||||
}
|
}
|
||||||
|
),
|
||||||
|
Tool(
|
||||||
|
name="create_pull_request",
|
||||||
|
description="Create a new pull request",
|
||||||
|
inputSchema={
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"title": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "PR title"
|
||||||
|
},
|
||||||
|
"body": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "PR description/body"
|
||||||
|
},
|
||||||
|
"head": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Source branch name (the branch with changes)"
|
||||||
|
},
|
||||||
|
"base": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Target branch name (the branch to merge into)"
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {"type": "string"},
|
||||||
|
"description": "Optional list of label names"
|
||||||
|
},
|
||||||
|
"repo": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Repository name (owner/repo format)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["title", "body", "head", "base"]
|
||||||
|
}
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -959,6 +994,8 @@ class GiteaMCPServer:
|
|||||||
result = await self.pr_tools.create_pr_review(**arguments)
|
result = await self.pr_tools.create_pr_review(**arguments)
|
||||||
elif name == "add_pr_comment":
|
elif name == "add_pr_comment":
|
||||||
result = await self.pr_tools.add_pr_comment(**arguments)
|
result = await self.pr_tools.add_pr_comment(**arguments)
|
||||||
|
elif name == "create_pull_request":
|
||||||
|
result = await self.pr_tools.create_pull_request(**arguments)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unknown tool: {name}")
|
raise ValueError(f"Unknown tool: {name}")
|
||||||
|
|
||||||
|
|||||||
@@ -272,3 +272,42 @@ class PullRequestTools:
|
|||||||
None,
|
None,
|
||||||
lambda: self.gitea.add_pr_comment(pr_number, body, repo)
|
lambda: self.gitea.add_pr_comment(pr_number, body, repo)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def create_pull_request(
|
||||||
|
self,
|
||||||
|
title: str,
|
||||||
|
body: str,
|
||||||
|
head: str,
|
||||||
|
base: str,
|
||||||
|
labels: Optional[List[str]] = None,
|
||||||
|
repo: Optional[str] = None
|
||||||
|
) -> Dict:
|
||||||
|
"""
|
||||||
|
Create a new pull request (async wrapper with branch check).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
title: PR title
|
||||||
|
body: PR description/body
|
||||||
|
head: Source branch name (the branch with changes)
|
||||||
|
base: Target branch name (the branch to merge into)
|
||||||
|
labels: Optional list of label names
|
||||||
|
repo: Override configured repo (for PMO multi-repo)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Created pull request dictionary
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
PermissionError: If operation not allowed on current branch
|
||||||
|
"""
|
||||||
|
if not self._check_branch_permissions('create_pull_request'):
|
||||||
|
branch = self._get_current_branch()
|
||||||
|
raise PermissionError(
|
||||||
|
f"Cannot create PR on branch '{branch}'. "
|
||||||
|
f"Switch to a development or feature branch to create PRs."
|
||||||
|
)
|
||||||
|
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
return await loop.run_in_executor(
|
||||||
|
None,
|
||||||
|
lambda: self.gitea.create_pull_request(title, body, head, base, labels, repo)
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user