podcast_server
This server exposes MCP tools for managing a Substack publication’s drafts and publishing workflow.
Create or update Substack drafts from Markdown with title, subtitle, audience, comment permissions, SEO title/description, slug, section, tags, and optional prepublish/publish/send/share flags.
Update an existing draft by ID with a field payload.
Add tags to a draft or post.
Run prepublish checks before publishing.
Publish drafts, including a safer checked path that requires confirmation and prepublish validation, with options for email delivery and auto-sharing.
Check authentication/status and list available publications.
List and fetch drafts, with pagination/filtering.
Schedule or unschedule drafts for release.
Integrates with Cloudflare R2 for storing podcast audio and media on your own domain, and with Cloudflare metrics for collecting episode/download analytics.
Crossposts companion blog posts to dev.to as unpublished drafts, keeping the original on your own site.
Backs up the podcast catalog and show data to a private Hugging Face repository.
Crossposts companion blog posts to Substack as unpublished drafts, keeping the original on your own site.
Publishes episodes to YouTube, sets video thumbnails, and collects YouTube metrics for published content.
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_serverPrep the Ada interview from Descript and draft show notes for my review"
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_server
A self-hosted podcast host with no dashboard. Your AI agent is the interface. You're the approver.
What it is · How it works · Quick start · MCP tools · Adopting guide · Agent guide
What it is
Podcast hosts charge a monthly fee for three things: storage for the audio, a database of episodes, and an RSS feed, all behind a web dashboard. podcast_server keeps those three on infrastructure you own and replaces the dashboard with MCP tools. The "user interface" is whichever AI agent you already use: Claude, Cursor, Codex, or your own.
You talk to your agent: "The interview with Ada is ready in Descript. Prep it." The agent stages the episode, drafts titles and show notes in your show's voice, and stops. You approve. The agent publishes, uploads the video, sets the thumbnail, posts the announcement, drafts copies of the companion blog post on dev.to and Substack (your site stays the original), and tells you a week later how it's doing.
flowchart LR
subgraph hosted["A hosting service"]
direction TB
Y1([You]) -->|click through| D[Their web dashboard]
D --> S1[(Their storage)]
D --> C1[(Their database)]
D --> F1[Their feed URL]
end
subgraph self["podcast_server"]
direction TB
Y2([You]) <-->|chat and approve| A[Your AI agent]
A -->|MCP tools| E[podcast_server]
E --> S2[(Your R2 bucket)]
E --> C2[(Your SQLite catalog)]
E --> F2[Feed on your domain]
end
hosted ~~~ selfHow to place it:
Question | Answer |
What job does it do? | Podcast hosting and publishing: storage, feed, catalog, distribution, and analytics |
Where does it run? | On your machine, against services you own: Cloudflare R2, your domain, and optionally a git-deployed website |
How do you operate it? | Through an AI agent over MCP. There is no web UI. |
Who decides? | You. The agent drafts and executes; nothing public happens without your approval. |
Closest category | A headless podcast host: like a headless CMS, where the missing "head" is an agent |
What it isn't:
Not an editor. Edit in Descript, or anything that exports an MP3. The engine takes it from there.
Not an AI that makes your show. The engine has no taste, on purpose. Your playbook holds the voice and standards, the agent applies them, and you approve.
Not a hosted service. Nobody runs it for you, and there's no account to sign up for.
Not a wrapper around someone else's product. It is the system of record: the catalog, the episode GUIDs, the published audio, and the log of every public action live here.
New here? Start with ADOPTING.md, a step-by-step guide to running your own show, including migrating from another host without losing subscribers.
Related MCP server: sitedrop
How it works
Three layers, two people
flowchart TB
YOU([You: taste and approval]) <--> AGENT([Your AI agent: drafts and executes])
AGENT -->|reads first| SHOW
AGENT -->|calls| SURFACE
subgraph SHOW["Show layer: your private DATA_DIR"]
PB[PLAYBOOK.md: voice, title rules, workflows]
TPL[templates: social posts, thumbnail style]
CAT[(catalog.db and logs of every public action)]
end
subgraph SURFACE["Surface: the MCP server"]
TOOLS[typed tools with safety annotations]
RULES[operating rules sent on connect, AGENTS.md]
end
subgraph ENGINE["Engine: show-neutral Python"]
CORE[core: catalog, storage, feed, site, discovery checks, art, doctor, capabilities]
IO[importers and exporters: RSS, Descript, YouTube, X, Instagram, dev.to, Substack]
MET[metrics: OP3, Cloudflare, X, Instagram, YouTube, dev.to, Substack]
end
subgraph OWNED["Services you own"]
R2[(Cloudflare R2 on your domain)]
WEB[your website repo]
PLAT[YouTube, X, Instagram, dev.to, and Substack accounts]
HF[(private Hugging Face backup)]
end
SURFACE --> ENGINE
CORE --> CAT
CORE --> R2
CORE --> WEB
CORE --> HF
IO --> PLAT
MET --> PLATLayer | Its job | Where it lives |
Engine | The hands: does each job correctly and refuses unsafe ones | this repo: |
Surface | The controls: typed tools, safety annotations, operating rules |
|
Show | The taste and the memory: voice, templates, catalog, logs | your private |
The engine and surface know nothing about any particular show. That's why this repo can be public while your show data stays private, and why one checkout can run several shows (one .env each).
A release, end to end
sequenceDiagram
actor You
participant Agent as Your AI agent
participant MCP as podcast_server
participant Svc as Your services
You->>Agent: The episode with Ada is ready in Descript
Agent->>MCP: podcast_stage_from_descript
MCP->>Svc: download the master audio
MCP-->>Agent: measured audio, chapters, draft show notes
Agent->>Agent: titles, notes, and a discovery brief in the playbook's voice
Agent->>MCP: podcast_render_thumbnail and podcast_render_episode_art
Agent-->>You: review package: titles, notes, brief next to each channel's fields, art
You->>Agent: approved, with edits
Agent->>MCP: podcast_stage_discovery
Agent->>MCP: podcast_publish with transcript and episode art
MCP->>MCP: check slug, GUID, exclusions, discovery fields, files, and audio before any upload
MCP->>Svc: upload audio, transcript, chapters, and art to R2, rebuild feed and pages, deploy
MCP-->>Agent: live URLs and deploy status
Agent->>MCP: podcast_backup
Agent-->>You: published, with linksAfter release: promote and measure
Every public action is logged, and metrics only count what the engine itself put out, so numbers are never mixed with the rest of your accounts:
flowchart LR
P[published episode] --> V[YouTube upload and thumbnail]
P --> X[X post]
P --> IG[Instagram post]
P --> DT[dev.to and Substack blog drafts]
V --> L[(logs in DATA_DIR/social)]
X --> L
IG --> L
DT --> L
L --> C[daily collectors: OP3, Cloudflare, X, Instagram, YouTube, dev.to, Substack]
C --> M[(metrics.db)]
M --> D[offline dashboard and metrics tools]
D -->|"how is the show doing?"| AG([your agent])Safety lives in the engine, not the prompt
An agent can misread a prompt; it can't get past code. The rules that matter are enforced by the engine itself: a database trigger blocks excluded episodes, publishing checks everything before the first upload, uploads never overwrite, and every public action is logged so a retry can't double-post. On top of that, every tool carries MCP annotations, so your client can run reads freely and ask you before anything public:
Level | What your client can do | Tools |
Read-only | run without asking |
|
Local files | run without asking |
|
Outside services, additive | run, safe to retry |
|
Public edit | ask you first; safe to retry |
|
Public release | ask you first; never repeat blindly |
|
Design principles
You own the infrastructure. Audio sits in your bucket, the feed on your domain, the catalog in a file you can open. Leaving means copying files, not exporting from someone's dashboard.
The agent operates; you approve. Nothing goes public without your explicit approval of the exact content. When a request is unclear, an identifier doesn't match exactly, or a step would bypass a tool, the agent asks instead of guessing, and it says up front what the engine can't do.
Guarantees live in code. GUIDs are permanent, exclusions are enforced by the database, and checks run before side effects.
Everything is inspectable. SQLite, Markdown, JSON, and append-only JSONL logs. No hidden state.
The engine is show-neutral. Your voice, templates, and data live in your own
DATA_DIR, never in this repo.
Features
Feed: RSS 2.0 + iTunes + Podcasting 2.0 (
<podcast:locked>,<podcast:guid>), exact enclosure byte lengths, Markdown show notes rendered tocontent:encoded, optional OP3 analytics.Two site modes:
noneserves the feed from your R2 domain, with no website needed.astrowrites one Markdown page per episode into your site repo and pushes only those files.Migration: a GUID-preserving importer for any host's RSS feed, with audio mirrored into R2.
Agent surface: MCP tools covering staging (including Descript), publishing, episode edits, deploys, backups, blog posts, social drafts, X and Instagram posts, media uploads, YouTube uploads and thumbnails, and audience metrics. Each tool carries MCP safety annotations (read-only, destructive, idempotent), and the server sends its operating rules to every client on connect, including when to stop and ask the human.
podcast_capabilitiestells the agent which channels are ready right now, and which requests need the human, so it can show you what's possible before planning the work.Discovery at publish time: each piece of content gets one approved brief (main search phrase, secondary phrases, audience, the question it answers, names people search for), and every publish path checks its own fields against it before anything goes public. That covers the episode title and summary, the blog's meta description, YouTube's title, the first 150 characters of the description, tags, and chapters, alt text and the visible start of an Instagram caption, and alt text on X media. A miss is refused, not warned about. New episodes also carry a transcript (
podcast:transcript, which Apple displays), a chapters file, and their own artwork in the feed; YouTube uploads get a caption track and a playlist. Applies to content published from now on; nothing already published is changed.Blog cross-posting: your site keeps the original; the companion blog post goes to dev.to and Substack as unpublished drafts, with site-relative links made absolute. dev.to drafts carry a canonical URL back to your site; Substack has no canonical setting, so its drafts open with an "Originally published at" link. You publish each one on its platform.
Instagram posts: approved images go to your Instagram professional account as feed posts, converted to JPEG and staged on your R2 domain for Meta to fetch. A one-time login saves a never-expiring Page token in its own 0600 file. Carousels, Stories, and Reels are planned.
Content management: agents can list what's on Instagram, YouTube, dev.to, and Substack and remove one item you named at a time: delete an Instagram post, YouTube video, or Substack post or draft (permanent), or unpublish a dev.to article back to draft (dev.to's API can't delete). Each removal checks the item is yours first, has a dry run, and is logged with what was removed.
YouTube Shorts: uploads that are square or vertical and up to 3 minutes become Shorts; with
expect_short, the engine measures the clip first (ffprobe, rotation-aware) and refuses one that would land as a regular video.YouTube thumbnails and episode art: your agent renders the art; the engine sets the headline in your show's fonts and colors (auto-fitted, clear of the duration badge, under 2 MB) and pushes it to YouTube after approval. The same art and headline also become the episode's square icon in Apple Podcasts, Spotify, and other apps (2048 px, with your show's wordmark), published with the episode;
REQUIRE_EPISODE_IMAGE=truemakes it mandatory.Audience metrics: pluggable collectors (OP3 downloads, Cloudflare audio traffic, X, Instagram, and YouTube engagement for the show's own posts and videos, dev.to and Substack reach for the blog posts the engine cross-posted, or your own) feed a local SQLite store, an offline HTML dashboard (an overview plus a tab per platform with every collected number, filtered by range or a stretch you drag across the timeline), and MCP tools agents can query.
Safety: see the list below; checks run before any upload.
Backups: incremental, to a private Hugging Face dataset.
What You Need
podcast_server doesn't host anything itself. It drives services you own. The pieces below are what a show needs; each links to its own setup docs, which are the authority for that service (and several have their own MCP server your agent can use for setup).
Required
Piece | What it's for | Get set up |
A domain | Your audio URLs (e.g. | Any registrar, e.g. Cloudflare Registrar. The domain's DNS must be on Cloudflare for the R2 custom domain (full setup). |
Cloudflare R2 bucket | Stores the MP3s and serves them (no egress fees, range requests) | R2 docs: create a bucket, an API token with Object Read & Write, and a custom domain on it. Cloudflare's MCP servers can help an agent do this. |
A home for the RSS feed | The URL podcast apps subscribe to | Either nothing extra ( |
Cover art | Required by every directory | Square JPEG/PNG, 1400–3000 px, at a public URL. See Apple's requirements. |
Directory listings | How listeners find the show | Submit your feed URL once to Apple Podcasts Connect, Spotify for Creators, and Podcast Index (which feeds many smaller apps). |
An MCP client and Python tooling | The agent that runs the tools, and the engine's runtime | Any MCP client (Claude Desktop, Cursor, ...), plus uv. |
Optional
Piece | Unlocks | Get set up |
Staging episodes straight from a Descript export (chapters from markers) | Export the transcript as Markdown; see ADOPTING. | |
X developer app + xurl |
| An app in the X developer portal (read and write, OAuth 2.0); X bills API use. Steps in ADOPTING. |
Instagram professional account (Business or Creator) + a Meta app |
| A Meta developer app; a never-expiring Page token for the Facebook Page linked to the account (or a Unified login token); your own account needs no App Review. Saved by |
dev.to API key |
| Generate a key under dev.to → Settings → Extensions; needs a website ( |
A Substack publication |
| Substack has no official publishing API: this uses the community python-substack library and your browser session cookie, which controls the whole account and expires. Steps in ADOPTING. For episodes, Substack's own Distribute to Substack reads your feed, no engine changes needed. |
YouTube Data + Analytics APIs |
| A Google Cloud OAuth client (Data API, Analytics API). Custom thumbnails need a phone-verified channel. |
An image model your agent can call | Thumbnail art (the engine only sets the text) | Whatever your agent supports (e.g. via its own MCP or API tools); put fonts and colors in |
Free, open download analytics (stats are public) |
| |
Cloudflare analytics token | Audio-traffic metrics from the edge | A token with Analytics Read on the zone (newer dashboards: a policy scoped to the Domain). |
Hugging Face account | Private, incremental backups of audio and catalog | A private dataset; Hugging Face also offers an MCP server. |
ADOPTING.md walks through each step in order with the exact settings.
Quick Start
# needs uv: https://docs.astral.sh/uv/ (curl -LsSf https://astral.sh/uv/install.sh | sh)
git clone <this repo> podcast_server && cd podcast_server
uv sync # creates .venv with Python 3.11+ and locked dependencies
cp .env.example .env && chmod 600 .env # fill in your show's values
uv run python -m core.doctor # verifies R2, domain, site, artwork, feed, and each optional integration
uv run python -m unittest discover -s testsADOPTING.md covers Cloudflare setup, configuration, first publish, and migration in detail.
Configuration
Every setting is documented inline in .env.example.
Group | Variables |
Show identity (required) |
|
Show identity (optional) |
|
Storage (required) |
|
Website |
|
Show data |
|
Publishing rules (optional) |
|
Integrations (optional) |
|
X posting (optional) |
|
Instagram posting (optional) |
|
Blog cross-posting (optional) |
|
Metrics (optional) |
|
There are no show values in the code: startup fails and lists any required variable that's missing. PODCAST_SERVER_ENV_FILE=/path/to/show.env selects a different config (one per show).
Your Show's Private Data
Keep DATA_DIR outside this checkout, ideally in its own private repo. examples/show-data/ is a template to copy:
my-show-data/
├── PLAYBOOK.md # your editorial voice, title rules, workflows (agents read this first)
├── DEPLOYMENT.md # your infrastructure record: DNS, R2, website, backups, credential locations
├── templates/ # social_long.txt, social_short.txt, thumbnail_style.json, fonts/
├── social/ # logs of public actions and removals (*_deletions.jsonl, devto_unpublished.jsonl): x_posts.jsonl, instagram_posts.jsonl, devto_posts.jsonl, substack_posts.jsonl, substack_newsletters.jsonl, youtube_uploads.jsonl, youtube_thumbnails.jsonl
├── newsletters/ # original Substack newsletter articles, <slug>.md (title, subtitle, tags frontmatter)
├── youtube_videos.json # optional: back-catalog videos → episodes, for YouTube metrics
├── thumbnails/ # staged art, rendered thumbnails, and newsletter previews (<slug>-substack.png)
├── media/ # staged GIFs and images for newsletters and blog posts (podcast_make_gif)
├── excluded_episodes.json # episodes that must never be (re)published
├── transcripts/ # <slug>.txt
├── catalog.db # created on first run; don't commit it (back it up instead)
├── metrics.db # collected audience metrics; safe to delete and re-collect
└── metrics/dashboard.html # written by `python -m metrics.report`Register the MCP Server
{
"mcpServers": {
"podcast_server": { "command": "/absolute/path/to/podcast_server/bin/run-mcp.sh" }
}
}Agents should read AGENTS.md and then your PLAYBOOK.md.
MCP Tools
Area | Tools |
Getting oriented |
|
Catalog |
|
Publishing |
|
Content |
|
Distribution |
|
Content management |
|
Metrics |
|
Common Commands
Task | Command |
Check the setup |
|
Rebuild feed (+ pages), no deploy |
|
Import a back catalog |
|
Back up |
|
Collect metrics + write the dashboard |
|
Connect or reconnect Instagram (access token) |
|
Connect or reconnect Substack (session cookie) |
|
Run tests |
|
Safety Guarantees
The engine refuses to:
Reuse a slug or regenerate a GUID, which would show duplicate episodes on every subscriber's device.
Re-add excluded episodes: a database trigger blocks them, even for raw SQL.
Publish unrelated website work: deploys commit only generated files, only from
WEBSITE_BRANCH.Publish broken downloads: episodes whose audio isn't in R2 are left out of the feed and pages.
Upload arbitrary files to the public bucket, or overwrite existing (immutably cached) objects.
Leak credentials: downloads are https-only, the Descript token is sent only to Descript hosts, the dev.to key only to dev.to, the Substack session cookie and the Instagram token stay in their own 0600 files (the doctor fails if others can read them), the Instagram token is sent only to Meta, in a header, and a user token pasted at login is swapped for the Page token and never stored, and X and YouTube tokens stay in their own CLIs' or OAuth files.
Publish cross-posts for you: dev.to and Substack copies are created as unpublished drafts (Substack never emails subscribers from the engine), and a blog post that already has a draft (logged, or found on the account) is never sent again.
Upload local files to Substack: Substack drafts accept only https images, because the library would otherwise upload any image path found on disk.
Attach arbitrary files: X media, Instagram images, and YouTube thumbnails accept only image/video types within each platform's size and shape limits, checked before any upload.
Post to the wrong Instagram account or twice: a token saved for another account than
INSTAGRAM_USERNAMEis refused, and the same image with the same caption returns the logged post instead of a second one.Remove content in bulk or blindly: the delete and unpublish tools (Instagram, YouTube, dev.to, Substack) take one id per call, read the item first, refuse anything that isn't your account's own, and log what they removed. There is no delete-all tool.
Publish content nobody can find: episodes, blog posts, YouTube uploads, Instagram posts, and X media are checked against the content's discovery brief before anything goes public; skipping a check takes a
discovery_overridewith a reason, which is recorded. YouTube titles, descriptions, and tags over the platform's limits are refused rather than silently cut.Lose track of public actions: every X and Instagram post, YouTube upload, and thumbnail change is logged as soon as it succeeds, so an agent can check before retrying instead of posting twice.
Troubleshooting
Symptom | Cause and fix |
| The site repo isn't on |
| Git has no usable credentials. On headless Linux, keyring helpers (libsecret) can't unlock; use |
Rewritten show notes don't reach podcast apps | Edit them with |
Backup re-downloads audio every run | Catalog byte lengths don't match R2; sizes must come from the stored object (the importer's |
| Your resolver cached an old "no such host" answer; it clears on its own. |
Doctor says a metrics source is ready, but collection fails | The doctor checks settings, not live access. The run's message says what to fix, e.g. Cloudflare |
| The session cookie expired or you signed out of Substack. Run |
| A draft of that post is already there (logged, or found on the account). To make a fresh one, delete the old draft and remove the slug's line from |
| The token expired or lost access (a password change or removed permission invalidates Page tokens; Unified login tokens follow their configuration's expiry). Get a new token as in ADOPTING and run |
| Instagram couldn't fetch the staged image from |
"No discovery brief for ''" or "Discovery check failed" | Save the approved brief with |
Transcripts don't show in Apple Podcasts | In Podcasts Connect, set the show to display transcripts you provide; Apple reads only |
YouTube upload result has | The video is live; only that step failed (often quota: a caption costs 400 units, a playlist entry 50). Add it by hand in YouTube Studio, or wait for the quota to reset. Don't re-upload. |
| Custom thumbnails aren't enabled for the channel; verify it in YouTube Studio (phone verification). |
Project Structure
podcast_server/
├── core/ # config, catalog (db), R2 storage, feed XML, site output + deploy, doctor and capabilities, discovery briefs and checks, episode transcript/chapters/art, thumbnail renderer, helpers
├── mcp_server/server.py # the MCP tools, their annotations, and the connection instructions
├── importers/ # rss.py (any host, GUID-preserving), descript.py (staging)
├── exporters/ # Astro pages, blog posts, social drafts, X and Instagram posting, dev.to and Substack drafts, YouTube upload + thumbnails
├── metrics/ # collectors (OP3, Cloudflare, X, Instagram, YouTube, dev.to, Substack, plugins), store, analysis, dashboard (Chart.js in vendor/)
├── bin/ # run-mcp.sh (MCP launcher), collect-metrics.sh (scheduler entry)
├── scripts/backup_to_hf.py
├── examples/show-data/ # template for your private DATA_DIR
├── tests/
├── pyproject.toml / uv.lock # uv project: dependencies + lockfile
├── ADOPTING.md # run your own show
├── AGENTS.md # operating guide for agents
└── PLAN.md # architecture and roadmapLicense
Business Source License 1.1: free for personal, open-source, and internal business use; not for offering a competing commercial hosted podcast service. Converts to Apache 2.0 on October 26, 2029.
Built by Harpreet Sahota to run a real show, then made show-neutral so you can run yours.
Available Tools
12 toolsadd_tagsAdd TagsB
Add tags to a specific draft/post.
| Name | Required | Description | Default |
|---|---|---|---|
| tags | Yes | string or list of tag names (e.g. `"tech"` or `["tech", "python"]`). | |
| draft_id | Yes | target draft identifier. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description must carry the full behavioral burden and falls short. It does not state whether tags are appended or replace existing tags, how duplicates or unknown tag names are handled, or whether the operation requires any special permissions. The output schema covers return values, but the mutation semantics remain opaque.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single, front-loaded sentence with no wasted words. It is appropriately sized for the operation, though it could have added one clarifying phrase about append-vs-replace behavior at little cost.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a mutation tool with no annotations and no output schema explanation required, the description is too thin. It omits key behavior such as whether tags are additive, what happens to existing tags, and any constraints on tag names or duplicates, leaving an agent to guess at mutation semantics.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents both the draft_id and tags parameters, including that tags may be a string or list. The description adds no parameter-level detail beyond referencing the target, which is the expected baseline when the schema is complete.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a clear verb (add) and resource (tags) plus the target type (draft/post), so an agent can immediately tell what it does. It does not explicitly distinguish itself from siblings like put_draft, which could also modify a draft's content, but the tag-focused operation is clear enough.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied: add tags to a draft. There is no explicit guidance on when to use this versus put_draft or other draft-modifying siblings, and no prerequisites or exclusions are mentioned.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_draftGet DraftC
Get a specific draft by its ID.
| Name | Required | Description | Default |
|---|---|---|---|
| draft_id | Yes | The identifier of the draft. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full behavioral burden, yet it says nothing about it being a read-only lookup, permission requirements, or what happens when the ID does not exist. An agent can infer "read" from the verb, but nothing about failure modes or access constraints is disclosed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single short sentence with the key qualifier ("by its ID") front-loaded and no filler. There is nothing to trim, though also little substance.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is simple: one required parameter with full schema coverage and an output schema that documents return values, so most needs are covered structurally. The missing piece is any note on behavior for an unknown draft ID or on how this fits alongside list_drafts.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% — draft_id is typed as an integer and documented — so the baseline of 3 applies. The description adds only that the ID identifies a "specific draft," offering no format or scoping detail beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ("Get a specific draft") and adds the retrieval key ("by its ID"), which implicitly distinguishes it from the list_drafts sibling. It stops short of naming any sibling explicitly, so it does not fully earn a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no guidance on when to use this versus list_drafts or the various draft-mutation siblings (put_draft, prepublish_draft, publish_draft). The only hint is "by its ID," implying the caller already has an identifier, which is implied usage at best.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_statusGet StatusA
Get the authentication status and basic user information.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description must carry the behavioral burden, and it does say the tool reads auth state plus basic user info. It stops short of disclosing important traits for a status tool — whether it errors when unauthenticated versus returning an unauthenticated status, and whether it is a cheap connectivity ping or a heavier lookup.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with no filler or redundancy. Nothing in it fails to earn its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be explained, and zero-parameter tools have a low bar. Even so, the description omits the one behavior that matters for a status tool — what happens when the caller is not authenticated — leaving it only minimally complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is no parameter semantics to document. The baseline of 4 applies; nothing in the description is needed or missing on this dimension.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Get') and a specific resource ('authentication status and basic user information'), so the agent knows exactly what it returns. It is clearly distinct from all siblings (draft/publication tools), though the description doesn't explicitly say so.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied — an agent can infer this is a session/credential check — but there is no explicit 'use this before X' or 'use this to verify Y' guidance, and no mention of alternatives. For a zero-parameter utility tool this is acceptable, but it leaves the agent to reason about when to call it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_draftsList DraftsC
List drafts for the current publication.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Max number of drafts to return. | |
| filter | No | Filter string, defaults to "draft". | draft |
| offset | No | Pagination offset. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full behavioral burden. It does not mention read-only/safety traits, pagination behavior, permissions, or rate limits; it only adds the 'current publication' scope.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single front-loaded sentence with no filler. It is appropriately concise, though its brevity contributes to gaps elsewhere.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is simple, has an output schema, and has complete parameter schema coverage, so the description needn't explain return values or parameter details. However, it still lacks enough usage and behavioral context for an agent to confidently choose it over sibling draft tools.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already explains limit, filter, and offset. The description adds no parameter semantics beyond the schema, which matches the baseline for fully documented parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (List) and resource (drafts) and scopes it to the current publication. It does not explicitly distinguish itself from nearby tools like get_draft, but the basic purpose is clear.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives no explicit when-to-use guidance, no exclusions, and no alternatives. An agent must infer that this is the bulk-listing tool versus get_draft or list_publications.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_publicationsList PublicationsB
List all publications available to the authenticated user.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It does disclose the access scope ('available to the authenticated user'), implying results are limited to the caller and that auth is required, and 'list' implies a read-only operation. It says nothing about pagination, ordering, or result limits, which is a meaningful gap for a collection endpoint.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with no filler; the scope constraint appears immediately after the verb and resource. Nothing could be removed without losing information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The presence of an output schema means return values need not be explained, and with zero parameters the schema burden is minimal. However, the absence of annotations leaves the description responsible for safety and behavior disclosure, and it omits pagination, ordering, and any exclusivity versus list_drafts, so it is only minimally viable.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing for the description to disambiguate; baseline is 4. No conflicting parameter hints are present.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('List') and resource ('publications') with a scope qualifier ('available to the authenticated user'), which is more precise than a bare name restatement. It does not explicitly distinguish itself from the sibling list_drafts, but the resource noun makes the difference inferable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No statement of when to use this instead of list_drafts or the draft-related siblings, and no stated prerequisites beyond a vague reference to being authenticated. The agent must infer the use case entirely from the tool name.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
post_draft_from_markdownPost Draft From MarkdownA
Create or update a Substack draft from Markdown.
This tool builds a Substack `Post` from markdown content and posts a draft.
It supports optional tag assignment, prepublish (setup check), and publishing.
Args:
title: Draft title.
markdown: Markdown body content.
subtitle: Optional subtitle text.
audience: One of `everyone`, `only_paid`, `founding`, `only_free`.
write_comment_permissions: One of `none`, `only_paid`, `everyone`.
search_engine_title: Optional title for search engine optimization.
search_engine_description: Optional description for search engine optimization.
slug: Optional URL slug for the post.
draft_section_id: Optional section ID for the draft.
tags: Tag or list of tags to attach to the post.
prepublish: If true, calls `prepublish_draft` after creation.
publish: If true, calls `publish_draft` after creation (and optionally prepublish).
send: Passed to `publish_draft` for newsletter delivery.
share_automatically: Passed to `publish_draft`.
Returns:
dict containing drafted post (`draft`), optional `tags`, `prepublish`, `publish` results.
Examples:
With the YAML structure from the README, a caller can map fields like:
```yaml
title: "My Post Title"
subtitle: "My Post Subtitle"
audience: "everyone"
write_comment_permissions: "everyone"
markdown: |
# Hello
This is the body.
tags:
- python
- substack
prepublish: true
publish: true
send: false
share_automatically: true
```
Then invoke via MCP directly:
```python
from substack_mcp.mcp_server import post_draft_from_markdown
result = await post_draft_from_markdown(
title='My Post Title',
markdown='# HelloThis is the body.', subtitle='My Post Subtitle', audience='everyone', write_comment_permissions='everyone', tags=['python', 'substack'], prepublish=True, publish=False, # set true when ready ) print(result) ```
A longer process with manual prepublish/publish calls:
```python
from substack_mcp.mcp_server import (
post_draft_from_markdown,
prepublish_draft,
publish_draft,
add_tags,
)
d = await post_draft_from_markdown(
title='Long flow',
markdown='Content',
tags=['a','b'],
publish=False,
)
draft_id = d['draft']['id']
await add_tags(draft_id, ['post-tag', 'news'])
await prepublish_draft(draft_id)
await publish_draft(draft_id, send=True, share_automatically=True)
```
This docstring example is meant to mirror the YAML-driven workflow and show how to decompose the same operations into explicit tool calls.
| Name | Required | Description | Default |
|---|---|---|---|
| send | No | ||
| slug | No | ||
| tags | No | ||
| title | Yes | ||
| publish | No | ||
| audience | No | everyone | |
| markdown | Yes | ||
| subtitle | No | ||
| prepublish | No | ||
| draft_section_id | No | ||
| search_engine_title | No | ||
| share_automatically | No | ||
| search_engine_description | No | ||
| write_comment_permissions | No | everyone |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations at all, the description carries the full burden, and it does disclose meaningful behavior: chained side effects when prepublish/publish are true, newsletter delivery via send, and the shape of the return dict. It omits the highest-stakes facts — that publish=true makes the post publicly live, that send=true (the schema default!) emails subscribers, and any auth prerequisite. The 'create or update' claim is also unexplained since no identifier parameter controls which mode applies.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The first sentence is well front-loaded and the Args block is scannable, but the description is bloated: a YAML block plus two full Python examples, a Returns section redundant with the output schema, and a meta-sentence explaining what the examples are for. Roughly half the text restates information an agent already has from the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 14-parameter mutation tool with 0% schema coverage, the description covers most of what an agent needs: parameter listing, enums, chaining semantics, and return shape (redundant given the output schema). The remaining gaps are the create-vs-update mechanism and the risk profile of publish/send, which matter for a write tool with no annotations.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0% and there are 14 parameters, so the description must compensate — and it does list every one, including enum values for audience and write_comment_permissions that the schema lacks. But several entries are near-tautological ('subtitle: Optional subtitle text') and send/share_automatically are dismissed as 'Passed to publish_draft' with no explanation, and no defaults are stated despite the surprising send=true default.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence gives a specific verb and resource ('Create or update a Substack draft from Markdown') and the body clarifies it builds a Post and posts a draft. It also names the sibling tools it can chain into (prepublish_draft, publish_draft), which helps distinguish it from those. It does not, however, differentiate itself from put_draft, the other draft-writing sibling.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The two worked examples implicitly establish when to use the combined call versus decomposing into add_tags/prepublish_draft/publish_draft manually, and the closing sentence states that intent explicitly. What is missing is a stated rule for choosing between the two approaches and any precondition ('the draft must not already be published').
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
prepublish_draftPrepublish DraftC
Invoke prepublish checks for a draft.
| Name | Required | Description | Default |
|---|---|---|---|
| draft_id | Yes | target draft identifier. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden and does not say whether invoking checks mutates the draft, whether it is idempotent, whether failures block publication, or what permissions are needed. It only restates the action.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with no filler, though the terseness comes at the cost of explaining 'prepublish checks'.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The output schema covers return values and the single parameter is documented, but for a tool sitting among several publish variants, the description should clarify how it differs from publish_draft_checked and whether the checks are read-only.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% for the single draft_id parameter, so the schema already documents the argument. The description adds no semantics about what draft identifiers are valid or how the checks relate to that draft beyond the name, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names an action ('invoke prepublish checks') on a resource ('draft'), which is more than a tautology, but 'prepublish checks' is unexplained jargon and nothing distinguishes it from siblings like publish_draft_checked or publish_draft, which plausibly also perform validation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no guidance on when to call this versus publish_draft, publish_draft_checked, or get_status. Given the dense cluster of publish-related siblings, the absence of any exclusion or sequencing hint leaves the agent to guess.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
publish_draftPublish DraftA
Publish a draft to live post state. (Legacy compatibility interface).
This tool remains for backward compatibility. It is recommended to use
publish_draft_checked instead, which provides a safer publishing path
with explicit confirmation and prepublish validation.
| Name | Required | Description | Default |
|---|---|---|---|
| send | No | if False then do not send email to subscribers. | |
| draft_id | Yes | target draft identifier. | |
| share_automatically | No | whether to auto-share (e.g. social propagation). |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the behavioral load: it discloses that this path publishes to a live state and lacks the confirmation and prepublish validation of the checked variant, which is a meaningful risk disclosure. It stops short of saying whether publishing is reversible or what side effects occur (e.g. default email send), though the output schema covers return shape.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose and the legacy routing are front-loaded and the text is short. The parenthetical "(Legacy compatibility interface)" is then restated as "This tool remains for backward compatibility", a small redundancy that keeps it from a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists and parameters are fully documented, so the description only needs to cover purpose, routing and risk. It does those, but leaves out what actually changes on the draft and whether the action is reversible — the one thing an agent deciding to call a publishing mutation would want.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all three parameters (send, draft_id, share_automatically) are already documented in the schema with defaults. The description adds no parameter-level meaning beyond that, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence gives a specific verb and target state ("Publish a draft to live post state"), which is precisely distinguishable from siblings like prepublish_draft and schedule_draft. It also names the near-duplicate sibling publish_draft_checked so the agent can tell the two apart without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states this tool exists only for backward compatibility and recommends publish_draft_checked instead, giving the reason (explicit confirmation and prepublish validation). That is a clear when-to-prefer-the-alternative rule, which is exactly what usage guidance should do.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
publish_draft_checkedPublish Draft CheckedB
A safer publishing path that requires confirmation and runs prepublish checks.
| Name | Required | Description | Default |
|---|---|---|---|
| send | No | if False then do not send email to subscribers. Defaults to False. | |
| confirm | No | Must be True to proceed with publication. | |
| draft_id | Yes | target draft identifier. | |
| share_automatically | No | whether to auto-share. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden. It usefully discloses that confirmation is required and prepublish checks are run, but it does not explain side effects, irreversibility, permissions, or what happens if checks fail.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The definition is a single front-loaded sentence with no wasted language. It is appropriately concise, though its brevity leaves important selection and safety details to other fields.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has a rich schema with full parameter coverage and an output schema, so return values need not be described. However, for a publishing mutation with no annotations, the description should do more to clarify how it differs from sibling publish/prepublish tools and what the checks imply.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all four parameters are already documented in the schema. The description adds no parameter-specific meaning beyond the existing schema text, making 3 the appropriate baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific action and resource: publishing a draft through a safer path with confirmation and prepublish checks. It partially distinguishes itself from siblings like publish_draft and prepublish_draft by calling out the safer/checked workflow, though it does not explicitly compare them.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'A safer publishing path' implies when to choose this over a normal publish operation, but the description never names alternatives or states when not to use it. Usage is inferable rather than explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
put_draftPut DraftC
Update an existing draft by draft ID.
| Name | Required | Description | Default |
|---|---|---|---|
| draft_id | Yes | target draft identifier. | |
| update_payload | Yes | dict of fields supported by Substack `put_draft` (e.g. `slug`, `draft_section_id`). |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden, yet it says nothing about whether the update is a full replacement or a merge, what permissions are required, whether changes are reversible, or what happens to omitted fields. 'Update an existing draft' implies mutation and a precondition (draft must exist), but that is the extent of the disclosure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with zero waste. The brevity edges into under-specification rather than verbosity, but structurally nothing is misplaced.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
This is a mutation tool with no annotations, a nested free-form update_payload, and only an output schema to lean on. The description should clarify which fields are updatable and what the payload actually does, but it offers nothing beyond the schema's example fields.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so both parameters are already documented in the schema, and the description adds no syntax, format, or constraint detail beyond it. Baseline 3 applies when the schema does all the work.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('update') and resource ('existing draft by draft ID'), which is clear enough on its own. It does not differentiate from siblings like post_draft_from_markdown or prepublish_draft that also touch drafts, so an agent still has to infer where the boundary lies.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives no conditions for use, no exclusions, and no alternatives. It never says whether this should be used for content edits versus publishing/scheduling flows covered by publish_draft or schedule_draft, leaving routing entirely to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
schedule_draftSchedule DraftC
Schedule a draft for release.
| Name | Required | Description | Default |
|---|---|---|---|
| at | Yes | ISO 8601 formatted datetime string (e.g., "2024-01-01T12:00:00Z"). | |
| draft_id | Yes | target draft identifier. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden, yet it discloses nothing about side effects: whether the draft auto-publishes at the given time, what permissions are required, whether scheduling an already-scheduled draft overwrites or errors, or whether the time must be in the future. For a mutation tool this is a substantial gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with no filler, which is efficient. Its brevity reflects under-specification more than careful economy, but on this dimension alone it is well-formed.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Although an output schema exists and parameters are fully documented, the description leaves the core behavior of a scheduling mutation unexplained (auto-publish semantics, preconditions, interaction with prepublish_draft). An agent could invoke it but could not predict its consequences.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% — both draft_id and the ISO 8601 format of `at` are documented in the schema — so the baseline of 3 applies. The description adds no meaning beyond it, notably omitting whether `at` must be in the future or which timezone assumptions apply.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The verb-and-resource pair ("schedule" + "draft") is identifiable, but "for release" is ambiguous — it does not say whether this queues automatic publishing, marks a prepublish date, or merely records intent. It also does not distinguish itself from close siblings such as publish_draft, prepublish_draft, and unschedule_draft.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance on when to schedule rather than publish_draft or prepublish_draft, and no mention of unschedule_draft as the inverse operation. An agent must infer the workflow entirely from sibling names.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
unschedule_draftUnschedule DraftC
Unschedule a previously scheduled draft.
| Name | Required | Description | Default |
|---|---|---|---|
| draft_id | Yes | target draft identifier. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden of behavioral disclosure. It identifies the operation as an unschedule mutation and implies a precondition, but says nothing about permissions, reversibility, error behavior if the draft is not scheduled, or what state the draft returns to.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single front-loaded sentence with no unnecessary words. It is appropriately sized for a simple one-parameter tool, though its brevity leaves important behavioral details unstated.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be explained. For a simple mutation tool with one well-documented parameter, the description is minimally adequate, but it omits behavioral context about post-conditions and failure modes that would matter when unscheduling a draft.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema has 100% description coverage and documents the single draft_id parameter. The description does not add any parameter-level meaning beyond what the schema already provides, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('Unschedule ... draft') and adds the precondition that the draft was previously scheduled. It is clear, but it does not explicitly distinguish itself from the sibling schedule_draft beyond the antonym in the verb.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no explicit guidance on when to use this tool versus alternatives such as schedule_draft or put_draft. The phrase 'previously scheduled' implies a precondition, but no when-to-use or when-not-to-use rules are stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
12 tool updates
v0.1.0- First observed
add_tags - First observed
get_draft - First observed
get_status - First observed
list_drafts - First observed
list_publications - First observed
post_draft_from_markdown - First observed
prepublish_draft - First observed
publish_draft - First observed
publish_draft_checked - First observed
put_draft - First observed
schedule_draft - First observed
unschedule_draft
TDQS
Scored across 12 tools
Several tools overlap: post_draft_from_markdown bundles create/update plus prepublish/publish/tags, duplicating add_tags, prepublish_draft, and publish_draft, and put_draft also updates drafts, muddying create-vs-update boundaries. publish_draft and publish_draft_checked are near-duplicates, though the descriptions explicitly flag the legacy/safer distinction. Descriptions help but an agent must still reason about which of several paths to use.
Most tools follow a clean verb_noun pattern (add_tags, prepublish_draft, publish_draft, get_status, list_drafts, get_draft, schedule_draft, unschedule_draft). Minor deviations: put_draft uses 'put' instead of 'update', and post_draft_from_markdown is a longer, less uniform form. Overall consistent and readable.
Twelve tools is well-scoped for a publishing workflow and each maps to a recognizable operation. Slight redundancy from the legacy publish_draft alongside publish_draft_checked, but count is reasonable.
Draft lifecycle is mostly covered (create, get, list, update, publish, schedule/unschedule, add tags), but there is no delete_draft and no way to remove tags. Publication listing exists without a corresponding get/select-publication operation, leaving a few dead ends.
Maintenance
Related MCP Connectors
- UniPostOAuthai.unipost
Hosted MCP server for AI agents to post to LinkedIn profiles and X and upload to YouTube.
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
Connect any AI agent to 11+ social platforms: schedule, publish & track posts via hosted MCP.
Agent-first social media publishing, scheduling, analytics, and unified inbox API + 16-tool MCP server across 18 social platforms (X, LinkedIn, Instagram, YouTube, TikTok, Threads, Bluesky, Reddit, Telegram, Discord, Mastodon, Facebook, Pinterest).
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables deployment of autonomous AI agents with memory and tool execution capabilities through a WebSocket-based MCP protocol. Provides production-ready infrastructure with REST API access, persistent state management, and extensible function registry for building self-hosted AI systems.-
- AlicenseNot gradedqualityDmaintenanceMCP server for self-hosted static site publishing on Cloudflare Workers. Enables AI coding agents to deploy pages with a single 'publish' tool and get live URLs, with support for atomic updates, versioning, and per-site passwords.2 npm2MIT
- AlicenseNot gradedqualityAmaintenanceEnables self-hosted AI gateway and agent control plane with governed MCP tools, virtual-key budgets, caching, and audit, supporting OpenAI, Anthropic, Gemini, MCP, and A2A protocols. It provides deterministic prompt enhancement and governed execution with a zero-credential first run.10Apache 2.0
- AlicenseNot gradedqualityBmaintenanceA hosted MCP server that lets AI assistants schedule and publish social media posts across Instagram, TikTok, X, LinkedIn, YouTube, Pinterest, Facebook, Telegram, Threads, and Bluesky. It provides tools for managing accounts, posts, media, and analytics, including post previewing before publishing.1MIT