Skip to main content
Glama
substack-saved-mcp

substack-saved-mcp

by toniher

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
SUBSTACK_SAVED_DB_PATHNoCustom path to the SQLite database file
SUBSTACK_SAVED_DATA_DIRNoCustom data directory for the database

Instructions

Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.

This server publishes no instructions, or was last inspected before Glama recorded them.

Capabilities

Features and capabilities supported by this server

Protocol revision2025-11-25

CapabilityDetails
tools
{
  "listChanged": true
}
logging
{}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}
extensions
{
  "io.modelcontextprotocol/ui": {}
}
experimental
{}

Tools

Functions exposed to the LLM to take actions

NameDescription
search_saved_postsA

Perform full-text FTS5 search across cached saved posts.

history=True searches the reading history instead: posts viewed on Substack (seen_at set), saved or not (is_saved tells which).

Searches title, excerpt, publication name, author, and content text (content is only present for posts fetched via get_post_content). query uses SQLite FTS5 syntax; a malformed query falls back to a substring match. Date filters take ISO 8601 strings (e.g. "2026-01-31"). 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', 'unfinished' (unread + in_progress), or 'started' — a post is 'finished' once its high-water reading progress crosses a threshold, default 0.95).

list_saved_postsA

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

history=True lists the reading history instead: posts viewed on Substack (seen_at set), saved or not (is_saved tells which), newest view first. sort_by can be 'saved_at' (default), 'published_at', 'read_progress', 'minutes_remaining', or 'seen_at' (default with history=True). 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', 'unfinished' (unread + in_progress), or 'started' — a post is 'finished' once its high-water reading progress crosses a threshold, default 0.95.

get_saved_postA

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

An all-digit value is always treated as the local ID, so a numeric Substack post ID will not match; pass the post URL instead. content_text is empty until get_post_content has fetched the post.

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). Confirmation comes from Substack's save API (keyed by the post's numeric ID, read from the page) or, failing that, from a best-effort bookmark-button click whose rendered state is checked. 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.

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.

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.

list_publicationsA

List all publications in local cache with post counts.

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.

saved_posts_statusA

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

sync_saved_postsA

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

Incremental (default) stops early once it reaches already-cached posts, so older posts' reading progress isn't refreshed. force=True fetches the whole list, refreshes every post, and soft-deletes local posts no longer saved on Substack. Returns a SyncRun whose status is 'success', 'partial' (the fetch was truncated, e.g. by rate limiting, and reconciliation was skipped), 'auth_required' (run 'substack-saved-mcp login'), or 'failed'. Requires an active authenticated Substack session and can take minutes on large accounts.

sync_reading_historyA

Sync the reading history (posts viewed on Substack, saved or not) into the local cache.

Fills seen_at and reading progress; never changes a cached post's saved state. Unsaved viewed posts are stored with is_saved=0 and only show up via history=True on list_saved_posts/search_saved_posts. force=True walks the whole history instead of stopping at already-cached views. Status is 'success', 'partial', 'auth_required', or 'failed'.

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. query uses SQLite FTS5 syntax. Allows filtering by author (name or handle) and original note date (posted_after/posted_before, ISO 8601 strings). Substack exposes no bookmark time for notes, so saved_at is always None and saved_after/saved_before match nothing.

list_saved_notesA

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

sort_by can be 'posted_at' (when the note was posted) or 'saved_at' (always None for notes, so it gives no meaningful order). restacks_only limits results to notes that restack a post.

get_saved_noteA

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

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.

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.

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.

sync_saved_notesA

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

Incremental (default) stops early once it reaches notes already saved locally. force=True fetches the whole list and soft-deletes local notes no longer saved on Substack. Returns a SyncRun whose status is 'success', 'partial' (the fetch was truncated, e.g. by rate limiting, and reconciliation was skipped), 'auth_required' (run 'substack-saved-mcp login'), or 'failed'. Requires an active authenticated Substack session.

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription
get_publications_resourceResource returning JSON list of cached Substack publications.

TDQS

A4/5.0

Scored across 18 tools

Disambiguation4/5

The tool set is mostly clearly split by resource (posts vs. notes) and action (save, unsave, list, search, sync, content). The main ambiguity is between content-fetching tools like get_post_content/get_note_content and cached-detail tools like get_saved_post/get_saved_note, which overlap somewhat in returning note/post bodies. The history flag on list/search saved posts adds a secondary mode but remains distinguishable from sync_reading_history.

Naming Consistency4/5

Most tools follow a predictable snake_case verb_noun pattern: save_post, unsave_note, list_saved_posts, search_saved_notes, sync_reading_history. Minor deviations like saved_posts_status and the get_*_content vs get_saved_* distinction keep it from being perfectly uniform, but the convention is still easy to follow.

Tool Count4/5

18 tools is slightly on the heavy side, but the server manages two related resource types (posts and notes) plus sync, search, listing, content retrieval, and status operations. Each tool maps to a fairly specific operation, so the count is reasonable rather than padded.

Completeness5/5

The surface covers the full saved-item lifecycle for both posts and notes: save, unsave, get cached details, fetch content, list, search, and sync. It also includes publication/audience discovery, cache/sync status, and reading-history sync, leaving no obvious dead ends for the stated purpose.

Maintenance

ActivityMaintained
ResponsivenessNo recent community issues