From 1bfb98b7c0223ecc4a55734ffd731a2b0f391a9c Mon Sep 17 00:00:00 2001 From: sHa Date: Fri, 20 Mar 2026 14:13:30 +0200 Subject: [PATCH] Add create_issue tool and update README with usage examples --- README.md | 2 ++ app/client.py | 8 ++++++++ app/entities/issues.py | 37 ++++++++++++++++++++++++++++++++++++- 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a8e8696..fa9d9e6 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ Exposes Redmine REST API as tools for Claude Code via MCP (Model Context Protoco | Tool | Description | |------|-------------| +| `create_issue` | Create a new issue in a project | | `get_issue` | Get issue details by ID | | `list_issues` | Query issues with filters (project, status, assignee) | | `update_issue` | Update title, description, priority, due date | @@ -64,6 +65,7 @@ claude mcp list Once registered, just ask Claude naturally in any project: +> "Create a new issue in project api-service: Fix login timeout" > "Get issue #1234" > "Search for issues related to login bug" > "Change status of issue #1234 to In Progress" diff --git a/app/client.py b/app/client.py index 3046732..378132e 100644 --- a/app/client.py +++ b/app/client.py @@ -24,3 +24,11 @@ def _put(path: str, body: dict) -> dict: resp = client.put(url, headers=_headers(), content=json.dumps(body)) resp.raise_for_status() return resp.json() if resp.content else {} + + +def _post(path: str, body: dict) -> dict: + url = f"{REDMINE_URL}{path}" + with httpx.Client(timeout=15) as client: + resp = client.post(url, headers=_headers(), content=json.dumps(body)) + resp.raise_for_status() + return resp.json() diff --git a/app/entities/issues.py b/app/entities/issues.py index 0d9fa60..d9c2de2 100644 --- a/app/entities/issues.py +++ b/app/entities/issues.py @@ -1,6 +1,41 @@ import json from ..mcp import mcp -from ..client import _get, _put +from ..client import _get, _put, _post + + +@mcp.tool() +def create_issue( + project_id: str, + subject: str, + description: str = "", + assigned_to_id: int = 0, + priority_id: int = 0, + due_date: str = "", +) -> str: + """ + Create a new Redmine issue. + + Args: + project_id: Project ID or slug + subject: Issue title + description: Issue description + assigned_to_id: User ID to assign (use get_project_members to find IDs) + priority_id: Priority ID (leave 0 for default) + due_date: Due date in YYYY-MM-DD format + """ + payload: dict = {"project_id": project_id, "subject": subject} + if description: + payload["description"] = description + if assigned_to_id: + payload["assigned_to_id"] = assigned_to_id + if priority_id: + payload["priority_id"] = priority_id + if due_date: + payload["due_date"] = due_date + + data = _post("/issues.json", {"issue": payload}) + issue = data["issue"] + return json.dumps({"id": issue["id"], "subject": issue["subject"]}, ensure_ascii=False, indent=2) @mcp.tool()