fix: address PR review feedback (round 2)
Bug fixes: - Catch OCSError/HTTPStatusError in all server tools, convert to McpError - Guard update_collective against empty body (raise ValueError) - Use restore_page response data in status message ADR-017 annotation fix: - Distinguish "remove" (reversible association) from "delete" (permanent): remove_tag and deck_remove_label_from_card no longer set destructiveHint - Update annotation test to exclude "remove" from destructive keywords Data model improvements: - Add trashTimestamp field to PageInfo - Create ListTrashedPagesResponse with is_trash context flag - Add collective_id to ListTagsResponse Test robustness: - Read NC credentials from environment variables (not hardcoded) - Filter landing page by parentId == 0 instead of assuming pages[0] Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
3393cd9756
commit
44a27bd9e9
@@ -58,8 +58,9 @@ async def test_destructive_tools_have_correct_annotations(nc_mcp_client: ClientS
|
||||
"""Verify destructive operations are marked correctly."""
|
||||
tools = await nc_mcp_client.list_tools()
|
||||
|
||||
# Known destructive operations
|
||||
destructive_keywords = ["delete", "remove", "revoke"]
|
||||
# Known destructive operations (permanently delete data).
|
||||
# "remove" is excluded — removing associations (labels, tags) is reversible.
|
||||
destructive_keywords = ["delete", "revoke"]
|
||||
|
||||
for tool in tools.tools:
|
||||
has_destructive_keyword = any(
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import uuid
|
||||
|
||||
import httpx
|
||||
@@ -11,9 +12,10 @@ from mcp import ClientSession
|
||||
logger = logging.getLogger(__name__)
|
||||
pytestmark = pytest.mark.integration
|
||||
|
||||
# Nextcloud credentials for direct API cleanup (matches docker-compose.yml)
|
||||
_NC_BASE = "http://localhost:8080"
|
||||
_NC_AUTH = ("admin", "admin")
|
||||
# Nextcloud credentials from environment (matches .envrc / docker-compose.yml defaults)
|
||||
_NC_BASE = os.environ.get("NEXTCLOUD_HOST", "http://localhost:8080")
|
||||
_NC_USER = os.environ.get("NEXTCLOUD_USERNAME", "admin")
|
||||
_NC_PASS = os.environ.get("NEXTCLOUD_PASSWORD", "admin")
|
||||
_OCS_HEADERS = {
|
||||
"OCS-APIRequest": "true",
|
||||
"Accept": "application/json",
|
||||
@@ -38,13 +40,15 @@ async def temporary_collective(nc_mcp_client: ClientSession):
|
||||
collective_id = data["id"]
|
||||
logger.info(f"Created temporary collective: {name} (ID: {collective_id})")
|
||||
|
||||
# Get the landing page ID (auto-created with each collective)
|
||||
# Get the landing page ID — filter by parentId == 0 (root page)
|
||||
pages_result = await nc_mcp_client.call_tool(
|
||||
"collectives_get_pages",
|
||||
{"collective_id": collective_id},
|
||||
)
|
||||
pages_data = json.loads(pages_result.content[0].text)
|
||||
landing_page_id = pages_data["pages"][0]["id"]
|
||||
root_pages = [p for p in pages_data["pages"] if p["parentId"] == 0]
|
||||
assert root_pages, "Expected at least one root page (landing page)"
|
||||
landing_page_id = root_pages[0]["id"]
|
||||
|
||||
yield {
|
||||
"id": collective_id,
|
||||
@@ -54,7 +58,9 @@ async def temporary_collective(nc_mcp_client: ClientSession):
|
||||
|
||||
# Cleanup: trash and permanently delete the collective via direct OCS API
|
||||
try:
|
||||
async with httpx.AsyncClient(base_url=_NC_BASE, auth=_NC_AUTH) as client:
|
||||
async with httpx.AsyncClient(
|
||||
base_url=_NC_BASE, auth=(_NC_USER, _NC_PASS)
|
||||
) as client:
|
||||
api = "/ocs/v2.php/apps/collectives/api/v1.0"
|
||||
await client.delete(
|
||||
f"{api}/collectives/{collective_id}",
|
||||
|
||||
Reference in New Issue
Block a user