| """ |
| Jira MCP Server |
| ================ |
| Exposes all Jira Sprint API endpoints as MCP tools. |
| The agent calls these tools natively β no JSON action routing needed. |
| |
| Run this first: |
| python jira_mcp_server.py |
| |
| Then in another terminal run the agent: |
| python jira_agent_mcp.py |
| """ |
|
|
| import os |
| import requests |
| from mcp.server.fastmcp import FastMCP |
| from dotenv import load_dotenv |
|
|
| load_dotenv() |
|
|
| JIRA_API_URL = os.environ.get("JIRA_API_URL", "http://0.0.0.0:8001") |
|
|
| mcp = FastMCP("Jira Sprint Manager") |
|
|
|
|
| def call(method: str, path: str, payload: dict = None) -> dict: |
| url = f"{JIRA_API_URL}{path}" |
| try: |
| if method == "GET": |
| r = requests.get(url, headers={"accept": "application/json"}, timeout=10) |
| else: |
| r = requests.request( |
| method, url, |
| headers={"Content-Type": "application/json", "accept": "application/json"}, |
| json=payload or {}, |
| timeout=10, |
| ) |
| r.raise_for_status() |
| return r.json() |
| except requests.RequestException as e: |
| return {"success": False, "error": str(e)} |
|
|
|
|
| |
|
|
| @mcp.tool() |
| def health_check() -> dict: |
| """Check if the Jira API server is running and healthy.""" |
| return call("GET", "/") |
|
|
|
|
| @mcp.tool() |
| def get_backlog() -> dict: |
| """ |
| Get all backlog issues β stories and tasks not assigned to any sprint. |
| Use this when the user asks about unassigned work, backlog items, or pending stories. |
| """ |
| return call("GET", "/api/backlog") |
|
|
|
|
| @mcp.tool() |
| def create_story(name: str, description: str) -> dict: |
| """ |
| Create a new story/task/issue in Jira. |
| |
| Args: |
| name: Short title of the story (e.g. 'Implement login API') |
| description: Detailed description of what needs to be done |
| """ |
| return call("POST", "/api/story", {"name": name, "description": description}) |
|
|
|
|
| @mcp.tool() |
| def get_active_sprint() -> dict: |
| """ |
| Get the current active sprint with all its issues and their statuses. |
| Use this when the user asks about sprint progress, current work, or what is in the sprint. |
| """ |
| return call("GET", "/api/sprint/active") |
|
|
|
|
| @mcp.tool() |
| def add_issues_to_sprint(sprint_id: int, issue_keys: list[str]) -> dict: |
| """ |
| Add one or more backlog issues into an active sprint. |
| |
| Args: |
| sprint_id: The numeric ID of the sprint (e.g. 9) |
| issue_keys: List of Jira issue keys to add (e.g. ['SCRUM-17', 'SCRUM-19']) |
| """ |
| return call("POST", f"/api/sprint/{sprint_id}/add-issues", {"issue_keys": issue_keys}) |
|
|
|
|
| @mcp.tool() |
| def transition_issue(issue_key: str, status_code: str, comment: str = "") -> dict: |
| """ |
| Update the status of a Jira issue. Optionally add a comment explaining the change. |
| |
| Status codes: |
| "1" = To Do |
| "2" = In Progress |
| "3" = Testing |
| "4" = Done |
| |
| Args: |
| issue_key: The Jira issue key (e.g. 'SCRUM-17') |
| status_code: One of "1", "2", "3", "4" |
| comment: Optional comment to add to the issue (e.g. 'All tests passed') |
| """ |
| payload = {"status": status_code} |
| if comment: |
| payload["comment"] = comment |
| return call("POST", f"/api/issue/{issue_key}/transition", payload) |
|
|
|
|
| @mcp.tool() |
| def sprint_rollover(new_sprint_name: str, add_backlog_to_new_sprint: bool = False) -> dict: |
| """ |
| Close the current active sprint and start a new one. |
| Unfinished issues are automatically carried forward to the new sprint. |
| |
| Args: |
| new_sprint_name: Name for the new sprint (e.g. 'Sprint 2') |
| add_backlog_to_new_sprint: If True, also pull all backlog items into the new sprint |
| """ |
| return call("POST", "/api/sprint/rollover", { |
| "new_sprint_name": new_sprint_name, |
| "add_backlog_to_new_sprint": add_backlog_to_new_sprint, |
| }) |
|
|
|
|
| |
| if __name__ == "__main__": |
| print("=" * 50) |
| print(" Jira MCP Server starting...") |
| print(f" Jira API : {JIRA_API_URL}") |
| print(" Tools : health_check, get_backlog, create_story,") |
| print(" get_active_sprint, add_issues_to_sprint,") |
| print(" transition_issue, sprint_rollover") |
| print("=" * 50 + "\n") |
| mcp.run(transport="stdio") |