An MCP server that manages personalized book recommendations based on user reading preferences, genres, and book ratings. Uses GitHub OAuth for authentication and Cloudflare Workers AI for generating recommendations.
The server defines 4 tools with schemas and descriptions, but exhibits significant gaps in consistency and LLM-optimized design. All tools are explicitly registered in src/index.ts with Zod schemas and descriptions. However, descriptions vary widely in completeness; parameter descriptions are present but minimal; output schemas are not formally documented; and error handling provides no recovery guidance. The tool naming follows verb_noun convention (getProfile, addGenre, rateBook, getRecommendations), which is correct. However, the descriptions lack the specificity production tools require, they state WHAT but rarely WHEN or WHY. For example, 'View your current reading preferences and statistics' does not explain when an agent should call this vs alternatives or what structure to expect. Parameter descriptions are one-liners without range/format details. No output schema is documented, forcing LLMs to infer structure from the implementation. Error handling is implicit (duplicate genre check returns a text message, but no structured error codes). The server is stateless and uses HTTP with Hono, meeting transport requirements. All tools have input schemas with proper Zod types and constraints (e.g., rating min:1 max:5), but schemas are inferred from Zod definitions rather than explicitly documented in the MCP response. Overall, this is a functional but below-average implementation that would require significant polish before production use.
Add a book genre you enjoy reading
View your current reading preferences and statistics
Get personalized book recommendations based on your preferences
Rate a book you've read to improve future recommendations
Tool descriptions lack LLM-optimization guidance: missing WHEN/WHY context and no downstream consequences stated. Examples: 'View your current reading preferences' does not explain when to call instead of getRecommendations, or that this is a READ_ONLY operation that can be retried safely.
Output schemas are undocumented. Tools return {content: [{type, text}]} but no formal schema is provided in tool registration. LLMs cannot plan downstream composition or extract structured data.
Parameter descriptions are minimal and missing format/constraint details. 'genre': 'A book genre you like (e.g., science fiction, mystery, romance)' uses example values instead of format constraints; 'count': description lacks the existing min/max bounds already present in Zod schema; 'rating': description is vague on what rating means in user/system context.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | D | 52 | 2026-07-28+ | v2 |
| 2026-03-09 | D | 57 | - | v1 |
Error handling is implicit and non-standard. addGenre returns duplicate-check as text in content block with no structured error type; no recovery guidance provided ('Try getProfile() to see current genres' would help LLMs self-correct).
Tool annotations missing. No readOnlyHint, destructiveHint, or idempotentHint declared. getProfile and getRecommendations are READ_ONLY but server does not signal this; rateBook and addGenre are WRITE but have no destructive or side-effect warnings.
getRecommendations tool is incomplete: prompt string is cut off mid-sentence in source ('let prompt = `Recommend ${count} books f'). Implementation is non-functional; AI recommendations engine is missing or stub.