feat: complete label management (name->id resolution, update/remove)

Resolves the long-standing problem that label tools passed names while Gitea's
API requires numeric label ids.

- gitea_client: add _resolve_label_ids() helper; create_issue and add_labels now
  resolve label names to ids (case-insensitive) and raise a clear "Unknown
  label(s)" error instead of a generic 500.
- New tools: remove_labels (by name) and update_label (located by current name).
- Register both write tools and document the name-based label contract.
- Tests: resolver mapping + unknown-label error, add_labels id translation,
  update_label and remove_labels handlers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-14 20:34:35 +02:00
parent f0db219ee8
commit c282ffe359
8 changed files with 306 additions and 11 deletions
+23
View File
@@ -24,7 +24,9 @@ from aegis_gitea_mcp.tools.write_tools import (
create_issue_tool,
create_label_tool,
create_pr_comment_tool,
remove_labels_tool,
update_issue_tool,
update_label_tool,
)
@@ -101,6 +103,17 @@ class StubGitea:
async def create_label(self, owner, repo, *, name, color, description="", exclusive=False):
return {"id": 5, "name": name, "color": color, "description": description, "url": "u"}
async def update_label(self, owner, repo, *, name, new_name=None, color=None, description=None):
return {
"id": 5,
"name": new_name or name,
"color": color or "#ffffff",
"description": description or "",
}
async def remove_labels(self, owner, repo, index, labels):
return []
class ErrorGitea(StubGitea):
"""Stub that raises backend errors for failure-mode coverage."""
@@ -178,6 +191,16 @@ async def test_extended_read_tools_failure_mode() -> None:
{"owner": "acme", "repo": "app", "name": "bug", "color": "#ff0000"},
"id",
),
(
update_label_tool,
{"owner": "acme", "repo": "app", "name": "bug", "new_name": "defect"},
"id",
),
(
remove_labels_tool,
{"owner": "acme", "repo": "app", "issue_number": 1, "labels": ["bug"]},
"removed",
),
],
)
async def test_write_tools_success(tool, args, expected_key):