podcast-feed MCP server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@podcast-feed MCP serveradd https://example.com/episode.mp3 to my default podcast feed"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
podcast-feed
Private podcast feeds from links to audio and video files. Add a direct link to a file, and podcast-feed downloads it, makes AAC audio of it (an MP3 file stays as it is) and publishes it as an episode in an RSS feed that any podcast app can subscribe to. An admin UI manages the feeds and episodes, and an MCP server lets Claude add and remove episodes.
Direct links only: a link to a file, not to a web page or a YouTube video. Every connection, redirects included, goes only to public addresses ("blocked address" otherwise), with limits on size, duration and free disk space.
Several feeds, created in the admin UI. One of them is the default for new episodes.
Retention: an episode is deleted 30 days after it was added (per-feed override).
One process: SQLite for the index, audio files on disk, ffmpeg for the conversion. A restart picks up the episodes it interrupted.
Listeners and routes
Port | Path | What |
8080 |
| A feed (RSS 2.0 with the iTunes tags) |
8080 |
| An episode's audio or artwork, or the feed's cover, with ranges |
8080 |
| The admin UI ( |
8000 |
| The MCP server (Streamable HTTP, stateless) |
The feed and media routes are the only public ones; everything else on 8080 is under /ui/.
Every request other than GET, HEAD or OPTIONS must come from the admin UI's own pages
(Sec-Fetch-Site: same-origin, or without that header an Origin whose host is the request's
Host), or it gets 403.
MCP tools, each with a title and all four hints: list_feeds, add_episode (returns at
once with status queued), list_episodes, get_episode (poll it until ready or failed)
and remove_episode. They never return a feed URL. Feeds are created, edited and deleted in
the admin UI only.
Neither listener has a login of its own. Put both behind something that authenticates every request: the author runs the admin UI and the MCP server behind Cloudflare Access.
Related MCP server: PodLearn MCP Server
Configuration
Variable | Default | |
| (required) | A secret. The public |
|
| The SQLite database ( |
|
| Audio and artwork, one folder per feed; |
|
| Days an episode is kept (1 to 3650), unless its feed says otherwise |
|
| Largest download (1 GiB) |
|
| Longest episode |
|
| Free space the media disk must keep; below it an episode fails with "disk nearly full" |
|
| Feeds, media and the admin UI |
|
| The MCP server |
|
| The address both listeners bind |
A missing or invalid setting stops the program at once with exit status 2; the message names
the variable and never quotes PODCAST_PUBLIC_BASE. podcast-feed never logs it or a feed URL:
the feed URLs appear only in the admin UI. media/ gets a CACHEDIR.TAG, so backups that
skip caches (restic --exclude-caches) leave the audio out.
The gateway and the secret
Podcast apps can't log in to Cloudflare Access, so the feeds and the audio reach them through
the device gateway of vps-infra: a Cloudflare Worker
on podcast-api.<domain> checks the podcast secret (the first path segment), strips it,
forwards only /feeds/ and /media/ and adds the service token Access expects, so Access
still stands in front of every request. The podcast gateway has its own secret, separate from
the device secret of the e-reader clients. PODCAST_PUBLIC_BASE is that gateway's address
plus the secret, which is why it is one. How the gateway works and how to rotate its secret:
docs/devices.md in
vps-infra.
Deployment
The image is ghcr.io/jprintz/podcast-feed:<version> (linux/amd64). It runs as uid 65532 with
a read-only root: give it writable /data and /media volumes and a /tmp (Podman's
ReadOnly=true mounts one; with Docker use --tmpfs /tmp). It needs no capabilities.
SIGTERM stops it within seconds; an open download gets 3 s to finish. If a listener or the
worker stops on its own, the process exits with status 1, so that the service manager
restarts it.
[Container]
ContainerName=podcast
Image=ghcr.io/jprintz/podcast-feed:0.1.0
Secret=podcast_public_base,type=env,target=PODCAST_PUBLIC_BASE
Volume=/srv/apps/podcast/data:/data:U
Volume=/srv/apps/podcast/media:/media:U
UserNS=auto
ReadOnly=true
NoNewPrivileges=true
DropCapability=ALLDevelopment
uv sync --all-extras
make test # unit tests; the conversion tests need ffmpeg and ffprobe on PATH (skipped otherwise)
make lint # ruff check and format check
make smoke # builds the image and smoke-tests it with Docker, as CI doesmake smoke runs scripts/smoke.sh: the container read-only with no capabilities, the
image's healthcheck, the MCP tools and their annotations (scripts/smoke_mcp.py), the admin UI,
a feed round trip, the public routes' answers, ffmpeg, and docker stop under 8 s with exit
status 0. CI runs the tests with REQUIRE_FFMPEG=1 (the static ffmpeg of the image) and the
same smoke test, and pushes the image from main and from v* tags.
License
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
Manage Listenbox podcasts, episodes, collaborators, and RSS imports through your AI assistant.
Run your social media from Claude, ChatGPT or any MCP client. Feedlio's MCP server lets your assistant list workspaces and connected accounts, import media from a URL, and create, edit, reschedule and cancel posts on Facebook, Instagram, LinkedIn, YouTube, TikTok, Threads, Bluesky, Mastodon and Dev.to, with per-platform settings for YouTube, TikTok and Dev.to. Sign in with OAuth on Feedlio's own page; MCP access is included on the Standard plan and up, with a 7-day free trial. Support: support@feedlio.io
- JellypodOAuthcom.jellypod
Create, import, and publish Jellypod podcast episodes from your AI assistant.
- PodhomeOAuthfm.podhome
Manage Podhome podcasts: episodes, chapters, transcripts, clips, people and analytics.
Related MCP Servers
- FlicenseNot gradedqualityCmaintenanceEnables Claude to fetch, parse, and manage RSS/Atom feeds through the Model Context Protocol. It supports feed registration and retrieval, allowing users to access article summaries and full content from multiple sources via natural language.1-
- FlicenseNot gradedqualityDmaintenanceEnables transcription, search, and lesson generation from podcast and YouTube URLs directly inside Claude Code.2-
- AlicenseCqualityBmaintenanceEnables Claude to read your Feedly subscriptions via a local stdio server, listing folders and feeds, fetching recent articles, checking unread counts, searching for new feeds, and optionally marking items as read while keeping your API token on your machine.6229 npmMIT
- AlicenseAqualityAmaintenanceEnables AI agents to search millions of podcasts, read and search episode transcripts with timestamps and speakers, and access chapters, soundbites, Podcasting 2.0 tags, value-for-value data, feed health, and index statistics through 36 tools.3654 npm1MIT