Potion

Potion's MCP server

Connect an MCP client to Potion at https://api.withpotion.io/mcp. Sign-in, the tools each endpoint carries, narration jobs, uploads, errors and limits.

Published 4 November 2026

Potion makes podcast feeds that a person adds to their podcast app, and narrates text into episodes for them. Its MCP server lets an assistant do all of that on the person's behalf: create a feed, narrate a script or a web article into it, add audio, and read back what is there.

The server is remote, over HTTP. There is nothing to install or run.

Endpoints

AddressWho it is forNarration
https://api.withpotion.io/mcpAny MCP client, including Claude custom connectors and ChatGPT developer modeyes
https://api.withpotion.io/mcp/chatgptThe Potion app in ChatGPT's app directoryyes
https://api.withpotion.io/mcp/claudeThe Potion connector in Claude's directory, and the Claude Code pluginno
https://api.withpotion.io/mcp/museThe Potion connector on Meta's Museyes

Use /mcp unless you are building for one of the listings. The other three are subsets of it, each shaped by its directory's rules, and the reasons are listed under What each listing leaves out.

Connecting

  • Transport: streamable HTTP. JSON-RPC requests are POSTed to the endpoint. The server sends no event stream of its own, so a GET that asks for text/event-stream gets 405. Protocol versions 2026-07-28, 2025-06-18 and 2025-03-26 are accepted.
  • Sign-in: OAuth 2.1 authorization code flow with PKCE (S256 only). A client registers itself through dynamic client registration (RFC 7591) at https://api.withpotion.io/oauth/register and gets its client ID there; none is issued in advance. Public clients, with no secret, are supported. Redirect URIs must match exactly and be https, http on a loopback address, or a private-use scheme.
  • Discovery: each endpoint has its own protected-resource document (RFC 9728) at https://api.withpotion.io/.well-known/oauth-protected-resource followed by the endpoint's path, so /.well-known/oauth-protected-resource/mcp, .../mcp/chatgpt, .../mcp/claude and .../mcp/muse. The authorization server's metadata (RFC 8414) is at https://api.withpotion.io/.well-known/oauth-authorization-server.
  • What the person sees: Potion's sign-in page asks for their email address and emails them a 6-digit code. Typing it in finishes the sign-in, and creates their Potion account if it is new. There is no password.
  • Scope: one scope, account, which gives the same access as the account's own API key.
  • Tokens: access tokens last seven days and come with a refresh token that lasts 90 days and is replaced each time it is used. A token issued for a named endpoint, through the resource parameter (RFC 8707), is refused on Potion's other endpoints.
  • API keys: a client that sends headers can use a Potion API key instead of OAuth: Authorization: Bearer pk_.... The API reference explains how an account gets one.
  • Without a token, tools/list answers, so a client can read the tools before anyone signs in. A tool call answers HTTP 401 with a WWW-Authenticate header pointing at the protected-resource document.

In Claude

In claude.ai and Claude Desktop, add https://api.withpotion.io/mcp as a custom connector. In the "Add custom connector" dialog, choose Sign in now under Authentication and Register automatically under OAuth client. Claude suggests other options for both, and Potion doesn't work with them.

The Potion connector in Claude's directory, and the Claude Code plugin (/plugin marketplace add withpotion/potion, then /plugin install potion@potion), use /mcp/claude, which doesn't narrate. For narration in Claude, use the custom connector above.

In ChatGPT

Add Potion from its page in ChatGPT's app directory. It connects to /mcp/chatgpt and signs in with the same emailed code.

Tools

/mcp carries every tool:

ToolWhat it does
create_feedCreates a feed. A feed is a show: the person adds its address to their podcast app once
list_feedsLists the account's feeds with their ids and subscribe addresses
get_feedOne feed's details, including whether a podcast app has fetched it yet
update_feedChanges a feed's title, description, author or artwork
delete_feedDeletes a feed, its episodes and their audio, permanently
add_episodeAdds an episode from an audio file at a public address, which Potion links to rather than copies
add_episodes_batchAdds several episodes by audio address in one call
create_upload_linkReturns a single-use address that turns one HTTP PUT of an audio file into an episode
list_episodesLists a feed's episodes, newest first
get_episodeOne episode's details, audio address and share page
update_episodeChanges an episode's title, description, artwork, publish date or numbering
delete_episodeDeletes an episode and any audio Potion stored for it
renderNarrates text into an episode. Returns a job at once; the narration runs in the background
ingest_urlFetches a web article and extracts its text into a job, and reports its length without narrating
start_renderStarts a job that is holding text
append_textAdds the next part of a long text to a job that hasn't started
get_jobA job's progress, the remaining wait, and the episode once it exists
list_jobsThe account's recent jobs
list_voicesThe narration voices, each with a sample to listen to
get_accountThe account's plan, limits and usage
get_plansPotion's plans, what each costs and what it allows
get_upgrade_linkA Stripe Checkout page for upgrading
get_billing_portal_linkA Stripe page for changing or cancelling a subscription
request_account_deletionStarts deleting the account; nothing goes until the person confirms by email
check_confirmationWhether a pending email confirmation has been confirmed
start_signupStarts an account by email, for clients that use an API key rather than OAuth
check_signupWhether a pending signup has been confirmed
get_changelogWhat has changed in Potion recently
dismiss_notificationsStops a changelog notice from being attached to later responses
get_legal_linksLinks to the terms and the privacy policy

Every tool carries MCP annotations (read-only, destructive, idempotent, open-world), and every tool's input and output schemas are in tools/list. The same operations are available as an HTTP API, described in the API reference.

What each listing leaves out

/mcp/chatgpt leaves out these tools:

  • get_plans, get_upgrade_link, get_billing_portal_link: OpenAI's guidelines don't allow an app to show subscription plans or link to a checkout.
  • create_upload_link: ChatGPT's code sandbox can't reach the internet to send the file.
  • dismiss_notifications: this endpoint doesn't attach changelog notices.

/mcp/claude leaves out these tools:

  • render, ingest_url, start_render, append_text, get_job, list_jobs, list_voices: Anthropic's directory policy doesn't allow software that uses AI models to generate audio, so this endpoint doesn't narrate. Adding audio the person already has works.
  • dismiss_notifications: this endpoint doesn't attach changelog notices.

/mcp/muse leaves out these tools:

  • get_upgrade_link, get_billing_portal_link: Meta's guidelines ask a connector to finish what it starts, and both of these hand the person a page to finish on.
  • create_upload_link: held back while the listing is in Meta's review.
  • dismiss_notifications: this endpoint doesn't attach changelog notices.

Meta's read and write classes for each Muse tool are on Potion's Muse tools page.

Narration

  • Jobs. render returns a job straight away. get_job reports progress and poll_after_seconds, the remaining wait; once the job finishes, its episode is already in the feed. The wait and the length of the audio are different numbers.
  • Long text goes in parts: render with is_final: false, then append_text until the last part, which has is_final: true. One job holds up to 400,000 characters.
  • Web articles. ingest_url reads public pages only. It identifies itself as PotionBot, follows robots.txt, refuses known paywalled and paywall-bypass sites, and refuses a page with fewer than 250 words of text. It stops before narrating and reports how long the audio would be; start_render starts it.
  • Spending. Narration is counted in seconds of finished audio, charged once the audio exists, and a failed job charges nothing. start_if_under_minutes is a ceiling in minutes of audio. On render, a job whose estimate comes out longer is held, with nothing charged, until start_render; on ingest_url, an article that comes in under it is narrated straight away. A scheduled run, with nobody there to confirm, should set it.
  • Voices. Nine, from list_voices. The six standard voices count minute for minute; the three premium voices count five times. Each episode has one narrator. The voices have only been checked in English.

Uploading audio

create_upload_link takes the feed and the episode's details and returns an address that works once, for one hour. One HTTP PUT of the file to that address creates the episode. A refused file leaves the link usable, so the corrected file can go to the same address. A single PUT carries up to 90 MB, or less if the account's plan sets a lower file limit; larger files go through the HTTP API's multipart upload.

Cloudflare, in front of Potion, refuses Python urllib's default user agent with a bare 403 before Potion sees the request, so set a user agent of your own when sending from Python.

Feeds are unlisted, not private

A feed's address is long and random, and the feed asks podcast directories not to list it. Anyone holding the address can subscribe, and there is no password. A feed's address can't be changed; deleting the feed is the only way to stop it working. Every episode also has a public share page that plays it in a browser. Treat a feed's address like a private link, and tell the person so when the content is personal.

Errors and limits

  • A refusal (a plan limit reached, the narration allowance spent, a page Potion won't read) comes back as a tool result with isError: true and a message saying what happened and what the person can do. On /mcp/chatgpt a refusal is a successful result carrying the same message, because ChatGPT replaces an error result's text with its own. Nothing is written when a call is refused.
  • Invalid input comes back the same way, with a message naming the field.
  • Per account: 120 reads and 60 writes a minute. Over the limit, the message says when it resets.
  • A tool that isn't on an endpoint answers JSON-RPC error -32602, Unknown tool.

Clients it has been run with

ChatGPT (the published app, and developer-mode connectors on /mcp), claude.ai custom connectors on /mcp, the Claude Code plugin, and the MCP Inspector CLI.

Questions and bugs: open an issue or email christo@withpotion.io. Terms and privacy: api.withpotion.io/legal.

Your own podcast, made by your AI

Ask your AI about anything. Potion turns it into an episode in your podcast app.

Which AI do you use?

I use another assistant

Free to start. Works with the free versions.