Skip to main content
Glama
toniher

substack-saved-mcp

by toniher

Substack Saved Posts & Notes MCP & CLI

PyPI version

A local, stdio-based Model Context Protocol (MCP) server and sync engine for your saved/bookmarked Substack posts and notes.

Features

  • Read & Search: Full-text search (SQLite FTS5) across saved post titles, excerpts, authors, and publications. Filter by publication, audience tier (e.g. everyone, only_paid), reading progress (--read-state unread/in_progress/finished/started), and date ranges (published_at vs saved_at). Search also covers a post's full body text, but only for posts whose content has already been fetched once via get-content / the get_post_content tool — a normal sync stores metadata and excerpts, not full bodies, so posts you haven't opened yet are matched on their title/excerpt/metadata only, not their full text.

  • Reading Progress: Substack tracks how far you've read each saved post (visible in its mobile apps, not the web UI) — this tool surfaces it. Each post reports is_fully_read and minutes_remaining, derived from the stored max_read_progress high-water mark at a configurable threshold (default 0.95, SUBSTACK_SAVED_FULLY_READ_THRESHOLD). Filter with --read-state, or sort a list by read_progress/minutes_remaining to find something short to finish. Progress refreshes on every sync a post is touched by; run sync --force to refresh it for your whole backlog.

  • Saved Notes, too: Substack's short-form notes are synced, searched, and cached separately from posts (they carry an author and body rather than a title or publication tier). Full-text search covers note bodies, authors, and restacked-post titles. Notes never require a browser at all — every notes operation (sync, save, unsave, full-content fetch) is a plain authenticated API call.

  • Full Content for LLMs: Fetch a saved post's or note's full content and get it back cleaned and formatted (headings, lists, links) for feeding directly to an LLM, with the result cached locally for next time.

  • Save & Unsave: Bookmark new Substack posts and notes, or unbookmark existing ones. Posts go through an authenticated browser session; notes are API-only.

  • Offline First: Fast, offline queries directly from local SQLite cache.

  • Privacy & Security: Keeps session credentials local, redacting tokens from logs.

  • FastMCP Protocol: Stdio MCP interface with rich tool suite and resources for both posts and notes.


Related MCP server: substack-article-mcp

Installation with uv

uv is the recommended fast Python package manager for installing and running substack-saved-mcp.

Option A: Install from PyPI as a System-wide Tool (uv tool install)

Install the published package from PyPI:

# Install system-wide into an isolated uv environment
uv tool install substack-saved-mcp

To install directly from a local repository folder instead:

# Navigate to the repository
cd /path/to/substack-saved-mcp

# Install system-wide into an isolated uv environment
uv tool install .

# Or install directly from a remote Git repository:
# uv tool install git+https://github.com/your-username/substack-saved-mcp.git

After installation, substack-saved-mcp is immediately available in your PATH:

# Verify installation
substack-saved-mcp --help

To update or uninstall:

# Upgrade installed tool
uv tool upgrade substack-saved-mcp

# Uninstall tool
uv tool uninstall substack-saved-mcp

Option B: Local Development / Development Environment (uv sync)

If you are developing or modifying the codebase:

# Clone and enter directory
cd substack-saved-mcp

# Install dependencies and dev tools (pytest)
uv sync --extra dev

# Run CLI commands using uv run
uv run substack-saved-mcp --help

# Run tests
uv run pytest

Quick Start

# 1. Initialize local database
substack-saved-mcp init

# 2. Authenticate with Substack (opens interactive browser window once)
substack-saved-mcp login

# 3. Sync saved posts AND notes into local cache (both entities by default)
substack-saved-mcp sync

# 3b. Or sync just one entity
substack-saved-mcp sync --only posts
substack-saved-mcp sync --only notes

# 4. Search saved posts via CLI
substack-saved-mcp search "artificial intelligence"

# 4b. Filter by publication or audience tier (see which tiers are cached with `audiences`)
substack-saved-mcp audiences
substack-saved-mcp list --audience only_paid
substack-saved-mcp search "artificial intelligence" --audience everyone

# 4c. Filter or sort by reading progress
substack-saved-mcp list --read-state finished
substack-saved-mcp list --read-state in_progress --sort-by minutes_remaining
substack-saved-mcp search "artificial intelligence" --read-state unread

# 5. Save or unsave a post
substack-saved-mcp save "https://example.substack.com/p/post-slug"
substack-saved-mcp unsave "https://example.substack.com/p/post-slug"

# 6. Get a saved post's full content, cleaned up and ready for an LLM
substack-saved-mcp get-content "https://example.substack.com/p/post-slug"

# 7. Work with saved notes the same way
substack-saved-mcp list-notes --limit 10
substack-saved-mcp search-notes "kubernetes" --author alice
substack-saved-mcp note-authors
substack-saved-mcp save-note "https://substack.com/@handle/note/c-123456"
substack-saved-mcp unsave-note "https://substack.com/@handle/note/c-123456"
substack-saved-mcp get-note "https://substack.com/@handle/note/c-123456"

# 8. Check combined status (posts and notes counts, last sync per entity)
substack-saved-mcp status

# 9. Launch stdio MCP server
substack-saved-mcp serve

Configuring MCP Clients (Claude Desktop, Goose, Cursor, etc.)

Add substack-saved-mcp to your MCP client's configuration file (e.g. claude_desktop_config.json).

Using System-Wide Installed Tool (uv tool or global binary)

{
  "mcpServers": {
    "substack-saved": {
      "command": "substack-saved-mcp",
      "args": ["serve"]
    }
  }
}

Using uv directly from the Repository Path

If you prefer running directly from your repository path without installing system-wide:

{
  "mcpServers": {
    "substack-saved": {
      "command": "uv",
      "args": [
        "--directory",
        "/path/to/substack-saved-mcp",
        "run",
        "substack-saved-mcp",
        "serve"
      ]
    }
  }
}

Frequently Asked Questions (FAQ)

Where is the database saved?

By default, the SQLite database is saved in your OS application data directory:

  • Linux / macOS: ~/.local/share/substack-saved-mcp/saved_posts.sqlite
    (or $XDG_DATA_HOME/substack-saved-mcp/saved_posts.sqlite if XDG_DATA_HOME is set)

You can specify a custom database path or directory using environment variables:

export SUBSTACK_SAVED_DB_PATH="/path/to/my/custom_database.sqlite"
# or
export SUBSTACK_SAVED_DATA_DIR="/path/to/my/data_dir"

Which Substack API does syncing saved posts use?

Saved posts are fetched from Substack's newer unified reader API by default, with automatic fallback to an older, posts-only API and finally to headless browser scraping if needed — you shouldn't normally need to think about this. If you ever want to force a specific source (e.g. while troubleshooting), set:

export SUBSTACK_SAVED_POSTS_SOURCE="unified"  # or "legacy" or "dom"

Leave it unset (or "auto") for the default, self-healing behavior.

What does a "partial" sync status mean?

If Substack rate-limits (HTTP 429) a sync so heavily that a page of results can't be fetched even after retrying, the sync keeps whatever it already fetched rather than failing outright, and reports status: partial (instead of success) in substack-saved-mcp status or the sync tool's response. When this happens on a sync --force, reconciliation (soft-deleting posts/notes no longer in the remote list) is automatically skipped for that run, so a post or note that merely couldn't be fetched is never mistaken for one you actually unsaved on Substack. Just run sync again later — a subsequent successful run picks up anything that was missed.

How is "fully read" determined, and how fresh is it?

Substack reports a max_read_progress high-water mark (0.0–1.0) per saved post; a post counts as fully read once that crosses a threshold (default 0.95 — real posts top out around 0.980.9999 rather than an exact 1.0). Override it with:

export SUBSTACK_SAVED_FULLY_READ_THRESHOLD="0.90"

Progress is refreshed whenever a post is re-fetched during sync. An incremental sync only touches recently-saved posts, so progress on older posts in your backlog can go stale between reads; run substack-saved-mcp sync --force to refresh it for everything. Notes have no reading-progress concept.

Will a browser window pop up when running as an MCP server?

No, a visible browser window will not open during normal MCP operations.

  • Read & Search Tools (search_saved_posts, list_saved_posts, get_saved_post, search_saved_notes, list_saved_notes, get_saved_note, list_publications, list_audiences, saved_posts_status):
    Operate 100% offline using the local SQLite database. Zero browser activity.

  • Post Sync & Write Tools (sync_saved_posts, save_post, unsave_post, get_post_content):
    Run in headless background mode using the pre-authenticated session stored in storage_state.json.

  • Note Sync & Write Tools (sync_saved_notes, save_note, unsave_note, get_note_content):
    Never open a browser page at all, headless or otherwise — Substack's notes endpoints are plain authenticated HTTP calls, so these tools only ever make direct API requests using storage_state.json.

  • Interactive Login:
    A visible browser window opens only when you manually run substack-saved-mcp login from your terminal. If your session expires while using an MCP client, the tool will return a clear error message instructing you to re-authenticate via substack-saved-mcp login instead of popping open a browser window unexpectedly.

What if I get a Playwright "Executable doesn't exist" error?

If you encounter an error like BrowserType.launch: Executable doesn't exist when running commands (especially login), it means Playwright hasn't installed its required browsers in the isolated environment.

To fix this, you need to run the playwright install command inside the environment where the tool is installed.

For a system-wide tool installation (via uv tool install), run:

~/.local/share/uv/tools/substack-saved-mcp/bin/playwright install

If you are using a local development environment (via uv sync), run:

uv run playwright install

I edited the source code, but the installed substack-saved-mcp command still behaves like the old version. Why?

uv tool install copies the package into its own isolated environment at install time — it does not track your working tree. If you edited files under src/ (or pulled new commits) after installing the tool system-wide, the globally installed copy is stale and keeps running the old code, even though uv run substack-saved-mcp ... from the repo would use the latest source.

Reinstall from your current working tree to pick up the changes:

uv tool install . --no-cache --force
  • --force replaces the existing installed version instead of skipping the install because a version is already present.

  • --no-cache ensures a fresh build rather than reusing a cached wheel/build artifact from before your edits.

Do this any time after modifying the codebase and before relying on the globally installed substack-saved-mcp binary (as opposed to uv run substack-saved-mcp, which always reflects the working tree).

Available Tools

17 tools
get_note_contentA

Fetch a saved note's full content, cleaned and formatted for LLM consumption.

Returns the cached body_text if a previous fetch already stored it, unless force_refetch is set. Otherwise fetches the note directly via Substack's reader API (no browser page needed) and caches the result. Requires an active authenticated Substack session.

ParametersJSON Schema
NameRequiredDescriptionDefault
url_or_idYes
force_refetchNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4/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 the full burden. It transparently discloses caching behavior, the use of an API instead of a browser, and the requirement for an authenticated session. This covers key behavioral aspects, though it does not mention error handling or response details beyond the output schema.

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 concise sentences, front-loaded with the primary purpose, and each sentence adds relevant detail without redundancy. It avoids jargon and is well-structured for quick consumption by an AI agent.

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 tool with only two parameters and an output schema, the description covers essential context: caching, API access, authentication, and the cleaning/formating. It is complete enough for most use cases, though it omits potential edge cases like failure modes or idempotency guarantees, which are not critical here.

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%, requiring the description to compensate. It explains force_refetch's effect on caching but does not clarify what url_or_id actually accepts beyond the schema's generic string type. This partial compensation leaves a gap for the primary 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 the verb 'Fetch' and the resource 'a saved note's full content', with additional value of 'cleaned and formatted for LLM consumption'. This distinguishes it from sibling tools like get_post_content or get_saved_note by explicitly focusing on notes and formatting for LLMs.

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?

While it mentions the caching mechanism and the API-based fetch, it does not explicitly contrast with alternatives or state when to prefer this over get_saved_note or get_post_content. It implies usage for fetching note content but lacks explicit when-not-to-use guidance or alternative recommendations.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_post_contentA

Fetch a saved post's full content, cleaned and formatted for LLM consumption.

Returns the cached content_text if a previous fetch already stored it, unless force_refetch is set. Otherwise fetches the post's page directly, extracts its body_html from Substack's server-rendered window._preloads blob, converts it to plain text (headings, list items, and links kept readable), and caches the result. Requires an active authenticated Substack session. If the content can't be located on the page (e.g. Substack changed how it embeds it, or the post is paywalled beyond this account's access), returns success=False with a message suggesting the caller run 'substack-saved-mcp inspect-network' while opening the post so the real content source can be captured.

ParametersJSON Schema
NameRequiredDescriptionDefault
url_or_idYes
force_refetchNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description fully carries the transparency burden. It discloses caching behavior, the fetch source (window._preloads blob), text conversion process, auth requirements, and failure mode with a suggested remediation. This is exceptional behavioral disclosure for a read tool, covering side effects and edge cases.

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 compact yet detailed, with three sentences covering purpose, behavior, and failure handling. The second sentence is long and clause-heavy, but every piece of information earns its place given the tool's complexity. It is front-loaded with the core purpose and avoids unnecessary repetition.

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 complexity, no annotations, and an output schema present, the description is remarkably complete. It covers caching, extraction method, auth prerequisites, and failure handling with a user- actionable next step. Nothing critical is omitted for an agent to invoke this tool safely and effectively.

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 schema has zero descriptions, but the tool description explicitly explains force_refetch semantics (overrides cached content) and implies url_or_id identifies the saved post. While the exact format for url_or_id isn't specified, the parameter name and context are sufficiently self-explanatory for an agent. The description compensates well for the schema's lack of detail.

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 fetches a saved post's full content, cleaned and formatted for LLM consumption. This specific verb+resource combination distinguishes it from siblings like list_saved_posts or get_saved_post, which likely handle metadata or listings.

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 establishes clear context: use when you need the full content of a saved post, with prerequisites (authenticated session) and caching behavior. It does not explicitly name alternative tools for different use cases, but the purpose is unambiguous and the troubleshooting tip for failed fetches adds practical guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_saved_noteA

Retrieve full cached note details (author, body, engagement counts) by URL or local ID.

ParametersJSON Schema
NameRequiredDescriptionDefault
url_or_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.8/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description clearly identifies that this is a read operation returning cached data, but with no annotations present, more context is needed—such as staleness implications, error handling, or authentication requirements. It is not misleading but stays surface-level.

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?

A single, front-loaded sentence that communicates action, resource, and details without redundancy. Every clause adds value and no space is wasted.

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 read tool with an output schema, the description sufficiently covers purpose and parameter semantics. It does not address edge cases like missing or invalid IDs, but overall it gives an adequate mental model for an agent to select and use the 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?

The parameter `url_or_id` is given meaningful explanation: it can be a URL or a local identifier. Since the schema description coverage is 0%, this is essential additional context. A concrete example or format hint would have been useful but is not required for the one-parameter tool.

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 a specific verb ('Retrieve'), specific resource ('full cached note details'), and enumerates returned fields ('author, body, engagement counts'). This clarifies the tool's scope and distinguishes it from sibling tools like get_note_content or search_saved_notes.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance about when to choose this tool over alternatives such as get_note_content or sync_saved_notes. It specifies the lookup mechanism (by URL or local ID) but does not explain contexts, exclusions, or constraints.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_saved_postA

Retrieve full cached post details, timestamps (published_at and saved_at), and content by URL or local ID.

ParametersJSON Schema
NameRequiredDescriptionDefault
url_or_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.8/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations available, the description carries the burden of behavioral disclosure and does convey that this is a read-only retrieval of cached data, not a mutation. However, it omits useful behavioral context such as what happens if the post is not found, whether the cache can be stale, or whether any fetch/refresh is triggered.

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 entire description is a single, well-ordered sentence that front-loads the action and then packs only useful qualifiers: full, cached, timestamps, content, URL/local ID. There is no filler, redundancy, or unnecessary elaboration.

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 one-parameter getter with an output schema already present, this description provides the essential information: what resource is being retrieved, what identifying data is accepted, and what major fields the result includes. It could be more complete by explaining how it relates to the sibling tools, but for this low-complexity tool the essentials are not missing.

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 schema provides only an unannotated string property 'url_or_id' with zero description coverage. The tool description compensates by explicitly stating that either a URL or a local ID can be supplied, which is the essential semantic needed to populate the single required parameter. It does not specify edge-case formats, but the coverage for a one-parameter tool is adequate.

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 uses a concrete verb 'retrieve' and clearly identifies the resource: full cached post details, including timestamps and content. The clarification that lookup can be by URL or local ID makes the tool's job unambiguous and distinguishes it from sibling tools like list_saved_posts or 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 Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No explicit guidance is provided about when to use this tool versus alternatives such as search_saved_posts, list_saved_posts, or get_post_content. The description implies a retrieval use case but does not state conditions, exclusions, or preferred scenarios.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_audiencesA

List distinct audience tiers present in local cache with post counts.

Discovers actual values in use (e.g. "everyone", "only_paid") rather than a hardcoded enum, since Substack's audience values aren't officially documented and may vary or grow over time.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the burden of behavioral disclosure. It explains that values are discovered from local cache and are dynamic, but it does not explicitly state safety traits (e.g., read-only, no side effects) or mention whether the cache might be stale or require syncing beforehand.

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, with the core function stated first and the rationale in the second sentence. Every sentence provides value and there is no redundancy or unnecessary detail.

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 the simplicity (0 params) and presence of an output schema, the description is mostly complete. It covers what the tool does, where it reads from, and why it uses live values. It could mention prerequisites like cache syncing, but that is a minor gap.

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 tool has zero parameters and the schema coverage is 100%, so the baseline is 4. The description adds context about why there are no parameters (it scans actual values in use) which is helpful, though not strictly necessary.

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 lists distinct audience tiers from local cache with post counts. It differentiates itself from sibling tools by focusing on audience tiers specifically, and the context about discovering actual values rather than hardcoded enums adds clarity.

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 usage for discovering dynamic audience values rather than relying on a static enum, but it does not explicitly state when to use this tool versus alternatives or mention any exclusions. No sibling tool is referenced for comparison.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_publicationsA

List all publications in local cache with post counts.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.5/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations are absent, so the description must disclose behavioral traits. The description only mentions listing local cache with post counts, but does not state whether the operation is read-only, requires prerequisites, or has side effects. It also does not clarify what 'local cache' means operationally.

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 a single, well-structured sentence that front-loads the core purpose and includes relevant detail ('with post counts'). No redundancy or unnecessary text.

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?

Though the tool is simple with no parameters and an output schema, the description lacks critical contextual details such as whether it is a read-only operation, how 'local cache' is populated, and when to choose this over sibling tools. Given the importance of an agent knowing side effects and usage, the description is minimally viable but has clear gaps.

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 tool has zero parameters, so the input schema is inherently fully covered. The description correctly omits parameter details, warranting the baseline score of 4 for no-parameter tools.

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 lists all publications in local cache and includes post counts, specifying the exact resource and scope. It is distinct from sibling tools like list_saved_posts, which focuses on saved posts rather than publications.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is provided regarding when to use this tool instead of alternatives like search_saved_posts or sync_saved_posts. There are no explicit context cues, prerequisites, or exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_saved_notesA

List cached saved notes with pagination and optional author/restack filters.

sort_by can be 'saved_at' (when note was bookmarked) or 'posted_at' (when note was posted). restacks_only limits results to notes that restack a post.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
authorNo
offsetNo
sort_byNosaved_at
restacks_onlyNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.8/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 the transparency burden. It discloses that notes are 'cached' (potential staleness) and explains sorting/filters, but does not state side effects (e.g., read-only implication) or any limitations beyond caching. It adds useful context but lacks explicit safety or behavioral notes.

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 compact, two sentences, front-loaded with the main purpose, and includes necessary filter details without redundancy. Every sentence adds value, and the structure is easy to scan.

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 the presence of an output schema, the description needn't explain return values. It covers pagination, filters, sorting, and the 'cached' nuance, which is sufficient for a list tool. However, it could mention author filtering behavior or note that results are limited to user's saved notes, though not essential.

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 explains two non-obvious parameters (sort_by and restacks_only) with examples. It omits limit, author, and offset, which are intuitive but still deserve mention. The partial coverage does not fully compensate for the zero 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?

The description clearly states 'List cached saved notes' with specific filters and pagination. It distinguishes from siblings like search_saved_notes (search vs list) and sync_saved_notes (sync vs list) by naming the core action and resource.

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 usage for listing saved notes and mentions filters but does not explicitly contrast with alternatives (e.g., when to use search_saved_notes instead). There is no explicit 'use this when' or 'don't use when' guidance, though the functionality is evident.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_saved_postsA

List cached saved posts with pagination and optional publication/audience filters.

sort_by can be 'saved_at', 'published_at', 'read_progress', or 'minutes_remaining'. audience filters by tier (see list_audiences for cached values, e.g. "everyone", "only_paid"). read_state filters by reading progress: 'unread', 'in_progress', 'finished', or 'started' — a post is 'finished' once its high-water reading progress crosses a threshold, default 0.95.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
offsetNo
sort_byNosaved_at
audienceNo
read_stateNo
publicationNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description must carry the burden of behavioral disclosure. It explains the nuanced behavior of read_state, including the 'finished' threshold concept, and the source of audience values (cached, from list_audiences). It provides insight into how sorting works (allowed values for sort_by). However, it does not describe the output format, pagination behavior (beyond having limit/offset), or error cases.

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 dense but well-organized: the first sentence gives the overview, and the following lines explain each filter clearly. Front-loads the core purpose and then details the filter semantics. The structure is efficient with no fluff.

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?

Has an output schema, so return type explanation is not needed. The description covers the main behavioral aspects: sorting options, audience filter, read_state filter with its threshold logic, and implicitly pagination. Manual page size and limit are inferable. The main missing piece is a note on how the response is shaped or how pagination actually works (e.g., whether offset is counts of posts), but that is likely covered by the output schema. Given the complexity, it is close to complete.

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?

Schema description coverage is 0%, so the description must fully compensate. It explains the semantics of sort_by (enumerating options), audience (referencing list_audiences for valid values), and read_state (explaining each value and the threshold concept). It also implies semantics for pagination (limit/offset). The description adds substantial meaning beyond the raw schema, which only shows parameter names and types.

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 ('List'), the resource ('cached saved posts'), and the key features (pagination, optional filters). It distinguishes itself from siblings like search_saved_posts by focusing on cached data listing with structured filters rather than search. The specific behaviors around read_state and sorting are unique to this tool.

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 implies usage by listing the available filters (audience, read_state, publication). It references list_audiences for cached values and explains the semantics of read_state clearly (including the threshold concept). However, it does not explicitly state when to prefer this over siblings like search_saved_posts or get_saved_post, nor does it provide explicit exclusions or alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

saved_posts_statusA

Return cache statistics, database path, and last sync run status.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
posts_unreadNo
database_pathYes
last_sync_statusNo
posts_fully_readNo
posts_in_progressNo
total_saved_notesNo
total_saved_postsYes
total_publicationsYes
total_unsaved_notesNo
total_unsaved_postsYes
last_successful_syncNo
last_note_sync_statusNo
minutes_remaining_totalNo
last_successful_note_syncNo

TDQS

A3.7/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It discloses the tool is read-only in nature (returns statistics and status) but doesn't state whether it triggers any side effects or has any rate limits. It doesn't describe what 'last sync run status' entails (e.g., success/failure details, timestamps) or what the database path refers to. The description is adequate but not rich in behavioral disclosure.

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 a single, efficient sentence that states the exact content of the return. It is appropriately sized for a zero-parameter tool that retrieves status information, with 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 the tool is simple with no parameters and the output schema exists (though not detailed here), the description covers what the tool does and what it returns. It doesn't explain the format of the output statistics or status, but the output schema presumably provides that. For a status-check tool of this simplicity, the description is mostly complete, though it could benefit from clarifying whether this is a read-only operation.

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 tool has zero parameters, so the input schema is empty (schema coverage 100% by default). The description correctly doesn't mention parameters since there are none. Per the rubric, 0 params gives a baseline 4, and the description appropriately focuses on what the tool returns rather than inputs. It adds value by specifying the exact output categories.

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 the tool returns three specific pieces of information: cache statistics, database path, and last sync run status. This distinguishes it from the sibling tools that operate on posts or notes directly, since this tool is about system status rather than content.

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 a diagnostic or monitoring use case ('Return cache statistics, database path, and last sync run status'), but does not explicitly state when to use this tool instead of other sync-related tools like sync_saved_posts or sync_saved_notes. There are no explicit exclusions or alternatives mentioned, leaving the agent to infer that this is for checking sync status without triggering a sync.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

save_noteA

Bookmark a Substack note remotely on Substack and save it to the local cache.

Requires an active authenticated Substack session (run 'substack-saved-mcp login' if expired). remote_confirmed=False means the note is still cached locally, but the tool could not verify the bookmark was actually created on Substack's side — a subsequent 'sync --force' will correct the local cache if the remote save didn't actually happen.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries full responsibility. It discloses the remote bookmark action, local caching, the authentication prerequisite, the remote_confirmed=False failure state, and the corrective sync behavior—this is substantial and actionable 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 concise, front-loaded with the purpose, and efficiently uses three sentences to cover action, prerequisite, and failure behavior. No filler or 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 single-parameter tool with an output schema, the description fully covers the essential context: authentication requirement, what the tool does, what failure looks like, and how to recover. Return-value details are unnecessary because an output schema exists.

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?

The input schema has 0% description coverage and only lists 'url' as a required string. The tool description implies 'url' is a Substack note URL, which adds limited meaning, but it doesn't explain URL format, validation, or any constraints beyond the 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 opens with a specific verb ('Bookmark') and resource ('Substack note'), and clarifies the dual action of remote bookmarking and local caching. This clearly distinguishes it from siblings like save_post, unsave_note, and sync_saved_notes.

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 usage context: it requires an active authenticated Substack session, mentions what to do if expired, and explains the recovery path when remote confirmation fails. It does not explicitly name alternative tools or say when not to use it, but the intended use is well implied.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

save_postA

Bookmark a Substack post remotely on Substack and save it to the local cache.

Requires an active authenticated Substack session (run 'substack-saved-mcp login' if expired). Remote confirmation is best-effort: Substack's bookmark button markup isn't officially documented, so this detects whether the button's rendered state provably changed after clicking. remote_confirmed=False means the post is still cached locally, but the tool could not verify the bookmark was actually created on Substack's side — a subsequent 'sync --force' will correct the local cache if the remote save didn't actually happen.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full behavioral burden. It discloses the auth requirement, the uncertainty of remote confirmation, the meaning of remote_confirmed=False, and the corrective role of sync --force. This is thorough and honest.

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 front-loaded with the core purpose and then provides two short, information-dense paragraphs about prerequisites and caveats. Every sentence adds value, and there is no redundancy or filler.

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 presence of an output schema, return values do not need further explanation. The description covers prerequisites, failure modes, local caching behavior, and recovery steps, making it fully complete for a remote save operation with uncertain confirmation.

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?

The schema has one required url parameter with zero description coverage. The description clarifies that the URL should refer to a Substack post, adding some semantics beyond the parameter name, but it does not specify accepted URL formats, edge cases, or example values.

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: 'Bookmark a Substack post remotely on Substack and save it to the local cache.' This uses specific verbs and resources, and it distinguishes the tool from siblings like unsave_post and list_saved_posts.

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?

It provides clear context about requiring an authenticated session and explains the best-effort nature of remote confirmation. However, it does not explicitly compare with alternatives like unsave_post or sync_saved_posts, though the usage is heavily implied.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_saved_notesA

Perform full-text FTS5 search across cached saved notes.

Searches body text, author name/handle, and (for restacks) the attached post's title. Allows filtering by author (name or handle), original note date (posted_at), and saved date (saved_at). Notes lack a bookmark timestamp on Substack's side, so saved_at is typically None.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
queryYes
authorNo
saved_afterNo
posted_afterNo
saved_beforeNo
posted_beforeNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.1/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 discloses that the search operates on 'cached' notes (implying synced data, not real-time), explains the FTS5 technology, and explicitly warns that saved_at is typically None due to Substack lacking a bookmark timestamp. These are meaningful behavioral traits beyond the schema. It does not state read-only status, but for a search tool that is implied. The caveats add solid transparency.

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 about 80 words, front-loaded with the primary purpose, then elaborates on search scope and filters, and ends with a caveat. It is dense with information but avoids redundant phrasing. Each sentence contributes to understanding, and there is no fluff.

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 the tool's complexity (7 params, 0% schema coverage), the description covers the key aspects: what is searched, available filters, and the saved_at caveat. An output schema exists, so return values are likely covered there. Missing details like date format and sorting behavior are minor and could be inferred or documented in the schema. Overall, it is sufficiently complete for effective 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 the description must compensate. It explains that author filtering accepts name or handle, and clarifies the distinction between posted_at (original note date) and saved_at (saved date), even noting the saved_at limitation. However, it does not explain the format for date parameters (e.g., ISO 8601) or the meaning of the limit parameter, leaving some semantics to be inferred.

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 performs 'full-text FTS5 search across cached saved notes', specifies the exact resources searched (body text, author name/handle, attached post title for restacks), and explicitly differentiates from sibling search_saved_posts by focusing on notes. It also lists filtering capabilities, making the purpose unambiguous.

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 implies usage context: it is for searching saved notes with content filtering, and explicitly differentiates from listing tools by highlighting full-text search. It also notes a key limitation (saved_at typically None) that affects usage. However, it does not explicitly state when not to use this tool versus alternatives like list_saved_notes or search_saved_posts, but the intent is clear enough.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_saved_postsA

Perform full-text FTS5 search across cached saved posts.

Searches title, excerpt, publication name, author, and content text. Allows filtering by publication name, audience tier (see list_audiences for cached values, e.g. "everyone", "only_paid"), original post date (published_at), saved date (saved_at), and read_state ('unread', 'in_progress', 'finished', or 'started' — a post is 'finished' once its high-water reading progress crosses a threshold, default 0.95).

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
queryYes
audienceNo
read_stateNo
publicationNo
saved_afterNo
saved_beforeNo
published_afterNo
published_beforeNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It does disclose what fields are searched (title, excerpt, publication, author, content), the exact semantics of read_state including the threshold definition (0.95), and that it operates on cached saved posts. This goes beyond a generic 'search' and gives the agent insight into how results are classified. It does not mention pagination or rate limits, but for a search tool this is acceptable.

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 compact and information-dense. The main action is stated in the first sentence, and subsequent sentences add only necessary filtering details. No filler or redundancy exists; every sentence provides value. The structure is logical: what it does, then what it searches, then how to filter.

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 that an output schema exists (covering return values), the description does not need to repeat that. It covers the core behavior, searchable fields, and all non-obvious filter semantics (especially read_state). It might benefit from clarifying the date format or limit behavior, but these are relatively minor and inferable from schema names. The description is sufficiently complete for an agent to invoke the tool correctly in most scenarios.

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 clarifies the meaning of query (full-text on multiple fields), audience (with example values and a reference to list_audiences), read_state (with explicit enumerated values and the 'finished' threshold), and publication (filter by name). It also mentions date filtering (published_at, saved_at) but does not explain that these are range parameters or their format. It entirely omits the 'limit' parameter. This partial coverage earns a 3.

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 opens with a precise verb-resource pair: 'Perform full-text FTS5 search across cached saved posts.' This distinguishes it from sibling search_saved_notes (search notes) and from list_saved_posts (listing vs. content search). The use of 'FTS5' and 'cached' adds specificity that leaves no ambiguity about what the tool operates on.

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 explains the filtering capabilities (publication, audience, dates, read_state), which implicitly tells the agent when this tool is suitable (when a full-text search or filtering is needed). However, it does not explicitly state when to prefer list_saved_posts over this tool (e.g., 'for a simple list without text search'), and it does not mention any exclusions or alternatives. The usage context is clear but not contrasted with siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

sync_saved_notesA

Trigger incremental or full resync of saved notes from Substack account into local SQLite cache.

Requires an active authenticated Substack session.

ParametersJSON Schema
NameRequiredDescriptionDefault
forceNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
idNo
entityNo
statusYes
sync_modeNo
started_atYes
completed_atNo
error_messageNo
fetched_countNo
upserted_countNo
reconciled_countNo

TDQS

A3.6/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 the burden. It discloses that the tool resyncs to a local cache and requires authentication, both important. However, it does not explain what force=true does, whether the cache is replaced or merged, or any potential rate limits/performance impacts.

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 fluff, and the key information is front-loaded. The auth requirement is relevant and succinctly stated.

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 basic purpose and an essential prerequisite. However, for a sync operation with side effects and a parameter that changes behavior, it lacks the force parameter mapping and details on what happens during a resync. The output schema likely covers return values, so that gap is acceptable, but the parameter gap is significant.

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 input schema has one boolean parameter 'force' with 0% description coverage. The description mentions 'incremental or full' resync but never explicitly connects force=true to full resync, leaving the parameter's effect ambiguous. The description should compensate for the sparse schema but does not.

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 identifies the action ('Trigger... resync'), the resource ('saved notes from Substack account into local SQLite cache'), and the mode ('incremental or full'). Distinguishes it from sibling tools like sync_saved_posts by specifying notes.

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 a useful prerequisite ('Requires an active authenticated Substack session') but does not explicitly say when to choose this over alternatives like sync_saved_posts or when to use incremental vs full. Usage context is implied but not fully elaborated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

sync_saved_postsC

Trigger incremental or full resync of saved posts from Substack account into local SQLite cache.

Requires an active authenticated Substack session.

ParametersJSON Schema
NameRequiredDescriptionDefault
forceNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
idNo
entityNo
statusYes
sync_modeNo
started_atYes
completed_atNo
error_messageNo
fetched_countNo
upserted_countNo
reconciled_countNo

TDQS

C2.9/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description must disclose side effects. It mentions writing to a local cache but does not clarify whether the sync is destructive, whether it overwrites or merges data, or if it has any other side effects. The behavior of 'incremental' vs 'full' is vague.

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 concise sentences with no redundant words or filler. It efficiently conveys the core purpose and prerequisite.

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?

The description leaves significant gaps: no explanation of the 'force' parameter, no detail on the sync behavior (incremental/full) or its implications, and no mention of output. Given the tool's side effects (writing to cache), this is incomplete.

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 only parameter 'force' is not explained anywhere. The description gives no indication of what 'force' does (e.g., force full resync regardless of state). This is a critical omission for correct usage.

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 action ('sync'), the resource ('saved posts'), the source ('Substack'), and the destination ('local SQLite cache'). It distinguishes from the sibling tool 'sync_saved_notes' by explicitly targeting saved posts.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description only mentions a prerequisite (authenticated session) but does not indicate when this tool should be used over alternatives, when incremental vs. full sync is appropriate, or any scenario-specific guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

unsave_noteA

Unbookmark a Substack note remotely and soft-delete it in local cache.

Soft-deletion preserves note history while removing it from active search/list outputs. remote_confirmed=False means the note was still soft-deleted locally, but the tool could not verify the unbookmark on Substack's side.

ParametersJSON Schema
NameRequiredDescriptionDefault
url_or_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

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 of behavioral disclosure, and it delivers: it explains the soft-delete side effect, what's preserved (note history), what's affected (active search/list outputs), and the meaning of remote_confirmed=False as a partial-failure state. It could add idempotency behavior or error handling, preventing a 5, but this is well above the minimum.

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, front-loaded core purpose, with each subsequent sentence adding meaningful behavior about soft-deletion and the remote_confirmed flag. Every word earns its place; no fluff or redundancy. This is textbook conciseness without under-specification.

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 single-parameter, low-complexity tool with an output schema, the description covers the essentials: remote action, local cache behavior, and a partial-failure state. Since an output schema exists, the description needn't explain return values, though it could have addressed edge cases like calling on an already-unsaved note or transient network errors.

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% and the description never explicitly explains the url_or_id parameter format or accepted values. However, the parameter name is largely self-descriptive, and the description's mention of remote_confirmed indirectly hints at the identifier's role in the unbookmark operation. A bit more explicit guidance (URL formats, prefix expectations) would justify a higher score.

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 uses a specific verb+resource+scope: 'Unbookmark a Substack note remotely and soft-delete it in local cache.' It clearly distinguishes from sibling unsave_post by scoping to notes and explicitly describes the dual remote/local action. This matches the 'specific verb+resource+scope' high bar.

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 usage context is clearly implied (use when you want to remove a bookmark from a note), but no explicit when/when-not rules or named alternatives are given. Unlike the calibration example that says 'use search_calls_extensive instead,' this description doesn't guide the agent to an alternative sibling, so it earns a 3 for implied usage without exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

unsave_postA

Unbookmark a Substack post remotely and soft-delete it in local cache.

Soft-deletion preserves post history while removing it from active search/list outputs. When the post's Substack ID is known (normally true after a sync), this calls Substack's real unsave endpoint directly and is reliably confirmed; otherwise it falls back to a best-effort DOM click (see save_post). remote_confirmed=False means the post was still soft-deleted locally, but the tool could not verify the unbookmark on Substack's side.

ParametersJSON Schema
NameRequiredDescriptionDefault
url_or_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description fully carries the behavioral disclosure burden. It explains soft-deletion semantics, that remote_confirmed=False indicates local deletion without remote verification, and describes the direct endpoint vs. best-effort DOM click fallback. This is transparent and detailed.

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 information-dense with no filler. The main action is front-loaded, and each subsequent sentence adds essential detail about soft-deletion, remote confirmation, and fallback behavior. The length is justified by the tool's nuanced behavior.

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?

Despite having only one parameter and an output schema, the description covers the tool's core behavior, failure mode, and local side effects. It explains the remote_confirmed field and points to save_post for the fallback, making the description sufficiently complete for an agent to invoke the tool correctly.

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?

The schema provides only a parameter name 'url_or_id' with no description, and the description does not explicitly define accepted formats. However, the description does add useful context about using the Substack ID when known and the fallback behavior, which partially compensates for the 0% 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?

The description opens with a specific verb and resource: 'Unbookmark a Substack post remotely and soft-delete it in local cache.' It clearly distinguishes this from sibling tools like save_post and list_saved_posts by describing both the remote action and local cache 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 clear context on when to use the tool—such as when the post's Substack ID is known after a sync—and references save_post for the fallback DOM-click behavior. However, it does not explicitly state when not to use this tool or compare it to alternatives like delete or list operations.

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. 4 tool updatesv0.3.1
    • Changedget_saved_post1 field changed
      • changedOutput schema / properties / result / anyOf
        Previous value: -[
        -  {
        -    "description": "Represents a Substack post record stored in SQLite.",
        -    "properties": {
        -      "audience": {
        -        "anyOf": [
        -          {
        -            "type": "string"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ],
        -        "default": null
        -      },
        -      "author_name": {
        -        "anyOf": [
        -          {
        -            "type": "string"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ],
        -        "default": null
        -      },
        -      "content_text": {
        -        "anyOf": [
        -          {
        -            "type": "string"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ],
        -        "default": null
        -      },
        -      "created_at": {
        -        "anyOf": [
        -          {
        -            "type": "string"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ],
        -        "default": null
        -      },
        -      "excerpt": {
        -        "anyOf": [
        -          {
        -            "type": "string"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ],
        -        "default": null
        -      },
        -      "id": {
        -        "anyOf": [
        -          {
        -            "type": "integer"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ],
        -        "default": null
        -      },
        -      "image_url": {
        -        "anyOf": [
        -          {
        -            "type": "string"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ],
        -        "default": null
        -      },
        -      "is_paywalled": {
        -        "default": 0,
        -        "type": "integer"
        -      },
        -      "is_saved": {
        -        "default": 1,
        -        "type": "integer"
        -      },
        -      "publication_name": {
        -        "type": "string"
        -      },
        -      "publication_url": {
        -        "anyOf": [
        -          {
        -            "type": "string"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ],
        -        "default": null
        -      },
        -      "published_at": {
        -        "anyOf": [
        -          {
        -            "type": "string"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ],
        -        "default": null
        -      },
        -      "reading_time_minutes": {
        -        "anyOf": [
        -          {
        -            "type": "integer"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ],
        -        "default": null
        -      },
        -      "saved_at": {
        -        "anyOf": [
        -          {
        -            "type": "string"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ],
        -        "default": null
        -      },
        -      "substack_post_id": {
        -        "anyOf": [
        -          {
        -            "type": "string"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ],
        -        "default": null
        -      },
        -      "title": {
        -        "type": "string"
        -      },
        -      "unsaved_at": {
        -        "anyOf": [
        -          {
        -            "type": "string"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ],
        -        "default": null
        -      },
        -      "updated_at": {
        -        "anyOf": [
        -          {
        -            "type": "string"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ],
        -        "default": null
        -      },
        -      "url": {
        -        "type": "string"
        -      },
        -      "word_count": {
        -        "anyOf": [
        -          {
        -            "type": "integer"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ],
        -        "default": null
        -      }
        -    },
        -    "required": [
        -      "url",
        -      "title",
        -      "publication_name"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "description": "Represents a Substack post record stored in SQLite.",
        +    "properties": {
        +      "audience": {
        +        "anyOf": [
        +          {
        +            "type": "string"
        +          },
        +          {
        +            "type": "null"
        +          }
        +        ],
        +        "default": null
        +      },
        +      "author_name": {
        +        "anyOf": [
        +          {
        +            "type": "string"
        +          },
        +          {
        +            "type": "null"
        +          }
        +        ],
        +        "default": null
        +      },
        +      "content_text": {
        +        "anyOf": [
        +          {
        +            "type": "string"
        +          },
        +          {
        +            "type": "null"
        +          }
        +        ],
        +        "default": null
        +      },
        +      "created_at": {
        +        "anyOf": [
        +          {
        +            "type": "string"
        +          },
        +          {
        +            "type": "null"
        +          }
        +        ],
        +        "default": null
        +      },
        +      "excerpt": {
        +        "anyOf": [
        +          {
        +            "type": "string"
        +          },
        +          {
        +            "type": "null"
        +          }
        +        ],
        +        "default": null
        +      },
        +      "id": {
        +        "anyOf": [
        +          {
        +            "type": "integer"
        +          },
        +          {
        +            "type": "null"
        +          }
        +        ],
        +        "default": null
        +      },
        +      "image_url": {
        +        "anyOf": [
        +          {
        +            "type": "string"
        +          },
        +          {
        +            "type": "null"
        +          }
        +        ],
        +        "default": null
        +      },
        +      "is_fully_read": {
        +        "readOnly": true,
        +        "type": "boolean"
        +      },
        +      "is_paywalled": {
        +        "default": 0,
        +        "type": "integer"
        +      },
        +      "is_saved": {
        +        "default": 1,
        +        "type": "integer"
        +      },
        +      "is_viewed": {
        +        "default": 0,
        +        "type": "integer"
        +      },
        +      "max_read_progress": {
        +        "anyOf": [
        +          {
        +            "type": "number"
        +          },
        +          {
        +            "type": "null"
        +          }
        +        ],
        +        "default": null
        +      },
        +      "minutes_remaining": {
        +        "anyOf": [
        +          {
        +            "type": "integer"
        +          },
        +          {
        +            "type": "null"
        +          }
        +        ],
        +        "readOnly": true
        +      },
        +      "publication_name": {
        +        "type": "string"
        +      },
        +      "publication_url": {
        +        "anyOf": [
        +          {
        +            "type": "string"
        +          },
        +          {
        +            "type": "null"
        +          }
        +        ],
        +        "default": null
        +      },
        +      "published_at": {
        +        "anyOf": [
        +          {
        +            "type": "string"
        +          },
        +          {
        +            "type": "null"
        +          }
        +        ],
        +        "default": null
        +      },
        +      "read_progress": {
        +        "anyOf": [
        +          {
        +            "type": "number"
        +          },
        +          {
        +            "type": "null"
        +          }
        +        ],
        +        "default": null
        +      },
        +      "reading_time_minutes": {
        +        "anyOf": [
        +          {
        +            "type": "integer"
        +          },
        +          {
        +            "type": "null"
        +          }
        +        ],
        +        "default": null
        +      },
        +      "saved_at": {
        +        "anyOf": [
        +          {
        +            "type": "string"
        +          },
        +          {
        +            "type": "null"
        +          }
        +        ],
        +        "default": null
        +      },
        +      "substack_post_id": {
        +        "anyOf": [
        +          {
        +            "type": "string"
        +          },
        +          {
        +            "type": "null"
        +          }
        +        ],
        +        "default": null
        +      },
        +      "title": {
        +        "type": "string"
        +      },
        +      "unsaved_at": {
        +        "anyOf": [
        +          {
        +            "type": "string"
        +          },
        +          {
        +            "type": "null"
        +          }
        +        ],
        +        "default": null
        +      },
        +      "updated_at": {
        +        "anyOf": [
        +          {
        +            "type": "string"
        +          },
        +          {
        +            "type": "null"
        +          }
        +        ],
        +        "default": null
        +      },
        +      "url": {
        +        "type": "string"
        +      },
        +      "word_count": {
        +        "anyOf": [
        +          {
        +            "type": "integer"
        +          },
        +          {
        +            "type": "null"
        +          }
        +        ],
        +        "default": null
        +      }
        +    },
        +    "required": [
        +      "url",
        +      "title",
        +      "publication_name",
        +      "is_fully_read",
        +      "minutes_remaining"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
    • Changedlist_saved_posts7 fields changed
      • addedInput schema / properties / read_state
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null
        +}
      • addedOutput schema / properties / result / items / properties / is_fully_read
        Added value: +{
        +  "readOnly": true,
        +  "type": "boolean"
        +}
      • addedOutput schema / properties / result / items / properties / is_viewed
        Added value: +{
        +  "default": 0,
        +  "type": "integer"
        +}
      • addedOutput schema / properties / result / items / properties / max_read_progress
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "number"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null
        +}
      • addedOutput schema / properties / result / items / properties / minutes_remaining
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "integer"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "readOnly": true
        +}
      • addedOutput schema / properties / result / items / properties / read_progress
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "number"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null
        +}
      • changedOutput schema / properties / result / items / required
        Previous value: -[
        -  "url",
        -  "title",
        -  "publication_name"
        -]New value: +[
        +  "url",
        +  "title",
        +  "publication_name",
        +  "is_fully_read",
        +  "minutes_remaining"
        +]
    • Changedsaved_posts_status4 fields changed
      • addedOutput schema / properties / minutes_remaining_total
        Added value: +{
        +  "default": 0,
        +  "type": "integer"
        +}
      • addedOutput schema / properties / posts_fully_read
        Added value: +{
        +  "default": 0,
        +  "type": "integer"
        +}
      • addedOutput schema / properties / posts_in_progress
        Added value: +{
        +  "default": 0,
        +  "type": "integer"
        +}
      • addedOutput schema / properties / posts_unread
        Added value: +{
        +  "default": 0,
        +  "type": "integer"
        +}
    • Changedsearch_saved_posts7 fields changed
      • addedInput schema / properties / read_state
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null
        +}
      • addedOutput schema / properties / result / items / properties / is_fully_read
        Added value: +{
        +  "readOnly": true,
        +  "type": "boolean"
        +}
      • addedOutput schema / properties / result / items / properties / is_viewed
        Added value: +{
        +  "default": 0,
        +  "type": "integer"
        +}
      • addedOutput schema / properties / result / items / properties / max_read_progress
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "number"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null
        +}
      • addedOutput schema / properties / result / items / properties / minutes_remaining
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "integer"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "readOnly": true
        +}
      • addedOutput schema / properties / result / items / properties / read_progress
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "number"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null
        +}
      • changedOutput schema / properties / result / items / required
        Previous value: -[
        -  "url",
        -  "title",
        -  "publication_name"
        -]New value: +[
        +  "url",
        +  "title",
        +  "publication_name",
        +  "is_fully_read",
        +  "minutes_remaining"
        +]
  2. 9 tool updatesv0.3.0
    • Addedget_note_content
    • Addedget_saved_note
    • Addedlist_saved_notes
    • Addedsave_note
    • Changedsaved_posts_status4 fields changed
      • addedOutput schema / properties / last_note_sync_status
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null
        +}
      • addedOutput schema / properties / last_successful_note_sync
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "default": null
        +}
      • addedOutput schema / properties / total_saved_notes
        Added value: +{
        +  "default": 0,
        +  "type": "integer"
        +}
      • addedOutput schema / properties / total_unsaved_notes
        Added value: +{
        +  "default": 0,
        +  "type": "integer"
        +}
    • Addedsearch_saved_notes
    • Addedsync_saved_notes
    • Changedsync_saved_posts1 field changed
      • addedOutput schema / properties / entity
        Added value: +{
        +  "default": "post",
        +  "type": "string"
        +}
    • Addedunsave_note
  3. 10 tool updatesv0.1.0
    • First observedget_post_content
    • First observedget_saved_post
    • First observedlist_audiences
    • First observedlist_publications
    • First observedlist_saved_posts
    • First observedsave_post
    • First observedsaved_posts_status
    • First observedsearch_saved_posts
    • First observedsync_saved_posts
    • First observedunsave_post

TDQS

A3.6/5.0
Disambiguation3/5

Tools are generally distinct in action (list, get, search, save, unsave, sync), but overlap exists between similar operations for posts and notes. Names like get_saved_post vs get_post_content may confuse agents without reading descriptions.

Naming Consistency2/5

Inconsistent use of 'saved' prefix (e.g., get_saved_post vs get_post_content), and lack of parallel structures like saved_notes_status. Mixed verb patterns and inconsistent inclusion of 'saved' in names reduce predictability.

Tool Count4/5

17 tools is on the higher end but justified by supporting two content types (posts and notes) with duplicate operations. Still slightly above typical range, but not excessive for the feature set.

Completeness5/5

The toolset covers listing, searching, retrieving, saving, unsaving, syncing, and auxiliary metadata (publications, audiences). No obvious missing operations for the bookmarking domain.

Maintenance

ActivityMaintained
ResponsivenessNo issues

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/toniher/substack-saved-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server