@@ -325,3 +325,105 @@ async def get_jira_project_summary() -> Dict[str, Any]:
325
325
326
326
except Exception as e :
327
327
return {"error" : f"Error generating project summary from Snowflake: { str (e )} " }
328
+
329
+ @mcp .tool ()
330
+ @track_tool_usage ("list_components" )
331
+ async def list_components (
332
+ project : Optional [str ] = None ,
333
+ archived : Optional [str ] = None ,
334
+ deleted : Optional [str ] = None ,
335
+ limit : int = 50 ,
336
+ search_text : Optional [str ] = None
337
+ ) -> Dict [str , Any ]:
338
+ """
339
+ List JIRA components from Snowflake with optional filtering.
340
+
341
+ Args:
342
+ project: Filter by project ID (e.g., '12325621')
343
+ archived: Filter by archived status ('Y' or 'N')
344
+ deleted: Filter by deleted status ('Y' or 'N')
345
+ limit: Maximum number of components to return (default: 50)
346
+ search_text: Search in component name and description fields
347
+
348
+ Returns:
349
+ Dictionary containing components list and metadata
350
+ """
351
+ try :
352
+ # Get the Snowflake token
353
+ snowflake_token = get_snowflake_token (mcp )
354
+ if not snowflake_token :
355
+ return {"error" : "Snowflake token not available" , "components" : []}
356
+
357
+ # Build SQL query with filters
358
+ sql_conditions = []
359
+
360
+ if project :
361
+ sql_conditions .append (f"PROJECT = '{ sanitize_sql_value (project )} '" )
362
+
363
+ if archived :
364
+ sql_conditions .append (f"ARCHIVED = '{ sanitize_sql_value (archived .upper ())} '" )
365
+
366
+ if deleted :
367
+ sql_conditions .append (f"DELETED = '{ sanitize_sql_value (deleted .upper ())} '" )
368
+
369
+ if search_text :
370
+ search_condition = f"(LOWER(CNAME) LIKE '%{ sanitize_sql_value (search_text .lower ())} %' OR LOWER(DESCRIPTION) LIKE '%{ sanitize_sql_value (search_text .lower ())} %')"
371
+ sql_conditions .append (search_condition )
372
+
373
+ where_clause = ""
374
+ if sql_conditions :
375
+ where_clause = "WHERE " + " AND " .join (sql_conditions )
376
+
377
+ sql = f"""
378
+ SELECT
379
+ ID, PROJECT, CNAME, DESCRIPTION, URL, LEAD,
380
+ ASSIGNEETYPE, ARCHIVED, DELETED, _FIVETRAN_SYNCED
381
+ FROM JIRA_COMPONENT_RHAI
382
+ { where_clause }
383
+ ORDER BY CNAME ASC
384
+ LIMIT { limit }
385
+ """
386
+
387
+ rows = await execute_snowflake_query (sql , snowflake_token )
388
+
389
+ components = []
390
+
391
+ # Expected column order based on SELECT statement
392
+ columns = [
393
+ "ID" , "PROJECT" , "CNAME" , "DESCRIPTION" , "URL" , "LEAD" ,
394
+ "ASSIGNEETYPE" , "ARCHIVED" , "DELETED" , "_FIVETRAN_SYNCED"
395
+ ]
396
+
397
+ for row in rows :
398
+ row_dict = format_snowflake_row (row , columns )
399
+
400
+ # Build component object
401
+ component = {
402
+ "id" : row_dict .get ("ID" ),
403
+ "project" : row_dict .get ("PROJECT" ),
404
+ "name" : row_dict .get ("CNAME" ),
405
+ "description" : row_dict .get ("DESCRIPTION" ) or "" ,
406
+ "url" : row_dict .get ("URL" ),
407
+ "lead" : row_dict .get ("LEAD" ),
408
+ "assignee_type" : row_dict .get ("ASSIGNEETYPE" ),
409
+ "archived" : row_dict .get ("ARCHIVED" ),
410
+ "deleted" : row_dict .get ("DELETED" ),
411
+ "synced" : row_dict .get ("_FIVETRAN_SYNCED" )
412
+ }
413
+
414
+ components .append (component )
415
+
416
+ return {
417
+ "components" : components ,
418
+ "total_returned" : len (components ),
419
+ "filters_applied" : {
420
+ "project" : project ,
421
+ "archived" : archived ,
422
+ "deleted" : deleted ,
423
+ "search_text" : search_text ,
424
+ "limit" : limit
425
+ }
426
+ }
427
+
428
+ except Exception as e :
429
+ return {"error" : f"Error reading components from Snowflake: { str (e )} " , "components" : []}
0 commit comments