MCP server for Inoreader - integrates Inoreader RSS reader with Claude for reading, searching, and analyzing articles
This MCP server exposes 8 tools with schemas and descriptions present, but exhibits significant quality gaps that prevent a higher score. Naming follows verb_noun convention (list_, search_, get_, mark_, etc.), which is correct. However, descriptions are consistently too brief (most are 30-80 chars vs. the 194-char production baseline), leaving LLMs without sufficient context for tool selection and parameter reasoning. Parameter descriptions are missing or trivial throughout. The server lacks output schema documentation, responses are returned as unstructured strings rather than structured JSON objects, forcing the LLM to parse free-text output. Error handling is minimal (generic catch-all try/except blocks that return error strings). No confirmation patterns for destructive operations (mark_as_read writes without user confirmation). Tool composition is reasonable but output IDs and references needed for chaining are embedded in text rather than structured fields. The server accepts some human-friendly identifiers (feed_id, article_id as strings) but provides no validation or recovery guidance when lookups fail.
Analyze multiple articles for trends, sentiment, or keywords
Get full content of a specific article
List recent articles with optional filters
List all subscribed feeds in Inoreader
Mark articles as read
Search for articles by keyword
Get statistics about unread articles
Output schemas not documented. All tools return unstructured strings (str) rather than structured JSON objects. The LLM must parse free-text responses like 'Found 5 articles: ...' without knowing the structure of returned article objects, making chaining fragile and forcing token-wasteful parsing.
Tool descriptions are too brief (25 - 50 chars vs. 194-char baseline). 'Generate a summary of an article' lacks context: does it use LLM summarization, Inoreader's built-in summary, or extraction? When should the agent call this vs. get_content? These ambiguities force the LLM to guess.
Inferred effective spec: <=2025-11-25.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | D | 50 | <=2025-11-25 | v2 |
| 2026-03-09 | D | 53 | 2024-11-05+ | v1 |
Generate a summary of an article
Parameter descriptions missing or trivial. 'limit' appears across multiple tools with identical description 'Number of articles to return (default: 20)' but no constraints (min/max). What happens if limit=0? limit=1000000? LLM may pass absurd values that break pagination or hang the API.
No confirmation or dry-run pattern for destructive operations. 'mark_as_read' modifies state without requiring user confirmation, dry-run capability, or rollback option. An agent error could bulk-mark hundreds of articles as read with no recovery path.
Error handling is generic and non-actionable. All exceptions caught with try/except return string messages like 'Error listing articles: {str(e)}' (see tools.py). The LLM learns nothing about why it failed or what to do next. No categorization of retryable vs. fatal errors, no suggestions for recovery.
'analyze' tool has vague 'analysis_type' enum with four options (summary, trends, sentiment, keywords) but no documentation of what each produces, when to use each, or expected output structure. LLM cannot reason about which analysis to request without trial-and-error.
Parameter 'days' appears in list_articles and search but has no explicit validation. Can days be 0? Negative? 365? The default is 7, but no bounds are enforced. An LLM might pass days=999999, causing unexpected API behavior or timeout.
Responses do not include IDs and references needed for chaining. For example, inoreader_list_articles returns formatted text; the LLM cannot extract article_ids to pass to inoreader_get_content or inoreader_mark_as_read without parsing the text output.