Commit e6fb2c6a authored by MD. SHAHIDUL ISLAM's avatar MD. SHAHIDUL ISLAM

feat: Add natural language transition mapping for Jira issues and corresponding tests

parent 57b9ee41
...@@ -28,6 +28,43 @@ from typing import Optional, List ...@@ -28,6 +28,43 @@ from typing import Optional, List
# Initialize FastMCP Server # Initialize FastMCP Server
mcp = FastMCP("Dohatec Jira Mcp") mcp = FastMCP("Dohatec Jira Mcp")
# Natural Language to Jira Transition Name Mapping
# This allows users to use natural language when transitioning issues
TRANSITION_MAPPING = {
# Natural language → Actual Jira transition name
"in progress": "Start Progress",
"start progress": "Start Progress",
"in prog": "Start Progress",
"progress": "Start Progress",
"qa assigned": "Ready for QA",
"ready for qa": "Ready for QA",
"qa ready": "Ready for QA",
"for qa": "Ready for QA",
"qa": "Ready for QA",
"qa completed": "QA Completed",
"qa done": "QA Completed",
"qa approved": "QA Completed",
"done": "Done",
"complete": "Done",
"completed": "Done",
"closed": "Close Issue",
"close issue": "Close Issue",
"stopped": "Stop Progress",
"stop progress": "Stop Progress",
"stopped progress": "Stop Progress",
}
def normalize_transition_name(user_input: str) -> str:
"""Convert natural language transition names to actual Jira transition names.
Falls back to original input if no mapping found.
"""
normalized = user_input.lower().strip()
return TRANSITION_MAPPING.get(normalized, user_input)
# Initialize Jira Client # Initialize Jira Client
JIRA_SERVER = os.environ.get("JIRA_SERVER") JIRA_SERVER = os.environ.get("JIRA_SERVER")
JIRA_USERNAME = os.environ.get("JIRA_USERNAME") JIRA_USERNAME = os.environ.get("JIRA_USERNAME")
...@@ -354,11 +391,15 @@ def assign_issue(issue_key: str, assignee_username: str) -> str: ...@@ -354,11 +391,15 @@ def assign_issue(issue_key: str, assignee_username: str) -> str:
@mcp.tool() @mcp.tool()
def transition_issue(issue_key: str, transition_name: str) -> str: def transition_issue(issue_key: str, transition_name: str) -> str:
"""Transitions an issue to a new status by name (e.g., 'Ready for QA'). """Transitions an issue to a new status by name. Accepts natural language (e.g., 'In Progress', 'QA Assigned')
and converts it to actual Jira transition names (e.g., 'Start Progress', 'Ready for QA').
IMPORTANT: You must explicitly ask the user for confirmation before executing this tool. IMPORTANT: You must explicitly ask the user for confirmation before executing this tool.
""" """
jira = _get_jira() jira = _get_jira()
try: try:
# Normalize natural language input to actual Jira transition name
normalized_transition = normalize_transition_name(transition_name)
# First, find available transitions # First, find available transitions
transitions = jira.transitions(issue_key) transitions = jira.transitions(issue_key)
transition_id = None transition_id = None
...@@ -366,18 +407,18 @@ def transition_issue(issue_key: str, transition_name: str) -> str: ...@@ -366,18 +407,18 @@ def transition_issue(issue_key: str, transition_name: str) -> str:
for t in transitions: for t in transitions:
available_names.append(t['name']) available_names.append(t['name'])
# Case-insensitive match # Case-insensitive match against normalized name
if t['name'].lower() == transition_name.lower(): if t['name'].lower() == normalized_transition.lower():
transition_id = t['id'] transition_id = t['id']
break break
if not transition_id: if not transition_id:
available_str = ", ".join(f"'{name}'" for name in available_names) available_str = ", ".join(f"'{name}'" for name in available_names)
return f"Transition '{transition_name}' not found for issue {issue_key}. Available transitions are: {available_str}" return f"Transition '{transition_name}' (normalized to '{normalized_transition}') not found for issue {issue_key}. Available transitions are: {available_str}"
# Execute the transition # Execute the transition
jira.transition_issue(issue_key, transition_id) jira.transition_issue(issue_key, transition_id)
return f"Successfully transitioned issue [{issue_key}]({BASE_URL}/browse/{issue_key}) to '{transition_name}'." return f"Successfully transitioned issue [{issue_key}]({BASE_URL}/browse/{issue_key}) to '{normalized_transition}'."
except Exception as e: except Exception as e:
return f"Error transitioning issue {issue_key}: {str(e)}" return f"Error transitioning issue {issue_key}: {str(e)}"
......
#!/usr/bin/env python3
"""Test natural language transition mapping."""
# Direct implementation of the mapping function for testing
TRANSITION_MAPPING = {
"in progress": "Start Progress",
"start progress": "Start Progress",
"in prog": "Start Progress",
"progress": "Start Progress",
"qa assigned": "Ready for QA",
"ready for qa": "Ready for QA",
"qa ready": "Ready for QA",
"for qa": "Ready for QA",
"qa": "Ready for QA",
"qa completed": "QA Completed",
"qa done": "QA Completed",
"qa approved": "QA Completed",
"done": "Done",
"complete": "Done",
"completed": "Done",
"closed": "Close Issue",
"close issue": "Close Issue",
"stopped": "Stop Progress",
"stop progress": "Stop Progress",
"stopped progress": "Stop Progress",
}
def normalize_transition_name(user_input: str) -> str:
"""Convert natural language transition names to actual Jira transition names."""
normalized = user_input.lower().strip()
return TRANSITION_MAPPING.get(normalized, user_input)
# Test cases
test_cases = [
("In Progress", "Start Progress"),
("in progress", "Start Progress"),
("QA Assigned", "Ready for QA"),
("qa assigned", "Ready for QA"),
("Done", "Done"),
("closed", "Close Issue"),
("Stop Progress", "Stop Progress"),
("PR", "PR"), # Unknown mapping - should return as-is
("Ready For QA", "Ready for QA"), # Natural format
]
print("Testing Natural Language Transition Mapping:\n")
passed = 0
failed = 0
for user_input, expected in test_cases:
result = normalize_transition_name(user_input)
status = "✅ PASS" if result == expected else "❌ FAIL"
if result == expected:
passed += 1
else:
failed += 1
print(f"{status}: '{user_input}' → '{result}' (expected: '{expected}')")
print(f"\n{'='*50}")
print(f"Results: {passed} passed, {failed} failed")
print(f"{'='*50}")
#!/usr/bin/env python3
"""Standalone test for natural language transition mapping - no Jira connection needed."""
# Direct implementation of the mapping function for testing
TRANSITION_MAPPING = {
"in progress": "Start Progress",
"start progress": "Start Progress",
"in prog": "Start Progress",
"progress": "Start Progress",
"qa assigned": "Ready for QA",
"ready for qa": "Ready for QA",
"qa ready": "Ready for QA",
"for qa": "Ready for QA",
"qa": "Ready for QA",
"qa completed": "QA Completed",
"qa done": "QA Completed",
"qa approved": "QA Completed",
"done": "Done",
"complete": "Done",
"completed": "Done",
"closed": "Close Issue",
"close issue": "Close Issue",
"stopped": "Stop Progress",
"stop progress": "Stop Progress",
"stopped progress": "Stop Progress",
}
def normalize_transition_name(user_input: str) -> str:
"""Convert natural language transition names to actual Jira transition names."""
normalized = user_input.lower().strip()
return TRANSITION_MAPPING.get(normalized, user_input)
# Test cases
test_cases = [
("In Progress", "Start Progress"),
("in progress", "Start Progress"),
("QA Assigned", "Ready for QA"),
("qa assigned", "Ready for QA"),
("Done", "Done"),
("closed", "Close Issue"),
("Stop Progress", "Stop Progress"),
("PR", "PR"), # Unknown mapping - should return as-is
("Ready For QA", "Ready for QA"), # Natural format
]
print("Testing Natural Language Transition Mapping:\n")
passed = 0
failed = 0
for user_input, expected in test_cases:
result = normalize_transition_name(user_input)
status = "✅ PASS" if result == expected else "❌ FAIL"
if result == expected:
passed += 1
else:
failed += 1
print(f"{status}: '{user_input}' → '{result}' (expected: '{expected}')")
print(f"\n{'='*50}")
print(f"Results: {passed} passed, {failed} failed")
print(f"{'='*50}")
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment