Mixed quality across tools. Strengths: all 10 tools have descriptions (194-char baseline met on average); most parameter schemas are present with types and descriptions; clear verb naming (get_, create_). Weaknesses: no output schemas documented for any tool; no error handling or recovery guidance; no tool annotations (readOnlyHint/destructiveHint); many parameters lack constraints (enums); descriptions lack 'when to use' context; no mention of idempotency or retry safety for write operations. The create_note tool (WRITE operation) lacks safeguards and confirmation patterns. Pagination present in 4 tools but inconsistently implemented.
Tools (10)
create_notewritesource verified65/100
Create a new zk note with the specified title and path.
No output/return schemas documented for any tool. LLMs cannot plan downstream calls or extract the right fields from responses. Baseline: 100% of A+ tools have documented return types.
create_note (WRITE operation) lacks confirmation/dry-run pattern and explicit error handling. No description states the operation modifies state or is irreversible. Agents need to know which calls are safe to retry.
No tool annotations (readOnlyHint/destructiveHint/idempotentHint). create_note should be marked destructive; read-only tools (get_*) lack readOnlyHint annotation.
all
Recommendations
Document output/return schemas for all 10 tools. Specify field names, types, and what data each field contains. Example for get_notes: { notes: [{ path: string, title: string, tags: string[], created_at: ISO8601, modified_at: ISO8601, link_count: int }], total_count: int, page: int, per_page: int }
Add tool annotations via Pydantic field metadata or tool metadata. Mark create_note with @tool(destructiveHint=true); all read-only tools with @tool(readOnlyHint=true). Idempotent tools get idempotentHint=true.
Expand create_note description to 50-150 chars and include: (1) What happens if path already exists? (2) Required vs optional fields? (3) Default content? (4) Does it auto-create directories? Example: 'Create a new zk note at the specified path. Fails if path exists. Title and content optional; defaults to empty note. Auto-creates parent directories.'
Add 'when to use' guidance to get_related_notes, get_link_to_notes, get_linked_by_notes. Explain the difference: get_link_to_notes returns notes THIS note links to (outbound); get_linked_by_notes returns notes that link TO this note (inbound); get_related_notes returns suggestions for new links based on content similarity or shared tags.
Document parameter semantics for title_match_mode, search_match_mode, tags_match_mode. Clarify: AND = all patterns must match (intersection); OR = any pattern matches (union). Example: title_patterns=['foo', 'bar'] + title_match_mode='AND' returns only notes with both 'foo' AND 'bar' in title.
Add min/max constraints to numeric parameters. page: min=1, per_page: min=1, max=100. Document: 'Pagination defaults to page 1, per page 10. Maximum 100 items per page to prevent context window exhaustion.'
Score history
Overall score trend
↑ 9 points across a rubric change (v1 → v2)
57/100
Scored
Grade
Overall
Spec posture
Rubric
2026-09-22
D
57
2026-07-28+
v2
2026-03-09
F
48
-
v1
read onlysource verified72/100
Find notes that could be good candidates for linking.
get_tagless_notesread onlysource verified67/100
Retrieve all notes that have no tags assigned.
get_tagsread onlysource verified67/100
Retrieve all available tags from the zk note collection.
Descriptions lack 'when to use' context and action guidance. E.g. get_related_notes says 'Find notes that could be good candidates for linking' but does not explain the algorithm (similarity, content overlap, tag proximity). Baseline: descriptions should state WHAT, WHEN, and any prerequisites.
Parameters like 'title_match_mode', 'search_match_mode', 'tags_match_mode' accept enums (AND/OR) but lack examples or clarification of semantics. Does AND mean all must match, or none must be absent? Undocumented parameter relationships cause misuse.
create_note description (14 chars: 'Create a new zk note') is below the 20-char floor for actionable descriptions. Does not state what happens if the path already exists, how conflicts are resolved, or what fields are required.
get_notes pagination parameters (page, per_page) have no documented limits. Can LLMs pass page=1000000 or per_page=10000? Unbounded numeric params invite absurd values and DoS.
No error handling or recovery guidance. Tools do not specify what errors are retryable, what user-fixable errors look like, or what to do if a note is not found. Pattern: recovery-guide missing.
Inconsistent pagination. get_notes includes page + per_page + defaults; get_tags has no pagination at all. If there are hundreds of tags, a single call could blow context. Either paginate all list tools or document why get_tags is exempt.
get_tagsget_tagless_notesget_random_note
Implement error handling and recovery patterns. For each tool, specify: (1) What errors can occur? (2) Are they retryable? (3) What should the LLM do? Example: 'Note not found at {path}. Call get_notes() to search for it, or create it with create_note().' Provide actionable guidance, not stack traces.
Paginate get_tags output. If there are >50 tags, split into pages. Document: 'Returns up to 50 tags per request. Use page and per_page parameters to iterate.' This prevents single calls from overwhelming context.
For get_note_content, document what happens with the optional 'headings' parameter. Does it extract just those sections? Include section links? Fails if heading not found? Clarify: 'If headings is provided, extracts only those h2 sections and returns them in order. Fails if any heading not found. Omit headings to return the entire note content.'
Add idempotency guidance for create_note. Is it safe to retry if the first call hung? Does it check for duplicates by path? Will calling twice with the same path create two notes or return the existing one? Specify: 'Idempotent by path. If called twice with the same path, returns the existing note (no duplicate created). Safe to retry on network failure.'