Substack-OPS
substack-ops
Standalone Substack CLI + 26-tool MCP server. Your IDE drafts the replies. Zero AI API keys.
Site → substack-ops.chavan.in · Source → 06ketan/substack-ops · Glama → mcp/servers/06ketan/substack-ops
Posts, notes, comments, replies, reactions, restacks, recommendations, search, profiles, feeds, automations, MCP server, Textual TUI. One Python install, one binary, MIT licensed.
TL;DR — MCP-native (no API key, one command)
uvx substack-ops mcp install cursor # or claude-desktop, claude-code, print
# Restart your host. Then in chat:
# "list unanswered comments on post 193866852"
# "draft a warm reply to comment 12345"
# "post that draft"Your host's LLM (Cursor's, Claude's) does the drafting via the
propose_reply / confirm_reply tools. No ANTHROPIC_API_KEY /
OPENAI_API_KEY needed.
Related MCP server: substack-mcp
Setup (dev / from source)
git clone https://github.com/06ketan/substack-ops && cd substack-ops
uv sync
uv sync --extra mcp # mcp SDK for the MCP server (recommended)
uv sync --extra tui # textual for the TUI
uv sync --extra chrome # pycryptodome + keyring for Chrome cookie auto-grabAuth defaults to ~/.cursor/mcp.json's mcpServers.substack-api.env. Override
with env or .env. Or use one of the auth flows in auth login / auth setup.
uv run substack-ops auth verify
uv run substack-ops quickstart # 20-step tourCommand surface
Grouped by intent. Every write defaults to --dry-run; flip with
--no-dry-run (and --yes-i-mean-it for the irreversible ones). All writes
land in .cache/audit.jsonl and are dedup-checked against .cache/actions.db.
Auth (4)
Command | What it does |
| Confirm the cookie works; print authed user/pub. |
| Same as verify, exit non-zero on failure (CI-friendly). |
| Auto-grab cookie from local Chromium browser via macOS Keychain. |
| Email magic-link → paste-the-link interactive flow. |
| Interactive paste of |
Read — Posts (8)
Command | What it does |
| List posts from a publication (yours by default). |
| Post metadata (title, dates, reactions, comment count). |
| Same as |
| HTML body (auth-aware for paywalled). |
| Engagement counts — reactions, comments. |
| Substack-side full-text search. |
| Boolean: is this post paywalled? |
| Add (or remove with |
| Restack a post (Substack does not support unrestack). |
Read — Notes (5)
Command | What it does |
| Your published Notes. |
| One note + its reply tree. |
| Publish a top-level Note. |
| React on any Note. |
| Restack a Note. |
Read + Write — Comments (5)
Command | What it does |
| Full nested comment tree as table. |
| Same tree as JSON. |
| New top-level comment. |
| React on a comment. |
| Destructive — your own comments only. |
Reply engine (6)
Command | What it does |
| Rule-based replies (no LLM). |
| LLM drafts each, you |
| Draft every comment to a file. Edit, set |
| Same for replies under a Note. |
| Posts only |
| Draft + post immediately. 30s rate limit. |
Read — Discovery (8)
Command | What it does |
| Reader feed (the Substack app feed). |
| Profile. |
| Public user info + their subs. |
| Audio posts. |
| Pub's recommended publications. |
| Pub's contributor list. |
| Substack's category taxonomy. |
Automations (3)
Command | What it does |
| List built-in YAML rules. |
| One-shot run a preset. |
| Loop forever; logs to audit. |
Operations + safety (3)
Command | What it does |
| Query the JSONL audit log. |
| Counts in the dedup SQLite DB. |
| 20-step interactive tour. |
MCP server (3)
Command | What it does |
| Auto-merge config into your host. |
| stdio MCP server (26 tools). |
| Print the tool registry. |
Other (1)
Command | What it does |
| Textual TUI — 6 tabs (Notes, Posts, Comments, Feed, Auto, Profile). |
Multi-publication
Every read command accepts --pub <subdomain|domain>. Defaults to your own
publication.
substack-ops posts list --pub stratechery --limit 5
substack-ops posts search "ai" --pub stratechery
substack-ops recommendations list --pub stratecheryReply modes
Mode | What it does | Safety |
| YAML keyword/regex rules under | dry-run default |
| LLM drafts each reply, you | dry-run default + manual gate per comment |
| LLM drafts every comment to | offline review, dedup-checked on send |
| Posts only items with | dry-run default; dedup DB prevents the M2 31-dup-replies regression |
| LLM drafts and posts immediately | requires |
After every live note-reply the engine re-fetches the new comment and asserts
ancestor_path is non-empty. If empty, the audit row's result_status is
flipped to "orphaned" (the M2 bug where parent_comment_id was silently
dropped — now caught).
Automations
Built-in presets (auto presets):
like-back — when someone reacts to your note, react to their latest note.
auto-reply — same trigger, but post a templated thank-you.
auto-restack — when a watchlist handle posts a new note, restack it.
follow-back — when someone follows you, follow them back.
Custom YAML rules under ~/.config/substack-ops/auto/*.yaml. Loop with
auto daemon <name> --interval 60.
MCP server
substack-ops mcp install cursor # auto-add to ~/.cursor/mcp.json
substack-ops mcp install claude-desktop # auto-add to claude_desktop_config.json
substack-ops mcp install claude-code # uses `claude mcp add` under the hood
substack-ops mcp install print # print the snippet only
substack-ops mcp install cursor --dry-run # preview without writing
substack-ops mcp serve # stdio server
substack-ops mcp list-tools # 26 toolsManual config snippet (if you prefer):
{
"mcpServers": {
"substack-ops": {
"command": "substack-ops",
"args": ["mcp", "serve"]
}
}
}If the mcp SDK is not installed, the server falls back to a minimal
stdin/stdout JSON-line dispatcher that's still useful for scripting:
echo '{"tool":"list_posts","args":{"limit":3}}' | substack-ops mcp serveMCP-native draft loop (no API key)
3 tools designed to let your host LLM draft for you:
Tool | What it does |
| Returns the worklist: comments where you have not yet replied (any depth). |
| Dry-run only. Returns a |
| Posts a previously-proposed reply by token. Idempotent via dedup DB. Token TTL 5 min. |
Differentiator tools (the safety + drafting stack that makes the unattended
mode safe): bulk_draft_replies, send_approved_drafts, audit_search,
dedup_status, get_unanswered_comments, propose_reply, confirm_reply.
LLM strategy
Two layers, both free:
MCP-native (default). Host LLM drafts via
propose_reply/confirm_reply. No env vars, no API key. Use this for interactive replies.Subprocess CLI (daemon path). For
reply auto/auto daemonwhen no human is in the loop. Auto-detectsclaude(Claude Code),cursor-agent, orcodexon PATH. Override withSUBSTACK_OPS_LLM_CMD.
There is no paid-API-key path. If you want one, vendor the old _anthropic /
_openai methods from substack-ops v0.2.0 yourself.
Textual TUI
substack-ops tui6 tabs: Notes / Posts / Comments / Feed / Auto / Profile. Sub-tabs: 1=mine, 2=following, 3=general. Keys: tab, 1-3, ↑/↓, enter, r, l, s, o, q/esc.
Auth methods
substack-ops auth verify # uses mcp.json or env
substack-ops auth login # auto-grab cookies from Chrome (macOS Keychain)
substack-ops auth login --browser brave
substack-ops auth login --email me@x.com # email magic-link, paste-the-link mode
substack-ops auth setup # interactive paste cookiesArchitecture
mcp.json | env | Chrome | OTP → auth.py / auth_chrome.py / auth_otp.py
│
.cache/cookies.json
│
SubstackClient (httpx)
│
┌──────┬──────┬───────┬───────┬───────┬──────┬──────┬─────┬──────┐
▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼
posts notes comments feed profile users recs cats ... reply_engine
│
┌───────────────┼────────────┐
▼ ▼ ▼
template ai_review ai_bulk + ai_auto
└───────────────┬────────────┘
▼
base.post_reply / post_note_reply
│
┌────────┼────────┐
▼ ▼ ▼
dedup audit ancestor_path
(SQLite) (jsonl) guardrail
auto/engine.py ────────────────┐
mcp/server.py ──── 23 tools ──┼─── all share SubstackClient
tui/app.py ──── 6 tabs ──┘Endpoints used
Action | Method + URL |
Auth check |
|
List posts |
|
Post by id |
|
Post by slug |
|
Post content | same as above; |
Post search |
|
Comments |
|
Reply to comment |
|
Add top-level comment | same with |
React to post |
|
Restack post |
|
Restack note |
|
Delete post-comment |
|
Delete note |
|
My notes |
|
Note thread |
|
Note replies |
|
Publish note |
|
Reply to note | same with |
React to comment |
|
Recommendations |
|
Authors |
|
Categories |
|
User profile |
|
Reader feed |
|
Tests
uv run pytest -q # 43 tests, ~0.6s, no live networkCoverage today: auth, client (read+write+engagement+delete), reply engine,
dedup DB, audit log search, MCP tool registry & dispatcher, automation engine
preset loader, the M2 parent_id regression test, the M2 host-mismatch
regression test.
GSD workflow
.planning/ scaffold for Get Shit Done
under ~/.claude/skills/gsd-*. Roadmap at .planning/ROADMAP.md,
per-phase plans at .planning/phases/M*/PHASE.md.
Known gaps
Full email stats (opens/clicks/views) — needs dashboard CSRF flow. Fallback: Playwright MCP scrape.
Reactions endpoint shape on POST/DELETE not yet probed live; current shape is a best-guess from upstream tool catalogs.
Auto-engine
new_follower/new_note_fromtriggers are stubbed (returnnote: "trigger not yet implemented").TUI sub-tabs (1/2/3) and reply/like/restack key bindings are scaffolded but not wired to the client yet.
Chrome cookie auto-grab tested only for macOS Chrome; Brave path included; Linux/Windows not supported.
License
MIT. See LICENSE.
The vendored httpx-port helpers under src/substack_ops/_substack/ are derived
from the MIT-licensed NHagar/substack_api package — kept here so this repo
ships zero runtime dependencies on third-party Substack libraries. Attribution
preserved in each file's module docstring.
Available Tools
26 toolsaudit_searchA
Read-only. Query the local audit.jsonl log of every write this server has performed (or attempted). Filters compose with AND. Use to debug 'did I post that?' or to pull rate-limit history. For a quick count summary use dedup_status.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | No | ||
| target | No | ||
| status | No | ||
| since | No | ||
| limit | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description states the tool is read-only and that filters compose with AND, but lacks details on authentication, rate limits, or response format. Without annotations, the description carries full burden; this is adequate but not comprehensive.
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 concise with three sentences, front-loading 'Read-only' and the purpose. No wasted words, but could be slightly more structured with parameter breakdown.
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?
Given no output schema and 5 parameters with 0% description coverage, the description lacks details on what the response contains or how to use the filters effectively. An agent would need more context for correct invocation.
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 0%, and the description only adds that 'Filters compose with AND' without explaining each parameter's values or format. Parameter names like 'kind', 'target', 'status' are self-explanatory but the description does not sufficiently compensate for the missing schema documentation.
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 clearly states the tool queries the local audit log for write operations, with a specific verb 'Query' and resource 'audit.jsonl log'. It also distinguishes itself from the sibling tool 'dedup_status' which provides a quick count summary.
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?
Explicit usage guidance is provided: 'Use to debug 'did I post that?' or to pull rate-limit history.' It also tells when to use an alternative: 'For a quick count summary use dedup_status.' This helps the agent select the correct tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
bulk_draft_repliesA
WRITE TO LOCAL FILE (no Substack call). Generate reply drafts for every comment on a post (kind='post') or every reply on a note (kind='note') using the daemon-path LLM (host CLI: claude / cursor-agent / codex on PATH, or SUBSTACK_OPS_LLM_CMD). Output is a JSONL drafts file with action='proposed' per row; review, edit action to 'approved' or 'rejected', then send via send_approved_drafts.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | No | post | |
| id | Yes | ||
| out | No | drafts.json | |
| model | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Without annotations, the description fully discloses the local file write, use of a local LLM CLI, and the output format with action='proposed'. It also notes the configurable LLM path. This goes beyond just stating it's a write operation.
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 concise at two sentences, but the first sentence is dense with parenthetical details, which could be better structured for readability. However, it is front-loaded with the key action.
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 description covers purpose, output, and workflow, but lacks details on parameter constraints (e.g., id format, valid kind values) and does not address error handling or dependencies like required CLI tools, leaving some gaps for an AI agent.
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?
With 0% schema description coverage, the description adds meaning by explaining kind (post vs note) and out (output file), but it does not specify valid values for kind, format for id, or default behavior for model, leaving gaps.
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 clearly states it generates reply drafts for comments on a post or replies on a note using an LLM, and writes to a local JSONL file. It distinguishes this from sibling tools like propose_reply and send_approved_drafts by noting it's a local operation and part of a workflow.
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 explains when to use this tool (for bulk drafting on posts/notes) and mentions the workflow: edit drafts and then use send_approved_drafts. It implies not to use it for single drafts or server-side operations, though explicit alternatives are not listed.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
comment_on_postA
WRITE. Add a NEW top-level comment under a post (not a reply to an existing comment). Defaults to dry_run=true. For replies to existing comments use propose_reply -> confirm_reply. Dedup-protected by (post_id, body) hash.
| Name | Required | Description | Default |
|---|---|---|---|
| post_id | Yes | ||
| body | Yes | ||
| pub | No | ||
| dry_run | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses that it's a write operation, defaults to dry_run=true, and is dedup-protected by (post_id, body) hash. Without annotations, this covers key behavioral traits well, though could mention response behavior or error handling.
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?
Two sentences with no wasted words. The most critical information (action, scope, alternatives) is front-loaded. Every sentence adds value.
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?
Given 4 parameters with 0% schema coverage and no output schema, the description is adequate for purpose and usage but lacks parameter details (especially 'pub') and does not describe the return value or error conditions.
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?
Description does not explain parameters beyond the schema. Schema coverage is 0%, so the description should add meaning, but only implies the role of post_id and body via dedup, and ignores pub and dry_run entirely.
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?
Description clearly identifies the tool as adding a new top-level comment (not a reply), with specific verb 'Add' and resource 'top-level comment'. It distinguishes from the propose_reply/confirm_reply flow for replies.
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?
Explicitly states when to use (for top-level comments) and when not (for replies), and names alternative tools (propose_reply, confirm_reply). Also notes the default dry_run behavior.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
confirm_replyA
EXECUTE the staged write. Look up the token from propose_reply, post to Substack, log to audit.jsonl, persist dedup row. Idempotent: if the same content was already sent, returns {deduped: true} without re-posting. Use force=true to bypass dedup (rare). Tokens are single-use and expire 5 min after propose_reply.
| Name | Required | Description | Default |
|---|---|---|---|
| token | Yes | ||
| force | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses idempotency (returns deduped: true), force usage, token single-use and 5-minute expiry. No annotations exist, so description carries full burden; it covers major behaviors well.
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?
Three sentences with key information front-loaded. No verbose or redundant content. Every sentence adds value.
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?
Covers usage, dedup, token expiry. But does not mention return format for normal success (only idempotent case) or error scenarios. With no output schema, more detail on response would help.
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%, but description explains token as from propose_reply with expiry, and force as bypass for dedup. Adds meaning, though token format could be specified.
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?
Description clearly states 'EXECUTE the staged write' and details the actions: look up token, post, log, persist. It distinguishes from siblings like propose_reply and comment_on_post by mentioning the token flow and dedup behavior.
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?
Provides clear context: token from propose_reply, force flag for rare bypass, idempotent behavior. Does not explicitly contrast with alternatives but implies the tool's role in a two-step reply process.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
dedup_statusA
Read-only. Return counts from the local dedup SQLite DB (one row per successful write, keyed by content hash). Quick health check; for filtered details use audit_search. No args.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description fully covers behavioral traits: read-only nature, data source (SQLite DB), data structure (one row per successful write, keyed by content hash), and no arguments. No missing behavioral context.
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?
Three short sentences, each adding distinct value: read-only, return contents, health check usage, alternative tool, no args. No redundant or unnecessary 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 tool is simple with no parameters and no output schema. The description adequately explains what it does and its purpose. Minor improvement could mention the output format (e.g., counts as integers), but not critical for a health check.
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 has 0 parameters and 100% description coverage. The description adds 'No args' which confirms the empty schema but does not add significant meaning beyond what the schema already shows.
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 clearly states it is read-only, returns counts from a local dedup SQLite DB, and is a quick health check. It explicitly distinguishes itself from the sibling 'audit_search' for filtered details.
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?
Provides direct usage guidance: 'Quick health check; for filtered details use audit_search.' This tells the agent when to use this tool and when to use an alternative.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_commentA
DESTRUCTIVE WRITE. Delete one of YOUR own comments (or one on your publication if you're the owner). Cannot be undone. Set kind='post' to delete a post comment (uses pub host) or kind='note' for a note reply. Defaults to dry_run=true — you must explicitly set false.
| Name | Required | Description | Default |
|---|---|---|---|
| comment_id | Yes | ||
| kind | No | post | |
| pub | No | ||
| dry_run | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses destructive nature ('DESTRUCTIVE WRITE', 'Cannot be undone'), ownership requirements, and dry_run safety mechanism. No annotations exist, so description carries full burden and meets it.
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?
Two efficient sentences front-loaded with 'DESTRUCTIVE WRITE.' No wasted words; every clause adds value.
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?
Given no annotations, no output schema, and 4 parameters, the description covers purpose, constraints, parameter behavior, and side effects. A minimal but complete guide for correct invocation.
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?
Adds meaning for all four parameters: comment_id (implied required), kind (explains values post vs note and implication on pub), pub (needed for post comments), dry_run (default true, must set false to execute). Schema has 0% description coverage; description compensates fully.
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 clearly states the tool deletes a comment (verb+resource) with ownership constraints, distinguishing it from siblings like react_to_comment or comment_on_post.
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?
Explicitly states when to use (own comments or owner's publication), warns about irreversibility, and explains how to override dry_run default.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_feedA
Read-only. Pull items from the reader feed you'd see in the Substack app/home. Pass tab='for-you' (personalized recommendations, default), 'subscribed' (only publications you've subscribed to), or 'category-{slug}' for a topic feed (e.g. 'category-tech', 'category-finance', 'category-politics'). Returns a list of {post_id, title, pub, byline, snippet, published_at}. For a single publication's chronological list use list_posts; for keyword search use search_posts.
| Name | Required | Description | Default |
|---|---|---|---|
| tab | No | for-you | |
| limit | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Starts with 'Read-only' indicating no side effects, and describes the return format. Without annotations, this is good but could mention pagination or rate limits.
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?
Two sentences, highly efficient, front-loads the essential info (read-only, purpose), no redundant text.
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?
Covers purpose, parameters with examples, return fields, and alternatives. Lacks mention of authentication, error cases, or pagination, but acceptable for a simple feed retrieval tool.
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?
Adds meaningful examples for 'tab' parameter (for-you, subscribed, category-slug) and hints at limit affecting list size, but does not elaborate on limit's exact role. Schema has 0% coverage, so description helps but not fully detailed.
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 clearly states the verb 'Pull' and the resource 'reader feed', and distinguishes from siblings list_posts and search_posts by specifying their use cases.
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?
Provides explicit when-to-use instructions with tab options and directs to alternatives: 'For a single publication's chronological list use list_posts; for keyword search use search_posts.'
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_own_profileA
Read-only. Return the authenticated user's full profile as a dict with keys: id, handle, name, bio, photo_url, subscriber_count, primary_publication. Use this for 'who am I'-style calls and for preflight checks before WRITE tools (the auth handle is needed to build the publish URL). For another user's profile by handle, call get_profile instead. No args.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Labels tool as 'Read-only', a key behavioral trait. Lists exact keys returned. No annotations provided, so description carries full burden, but could mention any rate limits or side effects; however, for a read-only profile retrieval, the description is sufficient.
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?
Description is succinct (4 sentences) with important information front-loaded: 'Read-only' and 'Return the authenticated user's full profile...'. Every sentence adds value with no redundancy.
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?
Given the tool's simplicity (no parameters, no output schema), the description fully explains purpose, when to use, what it returns (including key names), and how it differs from a sibling. Complete context for selection and invocation.
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?
Input schema has zero parameters and schema description coverage is 100%. Baseline is 4. Description explicitly states 'No args', consistent with schema, adding no new information but confirming behavior.
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?
Directly states the tool returns the authenticated user's profile as a dict with specific keys. Clearly distinguishes from sibling get_profile by specifying alternative for other users.
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?
Explicitly describes when to use ('who am I' calls, preflight checks) and when not to (for other user profiles, use get_profile). Provides context for auth handle needed for WRITE tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_postA
Read-only. Return one post's metadata (title, slug, dates, reactions, comment count) by numeric id OR slug. For HTML body use get_post_content. For id-only callers prefer get_post_by_id.
| Name | Required | Description | Default |
|---|---|---|---|
| post_id | Yes | ||
| pub | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Declares 'Read-only' behavior and lists returned metadata fields. Without annotations, it fully carries behavioral disclosure. Does not mention error handling or retrieval semantics, but sufficient for a read operation.
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?
Two concise sentences with front-loaded 'Read-only' and clear structure. No redundant or vague statements.
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?
Covers purpose, returned fields, and alternatives adequately. Lacks explanation of the optional 'pub' parameter, but overall sufficient for a simple read tool with no output schema.
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?
Explains that post_id can be numeric id or slug, but does not clarify the 'pub' parameter at all. With 0% schema coverage, the description partially compensates but leaves an undocumented parameter.
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?
Clearly states it returns one post's metadata (title, slug, dates, reactions, comment count) by numeric id or slug. Distinguishes from sibling tools get_post_content and get_post_by_id with explicit alternatives.
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?
Provides explicit when-to-use (get one post's metadata) and when-not-to-use (for HTML body use get_post_content; for id-only callers prefer get_post_by_id), along with alternative tool names.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_post_by_idA
Read-only. Strict-typed variant of get_post that ONLY accepts a numeric post id (e.g. 193866852) — no slug fallback. Use this when your caller already has an integer id (e.g. from list_posts response) and you want type safety + fewer round-trips. Returns the same shape as get_post (title, slug, dates, reactions, comment_count). For a slug-or-id input use get_post; for the post body use get_post_content.
| Name | Required | Description | Default |
|---|---|---|---|
| post_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses read-only nature and return shape, but with no annotations, additional details like idempotence or side effects are not explicitly stated, though implied.
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?
Four sentences, front-loaded with key info, no wasted words. Efficiently conveys purpose, usage, return, and alternatives.
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?
With single parameter well-explained, return shape described, and correct differentiation from siblings, the description is complete for a simple tool.
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%, but description adds value by clarifying the parameter type (numeric integer), providing an example, and explaining its use context.
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 explicitly states it is a read-only, strict-typed variant that accepts only a numeric post id, clearly distinguishing it from get_post and get_post_content.
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?
Provides explicit guidance: use when caller has integer id for type safety and fewer round-trips; use get_post for slug-or-id input and get_post_content for post body.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_post_contentA
Read-only. Return a post's body. Auth-aware: returns full text for paywalled posts you have access to, otherwise only the free preview. Set as_markdown=true to convert HTML to Markdown for LLM context.
| Name | Required | Description | Default |
|---|---|---|---|
| post_id | Yes | ||
| pub | No | ||
| as_markdown | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full burden. It discloses read-only behavior and auth-aware paywall handling, adding significant context. However, it omits details like rate limits, error handling, or side effects.
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?
Three concise sentences, front-loaded with key action. No unnecessary words. Every sentence adds value: first sets context, second adds nuance, third adds optional parameter guidance.
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?
Given no output schema and 3 parameters, the description covers core purpose and auth behavior but not return format or error states. It partially compensates for missing annotations but could be more explicit about output structure.
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%, so description must compensate. It only explains as_markdown (conversion to Markdown for LLM). Post_id and pub parameters are not described, leaving the agent guessing their purpose or format.
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 clearly states the tool returns a post's body, and specifies read-only nature with auth-aware behavior. It distinguishes from siblings like get_post by focusing on content body rather than metadata.
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 implies this tool is for fetching post body content, but does not explicitly state when to use it over alternatives like get_post or search_posts. No usage exclusions are provided, and siblings are not referenced.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_profileA
Read-only. Return any Substack user's public profile by their handle (the @-name from their URL, e.g. 'paulgraham' for paulgraham.substack.com). Returns id, handle, name, bio, photo_url, subscriber_count, and primary_publication. For YOUR own profile, prefer get_own_profile (faster, no handle needed, includes private fields). To list a user's posts after this, use list_posts with their pub url.
| Name | Required | Description | Default |
|---|---|---|---|
| handle | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Starts with 'Read-only', specifies it's public, and lists exact return fields. Compensates for absent annotations.
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?
Three concise sentences: purpose, return fields, and usage notes. No redundancy.
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 simple tool with one param and no output schema, description covers input, output, and usage context fully.
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?
Explains the 'handle' parameter with format and example, adding meaning beyond the schema which has 0% coverage.
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 clearly states the tool returns a Substack user's public profile by handle, differentiating from sibling 'get_own_profile'.
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?
Provides explicit guidance: use 'get_own_profile' for own profile (faster, no handle, private fields) and suggests 'list_posts' as next step.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_unanswered_commentsA
Read-only. Return comments on a post where the authenticated user has NOT yet replied (filters out the entire branch if you've replied anywhere in the ancestry). This is the canonical worklist tool: read each, draft a reply in your own context, then propose_reply -> confirm_reply per item. For the full unfiltered tree use list_comments.
| Name | Required | Description | Default |
|---|---|---|---|
| post_id | Yes | ||
| pub | No | ||
| limit | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It clearly states 'Read-only' and explains the filtering logic (filters out branches if user replied). It could add more details like pagination behavior or auth implications, but it sufficiently discloses key behavioral traits.
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 two sentences and a brief workflow hint. It front-loads 'Read-only' and the filtering logic, with no unnecessary words. Efficient and well-structured.
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?
Given no output schema and no annotations, the description covers the filtering logic and use case but fails to document parameters or return format. It provides workflow guidance but not enough for an AI agent to fully understand input 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?
The input schema has 0% description coverage, and the description does not explain any parameters (post_id, pub, limit). It relies entirely on parameter names, leaving 'pub' ambiguous. The description should at least clarify the role of each parameter.
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 clearly states 'Return comments on a post where the authenticated user has NOT yet replied', specifying the verb, resource, and unique filtering. It also distinguishes itself from the sibling tool 'list_comments' by mentioning the alternative for full unfiltered tree.
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?
Explicitly describes when to use it as 'the canonical worklist tool' and provides a workflow: read, draft, propose_reply, confirm_reply. It also advises when not to use it by pointing to 'list_comments' for the full tree.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_commentsA
Read-only. Return the full nested comment tree for a post (parent + replies, with author handle, body, date, reaction count). To find only the threads YOU haven't replied to yet, use get_unanswered_comments.
| Name | Required | Description | Default |
|---|---|---|---|
| post_id | Yes | ||
| pub | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description starts with 'Read-only' to indicate non-destructiveness, and outlines the return structure (parent + replies, fields). No annotations are provided, so the description carries the burden; it covers the key behavioral trait but does not mention potential limitations like pagination or rate limits.
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?
Two sentences, no wasted words. The first sentence is front-loaded with 'Read-only' and the core purpose, followed by a clear alternative in the second sentence.
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 description covers the tool's purpose, scope, and a sibling alternative, but lacks explanation of the 'pub' parameter. For a simple list tool, the context is adequate but not fully 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 schema has two parameters with 0% description coverage. The description does not explain the meaning of 'post_id' or 'pub', leaving the agent to infer from names alone. The 'pub' parameter especially lacks clarity.
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 clearly states the tool returns 'the full nested comment tree for a post' with specific fields, and explicitly distinguishes it from the sibling tool 'get_unanswered_comments'.
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 provides explicit guidance on when to use this tool (to get the full tree) and when to use an alternative ('get_unanswered_comments' for unread threads).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_notesA
Read-only. List the authenticated user's own published Notes (short-form, Twitter-like). For a comment thread on a post use list_comments. For replies under one note, fetch via the note id.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description explicitly marks the tool as 'Read-only', which is a key behavioral trait. However, since no annotations are provided, it should also disclose other behaviors like pagination, ordering, or what happens if no notes exist. The description adds some transparency but is not comprehensive, earning a 3.
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 extremely concise: three sentences total. The first sentence states the core purpose and read-only nature. The next two sentences provide alternative guidance. Every sentence adds value, and there is no wasted text. It is well front-loaded.
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?
Given the tool is simple (one parameter, no output schema), the description should fully cover usage. It lacks explanation of the 'limit' parameter and does not describe the output format or any pagination behavior. The absence of these details means the tool is not fully documented for an agent to use correctly.
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 description does not mention the only parameter 'limit' at all. With 0% schema description coverage, the description should explain the purpose and constraints of the parameter. The agent has no way to know that 'limit' controls the number of notes returned, making this a severe gap.
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 clearly states 'List the authenticated user's own published Notes (short-form, Twitter-like).' It uses a specific verb ('List'), identifies the resource ('Notes'), and specifies the scope ('authenticated user's own published'). Additionally, it distinguishes from siblings by explicitly saying to use list_comments for comment threads and to fetch via note id for replies, reducing ambiguity.
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 provides clear guidance on when to use alternatives (e.g., 'For a comment thread on a post use list_comments. For replies under one note, fetch via the note id.'). It implies authentication is needed by stating 'authenticated user', but does not explicitly list prerequisites or edge cases. A score of 4 reflects good guidance with minor omissions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_postsA
Read-only. List posts from a publication (yours by default). For a single post by id/slug use get_post; for full HTML body use get_post_content; to find by keyword use search_posts.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| pub | No | ||
| sort | No | new |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description states 'Read-only' which signals non-destructiveness, but provides no additional behavioral details such as pagination, rate limits, or authentication requirements. With no annotations, the description carries the burden but only partially satisfies it.
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?
Two sentences: first sentence defines purpose and default, second sentence lists alternatives. No unnecessary words, front-loaded with key info.
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 listed list tool with 3 parameters and no output schema, the description covers the essential: purpose, default, and when to use alternatives. It lacks details on pagination and parameter format, but given the simple context, it is nearly 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?
Schema description coverage is 0%, so the description must compensate. It explains the 'pub' parameter implicitly (publication, default yours) but does not clarify 'limit' or 'sort' beyond their names. Some value is added, but not enough for full understanding.
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?
Description clearly states 'list posts from a publication' with a default scope, and explicitly names sibling tools (get_post, get_post_content, search_posts) for alternative purposes, providing strong differentiation.
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 includes explicit when-to-use guidance: 'For a single post by id/slug use get_post; for full HTML body use get_post_content; to find by keyword use search_posts.' This helps an agent avoid misuse.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
propose_replyA
STAGE A WRITE (no Substack call yet). Validate a reply, compute its dedup hash, build the exact payload, store it under a token, return the token + preview. Show the preview to the user. On approval, call confirm_reply with the same token. Tokens expire in 5 minutes. kind='post' requires post_id + parent_comment_id (for replies under a comment); kind='note' requires note_id. For new top-level post comments use comment_on_post.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | No | post | |
| post_id | No | ||
| note_id | No | ||
| parent_comment_id | No | ||
| body | Yes | ||
| pub | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries full burden. It discloses that the tool performs a write (but no Substack call yet), computes a dedup hash, builds the exact payload, stores it under a token, and returns token+preview. It mentions token expiration. It doesn't cover authentication or rate limits, but overall provides good behavioral context.
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 four sentences, front-loaded with the core action ('STAGE A WRITE'). Every sentence adds value: validation, hashing, payload building, token storage, preview, approval flow, token expiry, and parameter guidance. No wasted words.
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?
Given no annotations, no output schema, and 6 parameters, the description covers the essential lifecycle (propose then confirm), parameter rules, and token expiration. It doesn't describe return format or error handling, but those are less critical for this two-phase workflow. It is sufficiently complete for the complexity.
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 input schema has 0% description coverage, so the description must add meaning. It explains that 'kind' determines which IDs are required: 'post' needs post_id and parent_comment_id, 'note' needs note_id. It also clarifies that body is required. It does not explain 'pub' or default values, but the key parameters are well explained.
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 the tool stages a write (validate, compute dedup hash, build payload, store under token) and returns token+preview. It distinguishes from siblings by explicitly noting that new top-level comments should use 'comment_on_post' and that approval requires 'confirm_reply' with the same token. This is a specific verb+resource with clear sibling differentiation.
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 tells when to use this tool (staging a reply before approval) and when not (for new top-level posts, use comment_on_post). It provides parameter requirements per kind: 'post' requires post_id + parent_comment_id, 'note' requires note_id. It also instructs to call confirm_reply on approval and notes token expiry in 5 minutes.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
publish_noteA
WRITE. Publish a new top-level Note (short-form post). Defaults to dry_run=true (no network write); set dry_run=false to actually post. Idempotent via dedup hash on body. For a reply to an existing note use reply_to_note. For long-form posts, use Substack's editor (not exposed).
| Name | Required | Description | Default |
|---|---|---|---|
| body | Yes | ||
| dry_run | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses dry_run default and idempotency via dedup hash. No annotations exist, so description carries full burden. Missing rate limits or side effects, but sufficient for safe usage.
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?
Four sentences, each adding essential information. No wasted words, front-loaded with purpose and safe defaults.
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?
Covers main points: purpose, parameter behavior, alternatives. No output schema, so return value is omitted, but tool is simple enough. Contextual completeness is adequate.
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%, but description explains dry_run parameter (default to true, set to false to post) and implies body is the note content. Could add character limits, but adds value beyond bare 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 explicitly states the action 'Publish a new top-level Note (short-form post)' and distinguishes from sibling tools like 'reply_to_note' (for replies) and Substack editor (for long-form).
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?
Clear guidance on when to use alternatives (reply_to_note, Substack editor) and explains dry_run behavior. However, no explicit when-not-to-use or prerequisites.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
react_to_commentB
WRITE. React on a comment (default ❤). Set kind='post' for comments under a post (uses the publication host) or kind='note' for replies on a Note (uses substack.com). Defaults to dry_run=true.
| Name | Required | Description | Default |
|---|---|---|---|
| comment_id | Yes | ||
| kind | No | post | |
| reaction | No | ❤ | |
| on | No | ||
| pub | No | ||
| dry_run | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries full burden. It discloses that this is a WRITE operation and defaults to dry_run=true, but fails to explain whether it adds/toggles/removes reactions, what happens when 'on' is false, or any permission requirements.
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 two sentences and front-loaded with 'WRITE', but it omits needed details. It is not overly verbose, but the brevity compromises clarity.
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?
Given no output schema, no annotations, and 6 parameters with 0% coverage, the description is incomplete. It lacks details about return values, side effects, and parameter behavior beyond kind and dry_run.
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?
With 0% schema description coverage, the description must compensate. It explains 'kind' and 'dry_run', but leaves 'comment_id', 'reaction', 'on', and 'pub' undefined. This is insufficient for a tool with 6 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?
The description clearly states it is a WRITE action to react on a comment, with a default reaction. It distinguishes between two kinds of comments (post vs note), which helps avoid confusion with sibling tools like react_to_post.
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?
Provides guidance on setting the 'kind' parameter based on comment type and notes the default dry_run=true. However, no exclusions or alternatives are mentioned, and it doesn't clarify when to use this tool versus similar tools like delete_comment or comment_on_post.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
react_to_postA
WRITE. Add (on=true, default) or remove (on=false) a reaction on a post. Defaults to ❤ and dry_run=true. For comment-level reactions use react_to_comment. Reactions are not deduped (Substack itself idempotent).
| Name | Required | Description | Default |
|---|---|---|---|
| post_id | Yes | ||
| reaction | No | ❤ | |
| on | No | ||
| pub | No | ||
| dry_run | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, so description carries full burden. It discloses default values (reaction=❤, on=true, dry_run=true), the behavior of 'on' parameter (true=add, false=remove), and the dedup/idempotency behavior. Missing explanation of error handling or side effects, but covers key behavioral aspects.
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?
Three sentences, no fluff. Starts with 'WRITE' to indicate operation type. Every sentence adds value: defaults, sibling alternative, behavioral nuance.
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 simple toggle tool, description covers core functionality, defaults, and cross-references sibling. Missing explanation of 'pub' parameter and output behavior (no output schema needed). Adequate for typical use.
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 has 0% description coverage, so description must add meaning. It explains 'reaction' defaults to ❤, 'on' controls add/remove, and 'dry_run' defaults true. However, 'pub' parameter is not explained at all, and 'dry_run' semantics are implied but not explicit (e.g., what does dry_run do?). Partial coverage.
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 clearly states 'Add or remove a reaction on a post', specifying the resource (post) and action (add/remove reaction). It also distinguishes from the sibling tool 'react_to_comment'.
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?
Explicitly says when to use this tool (reacting to a post) and when to use the alternative (react_to_comment for comment-level reactions). Also notes that reactions are not deduped but Substack handles idempotency.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
reply_to_noteA
WRITE. Reply to an existing Note (any author's). Defaults to dry_run=true. Dedup-protected: replays of the same body to the same note are no-ops. For replies to a post comment, use propose_reply -> confirm_reply (which run through the same safety stack).
| Name | Required | Description | Default |
|---|---|---|---|
| note_id | Yes | ||
| body | Yes | ||
| dry_run | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the behavioral burden. It discloses WRITE nature, dry_run default, dedup protection, and the safety stack. However, it does not detail the outcome of a successful reply (e.g., auto-publish or draft) or any permissions/rate limits, leaving some gaps.
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?
Three concise sentences front-load the action and key behaviors. No redundant words; every sentence adds essential 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?
For a simple 3-parameter tool with no output schema, the description covers core purpose, usage boundaries, and safety features. It omits return/outcome details but is largely sufficient.
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%, but the description adds value by explaining the dry_run parameter's default and the dedup logic tying note_id and body. It does not describe note_id format or body constraints, so it only partially compensates.
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 clearly states 'Reply to an existing Note (any author's)', specifying the verb 'reply' and the resource 'note'. It distinguishes this tool from siblings like 'comment_on_post' and 'propose_reply' by directing users to those for post comments.
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 says 'For replies to a post comment, use propose_reply -> confirm_reply', providing clear when-not use and specific alternative tools. It also explains the dry_run default and dedup behavior, aiding appropriate usage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
restack_noteA
WRITE. Restack a Note (Substack's reshare for short-form Notes), broadcasting it to your subscribers' feeds. Example: restack_note(note_id='123456789', dry_run=false). Defaults to dry_run=true so the first call is a no-op preview — set dry_run=false to actually publish. Like restack_post, Substack does not support un-restacking via the public API (on=false is a no-op). For long-form posts, use restack_post instead.
| Name | Required | Description | Default |
|---|---|---|---|
| note_id | Yes | ||
| on | No | ||
| dry_run | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description fully covers behavior: it indicates a write operation (WRITE), explains the dry_run preview mechanism, and notes the limitation that the on parameter cannot reverse a restack. No contradictions with annotations.
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 concise (two sentences plus example and sibling reference) and front-loaded with the action. It efficiently packs essential info but could benefit from slight restructuring for readability.
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?
Given no annotations, no output schema, and a simple tool with three params (0% schema coverage), the description provides complete context: purpose, parameters usage, limitations, and a sibling alternative. An agent has enough to use the tool correctly.
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 description adds meaning to key parameters: dry_run as preview and on as ineffective for false. It includes an example with note_id. While the schema has 0% description coverage, the description compensates well but could clarify note_id format more explicitly.
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 clearly states the tool's action ('restack a Note') and its effect ('broadcasting it to your subscribers' feeds'). It also distinguishes from the sibling tool 'restack_post' by specifying that this is for short-form Notes and that tool is for long-form posts.
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 provides explicit usage guidance: defaults to dry_run=true as a preview, must set dry_run=false to actually publish. It also warns that Substack does not support un-restacking (on=false is a no-op), which is critical for correct usage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
restack_postA
WRITE. Restack a post (Substack's reshare). Defaults to dry_run=true. Substack does NOT support unrestacking via the public API — once on, stays on. To restack a Note instead, use restack_note.
| Name | Required | Description | Default |
|---|---|---|---|
| post_id | Yes | ||
| on | No | ||
| dry_run | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses that Substack does not support unrestacking, making the irreversible nature clear. Also explains the default dry_run behavior, beyond what annotations provide (none).
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?
Four concise sentences, each adding value. Front-loaded with action and purpose. No unnecessary words.
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?
Covers purpose, usage guidelines, behavioral traits, and sibling link. Missing output description but acceptable for a write tool with no output schema.
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?
Although schema coverage is 0%, the description adds critical context about defaults (dry_run, on) and the fact that setting on=false is ineffective. It does not explain post_id format but compensates with behavioral nuance.
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?
Description clearly states 'WRITE. Restack a post (Substack's reshare).' It uses a specific verb-resource combination and distinguishes from restack_note.
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?
Explicitly mentions default dry_run=true and the irreversibility of restacking. Also directs to restack_note for notes, providing clear alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_postsA
Read-only. Full-text search posts in a publication. Use for keyword discovery; for chronological browsing use list_posts. Returns titles + ids only (call get_post / get_post_content for details).
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | ||
| limit | No | ||
| pub | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, but description itself states 'Read-only' and describes return format (titles+ids). Could mention pagination or rate limits, but overall good for a search tool.
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?
Two concise sentences, front-loaded with 'Read-only' and purpose. No wasted words.
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?
Covers purpose, usage, and return format. Lacks parameter details for 3-param tool with no output schema. Adequate but has clear gaps.
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% (no descriptions in schema), yet description does not explain individual parameters like query format, limit usage, or pub meaning. Only implicit mention of query via full-text search. Does not compensate for low schema coverage.
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?
Description clearly states tool is for full-text search of posts ('search_posts'), specifies it is read-only, and distinguishes from sibling 'list_posts' for chronological browsing.
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?
Explicitly says use for keyword discovery, not for chronological browsing (use list_posts). Also notes that results are only titles+ids and suggests calling get_post/get_post_content for details.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
send_approved_draftsA
WRITE. Sequentially post every entry in a drafts.json file where action=='approved'. Skips proposed/rejected/already-deduped rows. Honors rate_seconds throttle. Defaults dry_run=true; set false to actually post. Use force=true to bypass dedup (rare; reposts a previously-sent reply).
| Name | Required | Description | Default |
|---|---|---|---|
| drafts_path | Yes | ||
| dry_run | No | ||
| rate_seconds | No | ||
| force | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Given no annotations, the description adequately discloses behavioral traits: it is a write operation, it skips certain rows, honors throttle, defaults to dry run, and has a force option for dedup bypass. It does not mention idempotency or side effects on the drafts file, but covers the main mutation behavior.
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 concise and well-structured, using short sentences to convey key information. Every sentence adds value: the action, filtering, throttle, and flags. No redundant or vague phrasing.
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 description covers the tool's functional behavior well, but lacks information about the return value (e.g., success count or errors). Given the complexity of iterating over a file and posting, details on error handling or status reporting would improve completeness. The absence of output schema increases the need for such info in the description.
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?
With 0% schema description coverage, the description adds significant meaning beyond defaults: it explains that 'drafts_path' is the file to process, 'dry_run' defaults true, 'force' bypasses dedup, and 'rate_seconds' controls throttle. Three of four parameters are clarified, though 'drafts_path' is only implied.
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 clearly states the tool's purpose: to sequentially post approved drafts from a specific file. It uses a specific verb ('post') and resource ('drafts.json entries'), and distinguishes from sibling tools like 'bulk_draft_replies' by specifying the filtering and sequencing behavior.
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 provides good usage guidance, including which entries are processed (approved only), the throttle behavior (rate_seconds), and the dry_run and force flags. It implies when not to use (for proposed/rejected entries) but does not explicitly list alternatives or when to choose this over siblings like 'confirm_reply'.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
test_connectionA
Read-only. Verify the Substack session cookie works and return the authenticated user's id, handle, and primary publication. Call this first if other tools 401 or to confirm setup. No args.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Marks itself as read-only, which is critical behavioral information. With no annotations provided, the description carries the full burden and adequately communicates the tool's non-destructive nature. No additional behaviors like rate limits need mention for this simple test.
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?
Three succinct sentences, each adding value. Front-loads 'Read-only' and states purpose, usage, and arguments efficiently. No wasted words.
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 simple test tool with no parameters and no output schema, the description covers purpose, usage, and return value completely. It is self-contained and leaves no ambiguity.
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?
Input schema has zero parameters, and description confirms 'No args.' This adds clarity beyond the schema, which is implicit. Baseline for 0 parameters is 4, and the description fulfills this.
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?
Description clearly states the tool verifies a Substack session cookie and returns specific user details (id, handle, primary publication). This verb+resource combination is distinct from sibling tools, which perform different actions like searching or commenting.
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?
Explicitly advises calling this tool when other tools return 401 or to confirm setup. While it gives clear guidance on when to use, it does not mention when not to use or provide alternatives, but the context is sufficient for a diagnostic tool.
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. Dates show when Glama detected each change.
26 tool updates
v0.3.5- Changed
audit_search8 fields changed- added
Input schema / properties / kindAdded value: +{ + "default": null, + "title": "Kind", + "type": "string" +} - removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - added
Input schema / properties / limitAdded value: +{ + "default": 50, + "title": "Limit", + "type": "integer" +} - added
Input schema / properties / sinceAdded value: +{ + "default": null, + "title": "Since", + "type": "string" +} - added
Input schema / properties / statusAdded value: +{ + "default": null, + "title": "Status", + "type": "string" +} - added
Input schema / properties / targetAdded value: +{ + "default": null, + "title": "Target", + "type": "string" +} - removed
Input schema / requiredRemoved value: -[ - "kwargs" -] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"audit_searchArguments"
- Changed
bulk_draft_replies7 fields changed- added
Input schema / properties / idAdded value: +{ + "title": "Id", + "type": "string" +} - added
Input schema / properties / kindAdded value: +{ + "default": "post", + "title": "Kind", + "type": "string" +} - removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - added
Input schema / properties / modelAdded value: +{ + "default": null, + "title": "Model", + "type": "string" +} - added
Input schema / properties / outAdded value: +{ + "default": "drafts.json", + "title": "Out", + "type": "string" +} - changed
Input schema / requiredPrevious value: -[ - "kwargs" -]New value: +[ + "id" +] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"bulk_draft_repliesArguments"
- Changed
comment_on_post7 fields changed- added
Input schema / properties / bodyAdded value: +{ + "title": "Body", + "type": "string" +} - added
Input schema / properties / dry_runAdded value: +{ + "default": true, + "title": "Dry Run", + "type": "boolean" +} - removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - added
Input schema / properties / post_idAdded value: +{ + "title": "Post Id", + "type": "string" +} - added
Input schema / properties / pubAdded value: +{ + "default": null, + "title": "Pub", + "type": "string" +} - changed
Input schema / requiredPrevious value: -[ - "kwargs" -]New value: +[ + "post_id", + "body" +] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"comment_on_postArguments"
- Changed
confirm_reply5 fields changed- added
Input schema / properties / forceAdded value: +{ + "default": false, + "title": "Force", + "type": "boolean" +} - removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - added
Input schema / properties / tokenAdded value: +{ + "title": "Token", + "type": "string" +} - changed
Input schema / requiredPrevious value: -[ - "kwargs" -]New value: +[ + "token" +] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"confirm_replyArguments"
- Changed
dedup_status3 fields changed- removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - removed
Input schema / requiredRemoved value: -[ - "kwargs" -] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"dedup_statusArguments"
- Changed
delete_comment7 fields changed- added
Input schema / properties / comment_idAdded value: +{ + "title": "Comment Id", + "type": "string" +} - added
Input schema / properties / dry_runAdded value: +{ + "default": true, + "title": "Dry Run", + "type": "boolean" +} - added
Input schema / properties / kindAdded value: +{ + "default": "post", + "title": "Kind", + "type": "string" +} - removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - added
Input schema / properties / pubAdded value: +{ + "default": null, + "title": "Pub", + "type": "string" +} - changed
Input schema / requiredPrevious value: -[ - "kwargs" -]New value: +[ + "comment_id" +] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"delete_commentArguments"
- Changed
get_feed5 fields changed- removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - added
Input schema / properties / limitAdded value: +{ + "default": 20, + "title": "Limit", + "type": "integer" +} - added
Input schema / properties / tabAdded value: +{ + "default": "for-you", + "title": "Tab", + "type": "string" +} - removed
Input schema / requiredRemoved value: -[ - "kwargs" -] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"get_feedArguments"
- Changed
get_own_profile3 fields changed- removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - removed
Input schema / requiredRemoved value: -[ - "kwargs" -] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"get_own_profileArguments"
- Changed
get_post5 fields changed- removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - added
Input schema / properties / post_idAdded value: +{ + "title": "Post Id", + "type": "string" +} - added
Input schema / properties / pubAdded value: +{ + "default": null, + "title": "Pub", + "type": "string" +} - changed
Input schema / requiredPrevious value: -[ - "kwargs" -]New value: +[ + "post_id" +] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"get_postArguments"
- Changed
get_post_by_id4 fields changed- removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - added
Input schema / properties / post_idAdded value: +{ + "title": "Post Id", + "type": "integer" +} - changed
Input schema / requiredPrevious value: -[ - "kwargs" -]New value: +[ + "post_id" +] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"get_post_by_idArguments"
- Changed
get_post_content6 fields changed- added
Input schema / properties / as_markdownAdded value: +{ + "default": false, + "title": "As Markdown", + "type": "boolean" +} - removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - added
Input schema / properties / post_idAdded value: +{ + "title": "Post Id", + "type": "string" +} - added
Input schema / properties / pubAdded value: +{ + "default": null, + "title": "Pub", + "type": "string" +} - changed
Input schema / requiredPrevious value: -[ - "kwargs" -]New value: +[ + "post_id" +] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"get_post_contentArguments"
- Changed
get_profile4 fields changed- added
Input schema / properties / handleAdded value: +{ + "title": "Handle", + "type": "string" +} - removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - changed
Input schema / requiredPrevious value: -[ - "kwargs" -]New value: +[ + "handle" +] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"get_profileArguments"
- Changed
get_unanswered_comments6 fields changed- removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - added
Input schema / properties / limitAdded value: +{ + "default": 50, + "title": "Limit", + "type": "integer" +} - added
Input schema / properties / post_idAdded value: +{ + "title": "Post Id", + "type": "string" +} - added
Input schema / properties / pubAdded value: +{ + "default": null, + "title": "Pub", + "type": "string" +} - changed
Input schema / requiredPrevious value: -[ - "kwargs" -]New value: +[ + "post_id" +] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"get_unanswered_commentsArguments"
- Changed
list_comments5 fields changed- removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - added
Input schema / properties / post_idAdded value: +{ + "title": "Post Id", + "type": "string" +} - added
Input schema / properties / pubAdded value: +{ + "default": null, + "title": "Pub", + "type": "string" +} - changed
Input schema / requiredPrevious value: -[ - "kwargs" -]New value: +[ + "post_id" +] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"list_commentsArguments"
- Changed
list_notes4 fields changed- removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - added
Input schema / properties / limitAdded value: +{ + "default": 20, + "title": "Limit", + "type": "integer" +} - removed
Input schema / requiredRemoved value: -[ - "kwargs" -] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"list_notesArguments"
- Changed
list_posts6 fields changed- removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - added
Input schema / properties / limitAdded value: +{ + "default": 20, + "title": "Limit", + "type": "integer" +} - added
Input schema / properties / pubAdded value: +{ + "default": null, + "title": "Pub", + "type": "string" +} - added
Input schema / properties / sortAdded value: +{ + "default": "new", + "title": "Sort", + "type": "string" +} - removed
Input schema / requiredRemoved value: -[ - "kwargs" -] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"list_postsArguments"
- Changed
propose_reply9 fields changed- added
Input schema / properties / bodyAdded value: +{ + "title": "Body", + "type": "string" +} - added
Input schema / properties / kindAdded value: +{ + "default": "post", + "title": "Kind", + "type": "string" +} - removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - added
Input schema / properties / note_idAdded value: +{ + "default": null, + "title": "Note Id", + "type": "string" +} - added
Input schema / properties / parent_comment_idAdded value: +{ + "default": null, + "title": "Parent Comment Id", + "type": "string" +} - added
Input schema / properties / post_idAdded value: +{ + "default": null, + "title": "Post Id", + "type": "string" +} - added
Input schema / properties / pubAdded value: +{ + "default": null, + "title": "Pub", + "type": "string" +} - changed
Input schema / requiredPrevious value: -[ - "kwargs" -]New value: +[ + "body" +] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"propose_replyArguments"
- Changed
publish_note5 fields changed- added
Input schema / properties / bodyAdded value: +{ + "title": "Body", + "type": "string" +} - added
Input schema / properties / dry_runAdded value: +{ + "default": true, + "title": "Dry Run", + "type": "boolean" +} - removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - changed
Input schema / requiredPrevious value: -[ - "kwargs" -]New value: +[ + "body" +] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"publish_noteArguments"
- Changed
react_to_comment9 fields changed- added
Input schema / properties / comment_idAdded value: +{ + "title": "Comment Id", + "type": "string" +} - added
Input schema / properties / dry_runAdded value: +{ + "default": true, + "title": "Dry Run", + "type": "boolean" +} - added
Input schema / properties / kindAdded value: +{ + "default": "post", + "title": "Kind", + "type": "string" +} - removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - added
Input schema / properties / onAdded value: +{ + "default": true, + "title": "On", + "type": "boolean" +} - added
Input schema / properties / pubAdded value: +{ + "default": null, + "title": "Pub", + "type": "string" +} - added
Input schema / properties / reactionAdded value: +{ + "default": "❤", + "title": "Reaction", + "type": "string" +} - changed
Input schema / requiredPrevious value: -[ - "kwargs" -]New value: +[ + "comment_id" +] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"react_to_commentArguments"
- Changed
react_to_post8 fields changed- added
Input schema / properties / dry_runAdded value: +{ + "default": true, + "title": "Dry Run", + "type": "boolean" +} - removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - added
Input schema / properties / onAdded value: +{ + "default": true, + "title": "On", + "type": "boolean" +} - added
Input schema / properties / post_idAdded value: +{ + "title": "Post Id", + "type": "string" +} - added
Input schema / properties / pubAdded value: +{ + "default": null, + "title": "Pub", + "type": "string" +} - added
Input schema / properties / reactionAdded value: +{ + "default": "❤", + "title": "Reaction", + "type": "string" +} - changed
Input schema / requiredPrevious value: -[ - "kwargs" -]New value: +[ + "post_id" +] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"react_to_postArguments"
- Changed
reply_to_note6 fields changed- added
Input schema / properties / bodyAdded value: +{ + "title": "Body", + "type": "string" +} - added
Input schema / properties / dry_runAdded value: +{ + "default": true, + "title": "Dry Run", + "type": "boolean" +} - removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - added
Input schema / properties / note_idAdded value: +{ + "title": "Note Id", + "type": "string" +} - changed
Input schema / requiredPrevious value: -[ - "kwargs" -]New value: +[ + "note_id", + "body" +] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"reply_to_noteArguments"
- Changed
restack_note6 fields changed- added
Input schema / properties / dry_runAdded value: +{ + "default": true, + "title": "Dry Run", + "type": "boolean" +} - removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - added
Input schema / properties / note_idAdded value: +{ + "title": "Note Id", + "type": "string" +} - added
Input schema / properties / onAdded value: +{ + "default": true, + "title": "On", + "type": "boolean" +} - changed
Input schema / requiredPrevious value: -[ - "kwargs" -]New value: +[ + "note_id" +] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"restack_noteArguments"
- Changed
restack_post6 fields changed- added
Input schema / properties / dry_runAdded value: +{ + "default": true, + "title": "Dry Run", + "type": "boolean" +} - removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - added
Input schema / properties / onAdded value: +{ + "default": true, + "title": "On", + "type": "boolean" +} - added
Input schema / properties / post_idAdded value: +{ + "title": "Post Id", + "type": "string" +} - changed
Input schema / requiredPrevious value: -[ - "kwargs" -]New value: +[ + "post_id" +] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"restack_postArguments"
- Changed
search_posts6 fields changed- removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - added
Input schema / properties / limitAdded value: +{ + "default": 10, + "title": "Limit", + "type": "integer" +} - added
Input schema / properties / pubAdded value: +{ + "default": null, + "title": "Pub", + "type": "string" +} - added
Input schema / properties / queryAdded value: +{ + "title": "Query", + "type": "string" +} - changed
Input schema / requiredPrevious value: -[ - "kwargs" -]New value: +[ + "query" +] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"search_postsArguments"
- Changed
send_approved_drafts7 fields changed- added
Input schema / properties / drafts_pathAdded value: +{ + "title": "Drafts Path", + "type": "string" +} - added
Input schema / properties / dry_runAdded value: +{ + "default": true, + "title": "Dry Run", + "type": "boolean" +} - added
Input schema / properties / forceAdded value: +{ + "default": false, + "title": "Force", + "type": "boolean" +} - removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - added
Input schema / properties / rate_secondsAdded value: +{ + "default": 30, + "title": "Rate Seconds", + "type": "number" +} - changed
Input schema / requiredPrevious value: -[ - "kwargs" -]New value: +[ + "drafts_path" +] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"send_approved_draftsArguments"
- Changed
test_connection3 fields changed- removed
Input schema / properties / kwargsRemoved value: -{ - "title": "Kwargs" -} - removed
Input schema / requiredRemoved value: -[ - "kwargs" -] - changed
Input schema / titlePrevious value: -"_toolArguments"New value: +"test_connectionArguments"
26 tool updates
v0.1.0- First observed
audit_search - First observed
bulk_draft_replies - First observed
comment_on_post - First observed
confirm_reply - First observed
dedup_status - First observed
delete_comment - First observed
get_feed - First observed
get_own_profile - First observed
get_post - First observed
get_post_by_id - First observed
get_post_content - First observed
get_profile - First observed
get_unanswered_comments - First observed
list_comments - First observed
list_notes - First observed
list_posts - First observed
propose_reply - First observed
publish_note - First observed
react_to_comment - First observed
react_to_post - First observed
reply_to_note - First observed
restack_note - First observed
restack_post - First observed
search_posts - First observed
send_approved_drafts - First observed
test_connection
TDQS
Each tool has a clearly distinct purpose. Related tools like get_post, get_post_by_id, and get_post_content are differentiated by input type and output. The staged write process (propose_reply/confirm_reply) is well-separated from direct writes (comment_on_post, delete_comment). Debugging tools (audit_search, dedup_status) are distinct from operational ones.
All tool names follow a consistent verb_noun snake_case pattern. Verbs are descriptive (get_, list_, create_, publish_, propose_confirm_, etc.) and nouns match the resource (post, comment, note, profile, draft). There is no mixing of conventions or vague verbs.
With 26 tools, the set is comprehensive but slightly above the ideal range. However, each tool addresses a specific operation or workflow (e.g., staging replies, batch drafts, debugging), and no tools feel redundant. The count is justified by the breadth of Substack's features covered.
The tool set covers reading and writing for comments, notes, reactions, restacks, and basic post metadata. However, there is no tool for creating or editing long-form posts, which is a notable omission for a Substack management server. The server seems focused on community engagement rather than full publishing.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
- QuallaaOAuthcom.quallaa
Talk to your public-facing AI from any MCP client — Claude, ChatGPT, Cursor, Cline, Windsurf.
Remote MCP server for supportsheep: run AI interviews and manage support content for your blog.
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…
Deploy full-stack apps (Postgres, Redis, S3, workers, backups) from Claude or curl. 59 MCP tools.
Related MCP Servers
- AlicenseAqualityAmaintenanceMCP server for Substack — read posts, manage drafts, and upload images. Safe by design: cannot publish or delete.1411824MIT
- AlicenseAqualityDmaintenanceMCP server for Substack that lets Claude Code create drafts, upload images, set cover thumbnails, schedule, and publish posts on your Substack publication.1115MIT
- AlicenseNot gradedqualityCmaintenanceMCP server for Substack that enables reading articles, comments, feed, and subscriptions from AI clients like Cursor and Claude, with optional authentication for paid content.353MIT
- AlicenseAqualityAmaintenanceA local MCP server for managing saved Substack posts. Enables offline reading, searching, bookmarking, and unbookmarking of Substack content via CLI or MCP clients.17MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/06ketan/substack-ops'
If you have feedback or need assistance with the MCP directory API, please join our Discord server