@@ -28,10 +28,12 @@ def __init__(self, base_url: str, token: str):
2828 def search_issues (
2929 self , project_key : str , search_text : str , max_results : int = 10
3030 ) -> List [dict ]:
31- """Search for issues in a project by title or description."""
31+ """Search for issues in a project by title or description.
32+ Only returns open/in-progress issues, ordered by most recently updated."""
3233 jql = (
3334 f'project = "{ project_key } " AND '
34- f'(summary ~ "{ search_text } " OR description ~ "{ search_text } ") '
35+ f'(summary ~ "{ search_text } " OR description ~ "{ search_text } ") AND '
36+ f'status NOT IN (Closed, Resolved, Done, "Won\' t Do", "Won\' t Fix", Obsolete) '
3537 "ORDER BY updated DESC"
3638 )
3739
@@ -157,7 +159,10 @@ def get_allowed_projects() -> List[str]:
157159 Raises:
158160 ValueError: If no valid projects are configured
159161 """
160- projects_env = os .environ .get ("JIRA_ALLOWED_PROJECTS" , "OCPBUGS" )
162+ projects_env = os .environ .get ("JIRA_ALLOWED_PROJECTS" )
163+ if not projects_env :
164+ raise ValueError ("JIRA_ALLOWED_PROJECTS environment variable is not set" )
165+
161166 allowed = [p .strip () for p in projects_env .split ("," ) if p .strip ()]
162167
163168 logger .debug (f"Allowed Jira projects: { allowed } " )
@@ -178,8 +183,7 @@ def get_jira_client() -> JiraClient:
178183
179184 if not base_url or not token :
180185 raise ValueError (
181- "Missing Jira credentials: JIRA_BASE_URL and JIRA_TOKEN "
182- "must be set in environment"
186+ "Missing Jira credentials: JIRA_BASE_URL and JIRA_TOKEN must be set in environment"
183187 )
184188
185189 return JiraClient (base_url , token )
@@ -189,17 +193,18 @@ def get_jira_client() -> JiraClient:
189193def search_jira_issues (
190194 project_key : str , search_text : str , max_results : int = 10
191195) -> str :
192- """Search for Jira issues in a project by title or description.
196+ """Search for open Jira issues in a project by title or description.
193197 Use this tool to find related bugs or known issues that match the error patterns.
194- Call list_jira_projects() first if you're unsure which projects are available.
198+ Only returns open/in-progress issues (excludes Closed, Resolved, Done, Won't Do/Fix, Obsolete).
199+ Results are ordered by most recently updated first.
195200 Args:
196201 project_key: The Jira project key to search in
197202 (must be one of the configured allowed projects)
198203 search_text: Text to search for in issue summary or description
199204 (e.g., "etcd degraded", "CrashLoopBackOff", component names)
200205 max_results: Maximum number of results to return (default: 10)
201206 Returns:
202- JSON string containing matching issues with their details
207+ JSON string containing matching open issues with their details, ordered by recency
203208 """
204209 # Validate project key against allowed projects
205210 allowed_projects = get_allowed_projects ()
@@ -284,77 +289,5 @@ def get_jira_issue(issue_key: str) -> str:
284289 return f"Error getting Jira issue: { str (e )} "
285290
286291
287- @mcp .tool ()
288- def list_jira_projects () -> str :
289- """List the Jira projects that are allowed/configured for searching.
290- Use this tool first if you need to know which project keys are available
291- before calling search_jira_issues. The list is configured via the
292- JIRA_ALLOWED_PROJECTS environment variable.
293- Returns:
294- Formatted list of allowed project keys and their details
295- """
296- try :
297- # Get allowed projects from environment first
298- allowed_projects = get_allowed_projects ()
299-
300- # Return allowed projects list as a minimum
301- base_result = (
302- f"Allowed Jira project keys for search: "
303- f"{ ', ' .join (allowed_projects )} \n \n "
304- )
305-
306- # Try to fetch additional details from Jira API
307- try :
308- client = get_jira_client ()
309- response = requests .get (
310- f"{ client .base_url } /rest/api/2/project" ,
311- headers = client .headers ,
312- timeout = 30 ,
313- )
314- response .raise_for_status ()
315-
316- projects = response .json ()
317- project_list = []
318-
319- # Filter to only include allowed projects
320- for project in projects :
321- project_key = project .get ("key" )
322- if project_key in allowed_projects :
323- project_list .append (
324- {
325- "key" : project_key ,
326- "name" : project .get ("name" ),
327- "id" : project .get ("id" ),
328- }
329- )
330-
331- # Format with full details
332- if project_list :
333- formatted_result = base_result
334- formatted_result += "Project Details:\n \n "
335- for i , project in enumerate (project_list , 1 ):
336- formatted_result += (
337- f"{ i } . **{ project ['key' ]} ** - { project ['name' ]} \n "
338- )
339- formatted_result += f" - ID: { project ['id' ]} \n \n "
340- return formatted_result
341- else :
342- return base_result + (
343- "Note: Could not fetch additional project details from Jira API."
344- )
345-
346- except Exception as api_error :
347- logger .warning (f"Could not fetch project details: { api_error } " )
348- # Still return allowed projects list
349- return base_result + (
350- "Note: Could not fetch additional details from Jira API, "
351- "but you can use any of the allowed project keys above."
352- )
353-
354- except Exception as e :
355- logger .error (f"Error in list_jira_projects: { e } " )
356- return f"Error: { str (e )} "
357-
358-
359292if __name__ == "__main__" :
360293 mcp .run (transport = "stdio" , show_banner = False )
0 commit comments