Skip to content

Commit cdc74f7

Browse files
committed
A couple of changes to RSS tool
1 parent 0486f94 commit cdc74f7

File tree

3 files changed

+75
-12
lines changed

3 files changed

+75
-12
lines changed

README.md

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ Strands Agents Tools is a community-driven project that provides a powerful set
5555
- 🔄 **Multiple tools in Parallel** - Call multiple other tools at the same time in parallel with Batch Tool
5656
- 🔍 **Browser Tool** - Tool giving an agent access to perform automated actions on a browser (chromium)
5757
- 📈 **Diagram** - Create AWS cloud diagrams, basic diagrams, or UML diagrams using python libraries
58+
- 📰 **RSS Feed Manager** - Subscribe, fetch, and process RSS feeds with content filtering and persistent storage
5859

5960
## 📦 Installation
6061

@@ -67,7 +68,7 @@ pip install strands-agents-tools
6768
To install the dependencies for optional tools:
6869

6970
```bash
70-
pip install strands-agents-tools[mem0_memory, use_browser]
71+
pip install strands-agents-tools[mem0_memory, use_browser, rss]
7172
```
7273

7374
### Development Install
@@ -130,6 +131,7 @@ Below is a comprehensive table of all available tools, how to use them with an a
130131
| batch| `agent.tool.batch(invocations=[{"name": "current_time", "arguments": {"timezone": "Europe/London"}}, {"name": "stop", "arguments": {}}])` | Call multiple other tools in parallel. |
131132
| browser | `browser = LocalChromiumBrowser(); agent = Agent(tools=[browser.browser])` | Web scraping, automated testing, form filling, web automation tasks |
132133
| diagram | `agent.tool.diagram(diagram_type="cloud", nodes=[{"id": "s3", "type": "S3"}], edges=[])` | Create AWS cloud architecture diagrams, network diagrams, graphs, and UML diagrams (all 14 types) |
134+
| rss | `agent.tool.rss(action="subscribe", url="https://example.com/feed.xml", feed_id="tech_news")` | Manage RSS feeds: subscribe, fetch, read, search, and update content from various sources |
133135

134136
\* *These tools do not work on windows*
135137

@@ -504,6 +506,46 @@ result = agent.tool.diagram(
504506
)
505507
```
506508

509+
### RSS Feed Management
510+
511+
```python
512+
from strands import Agent
513+
from strands_tools import rss
514+
515+
agent = Agent(tools=[rss])
516+
517+
# Subscribe to a feed
518+
result = agent.tool.rss(
519+
action="subscribe",
520+
url="https://news.example.com/rss/technology"
521+
)
522+
523+
# List all subscribed feeds
524+
feeds = agent.tool.rss(action="list")
525+
526+
# Read entries from a specific feed
527+
entries = agent.tool.rss(
528+
action="read",
529+
feed_id="news_example_com_technology",
530+
max_entries=5,
531+
include_content=True
532+
)
533+
534+
# Search across all feeds
535+
search_results = agent.tool.rss(
536+
action="search",
537+
query="machine learning",
538+
max_entries=10
539+
)
540+
541+
# Fetch feed content without subscribing
542+
latest_news = agent.tool.rss(
543+
action="fetch",
544+
url="https://blog.example.org/feed",
545+
max_entries=3
546+
)
547+
```
548+
507549
## 🌍 Environment Variables Configuration
508550

509551
Agents Tools provides extensive customization through environment variables. This allows you to configure tool behavior without modifying code, making it ideal for different environments (development, testing, production).
@@ -659,6 +701,14 @@ The Mem0 Memory Tool supports three different backend configurations:
659701
| STRANDS_BROWSER_WIDTH | Default width of the browser | 1280 |
660702
| STRANDS_BROWSER_HEIGHT | Default height of the browser | 800 |
661703

704+
#### RSS Tool
705+
706+
| Environment Variable | Description | Default |
707+
|----------------------|-------------|---------|
708+
| STRANDS_RSS_MAX_ENTRIES | Default setting for maximum number of entries per feed | 100 |
709+
| STRANDS_RSS_UPDATE_INTERVAL | Default amount of time between updating rss feeds in minutes | 60 |
710+
| STRANDS_RSS_STORAGE_PATH | Default storage path where rss feeds are stored locally | ~/.strands/rss_feeds |
711+
662712

663713
## Contributing ❤️
664714

pyproject.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,10 @@ diagram = [
9797
"networkx>=2.8.0,<4.0.0",
9898
"diagrams>=0.23.0,<1.0.0",
9999
]
100+
rss = ["feedparser>=6.0.10,<7.0.0", "html2text>=2020.1.16,<2021.0.0"]
100101

101102
[tool.hatch.envs.hatch-static-analysis]
102-
features = ["mem0_memory", "local_chromium_browser", "agent_core_browser", "agent_core_code_interpreter", "a2a_client", "diagram"]
103+
features = ["mem0_memory", "local_chromium_browser", "agent_core_browser", "agent_core_code_interpreter", "a2a_client", "diagram", "rss"]
103104
dependencies = [
104105
"strands-agents>=1.0.0",
105106
"mypy>=0.981,<1.0.0",
@@ -118,7 +119,7 @@ lint-check = [
118119
lint-fix = ["ruff check --fix"]
119120

120121
[tool.hatch.envs.hatch-test]
121-
features = ["mem0_memory", "local_chromium_browser", "agent_core_browser", "agent_core_code_interpreter", "a2a_client", "diagram"]
122+
features = ["mem0_memory", "local_chromium_browser", "agent_core_browser", "agent_core_code_interpreter", "a2a_client", "diagram", "rss"]
122123
extra-dependencies = [
123124
"moto>=5.1.0,<6.0.0",
124125
"pytest>=8.0.0,<9.0.0",

src/strands_tools/rss.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import logging
33
import os
44
import re
5+
import tempfile
56
from datetime import datetime
67
from typing import Dict, List, Optional, Set, Union
78
from urllib.parse import urlparse
@@ -13,7 +14,8 @@
1314

1415
# Configure logging and defaults
1516
logger = logging.getLogger(__name__)
16-
DEFAULT_STORAGE_PATH = os.path.expanduser("~/.strands/rss_feeds")
17+
# Always use temporary directory for storage
18+
DEFAULT_STORAGE_PATH = os.path.join(tempfile.gettempdir(), "strands_rss_feeds")
1719
DEFAULT_MAX_ENTRIES = int(os.environ.get("STRANDS_RSS_MAX_ENTRIES", "100"))
1820
DEFAULT_UPDATE_INTERVAL = int(os.environ.get("STRANDS_RSS_UPDATE_INTERVAL", "60")) # minutes
1921

@@ -115,11 +117,21 @@ def save_feed_data(self, feed_id: str, data: Dict) -> None:
115117
with open(self.get_feed_file_path(feed_id), "w") as f:
116118
json.dump(data, f, indent=2)
117119

118-
def fetch_feed(self, url: str, auth: Optional[Dict] = None, user_agent: Optional[str] = None) -> Dict:
120+
def fetch_feed(self, url: str, auth: Optional[Dict] = None, headers: Optional[Dict] = None) -> Dict:
121+
# Initialize headers dictionary if not provided
122+
if headers is None:
123+
headers = {}
124+
# Handle case where headers might be a string (for backward compatibility with tests)
125+
elif isinstance(headers, str):
126+
headers = {"User-Agent": headers}
127+
128+
# If using basic auth, make the request with headers and auth
119129
if auth and auth.get("type") == "basic":
120-
headers = {"User-Agent": user_agent} if user_agent else {}
121130
response = requests.get(url, headers=headers, auth=(auth.get("username", ""), auth.get("password", "")))
122131
return feedparser.parse(response.content)
132+
133+
# For non-auth requests, extract User-Agent if present in headers
134+
user_agent = headers.get("User-Agent")
123135
return feedparser.parse(url, agent=user_agent)
124136

125137
def update_feed(self, feed_id: str, subscriptions: Dict[str, Dict]) -> Dict:
@@ -128,7 +140,7 @@ def update_feed(self, feed_id: str, subscriptions: Dict[str, Dict]) -> Dict:
128140

129141
try:
130142
feed_info = subscriptions[feed_id]
131-
feed = self.fetch_feed(feed_info["url"], feed_info.get("auth"), feed_info.get("user_agent"))
143+
feed = self.fetch_feed(feed_info["url"], feed_info.get("auth"), feed_info.get("headers"))
132144

133145
if not hasattr(feed, "entries"):
134146
return {"error": f"Could not parse feed from {feed_info['url']}"}
@@ -193,7 +205,7 @@ def rss(
193205
update_interval: Optional[int] = None,
194206
auth_username: Optional[str] = None,
195207
auth_password: Optional[str] = None,
196-
user_agent: Optional[str] = None,
208+
headers: Optional[Dict[str, str]] = None,
197209
) -> Union[str, List[Dict], Dict]:
198210
"""
199211
Interact with RSS feeds - fetch, subscribe, search, and manage feeds.
@@ -219,14 +231,14 @@ def rss(
219231
update_interval: Update interval in minutes
220232
auth_username: Username for authenticated feeds
221233
auth_password: Password for authenticated feeds
222-
user_agent: Custom user agent string
234+
headers: Dictionary of HTTP headers to send with requests (e.g., {"User-Agent": "MyRSSReader/1.0"})
223235
"""
224236
try:
225237
if action == "fetch":
226238
if not url:
227239
return "Error: URL is required for fetch action"
228240

229-
feed = feedparser.parse(url)
241+
feed = rss_manager.fetch_feed(url, headers=headers)
230242
if not hasattr(feed, "entries"):
231243
return f"Error: Could not parse feed from {url}"
232244

@@ -252,8 +264,8 @@ def rss(
252264

253265
if auth_username and auth_password:
254266
subscription["auth"] = {"type": "basic", "username": auth_username, "password": auth_password}
255-
if user_agent:
256-
subscription["user_agent"] = user_agent
267+
if headers:
268+
subscription["headers"] = headers
257269

258270
subscriptions[feed_id] = subscription
259271
rss_manager.save_subscriptions(subscriptions)

0 commit comments

Comments
 (0)