RSS/Atom feed aggregation and article content extraction server with OAuth authentication and DynamoDB storage
Conduit demonstrates solid tool design with clear verb-noun naming (add_feed, remove_feed, list_feeds, get_feed_items, get_article_content, get_all_items) and comprehensive descriptions (avg 180 chars). All 6 tools have input schemas with typed parameters and descriptions. However, output schemas are not documented in the source code, and error handling lacks actionable recovery guidance. The server properly separates concerns (one tool per action) and includes helpful parameter defaults (limit=50, limit=200). Risk annotations (WRITE, DESTRUCTIVE, READ_ONLY) are present but not formally declared as tool annotations in the MCP sense.
Add a feed subscription for the authenticated user. Validates that *url* is a reachable RSS/Atom feed before storing. Propagates :exc:`ValueError` from :func:`feeds.validate_feed` if the URL is unreachable or not a valid feed; in that case storage is never touched.
Fetch a headline index across all subscribed feeds (no summaries). Returns title, link, published, and feed_url for each item — summaries are intentionally omitted to keep the payload small. Use this tool to scan and filter headlines, then call get_feed_items for full content on feeds of interest. *limit* is divided evenly across feeds (``per_feed_limit = limit // len(feeds)``). Returns an empty list if no feeds are subscribed. Sort and filter by ``published`` timestamp on the LLM side.
Fetch and extract the full plain-text content of an article from its URL. *url* should be the ``link`` field from a feed item returned by :func:`get_feed_items` or :func:`get_all_items`. No subscription check is performed — any URL the authenticated user supplies is accepted. Returns a dict with ``title``, ``author``, ``published``, ``content`` (plain text, up to 100 000 characters), ``truncated`` (bool), and ``error`` (empty string on success). On failure ``error`` is non-empty and ``content`` is an empty string — this tool never raises.
Fetch live items from a single subscribed feed. Args: url: Feed URL. Must be an active subscription for this user. limit: Maximum number of items to return. Raises: ValueError: If the authenticated user is not subscribed to *url*.
Output schemas not documented. Tools return structured data (title, author, published, content, truncated, error for get_article_content; url, label, addedAt for list_feeds) but LLMs cannot see the response structure in the tool definition. This forces LLMs to guess field names and types.
Error handling lacks actionable recovery guidance. remove_feed and get_feed_items mention ValueError but do not explain what the LLM should do next (e.g., 'Call list_feeds() to see subscribed URLs'). get_article_content returns error in response but does not guide retry or fallback.
Tool annotations (readOnlyHint, destructiveHint, idempotentHint) are present in metadata (WRITE, DESTRUCTIVE, READ_ONLY) but not formally declared in MCP tool definitions. FastMCP may not expose these to the client.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 65 | 2026-07-28+ | v2 |
Return all feed subscriptions for the authenticated user. Each item includes ``url``, ``label``, and ``addedAt``.
Remove a feed subscription for the authenticated user.
remove_feed description is minimal (27 chars). Does not explain consequences (subscription deleted, items no longer fetched) or whether this is reversible. LLMs need explicit state-change warnings for destructive operations.
get_article_content accepts any URL without subscription check (per docstring), but get_feed_items enforces subscription. This asymmetry is documented but could confuse LLMs about which tools are safe to call with arbitrary URLs.