mirror of
https://github.com/shadoll/redmine-mcp.git
synced 2026-08-28 11:33:28 +00:00
107 lines
3.3 KiB
Python
107 lines
3.3 KiB
Python
import json
|
|
from ..mcp import mcp
|
|
from ..client import _get, _put
|
|
|
|
|
|
@mcp.tool()
|
|
def get_project_members(project_id: str) -> str:
|
|
"""
|
|
Get members of a Redmine project (useful for finding user IDs for assignment).
|
|
|
|
Args:
|
|
project_id: Project ID or identifier (slug)
|
|
"""
|
|
data = _get(f"/projects/{project_id}/memberships.json")
|
|
members = [
|
|
{
|
|
"id": m.get("user", {}).get("id"),
|
|
"name": m.get("user", {}).get("name"),
|
|
"roles": [r["name"] for r in m.get("roles", [])],
|
|
}
|
|
for m in data.get("memberships", [])
|
|
if "user" in m
|
|
]
|
|
return json.dumps(members, ensure_ascii=False, indent=2)
|
|
|
|
|
|
@mcp.tool()
|
|
def assign_issue(issue_id: int, assigned_to_id: int, comment: str = "") -> str:
|
|
"""
|
|
Assign a Redmine issue to a user.
|
|
|
|
Args:
|
|
issue_id: Redmine issue ID
|
|
assigned_to_id: User ID to assign to (use get_project_members to find IDs)
|
|
comment: Optional comment to add with the assignment
|
|
"""
|
|
body: dict = {"issue": {"assigned_to_id": assigned_to_id}}
|
|
if comment:
|
|
body["issue"]["notes"] = comment
|
|
_put(f"/issues/{issue_id}.json", body)
|
|
return f"Issue #{issue_id} assigned to user_id={assigned_to_id}."
|
|
|
|
|
|
@mcp.tool()
|
|
def unassign_issue(issue_id: int, comment: str = "") -> str:
|
|
"""
|
|
Remove the assignee from a Redmine issue (set to unassigned).
|
|
|
|
Args:
|
|
issue_id: Redmine issue ID
|
|
comment: Optional comment to add with the change
|
|
"""
|
|
body: dict = {"issue": {"assigned_to_id": ""}}
|
|
if comment:
|
|
body["issue"]["notes"] = comment
|
|
_put(f"/issues/{issue_id}.json", body)
|
|
return f"Issue #{issue_id} unassigned."
|
|
|
|
|
|
@mcp.tool()
|
|
def get_issue_participants(issue_id: int) -> str:
|
|
"""
|
|
Get all users who have participated in a Redmine issue:
|
|
author, current assignee, watchers, commenters, and editors.
|
|
|
|
Useful for understanding who works with a task and who wrote comments.
|
|
Each participant includes their roles (author, assignee, watcher,
|
|
commenter, editor) based on actual actions recorded in the issue history.
|
|
|
|
Args:
|
|
issue_id: Redmine issue ID
|
|
"""
|
|
data = _get(f"/issues/{issue_id}.json", params={"include": "journals,watchers"})
|
|
issue = data["issue"]
|
|
|
|
participants: dict[int, dict] = {}
|
|
|
|
def _add(uid: int, uname: str, role: str) -> None:
|
|
if uid not in participants:
|
|
participants[uid] = {"id": uid, "name": uname, "roles": []}
|
|
if role not in participants[uid]["roles"]:
|
|
participants[uid]["roles"].append(role)
|
|
|
|
author = issue.get("author", {})
|
|
if author.get("id"):
|
|
_add(author["id"], author["name"], "author")
|
|
|
|
assignee = issue.get("assigned_to", {})
|
|
if assignee.get("id"):
|
|
_add(assignee["id"], assignee["name"], "assignee")
|
|
|
|
for w in issue.get("watchers", []):
|
|
if w.get("id"):
|
|
_add(w["id"], w["name"], "watcher")
|
|
|
|
for j in issue.get("journals", []):
|
|
user = j.get("user", {})
|
|
uid = user.get("id")
|
|
uname = user.get("name", "?")
|
|
if uid:
|
|
if j.get("notes"):
|
|
_add(uid, uname, "commenter")
|
|
if j.get("details"):
|
|
_add(uid, uname, "editor")
|
|
|
|
return json.dumps(list(participants.values()), ensure_ascii=False, indent=2)
|