Repository: sooperset/mcp-atlassian

Issue:
Add Confluence page delete tool
# Overview
The MCP Atlassian integration currently supports creating, reading, and updating Confluence pages, but lacks the ability to delete pages. This feature is needed to provide full CRUD functionality for Confluence content management.

# Requirements
- Implement a new `confluence_delete_page` tool that allows users to delete a Confluence page by its ID
- Add appropriate error handling for permission issues, non-existent pages, etc.
- Follow the existing patterns established in the other Confluence page operations
- Update documentation to include this new tool
- Add unit tests to verify functionality

# Implementation Details
The implementation should follow the same structure as other Confluence tools:
1. Update the `src/mcp_atlassian/confluence/pages.py` file to include a delete method
2. Update relevant models if needed
3. Register the new tool with the MCP server
4. Follow the project's code quality guidelines (type hints, docstrings, etc.)

# Acceptance Criteria
- Tool allows deleting a Confluence page using its page ID
- Appropriate error handling and user feedback
- Unit tests covering success and failure cases
- Documentation updated in README

Hints from issue comments:
@sooperset: resolved in #89 

Solution patch:
diff --git a/README.md b/README.md
index 8570ea9..20bf31b 100644
--- a/README.md
+++ b/README.md
@@ -265,6 +265,7 @@ Configure in Claude Desktop:
 | `confluence_get_comments` | Get comments for a specific Confluence page |
 | `confluence_create_page` | Create a new Confluence page |
 | `confluence_update_page` | Update an existing Confluence page |
+| `confluence_delete_page` | Delete an existing Confluence page |
 | `jira_get_issue` | Get details of a specific Jira issue |
 | `jira_search` | Search Jira issues using JQL |
 | `jira_get_project_issues` | Get all issues for a specific Jira project |
diff --git a/src/mcp_atlassian/confluence/pages.py b/src/mcp_atlassian/confluence/pages.py
index d64bf9f..eb122f8 100644
--- a/src/mcp_atlassian/confluence/pages.py
+++ b/src/mcp_atlassian/confluence/pages.py
@@ -265,3 +265,26 @@ class PagesMixin(ConfluenceClient):
         except Exception as e:
             logger.error(f"Error updating page {page_id}: {str(e)}")
             raise Exception(f"Failed to update page {page_id}: {str(e)}") from e
+
+    def delete_page(self, page_id: str) -> bool:
+        """
+        Delete a Confluence page by its ID.
+
+        Args:
+            page_id: The ID of the page to delete
+
+        Returns:
+            Boolean indicating success (True) or failure (False)
+
+        Raises:
+            Exception: If there is an error deleting the page
+        """
+        try:
+            logger.debug(f"Deleting page {page_id}")
+            result = self.confluence.remove_page(page_id=page_id)
+
+            # The API should return True on success, but let's ensure we return a boolean
+            return bool(result)
+        except Exception as e:
+            logger.error(f"Error deleting page {page_id}: {str(e)}")
+            raise Exception(f"Failed to delete page {page_id}: {str(e)}") from e
diff --git a/src/mcp_atlassian/server.py b/src/mcp_atlassian/server.py
index f262090..6002a80 100644
--- a/src/mcp_atlassian/server.py
+++ b/src/mcp_atlassian/server.py
@@ -413,6 +413,20 @@ async def list_tools() -> list[Tool]:
                         "required": ["page_id", "title", "content"],
                     },
                 ),
+                Tool(
+                    name="confluence_delete_page",
+                    description="Delete an existing Confluence page",
+                    inputSchema={
+                        "type": "object",
+                        "properties": {
+                            "page_id": {
+                                "type": "string",
+                                "description": "The ID of the page to delete",
+                            },
+                        },
+                        "required": ["page_id"],
+                    },
+                ),
             ]
         )
 
@@ -883,34 +897,48 @@ async def call_tool(name: str, arguments: Any) -> Sequence[TextContent]:
             if not ctx or not ctx.confluence:
                 raise ValueError("Confluence is not configured.")
 
-            # Extract arguments
             page_id = arguments.get("page_id")
             title = arguments.get("title")
             content = arguments.get("content")
             is_minor_edit = arguments.get("is_minor_edit", False)
             version_comment = arguments.get("version_comment", "")
 
+            if not page_id or not title or not content:
+                raise ValueError(
+                    "Missing required parameters: page_id, title, and content are required."
+                )
+
             # Convert markdown to Confluence storage format
-            storage_format = markdown_to_confluence_storage(content)
+            html_content = markdown_to_confluence_storage(content)
 
             # Update the page
-            page = ctx.confluence.update_page(
+            updated_page = ctx.confluence.update_page(
                 page_id=page_id,
                 title=title,
-                body=storage_format,
+                body=html_content,
                 is_minor_edit=is_minor_edit,
                 version_comment=version_comment,
             )
 
-            # Format the result
-            result = page.to_simplified_dict()
+            # Format results
+            page_data = updated_page.to_simplified_dict()
 
-            return [
-                TextContent(
-                    type="text",
-                    text=f"Page updated successfully:\n{json.dumps(result, indent=2, ensure_ascii=False)}",
-                )
-            ]
+            return [TextContent(text=json.dumps({"page": page_data}))]
+
+        elif name == "confluence_delete_page":
+            if not ctx or not ctx.confluence:
+                raise ValueError("Confluence is not configured.")
+
+            page_id = arguments.get("page_id")
+
+            if not page_id:
+                raise ValueError("Missing required parameter: page_id is required.")
+
+            # Delete the page
+            result = ctx.confluence.delete_page(page_id=page_id)
+
+            # Format results
+            return [TextContent(text=json.dumps({"success": result}))]
 
         # Jira operations
         elif name == "jira_get_issue":


Test patch:
diff --git a/tests/unit/confluence/test_pages.py b/tests/unit/confluence/test_pages.py
index cc9471f..8d28826 100644
--- a/tests/unit/confluence/test_pages.py
+++ b/tests/unit/confluence/test_pages.py
@@ -289,8 +289,31 @@ class TestPagesMixin:
         pages_mixin.confluence.update_page.side_effect = Exception("API Error")
 
         # Act/Assert
-        with pytest.raises(Exception, match="API Error"):
-            pages_mixin.update_page("987654321", "Test", "<p>Content</p>")
+        with pytest.raises(Exception, match="Failed to update page"):
+            pages_mixin.update_page("987654321", "Test Page", "<p>Content</p>")
+
+    def test_delete_page_success(self, pages_mixin):
+        """Test successfully deleting a page."""
+        # Arrange
+        page_id = "987654321"
+        pages_mixin.confluence.remove_page.return_value = True
+
+        # Act
+        result = pages_mixin.delete_page(page_id)
+
+        # Assert
+        pages_mixin.confluence.remove_page.assert_called_once_with(page_id=page_id)
+        assert result is True
+
+    def test_delete_page_error(self, pages_mixin):
+        """Test error handling when deleting a page."""
+        # Arrange
+        page_id = "987654321"
+        pages_mixin.confluence.remove_page.side_effect = Exception("API Error")
+
+        # Act/Assert
+        with pytest.raises(Exception, match="Failed to delete page"):
+            pages_mixin.delete_page(page_id)
 
     def test_get_page_success(self, pages_mixin):
         """Test successful page retrieval."""