Skip to main content

myrssfeed

Read RSS/Atom feeds, and print only the items that match criteria you describe in plain English. Claude makes the call on each item; you get the headline, the link, and a one-line reason.

$ myrssfeed --prompt "Export controls on AI chips, and EU tech regulation" \
         --feed https://feeds.arstechnica.com/arstechnica/technology-lab \
         --feed https://www.theregister.com/headlines.atom
US tightens accelerator export rules for third-country resellers
  https://www.theregister.com/2026/10/06/export_rules/
  The Register · 2026-10-06T09:12:00+00:00
  why: Directly about chip export controls.

EU Commission opens consultation on cloud switching rules
  https://arstechnica.com/2026/10/06/eu-cloud/
  Ars Technica · 2026-10-06T07:40:00+00:00
  why: EU tech regulation affecting cloud providers.

Everything else in those feeds is dropped silently.

Why

Feed readers filter on keywords, which is both too narrow ("accelerator" misses "GPU") and too broad (every mention of "chips" matches). A prompt describes what you actually want: "new CVEs in Python packages I'd have to patch, not general security commentary."

Install

myrssfeed is not on PyPI yet, so install it from a checkout:

git clone https://github.com/fabianhogger/myrssfeed
cd myrssfeed
pip install .

For development, install it editable with the test and lint tools:

pip install -e ".[dev]"

Once the project is published (see Releasing), the usual one-liner will work:

pip install myrssfeed     # not available until the first release is published

Requires Python 3.9+ and an Anthropic API key:

export ANTHROPIC_API_KEY=sk-ant-...

Check that it landed:

myrssfeed --version

If the myrssfeed command is not found, your Python scripts directory is not on PATH; python -m myrssfeed works identically.

Usage

One-off run

myrssfeed --prompt "Rust async ecosystem news" --feed https://blog.rust-lang.org/feed.xml

With a config file

myrssfeed.toml:

system_prompt = """
Keep items about:
  - security advisories affecting Python or Node packages
  - outages at major cloud providers

Skip funding rounds, opinion pieces, and conference announcements.
"""

feeds = [
  "https://feeds.feedburner.com/TheHackersNews",
  "https://status.cloud.google.com/en/feed.atom",
]

interval = "30m"
state_path = "~/.local/state/myrssfeed/state.json"
output_format = "markdown"
myrssfeed --config myrssfeed.toml

JSON config files work the same way; the format is chosen by file extension.

Scheduling

Two options, depending on whether you want a resident process.

Built-in loop — runs until you stop it, and shuts down cleanly on Ctrl-C/SIGTERM so the state file is never left half-written:

myrssfeed --config myrssfeed.toml --interval 45m

cron or systemd — have myrssfeed run one pass and exit. The state file means each run only reports items it has not reported before:

*/30 * * * * ANTHROPIC_API_KEY=sk-ant-... /usr/local/bin/myrssfeed --config ~/myrssfeed.toml --once >> ~/myrssfeed.log 2>&1

A systemd timer unit is in examples/.

Checking a setup before spending anything

--dry-run fetches the feeds and lists what would be judged, without calling the API, and without marking anything as seen:

myrssfeed --config myrssfeed.toml --dry-run -v

Output formats

--format For
text Reading in a terminal (default)
markdown Pasting into notes, or posting to chat
json Piping into jq or another program

Logs go to stderr and results to stdout, so myrssfeed ... | jq works as expected.

How it works

  1. Each feed is fetched with If-None-Match/If-Modified-Since, so an unchanged feed costs one small request and no tokens.
  2. Entries already handled on an earlier run are dropped using the state file.
  3. The rest are sent to Claude in batches, with your prompt as the system prompt and a JSON schema constraining the reply to one verdict per item.
  4. Matching items are printed; every item that got a verdict is recorded as seen.

Items in a batch that fails (a transport error, a declined request) are not recorded, so the next run retries them rather than dropping them silently.

Cost

Only titles, summaries and dates are sent — never full articles — and summaries are truncated. The system prompt is cached across the batches of a run.

The knobs that matter:

  • --batch-size (default 10): larger batches spread the prompt over more items.
  • --effort (default low): relevance is a classification task and rarely needs more. Raise it if the criteria are subtle.
  • --no-reasons: drops the per-item justification, and the output tokens with it.
  • --max-items N: caps how many entries per feed are considered at all.

Configuration reference

Every key below can be set in a config file, as an environment variable MYRSSFEED_<KEY> (uppercase), or as a command-line flag. Precedence is file < environment < flags.

Key Default Meaning
system_prompt required Your criteria
feeds required Feed URLs (comma-separated in the environment)
api_key ANTHROPIC_API_KEY Anthropic API key
model claude-opus-5-5 Claude model id
effort low low, medium, high, xhigh, max
batch_size 10 Items per API request
max_items_per_feed 50 Newest entries considered per feed; 0 for no limit
state_path none Where handled items are recorded
state_ttl_days 30 How long they are remembered
output_format text text, markdown, json
interval none Run repeatedly, e.g. 30m, 2h, 1h30m
timeout 120 Per-request HTTP timeout, seconds
max_retries 3 SDK retries on transient failures
max_tokens 16000 Output ceiling per request
refusal_fallback true Route a declined request to a fallback model
include_reasons true Ask for and show a reason per item
user_agent myrssfeed (...) User-Agent used when fetching feeds

--api-key also exists, but prefer the environment variable: flags are visible in ps output and end up in shell history.

Exit status

Code Meaning
0 Ran successfully (whether or not it matched)
1 The run failed
2 Bad arguments or configuration
3 Ran, but some feeds or batches failed

Library use

The CLI is a thin wrapper; the same thing works from Python:

from myrssfeed import Config, Runner

config = Config(
    system_prompt="Release notes for Python web frameworks.",
    feeds=["https://blog.djangoproject.com/rss/"],
    state_path="~/.local/state/myrssfeed/state.json",
)

result = Runner(config).run_once()
for match in result.matches:
    print(match.item.title, match.item.link, "-", match.verdict.reason)

Runner accepts a reader, a relevance_filter and a state if you want to substitute your own — which is how the test suite avoids the network entirely.

Writing a good prompt

The prompt is the whole filter, so be concrete about both halves:

Keep:
  - Postgres release announcements and migration-breaking changes
  - Write-ups of real production incidents with root-cause detail

Skip:
  - Vendor benchmarks and marketing posts
  - "Top 10 database tips" listicles
  - Anything about MySQL unless it compares directly to Postgres

The model is told to prefer precision over recall, so tangential items are dropped. If you are missing things you wanted, say so in the prompt explicitly ("err on the side of including borderline items") or raise --effort.

Development

pip install -e ".[dev]"
pytest                 # no network or API calls
ruff check . && ruff format --check .
mypy

The test suite never reaches the network or the Anthropic API: feeds are parsed from in-memory XML and the classifier is a scripted fake, so it runs offline and without an API key.

Releasing

Publishing uses trusted publishing, so no API token is stored in this repository. One-time setup, done once per index at https://pypi.org/manage/account/publishing/ and the same page on https://test.pypi.org:

Field Value
Project myrssfeed
Owner fabianhogger
Repository myrssfeed
Workflow release.yml
Environment pypi on PyPI, testpypi on TestPyPI

Then, per release:

# 1. Rehearse on TestPyPI, and check the result installs
gh workflow run release.yml
pip install --index-url https://test.pypi.org/simple/ \
            --extra-index-url https://pypi.org/simple/ myrssfeed

# 2. Bump __version__ in src/myrssfeed/__init__.py, move the CHANGELOG entry out
#    of [Unreleased] into a version heading, then tag and push
git commit -am "Release 0.1.0"
git tag -a v0.1.0 -m "0.1.0"
git push origin main v0.1.0

The tag push is what publishes to the real PyPI. The workflow refuses to upload if the tag does not match __version__, because PyPI never allows a version number to be reused — a mistake means yanking the release and shipping a new number, so it is checked before the upload rather than after.

To inspect a build locally first:

pip install build twine
python -m build
twine check --strict dist/*

License

MIT.

Metadata

Release files for myrssfeed 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for myrssfeed 0.1.0
File Size Uploaded Provenance Actions
myrssfeed-0.1.0.tar.gz 41.6 kB With provenance Details for myrssfeed-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for myrssfeed 0.1.0
File Interpreter ABI Platform Actions
myrssfeed-0.1.0-py3-none-any.whl Python 3 none any Details for myrssfeed-0.1.0-py3-none-any.whl

Total release size: 75.2 kB

Release files / myrssfeed-0.1.0.tar.gz

Download URL myrssfeed-0.1.0.tar.gz
Size 41.6 kB
Tags Source
SHA-256 checksum
How to use checksums
4273511687bdaa9620ba114bf770e67c0efc58ef3d3964bfcf43484c87f6bcb3
BLAKE2b-256 checksum
How to use checksums
d2f6990c5b01e2e8c8ede536f119e87cd502a791a759c6b3bed4f8e6b98c842a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 7, 2026.

Transparency log

Release files / myrssfeed-0.1.0-py3-none-any.whl

Download URL myrssfeed-0.1.0-py3-none-any.whl
Size 33.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d7cfa898a962e6dddfcfee14193eb1b11eb41a789d65b87690fa25f6659d18ed
BLAKE2b-256 checksum
How to use checksums
102b73430e050943d0862522b18e0a1541ff2f13a4f6190337e7b6e27be730d8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 7, 2026.

Transparency log

Security for myrssfeed 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for myrssfeed-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 7, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page