From 643a886f36b00bcfcb04788c1e09b869d2877bc4 Mon Sep 17 00:00:00 2001 From: Kevin Howell Date: Tue, 9 Sep 2025 13:52:13 -0400 Subject: [PATCH 1/4] feat(jira): add visibility parameter to comment creation Allow restricting comment visibility when adding comments to Jira issues. The visibility parameter accepts a dictionary specifying access restrictions (e.g., {"type":"group","value":"jira-users"}). --- src/mcp_atlassian/jira/comments.py | 5 +++-- src/mcp_atlassian/servers/jira.py | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/mcp_atlassian/jira/comments.py b/src/mcp_atlassian/jira/comments.py index 8e7052bf2..64df4dc44 100644 --- a/src/mcp_atlassian/jira/comments.py +++ b/src/mcp_atlassian/jira/comments.py @@ -52,13 +52,14 @@ def get_issue_comments( logger.error(f"Error getting comments for issue {issue_key}: {str(e)}") raise Exception(f"Error getting comments: {str(e)}") from e - def add_comment(self, issue_key: str, comment: str) -> dict[str, Any]: + def add_comment(self, issue_key: str, comment: str, visibility: dict = None) -> dict[str, Any]: """ Add a comment to an issue. Args: issue_key: The issue key (e.g. 'PROJ-123') comment: Comment text to add (in Markdown format) + visibility: (optional) Restrict comment visibility (e.g. {"type":"group","value:"jira-users"}) Returns: The created comment details @@ -70,7 +71,7 @@ def add_comment(self, issue_key: str, comment: str) -> dict[str, Any]: # Convert Markdown to Jira's markup format jira_formatted_comment = self._markdown_to_jira(comment) - result = self.jira.issue_add_comment(issue_key, jira_formatted_comment) + result = self.jira.issue_add_comment(issue_key, jira_formatted_comment, visibility) if not isinstance(result, dict): msg = f"Unexpected return value type from `jira.issue_add_comment`: {type(result)}" logger.error(msg) diff --git a/src/mcp_atlassian/servers/jira.py b/src/mcp_atlassian/servers/jira.py index 7caed34af..ee2dec8f1 100644 --- a/src/mcp_atlassian/servers/jira.py +++ b/src/mcp_atlassian/servers/jira.py @@ -985,6 +985,7 @@ async def add_comment( ctx: Context, issue_key: Annotated[str, Field(description="Jira issue key (e.g., 'PROJ-123')")], comment: Annotated[str, Field(description="Comment text in Markdown format")], + visibility: Annotated[dict, Field(description="""(Optional) Comment visibility (e.g. {"type":"group","value":"jira-users"})""")] = None, ) -> str: """Add a comment to a Jira issue. @@ -992,6 +993,7 @@ async def add_comment( ctx: The FastMCP context. issue_key: Jira issue key. comment: Comment text in Markdown. + visibility: (Optional) Comment visibility (e.g. {"type":"group","value":"jira-users"}). Returns: JSON string representing the added comment object. @@ -1001,7 +1003,7 @@ async def add_comment( """ jira = await get_jira_fetcher(ctx) # add_comment returns dict - result = jira.add_comment(issue_key, comment) + result = jira.add_comment(issue_key, comment, visibility) return json.dumps(result, indent=2, ensure_ascii=False) From ba94b1e00a0d2f4aedddeac1fdf4444a9cb4b1de Mon Sep 17 00:00:00 2001 From: Kevin Howell Date: Tue, 9 Sep 2025 14:32:40 -0400 Subject: [PATCH 2/4] style(jira): reformat via pre-commit run --- src/mcp_atlassian/jira/comments.py | 8 ++++++-- src/mcp_atlassian/servers/jira.py | 7 ++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/mcp_atlassian/jira/comments.py b/src/mcp_atlassian/jira/comments.py index 64df4dc44..20e25d3a1 100644 --- a/src/mcp_atlassian/jira/comments.py +++ b/src/mcp_atlassian/jira/comments.py @@ -52,7 +52,9 @@ def get_issue_comments( logger.error(f"Error getting comments for issue {issue_key}: {str(e)}") raise Exception(f"Error getting comments: {str(e)}") from e - def add_comment(self, issue_key: str, comment: str, visibility: dict = None) -> dict[str, Any]: + def add_comment( + self, issue_key: str, comment: str, visibility: dict = None + ) -> dict[str, Any]: """ Add a comment to an issue. @@ -71,7 +73,9 @@ def add_comment(self, issue_key: str, comment: str, visibility: dict = None) -> # Convert Markdown to Jira's markup format jira_formatted_comment = self._markdown_to_jira(comment) - result = self.jira.issue_add_comment(issue_key, jira_formatted_comment, visibility) + result = self.jira.issue_add_comment( + issue_key, jira_formatted_comment, visibility + ) if not isinstance(result, dict): msg = f"Unexpected return value type from `jira.issue_add_comment`: {type(result)}" logger.error(msg) diff --git a/src/mcp_atlassian/servers/jira.py b/src/mcp_atlassian/servers/jira.py index ee2dec8f1..18a701559 100644 --- a/src/mcp_atlassian/servers/jira.py +++ b/src/mcp_atlassian/servers/jira.py @@ -985,7 +985,12 @@ async def add_comment( ctx: Context, issue_key: Annotated[str, Field(description="Jira issue key (e.g., 'PROJ-123')")], comment: Annotated[str, Field(description="Comment text in Markdown format")], - visibility: Annotated[dict, Field(description="""(Optional) Comment visibility (e.g. {"type":"group","value":"jira-users"})""")] = None, + visibility: Annotated[ + dict, + Field( + description="""(Optional) Comment visibility (e.g. {"type":"group","value":"jira-users"})""" + ), + ] = None, ) -> str: """Add a comment to a Jira issue. From 8ca9e3c9231e0262c2b266fd4b96ba75ef2062eb Mon Sep 17 00:00:00 2001 From: Kevin Howell Date: Tue, 9 Sep 2025 14:47:41 -0400 Subject: [PATCH 3/4] feat(jira): improve type hints for comment visibility parameter Replace generic dict with specific dict[str, str] type annotation --- src/mcp_atlassian/jira/comments.py | 2 +- src/mcp_atlassian/servers/jira.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mcp_atlassian/jira/comments.py b/src/mcp_atlassian/jira/comments.py index 20e25d3a1..ebb1743e4 100644 --- a/src/mcp_atlassian/jira/comments.py +++ b/src/mcp_atlassian/jira/comments.py @@ -53,7 +53,7 @@ def get_issue_comments( raise Exception(f"Error getting comments: {str(e)}") from e def add_comment( - self, issue_key: str, comment: str, visibility: dict = None + self, issue_key: str, comment: str, visibility: dict[str, str] = None ) -> dict[str, Any]: """ Add a comment to an issue. diff --git a/src/mcp_atlassian/servers/jira.py b/src/mcp_atlassian/servers/jira.py index 18a701559..bf6f98132 100644 --- a/src/mcp_atlassian/servers/jira.py +++ b/src/mcp_atlassian/servers/jira.py @@ -986,7 +986,7 @@ async def add_comment( issue_key: Annotated[str, Field(description="Jira issue key (e.g., 'PROJ-123')")], comment: Annotated[str, Field(description="Comment text in Markdown format")], visibility: Annotated[ - dict, + dict[str, str], Field( description="""(Optional) Comment visibility (e.g. {"type":"group","value":"jira-users"})""" ), From b79a0a3fa0c444a435015d54209e21609ca20ab7 Mon Sep 17 00:00:00 2001 From: Kevin Howell Date: Tue, 9 Sep 2025 14:48:08 -0400 Subject: [PATCH 4/4] test(jira): update comment tests to include visibility parameter Update test assertions to match the new method signature that includes the visibility parameter. All existing tests now pass None for visibility, and add a new test case for restricted visibility comments. --- tests/unit/jira/test_comments.py | 37 +++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/tests/unit/jira/test_comments.py b/tests/unit/jira/test_comments.py index d0fe7845a..86ec74ac8 100644 --- a/tests/unit/jira/test_comments.py +++ b/tests/unit/jira/test_comments.py @@ -171,7 +171,7 @@ def test_add_comment_basic(self, comments_mixin): "Test comment" ) comments_mixin.jira.issue_add_comment.assert_called_once_with( - "TEST-123", "*This* is _Jira_ formatted" + "TEST-123", "*This* is _Jira_ formatted", None ) assert result["id"] == "10001" assert result["body"] == "This is a comment" @@ -211,7 +211,7 @@ def hello(): markdown_comment ) comments_mixin.jira.issue_add_comment.assert_called_once_with( - "TEST-123", "*This* is _Jira_ formatted" + "TEST-123", "*This* is _Jira_ formatted", None ) assert result["body"] == "*This* is _Jira_ formatted" @@ -230,9 +230,40 @@ def test_add_comment_with_empty_comment(self, comments_mixin): # Verify - for empty comments, markdown_to_jira should NOT be called as per implementation comments_mixin.preprocessor.markdown_to_jira.assert_not_called() - comments_mixin.jira.issue_add_comment.assert_called_once_with("TEST-123", "") + comments_mixin.jira.issue_add_comment.assert_called_once_with( + "TEST-123", "", None + ) assert result["body"] == "" + def test_add_comment_with_restricted_visibility(self, comments_mixin): + """Test add_comment with visibility set.""" + # Setup mock response + comments_mixin.jira.issue_add_comment.return_value = { + "id": "10001", + "body": "This is a comment", + "created": "2024-01-01T10:00:00.000+0000", + "author": {"displayName": "John Doe"}, + } + + # Call the method + result = comments_mixin.add_comment( + "TEST-123", "Test comment", {"type": "group", "value": "restricted"} + ) + + # Verify + comments_mixin.preprocessor.markdown_to_jira.assert_called_once_with( + "Test comment" + ) + comments_mixin.jira.issue_add_comment.assert_called_once_with( + "TEST-123", + "*This* is _Jira_ formatted", + {"type": "group", "value": "restricted"}, + ) + assert result["id"] == "10001" + assert result["body"] == "This is a comment" + assert result["created"] == "2024-01-01 10:00:00+00:00" # Parsed date + assert result["author"] == "John Doe" + def test_add_comment_with_error(self, comments_mixin): """Test add_comment with an error response.""" # Setup mock to raise exception