refactor(mcp): Apply consistent error handling to all MCP tools

Comprehensive update to MCP server error handling:

Error Handling Improvements:
- Applied MCPErrorFormatter to all remaining MCP tool files
- Replaced all hardcoded timeout values with configurable timeout system
- Converted all simple string errors to structured error format
- Added proper httpx exception handling with detailed context

Tools Updated:
- document_tools.py: All 5 document management tools
- version_tools.py: All 4 version management tools
- feature_tools.py: Project features tool
- project_tools.py: Remaining 3 project tools (get, list, delete)
- task_tools.py: Remaining 4 task tools (get, list, update, delete)

Test Improvements:
- Removed backward compatibility checks from all tests
- Tests now enforce structured error format (dict not string)
- Any string error response is now considered a bug
- All 20 tests passing with new strict validation

This completes the error handling refactor for all MCP tools,
ensuring consistent client experience and better debugging.
This commit is contained in:
Rasmus Widing
2025-08-19 16:07:07 +03:00
parent cf3d7b17fe
commit ed6479b4c3
10 changed files with 255 additions and 142 deletions

View File

@@ -169,4 +169,8 @@ async def test_delete_document_not_found(mock_mcp, mock_context):
result_data = json.loads(result)
assert result_data["success"] is False
assert "not found" in result_data["error"]
# Error must be structured format (dict), not string
assert "error" in result_data
assert isinstance(result_data["error"], dict), "Error should be structured format, not string"
assert result_data["error"]["type"] == "not_found"
assert "not found" in result_data["error"]["message"].lower()

View File

@@ -95,7 +95,10 @@ async def test_create_version_invalid_field(mock_mcp, mock_context):
result_data = json.loads(result)
assert result_data["success"] is False
assert "Must be one of: docs, features, data, or prd" in result_data["error"]
# Error must be structured format (dict), not string
assert "error" in result_data
assert isinstance(result_data["error"], dict), "Error should be structured format, not string"
assert result_data["error"]["type"] == "validation_error"
@pytest.mark.asyncio

View File

@@ -171,4 +171,8 @@ async def test_get_project_not_found(mock_mcp, mock_context):
result_data = json.loads(result)
assert result_data["success"] is False
assert "not found" in result_data["error"]
# Error must be structured format (dict), not string
assert "error" in result_data
assert isinstance(result_data["error"], dict), "Error should be structured format, not string"
assert result_data["error"]["type"] == "not_found"
assert "not found" in result_data["error"]["message"].lower()

View File

@@ -210,4 +210,8 @@ async def test_delete_task_already_archived(mock_mcp, mock_context):
result_data = json.loads(result)
assert result_data["success"] is False
assert "already archived" in result_data["error"]
# Error must be structured format (dict), not string
assert "error" in result_data
assert isinstance(result_data["error"], dict), "Error should be structured format, not string"
assert result_data["error"]["type"] == "already_archived"
assert "already archived" in result_data["error"]["message"].lower()

View File

@@ -120,4 +120,8 @@ async def test_get_project_features_not_found(mock_mcp, mock_context):
result_data = json.loads(result)
assert result_data["success"] is False
assert "not found" in result_data["error"]
# Error must be structured format (dict), not string
assert "error" in result_data
assert isinstance(result_data["error"], dict), "Error should be structured format, not string"
assert result_data["error"]["type"] == "not_found"
assert "not found" in result_data["error"]["message"].lower()