MCP server for book-memex library management. Provides tools for querying/updating book metadata, managing marginalia (notes/highlights), reading sessions, and full-text search across ebook libraries.
The server has 21 tools with generally clear naming and functional purpose, but suffers from inconsistent schema definition rigor and missing output documentation. Most tools have verb_noun naming (get_, list_, update_, delete_, search_) which is good. Descriptions are present on all tools (ranging 100-400 chars) and cover WHAT the tool does. However, parameter descriptions are minimal or missing, and NO tools have documented output schemas, critical omission for agent composition. The server conflates input parameter arrays/objects without explaining their internal structure, forcing LLMs to guess at format. Error handling exists but is not systematically described in tool docs. Tools like `execute_sql` accept complex parameters (sql string, params array) but do not explain the expected structure of params or the shape of returned rows. Tools like `update_marginalia` lack field descriptions for optional parameters. Only 3 tools show genuine attempt at structured input documentation (update_books with batch format, add_marginalia with position object). Risk scores (READ_ONLY, WRITE, DESTRUCTIVE) are helpful but do not compensate for missing schema detail.
Create marginalia linked to 0 or more books by URI. A single book + location = highlight; single book no location = book_note; no books = collection_note; multiple books = cross_book_note.
Soft-delete a marginalia (archive it). Pass hard=True to irreversibly delete.
Soft-delete (archive) or hard-delete a reading session.
End a reading session by uuid. Idempotent: ending an already-ended session returns it unchanged.
Execute a read-only SQL SELECT query against the library database. Use positional ? placeholders for parameters. Returns columns, rows, and row_count. Maximum 1000 rows returned (truncated flag set if more exist).
Get a marginalia record by its uuid, or by its full book-memex://marginalia/<uuid> URI. Both are accepted.
NO OUTPUT SCHEMAS DOCUMENTED. Tools return complex objects (rows, snippets, records) but LLMs are given zero guidance on response structure. This breaks tool composition, agents cannot reliably extract fields for downstream calls.
PARAMETER STRUCTURE UNDOCUMENTED. Tools like execute_sql accept 'params' array but never explain what the array elements are or how they bind to placeholders. add_marginalia has a 'position' object parameter with NO description of its internal fields (anchor? offset?).
PARAMETER DESCRIPTIONS SPARSE OR MISSING. Many optional parameters lack guidance on valid values, ranges, or effects. E.g., update_marginalia's 'color' parameter, what format? hex, RGB, CSS names? update_books' batch dict structure, what field names are valid? This forces LLMs to guess.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 60 | 2026-07-28+ | v2 |
Get the current reading progress (anchor + percentage) for a book.
Resolve a book-memex:// URI to its record. Dispatches on kind: book-memex://book/<unique_id>, book-memex://marginalia/<uuid>, or book-memex://reading/<uuid>. This is the archive's record-resolution contract tool, used by the federation to follow cross-archive trail steps. Archived records still resolve.
Get the library database schema: tables, columns, relationships, and enums. Use this to understand the data model before writing SQL queries.
Get a single content segment by ID. Returns the segment's text, metadata, and anchor.
Get multiple content segments by their IDs. Efficient batch retrieval.
List marginalia. With book_id, lists that book's marginalia; with book_id omitted/null, lists collection notes (marginalia attached to no book). Archived entries are excluded by default (set include_archived=True to include them). Default limit is 50. Optional scope filter selects one of: highlight (passage-anchored), book_note (whole-book note), collection_note (not attached to any book), cross_book_note (spans 2+ books).
List reading sessions for a book.
Restore a soft-deleted marginalia (clear archived_at).
Restore a soft-deleted reading session.
FTS5 search within a single book. Returns ranked snippets with an anchor and a pre-built URI fragment. Safe against FTS5 operator injection by default (advanced=True opts into raw FTS5 syntax).
FTS5 search across every book. Same response shape as search_book_content; results include book_uri per hit.
Set reading progress for a book. Rejects backward progress unless force=True.
Start a reading session for a book. Optional start_anchor (CFI or page).
Update book metadata in batch. Pass a dict of book_id -> {field: value, ...}. Scalar fields: any Book or PersonalMetadata column. Collection ops: add_tags/remove_tags, add_authors/remove_authors, add_subjects/remove_subjects. Special: merge_into (mutually exclusive with other fields).
Update editable fields of a marginalia by uuid.
NO ENUM CONSTRAINTS ON MULTI-VALUE PARAMETERS. Tools like list_marginalia 'scope' parameter lists four valid values in the description text, but no JSON Schema enum. Tools like search_book_content 'advanced' flag is described casually rather than as a boolean. LLMs cannot reliably validate before calling.
DESTRUCTIVE OPERATIONS LACK CONFIRMATION PATTERN. Tools delete_marginalia and delete_reading_session support hard=True for irreversible deletion but offer no dry-run or confirmation step. Agents can trivially erase records without safeguards.
PAGINATION NOT MENTIONED. Tools like list_marginalia and list_reading_sessions return results but do NOT document offset/limit semantics or whether results are truncated. LLMs cannot safely paginate large result sets.
AMBIGUOUS OR MISSING FIELD MAPPINGS. Tools expect book-memex:// URIs but never explain the URI format or how they map to internal book_id integers. get_record accepts URIs with kind dispatch but does not list valid kinds or example formats for agents to construct URIs.
NO ERROR RECOVERY GUIDANCE. Tools have no documented error cases or recovery hints. If execute_sql fails, what went wrong? Syntax error? Permission denied? Table not found? LLMs get no actionable guidance.