1212from mcp_atlassian .jira .constants import DEFAULT_READ_JIRA_FIELDS
1313from mcp_atlassian .models .jira .common import JiraUser
1414from mcp_atlassian .servers .dependencies import get_jira_fetcher
15- from mcp_atlassian .utils .decorators import check_write_access
15+ from mcp_atlassian .utils .decorators import check_write_access , handle_tool_errors
1616
1717logger = logging .getLogger (__name__ )
1818
2323
2424
2525@jira_mcp .tool (tags = {"jira" , "read" })
26+ @handle_tool_errors (default_return_key = "user" , service_name = "Jira" )
2627async def get_user_profile (
2728 ctx : Context ,
2829 user_identifier : Annotated [
@@ -81,6 +82,7 @@ async def get_user_profile(
8182
8283
8384@jira_mcp .tool (tags = {"jira" , "read" })
85+ @handle_tool_errors (default_return_key = "issue" , service_name = "Jira" )
8486async def get_issue (
8587 ctx : Context ,
8688 issue_key : Annotated [str , Field (description = "Jira issue key (e.g., 'PROJ-123')" )],
@@ -164,6 +166,7 @@ async def get_issue(
164166
165167
166168@jira_mcp .tool (tags = {"jira" , "read" })
169+ @handle_tool_errors (default_return_key = "issues" , service_name = "Jira" )
167170async def search (
168171 ctx : Context ,
169172 jql : Annotated [
@@ -251,6 +254,7 @@ async def search(
251254
252255
253256@jira_mcp .tool (tags = {"jira" , "read" })
257+ @handle_tool_errors (default_return_key = "fields" , service_name = "Jira" )
254258async def search_fields (
255259 ctx : Context ,
256260 keyword : Annotated [
@@ -285,6 +289,7 @@ async def search_fields(
285289
286290
287291@jira_mcp .tool (tags = {"jira" , "read" })
292+ @handle_tool_errors (default_return_key = "issues" , service_name = "Jira" )
288293async def get_project_issues (
289294 ctx : Context ,
290295 project_key : Annotated [str , Field (description = "The project key" )],
@@ -317,6 +322,7 @@ async def get_project_issues(
317322
318323
319324@jira_mcp .tool (tags = {"jira" , "read" })
325+ @handle_tool_errors (default_return_key = "transitions" , service_name = "Jira" )
320326async def get_transitions (
321327 ctx : Context ,
322328 issue_key : Annotated [str , Field (description = "Jira issue key (e.g., 'PROJ-123')" )],
@@ -337,6 +343,7 @@ async def get_transitions(
337343
338344
339345@jira_mcp .tool (tags = {"jira" , "read" })
346+ @handle_tool_errors (default_return_key = "worklogs" , service_name = "Jira" )
340347async def get_worklog (
341348 ctx : Context ,
342349 issue_key : Annotated [str , Field (description = "Jira issue key (e.g., 'PROJ-123')" )],
@@ -357,6 +364,7 @@ async def get_worklog(
357364
358365
359366@jira_mcp .tool (tags = {"jira" , "read" })
367+ @handle_tool_errors (default_return_key = "result" , service_name = "Jira" )
360368async def download_attachments (
361369 ctx : Context ,
362370 issue_key : Annotated [str , Field (description = "Jira issue key (e.g., 'PROJ-123')" )],
@@ -380,6 +388,7 @@ async def download_attachments(
380388
381389
382390@jira_mcp .tool (tags = {"jira" , "read" })
391+ @handle_tool_errors (default_return_key = "boards" , service_name = "Jira" )
383392async def get_agile_boards (
384393 ctx : Context ,
385394 board_name : Annotated [
@@ -430,6 +439,7 @@ async def get_agile_boards(
430439
431440
432441@jira_mcp .tool (tags = {"jira" , "read" })
442+ @handle_tool_errors (default_return_key = "issues" , service_name = "Jira" )
433443async def get_board_issues (
434444 ctx : Context ,
435445 board_id : Annotated [str , Field (description = "The id of the board (e.g., '1001')" )],
@@ -507,6 +517,7 @@ async def get_board_issues(
507517
508518
509519@jira_mcp .tool (tags = {"jira" , "read" })
520+ @handle_tool_errors (default_return_key = "sprints" , service_name = "Jira" )
510521async def get_sprints_from_board (
511522 ctx : Context ,
512523 board_id : Annotated [str , Field (description = "The id of board (e.g., '1000')" )],
@@ -544,6 +555,7 @@ async def get_sprints_from_board(
544555
545556
546557@jira_mcp .tool (tags = {"jira" , "read" })
558+ @handle_tool_errors (default_return_key = "sprint_issues" , service_name = "Jira" )
547559async def get_sprint_issues (
548560 ctx : Context ,
549561 sprint_id : Annotated [str , Field (description = "The id of sprint (e.g., '10001')" )],
@@ -592,6 +604,7 @@ async def get_sprint_issues(
592604
593605
594606@jira_mcp .tool (tags = {"jira" , "read" })
607+ @handle_tool_errors (default_return_key = "link_types" , service_name = "Jira" )
595608async def get_link_types (ctx : Context ) -> str :
596609 """Get all available issue link types.
597610
@@ -609,6 +622,7 @@ async def get_link_types(ctx: Context) -> str:
609622
610623@jira_mcp .tool (tags = {"jira" , "write" })
611624@check_write_access
625+ @handle_tool_errors (default_return_key = "issue" , service_name = "Jira" )
612626async def create_issue (
613627 ctx : Context ,
614628 project_key : Annotated [
@@ -714,6 +728,7 @@ async def create_issue(
714728
715729@jira_mcp .tool (tags = {"jira" , "write" })
716730@check_write_access
731+ @handle_tool_errors (default_return_key = "issues" , service_name = "Jira" )
717732async def batch_create_issues (
718733 ctx : Context ,
719734 issues : Annotated [
@@ -782,6 +797,7 @@ async def batch_create_issues(
782797
783798
784799@jira_mcp .tool (tags = {"jira" , "read" })
800+ @handle_tool_errors (default_return_key = "changelogs" , service_name = "Jira" )
785801async def batch_get_changelogs (
786802 ctx : Context ,
787803 issue_ids_or_keys : Annotated [
@@ -855,6 +871,7 @@ async def batch_get_changelogs(
855871
856872@jira_mcp .tool (tags = {"jira" , "write" })
857873@check_write_access
874+ @handle_tool_errors (default_return_key = "issue" , service_name = "Jira" )
858875async def update_issue (
859876 ctx : Context ,
860877 issue_key : Annotated [str , Field (description = "Jira issue key (e.g., 'PROJ-123')" )],
@@ -864,16 +881,9 @@ async def update_issue(
864881 description = (
865882 "Dictionary of fields to update. For 'assignee', provide a string identifier (email, name, or accountId). "
866883 "Example: `{'assignee': '[email protected] ', 'summary': 'New Summary'}`" 867- )
884+ ),
868885 ),
869886 ],
870- additional_fields : Annotated [
871- dict [str , Any ] | None ,
872- Field (
873- description = "(Optional) Dictionary of additional fields to update. Use this for custom fields or more complex updates." ,
874- default = None ,
875- ),
876- ] = None ,
877887 attachments : Annotated [
878888 str | None ,
879889 Field (
@@ -890,8 +900,7 @@ async def update_issue(
890900 Args:
891901 ctx: The FastMCP context.
892902 issue_key: Jira issue key.
893- fields: Dictionary of fields to update.
894- additional_fields: Optional dictionary of additional fields.
903+ fields: (Required) Dictionary of fields to update.
895904 attachments: Optional JSON array string or comma-separated list of file paths.
896905
897906 Returns:
@@ -906,11 +915,6 @@ async def update_issue(
906915 raise ValueError ("fields must be a dictionary." )
907916 update_fields = fields
908917
909- # Use additional_fields directly as dict
910- extra_fields = additional_fields or {}
911- if not isinstance (extra_fields , dict ):
912- raise ValueError ("additional_fields must be a dictionary." )
913-
914918 # Parse attachments
915919 attachment_paths = []
916920 if attachments :
@@ -932,7 +936,7 @@ async def update_issue(
932936 )
933937
934938 # Combine fields and additional_fields
935- all_updates = {** update_fields , ** extra_fields }
939+ all_updates = {** update_fields }
936940 if attachment_paths :
937941 all_updates ["attachments" ] = attachment_paths
938942
@@ -956,6 +960,7 @@ async def update_issue(
956960
957961@jira_mcp .tool (tags = {"jira" , "write" })
958962@check_write_access
963+ @handle_tool_errors (default_return_key = "result" , service_name = "Jira" )
959964async def delete_issue (
960965 ctx : Context ,
961966 issue_key : Annotated [str , Field (description = "Jira issue key (e.g. PROJ-123)" )],
@@ -981,6 +986,7 @@ async def delete_issue(
981986
982987@jira_mcp .tool (tags = {"jira" , "write" })
983988@check_write_access
989+ @handle_tool_errors (default_return_key = "comment" , service_name = "Jira" )
984990async def add_comment (
985991 ctx : Context ,
986992 issue_key : Annotated [str , Field (description = "Jira issue key (e.g., 'PROJ-123')" )],
@@ -1007,6 +1013,7 @@ async def add_comment(
10071013
10081014@jira_mcp .tool (tags = {"jira" , "write" })
10091015@check_write_access
1016+ @handle_tool_errors (default_return_key = "worklog" , service_name = "Jira" )
10101017async def add_worklog (
10111018 ctx : Context ,
10121019 issue_key : Annotated [str , Field (description = "Jira issue key (e.g., 'PROJ-123')" )],
@@ -1074,6 +1081,7 @@ async def add_worklog(
10741081
10751082@jira_mcp .tool (tags = {"jira" , "write" })
10761083@check_write_access
1084+ @handle_tool_errors (default_return_key = "issue" , service_name = "Jira" )
10771085async def link_to_epic (
10781086 ctx : Context ,
10791087 issue_key : Annotated [
@@ -1107,6 +1115,7 @@ async def link_to_epic(
11071115
11081116@jira_mcp .tool (tags = {"jira" , "write" })
11091117@check_write_access
1118+ @handle_tool_errors (default_return_key = "result" , service_name = "Jira" )
11101119async def create_issue_link (
11111120 ctx : Context ,
11121121 link_type : Annotated [
@@ -1175,6 +1184,7 @@ async def create_issue_link(
11751184
11761185@jira_mcp .tool (tags = {"jira" , "write" })
11771186@check_write_access
1187+ @handle_tool_errors (default_return_key = "result" , service_name = "Jira" )
11781188async def create_remote_issue_link (
11791189 ctx : Context ,
11801190 issue_key : Annotated [
@@ -1257,6 +1267,7 @@ async def create_remote_issue_link(
12571267
12581268@jira_mcp .tool (tags = {"jira" , "write" })
12591269@check_write_access
1270+ @handle_tool_errors (default_return_key = "result" , service_name = "Jira" )
12601271async def remove_issue_link (
12611272 ctx : Context ,
12621273 link_id : Annotated [str , Field (description = "The ID of the link to remove" )],
@@ -1283,6 +1294,7 @@ async def remove_issue_link(
12831294
12841295@jira_mcp .tool (tags = {"jira" , "write" })
12851296@check_write_access
1297+ @handle_tool_errors (default_return_key = "issue" , service_name = "Jira" )
12861298async def transition_issue (
12871299 ctx : Context ,
12881300 issue_key : Annotated [str , Field (description = "Jira issue key (e.g., 'PROJ-123')" )],
@@ -1356,6 +1368,7 @@ async def transition_issue(
13561368
13571369@jira_mcp .tool (tags = {"jira" , "write" })
13581370@check_write_access
1371+ @handle_tool_errors (default_return_key = "sprint" , service_name = "Jira" )
13591372async def create_sprint (
13601373 ctx : Context ,
13611374 board_id : Annotated [str , Field (description = "The id of board (e.g., '1000')" )],
@@ -1401,6 +1414,7 @@ async def create_sprint(
14011414
14021415@jira_mcp .tool (tags = {"jira" , "write" })
14031416@check_write_access
1417+ @handle_tool_errors (default_return_key = "sprint" , service_name = "Jira" )
14041418async def update_sprint (
14051419 ctx : Context ,
14061420 sprint_id : Annotated [str , Field (description = "The id of sprint (e.g., '10001')" )],
@@ -1458,6 +1472,7 @@ async def update_sprint(
14581472
14591473
14601474@jira_mcp .tool (tags = {"jira" , "read" })
1475+ @handle_tool_errors (default_return_key = "versions" , service_name = "Jira" )
14611476async def get_project_versions (
14621477 ctx : Context ,
14631478 project_key : Annotated [str , Field (description = "Jira project key (e.g., 'PROJ')" )],
@@ -1469,6 +1484,7 @@ async def get_project_versions(
14691484
14701485
14711486@jira_mcp .tool (tags = {"jira" , "read" })
1487+ @handle_tool_errors (default_return_key = "projects" , service_name = "Jira" )
14721488async def get_all_projects (
14731489 ctx : Context ,
14741490 include_archived : Annotated [
@@ -1533,8 +1549,60 @@ async def get_all_projects(
15331549 return json .dumps (projects , indent = 2 , ensure_ascii = False )
15341550
15351551
1552+ @jira_mcp .tool (tags = {"jira" , "read" })
1553+ @handle_tool_errors (default_return_key = "issue_types" , service_name = "Jira" )
1554+ async def get_all_issue_types (
1555+ ctx : Context ,
1556+ project_key : Annotated [str , Field (description = "Jira project key (e.g., 'PROJ')" )],
1557+ ) -> str :
1558+ """Get all issue types for a specific Jira project.
1559+ Args:
1560+ ctx: The FastMCP context.
1561+ project_key: The project key.
1562+
1563+ Returns:
1564+ JSON string representing a list of issue types.
1565+
1566+ Raises:
1567+ ValueError: If the Jira client is not configured or available.
1568+ """
1569+ jira = await get_jira_fetcher (ctx )
1570+ issue_types = jira .get_project_issue_types (project_key )
1571+ return json .dumps (issue_types , indent = 2 , ensure_ascii = False )
1572+
1573+
1574+ @jira_mcp .tool (tags = {"jira" , "read" })
1575+ @handle_tool_errors (default_return_key = "required_fields" , service_name = "Jira" )
1576+ async def get_required_fields (
1577+ ctx : Context ,
1578+ issue_type : Annotated [
1579+ str ,
1580+ Field (
1581+ description = "Jira issue type (e.g., 'Task', 'Bug', 'Story', 'Epic', 'Subtask')"
1582+ ),
1583+ ],
1584+ project_key : Annotated [str , Field (description = "Jira project key (e.g., 'PROJ')" )],
1585+ ) -> str :
1586+ """Get required fields for a specific Jira issue type in a project.
1587+ Args:
1588+ ctx: The FastMCP context.
1589+ issue_type: The issue type.
1590+ project_key: The project key.
1591+
1592+ Returns:
1593+ JSON string representing a list of required fields.
1594+
1595+ Raises:
1596+ ValueError: If the Jira client is not configured or available.
1597+ """
1598+ jira = await get_jira_fetcher (ctx )
1599+ required_fields = jira .get_required_fields (issue_type , project_key )
1600+ return json .dumps (required_fields , indent = 2 , ensure_ascii = False )
1601+
1602+
15361603@jira_mcp .tool (tags = {"jira" , "write" })
15371604@check_write_access
1605+ @handle_tool_errors (default_return_key = "version" , service_name = "Jira" )
15381606async def create_version (
15391607 ctx : Context ,
15401608 project_key : Annotated [str , Field (description = "Jira project key (e.g., 'PROJ')" )],
@@ -1583,6 +1651,7 @@ async def create_version(
15831651
15841652@jira_mcp .tool (name = "batch_create_versions" , tags = {"jira" , "write" })
15851653@check_write_access
1654+ @handle_tool_errors (default_return_key = "results" , service_name = "Jira" )
15861655async def batch_create_versions (
15871656 ctx : Context ,
15881657 project_key : Annotated [str , Field (description = "Jira project key (e.g., 'PROJ')" )],
0 commit comments