From 940dd0c08e2b9d16e9718f108bc521edc8607fa8 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 25 Mar 2026 10:48:57 -0400 Subject: [PATCH] feat(fable-mcp): add RSS feed management tools (list/add/remove) Co-Authored-By: Claude Sonnet 4.6 --- fable-mcp/fable_mcp/server.py | 43 ++++++++++++++++++++++++++- fable-mcp/fable_mcp/tools/briefing.py | 32 ++++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 fable-mcp/fable_mcp/tools/briefing.py diff --git a/fable-mcp/fable_mcp/server.py b/fable-mcp/fable_mcp/server.py index 492d068..bf0a361 100644 --- a/fable-mcp/fable_mcp/server.py +++ b/fable-mcp/fable_mcp/server.py @@ -5,7 +5,7 @@ from mcp.server.fastmcp import FastMCP from dotenv import load_dotenv from fable_mcp.client import FableClient -from fable_mcp.tools import notes, tasks, projects, milestones, search, chat, admin +from fable_mcp.tools import notes, tasks, projects, milestones, search, chat, admin, briefing load_dotenv() @@ -366,6 +366,47 @@ async def fable_get_app_logs( ) +# --------------------------------------------------------------------------- +# Briefing / RSS +# --------------------------------------------------------------------------- + + +@mcp.tool() +async def fable_list_rss_feeds() -> dict: + """List all RSS feeds configured in Fable for the current user.""" + async with FableClient() as client: + return await briefing.list_rss_feeds(client) + + +@mcp.tool() +async def fable_add_rss_feed( + url: str, + title: str = "", + category: str = "", +) -> dict: + """Add a new RSS feed to Fable. Title is optional — auto-populated from feed metadata. + + Args: + url: The RSS/Atom feed URL. + title: Optional display name override. + category: Optional category label (e.g. 'news', 'tech'). + """ + async with FableClient() as client: + return await briefing.add_rss_feed( + client, + url=url, + title=title or None, + category=category or None, + ) + + +@mcp.tool() +async def fable_remove_rss_feed(feed_id: int) -> dict: + """Remove an RSS feed from Fable by its ID.""" + async with FableClient() as client: + return await briefing.remove_rss_feed(client, feed_id=feed_id) + + # --------------------------------------------------------------------------- # Entry point # --------------------------------------------------------------------------- diff --git a/fable-mcp/fable_mcp/tools/briefing.py b/fable-mcp/fable_mcp/tools/briefing.py new file mode 100644 index 0000000..6c21c7e --- /dev/null +++ b/fable-mcp/fable_mcp/tools/briefing.py @@ -0,0 +1,32 @@ +"""MCP tools for Fable RSS feed management.""" +from __future__ import annotations + +from typing import Any + +from fable_mcp.client import FableClient + + +async def list_rss_feeds(client: FableClient) -> dict[str, Any]: + """List the user's RSS feeds.""" + return await client.get("/api/briefing/feeds") + + +async def add_rss_feed( + client: FableClient, + *, + url: str, + title: str | None = None, + category: str | None = None, +) -> dict[str, Any]: + """Add a new RSS feed. Title is optional — auto-populated from feed metadata.""" + payload: dict[str, Any] = {"url": url} + if title: + payload["title"] = title + if category: + payload["category"] = category + return await client.post("/api/briefing/feeds", json=payload) + + +async def remove_rss_feed(client: FableClient, *, feed_id: int) -> dict[str, Any]: + """Remove an RSS feed by ID.""" + return await client.delete(f"/api/briefing/feeds/{feed_id}")