fix(mcp): add directory-compliant tool annotations#1031
Conversation
Anthropic directory review reads ToolAnnotations.title rather than FastMCP's top-level title kwarg, so every tool was flagged as missing a title annotation and write tools as missing readOnlyHint. - add "title" inside every tool's annotations dict - add explicit readOnlyHint: False to the seven write tools - flip edit_note to destructiveHint: True (find_replace and replace_section overwrite existing content, so the tool is not purely additive) - cite target API docs in build_context and read_content descriptions per the directory criteria for tools accepting freeform paths - add a table-driven contract test asserting the wire-level annotations for every registered tool, so new tools fail CI until they declare directory-compliant annotations Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: phernandez <paul@basicmachines.co>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 90c843eed6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| "canvas": {"readOnlyHint": False, "destructiveHint": False}, | ||
| "create_memory_project": {"readOnlyHint": False, "destructiveHint": False}, |
There was a problem hiding this comment.
Set write tool destructive hints to true
For the directory-submission contract, these expectations now lock canvas and create_memory_project to destructiveHint: False even though both mutate user data (canvas creates/updates a .canvas file, and project creation changes project/config or cloud state); move_note below has the same false expectation. Anthropic's review criteria require destructiveHint: true for tools that modify or delete data, so this test can pass while the submission still fails; update the write-tool annotations and expectations to true.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Partially adopted in 8f658cc. canvas is now destructiveHint: True — it falls back to PUT when the file exists, replacing its content, same standard as write_note/edit_note. move_note and create_memory_project deliberately stay False: move refuses to overwrite an existing destination (entity_service raises "file already exists at destination path") and preserves all content, and project creation is purely additive and errors if the target exists. Per the MCP spec, destructiveHint: false asserts additive/non-destructive updates, which both satisfy — and keeping them non-destructive lets clients allowlist bulk lifecycle moves without per-call confirmation. Rationale is recorded in the contract test table.
Codex review follow-up: canvas falls back to PUT when the file already exists, replacing its content, so it warrants destructiveHint: True by the same standard as write_note and edit_note. move_note and create_memory_project stay non-destructive deliberately: move refuses to overwrite an existing destination and preserves all content, and project creation is purely additive and errors if the target exists. Rationale recorded in the contract test table. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: phernandez <paul@basicmachines.co>
Summary
Prep for the Anthropic directory submission. The directory validator reads
ToolAnnotations.title(inside theannotationsobject), not FastMCP's top-leveltitle=kwarg, so the review flagged every tool as missing a title annotation and every write tool as missingreadOnlyHint."title"inside every tool'sannotationsdict (23 tools, plus the two disabled MCP-UI tools for consistency)readOnlyHint: Falseto the seven write toolsedit_notetodestructiveHint: True—find_replace/replace_sectionoverwrite existing content, so the tool is not purely additive; matches the directory criteria requiringdestructiveHint: truefor tools modifying databuild_contextandread_contentdescriptions (the criteria require tools accepting freeform paths to reference their API; both URLs verified live on docs.basicmemory.com)test_mcp_tool_annotations_meet_directory_requirementstotests/mcp/test_tool_contracts.py: a table-driven contract test asserting the wire-level annotations (to_mcp_tool()) for every registered tool — unannotated new tools fail CI with instructionsTest plan
pytest tests/mcp/test_tool_contracts.py tests/mcp/test_tool_edit_note.py— 62 passedpytest tests/mcp/test_ui_sdk.py tests/mcp/test_tool_contracts.py— 11 passed (proves the contract test is independent of whether the optional UI tools registered)ruff check/ruff format --check/ty check src testscleanFollow-up
basic-memory-cloud pins this repo by git rev; after this lands, the pin bump + deploy must happen before re-running the directory review (companion PR updates the cloud-side
activity_feedannotations and removescanvasfrom the hosted server).🤖 Generated with Claude Code