Skip to main content
Glama

substack-ops

PyPI version PyPI downloads Python 3.12+ License: MIT MCP compatible MCP Registry Anthropic DXT Glama MCP server MCP Badge CI

Standalone Substack CLI + 26-tool MCP server for Cursor MCP, Claude MCP, OpenCode MCP, and any stdio MCP host. 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, opencode, 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.

Wrong install?

This project is substack-ops on PyPI — install with uv / uvx, not unrelated npx packages that appear when searching “Substack MCP”. Canonical listing: Glama — 06ketan/substack-ops.

Related MCP server: substack-mcp

Works with (MCP)

These rows help discovery (search keywords); confirm each host’s current MCP docs before upgrading.

Open source–oriented hosts

Host

Documentation

Typical wire-up

OpenCode

OpenCode MCP servers

uvx substack-ops mcp install opencode

Continue

Continue

uvx substack-ops mcp install print — paste the snippet into Continue’s MCP settings

Zed

Zed

Configure stdio MCP per Zed’s docs

Cline

Cline

MCP setup per extension / marketplace docs

Goose

Goose

MCP extensions per Goose docs

Large commercial stacks

Host

Documentation

Typical wire-up

Cursor

Cursor MCP

uvx substack-ops mcp install cursor

Claude (Desktop / Code)

Claude Desktop, Claude Code

mcp install claude-desktop / claude-code

GitHub Copilot

Copilot

MCP in VS Code / Copilot where supported — use print + host docs

ChatGPT

OpenAI

Developer / connector flows — often REST (Slideshot API) for tools without MCP

Google Gemini

Gemini

Gemini CLI / IDE features per Google docs — stdio where supported

OpenCode (copy-paste)

Auto-install:

uvx substack-ops mcp install opencode

Manual (~/.config/opencode/opencode.json):

{
  "mcp": {
    "substack-ops": {
      "type": "local",
      "command": ["uvx", "substack-ops", "mcp", "serve"],
      "enabled": true
    }
  }
}

Optional version pin: use ["uvx", "substack-ops==0.3.5", "mcp", "serve"] (replace with current PyPI release).

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-grab

Auth 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 tour

Command 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

auth verify

Confirm the cookie works; print authed user/pub.

auth test

Same as verify, exit non-zero on failure (CI-friendly).

auth login --browser chrome|brave

Auto-grab cookie from local Chromium browser via macOS Keychain.

auth login --email me@x.com

Email magic-link → paste-the-link interactive flow.

auth setup

Interactive paste of connect.sid cookie.

Read — Posts (8)

Command

What it does

posts list [--pub] [--limit] [--sort new|top]

List posts from a publication (yours by default).

posts show <id|slug> [--pub]

Post metadata (title, dates, reactions, comment count).

posts get --slug <slug> [--pub]

Same as show but slug-only.

posts content <id> [--md] [--pub]

HTML body (auth-aware for paywalled). --md converts to Markdown.

posts stats <id>

Engagement counts — reactions, comments.

posts search <query> [--pub] [--limit]

Substack-side full-text search.

posts paywalled <id> [--pub]

Boolean: is this post paywalled?

posts react <id> [--off] [--pub]

Add (or remove with --off) a reaction. Defaults to ❤.

posts restack <id> [--off]

Restack a post (Substack does not support unrestack).

Read — Notes (5)

Command

What it does

notes list [--limit]

Your published Notes.

notes show <id>

One note + its reply tree.

notes publish <body> [--no-dry-run]

Publish a top-level Note.

notes react <id> [--off]

React on any Note.

notes restack <id> [--off]

Restack a Note.

Read + Write — Comments (5)

Command

What it does

comments tree <post_id> [--pub]

Full nested comment tree as table.

comments export <post_id> --out file.json [--pub]

Same tree as JSON.

comments add <post_id> <body> [--pub] [--no-dry-run]

New top-level comment.

comments react <id> --kind post|note [--off]

React on a comment.

comments delete <id> --kind post|note [--no-dry-run]

Destructive — your own comments only.

Reply engine (6)

Command

What it does

reply template <post_id> --template thanks

Rule-based replies (no LLM).

reply review <post_id>

LLM drafts each, you [a]ccept / [e]dit / [s]kip / [q]uit.

reply bulk <post_id> --out drafts.json

Draft every comment to a file. Edit, set action: "approved".

reply note-bulk <note_id> --out drafts.json

Same for replies under a Note.

reply bulk-send drafts.json [--no-dry-run]

Posts only approved rows. Dedup-checked.

reply auto <post_id> --no-dry-run --yes-i-mean-it

Draft + post immediately. 30s rate limit.

Read — Discovery (8)

Command

What it does

feed list --tab for-you|subscribed|category-{slug}

Reader feed (the Substack app feed).

profile me / profile get <handle>

Profile.

users get <handle> / users subscriptions <handle>

Public user info + their subs.

podcasts list [--pub]

Audio posts.

recommendations list [--pub]

Pub's recommended publications.

authors list [--pub]

Pub's contributor list.

categories list / categories get --name <X>

Substack's category taxonomy.

Automations (3)

Command

What it does

auto presets

List built-in YAML rules.

auto run <name>

One-shot run a preset.

auto daemon <name> --interval 60

Loop forever; logs to audit.

Operations + safety (3)

Command

What it does

audit search [--kind] [--target] [--status] [--since 7d]

Query the JSONL audit log.

audit dedup-status

Counts in the dedup SQLite DB.

quickstart

20-step interactive tour.

MCP server (3)

Command

What it does

mcp install <cursor|claude-desktop|claude-code|opencode|print> [--dry-run]

Auto-merge config into your host.

mcp serve

stdio MCP server (26 tools).

mcp list-tools

Print the tool registry.

Other (1)

Command

What it does

tui

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 stratechery

Reply modes

Mode

What it does

Safety

template

YAML keyword/regex rules under src/substack_ops/templates/*.yaml

dry-run default

review

LLM drafts each reply, you [a]ccept / [e]dit / [s]kip / [q]uit

dry-run default + manual gate per comment

bulk

LLM drafts every comment to drafts.json. Edit file, set action: "approved"

offline review, dedup-checked on send

bulk-send

Posts only items with action: "approved"

dry-run default; dedup DB prevents the M2 31-dup-replies regression

auto

LLM drafts and posts immediately

requires --no-dry-run --yes-i-mean-it, 30s rate limit

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):

  1. like-back — when someone reacts to your note, react to their latest note.

  2. auto-reply — same trigger, but post a templated thank-you.

  3. auto-restack — when a watchlist handle posts a new note, restack it.

  4. 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 opencode          # auto-add to ~/.config/opencode/opencode.json
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 tools

Manual 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 serve

MCP-native draft loop (no API key)

3 tools designed to let your host LLM draft for you:

Tool

What it does

get_unanswered_comments

Returns the worklist: comments where you have not yet replied (any depth).

propose_reply

Dry-run only. Returns a token + payload preview. No write.

confirm_reply

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:

  1. MCP-native (default). Host LLM drafts via propose_reply / confirm_reply. No env vars, no API key. Use this for interactive replies.

  2. Subprocess CLI (daemon path). For reply auto / auto daemon when no human is in the loop. Auto-detects claude (Claude Code), cursor-agent, or codex on PATH. Override with SUBSTACK_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 tui

6 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 cookies

Architecture

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

GET https://substack.com/api/v1/subscriptions

List posts

GET {pub}/api/v1/archive

Post by id

GET {pub}/api/v1/posts/by-id/{id}

Post by slug

GET {pub}/api/v1/posts/{slug}

Post content

same as above; body_html field

Post search

GET {pub}/api/v1/archive?search=

Comments

GET {pub}/api/v1/post/{id}/comments?all_comments=true

Reply to comment

POST {pub}/api/v1/post/{id}/comment body {body, parent_id}

Add top-level comment

same with parent_id: null

React to post

POST {pub}/api/v1/post/{id}/reaction body {reaction}

Restack post

POST https://substack.com/api/v1/restack body {post_id}

Restack note

POST https://substack.com/api/v1/restack body {comment_id}

Delete post-comment

DELETE {pub}/api/v1/comment/{id} (PUB host)

Delete note

DELETE https://substack.com/api/v1/comment/{id} (BARE host)

My notes

GET https://substack.com/api/v1/reader/feed/profile/{user_id}

Note thread

GET https://substack.com/api/v1/reader/comment/{note_id}

Note replies

GET https://substack.com/api/v1/reader/comment/{note_id}/replies

Publish note

POST https://substack.com/api/v1/comment/feed body {bodyJson}

Reply to note

same with {bodyJson, parent_id} (NOT parent_comment_id — known M2 bug)

React to comment

POST {host}/api/v1/comment/{id}/reaction (host = pub for post-comments, substack.com for notes)

Recommendations

GET {pub}/api/v1/recommendations/from/{publication_id}

Authors

GET {pub}/api/v1/publication/users/ranked?public=true

Categories

GET https://substack.com/api/v1/categories

User profile

GET https://substack.com/api/v1/user/{handle}/public_profile (auto-redirects on 404)

Reader feed

GET https://substack.com/api/v1/reader/feed/{recommended|subscribed|category/{slug}}

Tests

uv run pytest -q     # 43 tests, ~0.6s, no live network

Coverage 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_from triggers are stubbed (return note: "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 tools
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.

ParametersJSON Schema
NameRequiredDescriptionDefault
kindNopost
idYes
outNodrafts.json
modelNo

TDQS

A4/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
post_idYes
bodyYes
pubNo
dry_runNo

TDQS

A4.2/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters2/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
tokenYes
forceNo

TDQS

A4.3/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.6/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
comment_idYes
kindNopost
pubNo
dry_runNo

TDQS

A5/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
tabNofor-you
limitNo

TDQS

A4.4/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.7/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
post_idYes
pubNo

TDQS

A4.4/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
post_idYes

TDQS

A4.7/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
post_idYes
pubNo
as_markdownNo

TDQS

A3.8/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters2/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
handleYes

TDQS

A5/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters5/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
post_idYes
pubNo
limitNo

TDQS

A4/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters1/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
post_idYes
pubNo

TDQS

A4.2/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters2/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo

TDQS

A3.5/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness2/5

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.

Parameters1/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
pubNo
sortNonew

TDQS

A4.2/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
kindNopost
post_idNo
note_idNo
parent_comment_idNo
bodyYes
pubNo

TDQS

A4.6/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
bodyYes
dry_runNo

TDQS

A4.4/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
comment_idYes
kindNopost
reactionNo
onNo
pubNo
dry_runNo

TDQS

B3/5.0
Behavior3/5

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.

Conciseness3/5

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.

Completeness2/5

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.

Parameters2/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
post_idYes
reactionNo
onNo
pubNo
dry_runNo

TDQS

A4.4/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
note_idYes
bodyYes
dry_runNo

TDQS

A4.4/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
note_idYes
onNo
dry_runNo

TDQS

A4.8/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
post_idYes
onNo
dry_runNo

TDQS

A4.8/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYes
limitNo
pubNo

TDQS

A4.2/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters2/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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).

ParametersJSON Schema
NameRequiredDescriptionDefault
drafts_pathYes
dry_runNo
rate_secondsNo
forceNo

TDQS

A4.3/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.5/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

  1. 26 tool updatesv0.3.5
    • Changedaudit_search8 fields changed
      • addedInput schema / properties / kind
        Added value: +{
        +  "default": null,
        +  "title": "Kind",
        +  "type": "string"
        +}
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • addedInput schema / properties / limit
        Added value: +{
        +  "default": 50,
        +  "title": "Limit",
        +  "type": "integer"
        +}
      • addedInput schema / properties / since
        Added value: +{
        +  "default": null,
        +  "title": "Since",
        +  "type": "string"
        +}
      • addedInput schema / properties / status
        Added value: +{
        +  "default": null,
        +  "title": "Status",
        +  "type": "string"
        +}
      • addedInput schema / properties / target
        Added value: +{
        +  "default": null,
        +  "title": "Target",
        +  "type": "string"
        +}
      • removedInput schema / required
        Removed value: -[
        -  "kwargs"
        -]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"audit_searchArguments"
    • Changedbulk_draft_replies7 fields changed
      • addedInput schema / properties / id
        Added value: +{
        +  "title": "Id",
        +  "type": "string"
        +}
      • addedInput schema / properties / kind
        Added value: +{
        +  "default": "post",
        +  "title": "Kind",
        +  "type": "string"
        +}
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • addedInput schema / properties / model
        Added value: +{
        +  "default": null,
        +  "title": "Model",
        +  "type": "string"
        +}
      • addedInput schema / properties / out
        Added value: +{
        +  "default": "drafts.json",
        +  "title": "Out",
        +  "type": "string"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "kwargs"
        -]New value: +[
        +  "id"
        +]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"bulk_draft_repliesArguments"
    • Changedcomment_on_post7 fields changed
      • addedInput schema / properties / body
        Added value: +{
        +  "title": "Body",
        +  "type": "string"
        +}
      • addedInput schema / properties / dry_run
        Added value: +{
        +  "default": true,
        +  "title": "Dry Run",
        +  "type": "boolean"
        +}
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • addedInput schema / properties / post_id
        Added value: +{
        +  "title": "Post Id",
        +  "type": "string"
        +}
      • addedInput schema / properties / pub
        Added value: +{
        +  "default": null,
        +  "title": "Pub",
        +  "type": "string"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "kwargs"
        -]New value: +[
        +  "post_id",
        +  "body"
        +]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"comment_on_postArguments"
    • Changedconfirm_reply5 fields changed
      • addedInput schema / properties / force
        Added value: +{
        +  "default": false,
        +  "title": "Force",
        +  "type": "boolean"
        +}
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • addedInput schema / properties / token
        Added value: +{
        +  "title": "Token",
        +  "type": "string"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "kwargs"
        -]New value: +[
        +  "token"
        +]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"confirm_replyArguments"
    • Changeddedup_status3 fields changed
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • removedInput schema / required
        Removed value: -[
        -  "kwargs"
        -]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"dedup_statusArguments"
    • Changeddelete_comment7 fields changed
      • addedInput schema / properties / comment_id
        Added value: +{
        +  "title": "Comment Id",
        +  "type": "string"
        +}
      • addedInput schema / properties / dry_run
        Added value: +{
        +  "default": true,
        +  "title": "Dry Run",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / kind
        Added value: +{
        +  "default": "post",
        +  "title": "Kind",
        +  "type": "string"
        +}
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • addedInput schema / properties / pub
        Added value: +{
        +  "default": null,
        +  "title": "Pub",
        +  "type": "string"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "kwargs"
        -]New value: +[
        +  "comment_id"
        +]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"delete_commentArguments"
    • Changedget_feed5 fields changed
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • addedInput schema / properties / limit
        Added value: +{
        +  "default": 20,
        +  "title": "Limit",
        +  "type": "integer"
        +}
      • addedInput schema / properties / tab
        Added value: +{
        +  "default": "for-you",
        +  "title": "Tab",
        +  "type": "string"
        +}
      • removedInput schema / required
        Removed value: -[
        -  "kwargs"
        -]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"get_feedArguments"
    • Changedget_own_profile3 fields changed
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • removedInput schema / required
        Removed value: -[
        -  "kwargs"
        -]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"get_own_profileArguments"
    • Changedget_post5 fields changed
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • addedInput schema / properties / post_id
        Added value: +{
        +  "title": "Post Id",
        +  "type": "string"
        +}
      • addedInput schema / properties / pub
        Added value: +{
        +  "default": null,
        +  "title": "Pub",
        +  "type": "string"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "kwargs"
        -]New value: +[
        +  "post_id"
        +]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"get_postArguments"
    • Changedget_post_by_id4 fields changed
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • addedInput schema / properties / post_id
        Added value: +{
        +  "title": "Post Id",
        +  "type": "integer"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "kwargs"
        -]New value: +[
        +  "post_id"
        +]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"get_post_by_idArguments"
    • Changedget_post_content6 fields changed
      • addedInput schema / properties / as_markdown
        Added value: +{
        +  "default": false,
        +  "title": "As Markdown",
        +  "type": "boolean"
        +}
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • addedInput schema / properties / post_id
        Added value: +{
        +  "title": "Post Id",
        +  "type": "string"
        +}
      • addedInput schema / properties / pub
        Added value: +{
        +  "default": null,
        +  "title": "Pub",
        +  "type": "string"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "kwargs"
        -]New value: +[
        +  "post_id"
        +]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"get_post_contentArguments"
    • Changedget_profile4 fields changed
      • addedInput schema / properties / handle
        Added value: +{
        +  "title": "Handle",
        +  "type": "string"
        +}
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • changedInput schema / required
        Previous value: -[
        -  "kwargs"
        -]New value: +[
        +  "handle"
        +]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"get_profileArguments"
    • Changedget_unanswered_comments6 fields changed
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • addedInput schema / properties / limit
        Added value: +{
        +  "default": 50,
        +  "title": "Limit",
        +  "type": "integer"
        +}
      • addedInput schema / properties / post_id
        Added value: +{
        +  "title": "Post Id",
        +  "type": "string"
        +}
      • addedInput schema / properties / pub
        Added value: +{
        +  "default": null,
        +  "title": "Pub",
        +  "type": "string"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "kwargs"
        -]New value: +[
        +  "post_id"
        +]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"get_unanswered_commentsArguments"
    • Changedlist_comments5 fields changed
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • addedInput schema / properties / post_id
        Added value: +{
        +  "title": "Post Id",
        +  "type": "string"
        +}
      • addedInput schema / properties / pub
        Added value: +{
        +  "default": null,
        +  "title": "Pub",
        +  "type": "string"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "kwargs"
        -]New value: +[
        +  "post_id"
        +]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"list_commentsArguments"
    • Changedlist_notes4 fields changed
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • addedInput schema / properties / limit
        Added value: +{
        +  "default": 20,
        +  "title": "Limit",
        +  "type": "integer"
        +}
      • removedInput schema / required
        Removed value: -[
        -  "kwargs"
        -]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"list_notesArguments"
    • Changedlist_posts6 fields changed
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • addedInput schema / properties / limit
        Added value: +{
        +  "default": 20,
        +  "title": "Limit",
        +  "type": "integer"
        +}
      • addedInput schema / properties / pub
        Added value: +{
        +  "default": null,
        +  "title": "Pub",
        +  "type": "string"
        +}
      • addedInput schema / properties / sort
        Added value: +{
        +  "default": "new",
        +  "title": "Sort",
        +  "type": "string"
        +}
      • removedInput schema / required
        Removed value: -[
        -  "kwargs"
        -]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"list_postsArguments"
    • Changedpropose_reply9 fields changed
      • addedInput schema / properties / body
        Added value: +{
        +  "title": "Body",
        +  "type": "string"
        +}
      • addedInput schema / properties / kind
        Added value: +{
        +  "default": "post",
        +  "title": "Kind",
        +  "type": "string"
        +}
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • addedInput schema / properties / note_id
        Added value: +{
        +  "default": null,
        +  "title": "Note Id",
        +  "type": "string"
        +}
      • addedInput schema / properties / parent_comment_id
        Added value: +{
        +  "default": null,
        +  "title": "Parent Comment Id",
        +  "type": "string"
        +}
      • addedInput schema / properties / post_id
        Added value: +{
        +  "default": null,
        +  "title": "Post Id",
        +  "type": "string"
        +}
      • addedInput schema / properties / pub
        Added value: +{
        +  "default": null,
        +  "title": "Pub",
        +  "type": "string"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "kwargs"
        -]New value: +[
        +  "body"
        +]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"propose_replyArguments"
    • Changedpublish_note5 fields changed
      • addedInput schema / properties / body
        Added value: +{
        +  "title": "Body",
        +  "type": "string"
        +}
      • addedInput schema / properties / dry_run
        Added value: +{
        +  "default": true,
        +  "title": "Dry Run",
        +  "type": "boolean"
        +}
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • changedInput schema / required
        Previous value: -[
        -  "kwargs"
        -]New value: +[
        +  "body"
        +]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"publish_noteArguments"
    • Changedreact_to_comment9 fields changed
      • addedInput schema / properties / comment_id
        Added value: +{
        +  "title": "Comment Id",
        +  "type": "string"
        +}
      • addedInput schema / properties / dry_run
        Added value: +{
        +  "default": true,
        +  "title": "Dry Run",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / kind
        Added value: +{
        +  "default": "post",
        +  "title": "Kind",
        +  "type": "string"
        +}
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • addedInput schema / properties / on
        Added value: +{
        +  "default": true,
        +  "title": "On",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / pub
        Added value: +{
        +  "default": null,
        +  "title": "Pub",
        +  "type": "string"
        +}
      • addedInput schema / properties / reaction
        Added value: +{
        +  "default": "❤",
        +  "title": "Reaction",
        +  "type": "string"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "kwargs"
        -]New value: +[
        +  "comment_id"
        +]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"react_to_commentArguments"
    • Changedreact_to_post8 fields changed
      • addedInput schema / properties / dry_run
        Added value: +{
        +  "default": true,
        +  "title": "Dry Run",
        +  "type": "boolean"
        +}
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • addedInput schema / properties / on
        Added value: +{
        +  "default": true,
        +  "title": "On",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / post_id
        Added value: +{
        +  "title": "Post Id",
        +  "type": "string"
        +}
      • addedInput schema / properties / pub
        Added value: +{
        +  "default": null,
        +  "title": "Pub",
        +  "type": "string"
        +}
      • addedInput schema / properties / reaction
        Added value: +{
        +  "default": "❤",
        +  "title": "Reaction",
        +  "type": "string"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "kwargs"
        -]New value: +[
        +  "post_id"
        +]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"react_to_postArguments"
    • Changedreply_to_note6 fields changed
      • addedInput schema / properties / body
        Added value: +{
        +  "title": "Body",
        +  "type": "string"
        +}
      • addedInput schema / properties / dry_run
        Added value: +{
        +  "default": true,
        +  "title": "Dry Run",
        +  "type": "boolean"
        +}
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • addedInput schema / properties / note_id
        Added value: +{
        +  "title": "Note Id",
        +  "type": "string"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "kwargs"
        -]New value: +[
        +  "note_id",
        +  "body"
        +]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"reply_to_noteArguments"
    • Changedrestack_note6 fields changed
      • addedInput schema / properties / dry_run
        Added value: +{
        +  "default": true,
        +  "title": "Dry Run",
        +  "type": "boolean"
        +}
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • addedInput schema / properties / note_id
        Added value: +{
        +  "title": "Note Id",
        +  "type": "string"
        +}
      • addedInput schema / properties / on
        Added value: +{
        +  "default": true,
        +  "title": "On",
        +  "type": "boolean"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "kwargs"
        -]New value: +[
        +  "note_id"
        +]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"restack_noteArguments"
    • Changedrestack_post6 fields changed
      • addedInput schema / properties / dry_run
        Added value: +{
        +  "default": true,
        +  "title": "Dry Run",
        +  "type": "boolean"
        +}
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • addedInput schema / properties / on
        Added value: +{
        +  "default": true,
        +  "title": "On",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / post_id
        Added value: +{
        +  "title": "Post Id",
        +  "type": "string"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "kwargs"
        -]New value: +[
        +  "post_id"
        +]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"restack_postArguments"
    • Changedsearch_posts6 fields changed
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • addedInput schema / properties / limit
        Added value: +{
        +  "default": 10,
        +  "title": "Limit",
        +  "type": "integer"
        +}
      • addedInput schema / properties / pub
        Added value: +{
        +  "default": null,
        +  "title": "Pub",
        +  "type": "string"
        +}
      • addedInput schema / properties / query
        Added value: +{
        +  "title": "Query",
        +  "type": "string"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "kwargs"
        -]New value: +[
        +  "query"
        +]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"search_postsArguments"
    • Changedsend_approved_drafts7 fields changed
      • addedInput schema / properties / drafts_path
        Added value: +{
        +  "title": "Drafts Path",
        +  "type": "string"
        +}
      • addedInput schema / properties / dry_run
        Added value: +{
        +  "default": true,
        +  "title": "Dry Run",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / force
        Added value: +{
        +  "default": false,
        +  "title": "Force",
        +  "type": "boolean"
        +}
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • addedInput schema / properties / rate_seconds
        Added value: +{
        +  "default": 30,
        +  "title": "Rate Seconds",
        +  "type": "number"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "kwargs"
        -]New value: +[
        +  "drafts_path"
        +]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"send_approved_draftsArguments"
    • Changedtest_connection3 fields changed
      • removedInput schema / properties / kwargs
        Removed value: -{
        -  "title": "Kwargs"
        -}
      • removedInput schema / required
        Removed value: -[
        -  "kwargs"
        -]
      • changedInput schema / title
        Previous value: -"_toolArguments"New value: +"test_connectionArguments"
  2. 26 tool updatesv0.1.0
    • First observedaudit_search
    • First observedbulk_draft_replies
    • First observedcomment_on_post
    • First observedconfirm_reply
    • First observeddedup_status
    • First observeddelete_comment
    • First observedget_feed
    • First observedget_own_profile
    • First observedget_post
    • First observedget_post_by_id
    • First observedget_post_content
    • First observedget_profile
    • First observedget_unanswered_comments
    • First observedlist_comments
    • First observedlist_notes
    • First observedlist_posts
    • First observedpropose_reply
    • First observedpublish_note
    • First observedreact_to_comment
    • First observedreact_to_post
    • First observedreply_to_note
    • First observedrestack_note
    • First observedrestack_post
    • First observedsearch_posts
    • First observedsend_approved_drafts
    • First observedtest_connection

TDQS

A3.9/5.0
Disambiguation5/5

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.

Naming Consistency5/5

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.

Tool Count4/5

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.

Completeness3/5

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

ActivityInactive
ResponsivenessUnresponsive

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

Related MCP Servers

Latest Blog Posts

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