claudemcp demonstrates basic tool structure but exhibits significant quality gaps across naming, descriptions, and schema documentation. All 8 tools are defined with input schemas and descriptions, but most descriptions are generic (20-50 chars), parameter descriptions are minimal or absent, and output schemas are undocumented. The server lacks error recovery guidance, has no tool annotations, and parameter constraints are under-specified. This is typical of community STDIO servers, functional but well below production grade. Naming is reasonable (verb_noun pattern) but descriptions lack LLM optimization, and there is no indication of pagination support or result limits for potentially large datasets (e.g., search, list_notes).
All tool descriptions are generic and under 50 characters. LLM cannot distinguish when to call search vs fetch_page, or differentiate search_notes from search. Descriptions lack 'WHEN to use' context and recovery hints.
No input parameter descriptions. 'query' param in search() and search_notes() lacks guidance on format, length, or semantics. 'name' param in read_note/write_note/delete_note does not clarify whether to include .md extension or not (code shows without, but LLM must infer).
Output schemas are undocumented. Tools return unstructured responses (e.g., search returns list[dict] but structure is inferred from code, not declared). LLM cannot know what fields to expect or chain results to downstream tools.
list_notes
Recommendations
Expand all tool descriptions to 150-250 characters. Include WHAT (e.g., 'Searches all Markdown notes for a query string'), WHEN (e.g., 'Use to find notes by topic or keyword'), and RETURNS (e.g., 'Returns matching note names and line excerpts'). Example: 'Calculate: Safely evaluate a mathematical expression using +, -, *, /, sqrt, sin, cos, log, pi, e. Returns a numeric result. Use for math problems, unit conversions, date calculations. Does not support variables or user-defined functions.'
Add parameter descriptions for all tools. For each parameter, state: type, valid range/format, required behavior. E.g., 'expression (string): A mathematical expression using operators (+,-,*,/,**) and functions (sqrt, sin, cos, log, ceil, floor, abs, min, max, round). Max 500 chars. Example: sqrt(16) + pi. Raises error if expression contains undefined variables or unsupported operations.'
Document output schemas in tool descriptions. E.g., 'Returns: {success: bool, result: list[{name: string, matches: string[], size: int}], total: int}'. Include field descriptions and types so LLMs can compose results to downstream tools.
Implement pagination for list_notes() and search_notes(). Add 'limit' (default 20, max 100) and 'offset' (default 0) parameters. Return {notes: [...], total: int, next_offset: int|null} so agent can iterate.
Add max_results parameter to fetch_page() with documented limit (e.g., max 10000 bytes). Clarify that max_length truncates at paragraph boundary, not mid-word, to preserve readability.
No result limiting or pagination for potentially large datasets. list_notes() and search_notes() return all matches. search() accepts max_results (default 5) but fetch_page() accepts max_length (default 5000 bytes), inconsistent semantics. No pagination tokens or cursor support.
Error handling lacks recovery guidance. read_note, delete_note raise FileNotFoundError with minimal context. No suggestion of alternatives ('Did you mean X?'), no list of available notes, no actionable next steps for the LLM.
Tool annotations missing. No readOnlyHint, destructiveHint, or idempotentHint declared. Client cannot determine which tools are safe to retry or require confirmation. delete_note is destructive but unmarked.
No dry-run or confirmation for delete_note. Agents cannot preview or confirm destruction of user data. Should support a 'confirm' flag or separate confirm_delete tool.
search() and fetch_page() lack input validation and timeout specification. No guidance on URL format, max payload size, retry behavior, or timeout values. LLM could pass malformed URLs or trigger long hangs.
calculate() lacks parameter description. 'expression' parameter has no guidance on supported functions, operators, range limits, or edge cases (division by zero, negative sqrt, etc.). Code supports 'pi' and 'e' but this is not documented.
Composition issue: write_note always overwrites. No upsert mode, merge, or conflict detection. If two agents call write_note('summary', ...) concurrently, one silently loses. No idempotency guarantee.
write_note
Enhance error messages with recovery hints. E.g., 'Note "meeting" not found. Available notes: meeting-2024-01-15, meeting-notes, meeting-prep. Did you mean one of these?'
Add tool annotations. Mark delete_note with destructiveHint: true and idempotent: false. Mark read_note, list_notes, search_notes, search, fetch_page, calculate with readOnlyHint: true and idempotent: true.
Add a 'confirm_delete' boolean parameter to delete_note (default false). If true, return {would_delete: name, size: bytes, modified: ISO8601} without deletion; only delete when called again with confirm_delete: true and the same name. Prevents accidental irreversible operations.
Document constraints for calculate(). E.g., 'expression must be < 500 chars, must contain only numbers, operators (+,-,*,/,**,%), and functions (sqrt, sin, cos, tan, log, log10, ceil, floor, abs, min, max, round, pi, e). Division by zero returns error "DivisionByZeroError".'
For search() and fetch_page(), document timeout (suggest 10s for fetch_page, 5s for search). Add error handling: 'Returns {error: "timeout", retry_after: 5} if request exceeds timeout. HTTP errors return {error: "http_<code>", status: int, body_preview: string}.'
Add idempotency semantics to write_note. Either: (1) accept optional 'if_modified_since' timestamp to support conditional writes, or (2) document that repeated write_note calls with same name+content are safe (idempotent). Currently the tool is re-executable but the behavior is not declared.
Avoid generic 'Search' tool name, rename to 'search_web' to distinguish from search_notes. Add to description: 'Searches the public web via DuckDuckGo. Use for external research. search_notes is for internal note retrieval.' This reduces LLM confusion when both tools are available.
Validate all external inputs early. For fetch_page(url), check that url is http(s)://, not file:// or javascript:. For search(query), reject queries > 1000 chars. Return structured validation errors: {error: "invalid_url", reason: "must start with http:// or https://", received: url}.
Add structured output support. Instead of returning a raw string from fetch_page(), return {url: string, title: string|null, content: string, length: int, truncated: bool}. This enables chaining and lets LLMs know when content was truncated.