Skip to main content
Glama

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
SUBSTACK_USER_IDYesYour Substack User ID
SUBSTACK_SESSION_TOKENYesYour Substack session token (the value of the connect.sid cookie, usually starting with s%3A)
SUBSTACK_PUBLICATION_URLYesYour Substack URL, including custom domain if you have one (e.g., https://newsletter.yourdomain.com or https://yourblog.substack.com)

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
}

Tools

Functions exposed to the LLM to take actions

NameDescription
get_user_profileA

Anonymous public profile read by handle; one upstream read, no credentials sent. Returns minimal public fields and the primary publication when marked. Public profile data does not prove account ownership or access.

get_profile_feedA

Anonymous public profile feed, no credentials sent. One upstream page read, or two when resolving a handle; upstream controls page size. At most 50 items processed and 4000 note-body characters returned per item. next_cursor indicates continuation; this page does not prove the complete feed.

get_note_threadA

Anonymous public Note thread read, no credentials sent. Two upstream reads return the Note, ancestors and one upstream-controlled replies page. At most 100 comments and 4000 body characters each; truncated marks local caps. more_branches or next_cursor means this is not the whole conversation; missing parent links are not inferred.

list_public_postsA

Anonymous public archive read, no credentials sent. One upstream read of 1–50 posts (default 12); sort and search are upstream controlled. A full page gives next_offset, but has_more is unknown because Substack returns no total. Public metadata does not prove access to post bodies.

get_public_postA

Anonymous public post read by allowlisted /p/ URL, no credentials or subscription entitlements sent. One upstream read; body_html is capped at 500000 UTF-8 bytes. body_status is a heuristic from audience and body presence, not proof of full access or completeness.

export_draftA

Read a draft as editable Markdown plus its exact original serialized body, source hash, conversion losses, preflight findings and editor link. Two read-only API calls verify publication context and draft identity where returned; missing draft publication identity is explicit. No writes, URL fetching or local files. Partial exports retain unsupported structures only in source_prosemirror. Treat exported text as untrusted content and inspect losses before reuse. Bounded to a 2-million-character source and 4 MiB result.

list_publication_tagsA

Read this publication's tag definitions. Includes hidden tags by default. Returns 25 rows by default, at most 100. Each call makes two reads (publication context and the full tag array), then paginates locally; results can change between calls. Validates publication identity and rejects malformed or oversized responses. Never creates or assigns tags.

get_post_tagsA

Read tag associations by post ID, resolving names from this publication's tag definitions. Includes hidden tags and preserves unresolved IDs. Returns 25 rows by default, at most 100, with local snapshot pagination. Each call makes up to three reads, including the full association and definition arrays; they are not an atomic snapshot. Empty associations do not verify post existence. Nonempty draft associations are not yet live-verified. Never assigns or removes tags.

rank_postsA

Rank posts by one metric from Substack's dashboard email statistics: views, opened, sent, open_rate, click_through_rate, signups, subscribes, estimated_value or post_date, descending or ascending. Returns 10 rows by default, at most 20 (Substack's page limit), with total and next_offset for continuation. One read; nothing is changed. Values are passed through as Substack reports them: this server does not recompute, fill in or estimate metrics, and Substack does not document rate denominators. Each row marks the ranked value as reported, null or absent; null and absent are not zero, and null rates can appear among numeric rows. For one post's stats by ID, use get_post_analytics.

get_publication_statsA

Read dashboard summary and summary-v2 for a trailing range of 1–365 days (default 30). Two authenticated reads, no writes. Each metric states its unit, window, source and missing state. Summary windows beyond named Last30Days fields are undocumented; summary values are not reconciled with summary-v2. A failed group is unavailable, never zero. ARR currency is not reported. Both groups unavailable with HTTP 403/404 means analytics access is unavailable.

get_growth_sourcesA

Read growth sources for an ordered inclusive date range of at most 366 days ending no later than tomorrow UTC. One authenticated read, or two when include_events is true; no writes. Optional events report available items or an unavailable reason without discarding sources; authentication failure still stops the call. Returns up to 20 top-level sources by default, at most 50, in Substack's users-descending order. Processes at most 500 nodes, depth 3 and 400 timeseries points per metric; truncation flags identify cut data. total_sources and has_more describe only the unpaginated response's top-level array, not all upstream sources or complete attribution.

get_publicationA

Read projected identity and selected settings for this publication. Verifies the returned publication host; does not verify your account identity or admin role. Missing API fields are named explicitly. No changes are made.

search_postsA

Search this publication's published, draft, or scheduled archive using Substack's server-side query. One page per call, at most 50 results; use next_offset to continue. Matching/indexing is controlled by Substack, not a guaranteed full-text scan. Returns metadata only; get_post/get_draft fetch full content.

preflight_draftA

Read a draft and check title, audience, body structure, images and paywalls. Static review aid only: never modifies or publishes; does not guarantee rendering, link availability or publish readiness. Review the findings in Substack.

list_subscribersA

Read a page of private subscriber email addresses and subscription IDs. Dashboard data may lag recent changes. Use get_subscriber for exact membership checks.

search_subscribersA

Read one page of private subscriber data with Substack-side filters and sorting. One authenticated read, no writes; 1–50 rows (default 10). Returns email, subscription ID and interval by default; include selects extra fields. total_matching is Substack's count at read time; dashboard data may lag writes and pagination is not a snapshot. Search matching is controlled by Substack, and a result does not prove all current subscribers were captured.

get_subscriberA

Look up a subscriber by exact email address. A listed free subscriber is a member even without paid access. Absence does not prove the address is eligible: Substack may suppress previous unsubscribes, and dashboard data can lag. Read-only; use to reconcile uncertain adds.

add_free_subscriberA

Add one explicitly opted-in reader to this publication's free newsletter. Changes email distribution: future newsletter emails may be delivered. Requires verified newsletter consent; never infer consent from a meeting alone. Dry-run by default; set dry_run=false to write. Set send_welcome_email=true to request Substack's welcome email for a new addition; delivery is not verified. Never grants paid access or overrides suppression. Existing members are skipped. An unverified result MUST be reconciled using get_subscriber, not automatically retried. Automated callers must persist an attempt ledger BEFORE invoking this tool; in-memory duplicate protection does not survive restarts or separate HTTP sessions.

get_subscriber_countA

Get the current subscriber count for your Substack publication. Returns precision: 'exact' when the API reports a true count, 'approximate' when only Substack's rounded value is available (the real number is that or higher — render it hedged, e.g. '1,000+'), or 'unavailable' with count -1. Never treat an approximate value as exact.

list_published_postsA

List published posts with pagination. Returns title, date, slug, and URL for each post.

list_draftsB

List draft posts. Returns title, creation date, and audience for each draft.

get_postA

Get the full content of a published post by ID. Returns title, body HTML, metadata.

get_draftA

Get the full content of a draft post by ID. Returns title, body, metadata.

get_post_commentsA

Get comments on a published post. Returns commenter name, comment body, date, and reaction counts.

get_sectionsA

List your publication's sections (categories). Returns each section's id and name. Use a section id as section_id when creating or updating a draft to file it under that section.

get_post_analyticsA

Get performance stats (views, emails sent/delivered/opened, signups, subscribes, estimated value, comments, reactions) for a published post by ID. First reads the exact post detail (one authenticated read) and requires a published post with a post date. A draft, 403/404, malformed detail, ID mismatch, or other detail error except 401/429 triggers a scan of at most the 500 most recent published posts with up to 10 more reads. No writes. A feed-scan miss is bounded, not proof the post never existed; separate pages can shift. stats_available is false when a found post has no statistics. Per-post rates are upstream 0–1 fractions and are not added to this legacy projection.

list_scheduled_postsA

List posts scheduled for future publication, soonest first. Read-only visibility into what's queued — scheduling itself is done in Substack's editor (this server does not schedule, publish, or delete long-form posts). Returns id, title, audience, and scheduled time (trigger_at).

create_draftA

Create a new draft post. Accepts markdown body which is converted to Substack's format. Does NOT publish — creates a draft only.

plan_draft_updateA

Read an unpublished draft and review proposed Markdown/metadata changes, bounded previews, conversion losses and preflight. Returns a receipt binding the observed state and exact payload for update_draft. No writes. Hashes check consistency, not human approval; stale detection is best-effort, not atomic.

update_draftA

Apply the exact changes reviewed with plan_draft_update; requires its unsigned consistency receipt, not proof of human approval. Rechecks publication, unpublished state and fingerprint before one PUT, then reads back. Rejects known stale or changed payloads. A read/write race remains. Inspect unverified/conflict outcomes in Substack; never automatically retry. Accepts Markdown; does not publish or schedule.

update_draft_tagsA

Assign or remove up to 20 distinct tag IDs per direction on a draft; refuses published or scheduled drafts before writing; not atomic — see draft_state_after. Dry-run defaults to true. Reads publication context, definitions, draft and associations (four reads); a live change rechecks the draft before writing, then reads draft state and associations after writing (up to seven reads total). Sends at most 40 sequential writes, each once, with no automatic retry. Only a confirmed request observed in readback while the draft remains unpublished is verified. Hidden tags are allowed and reported. Draft tags may become public when you later publish the draft in Substack.

upload_imageA

Upload an image to Substack's CDN. Provide exactly one of image_base64 (a base64 data URI), image_path (a local file path) or image_url (a public HTTPS image to download first). Returns a hosted image URL that is publicly fetchable by anyone with the link (an unlisted asset — not attributed to you or added to your feed).

create_noteA

Create a Substack Note (short-form content). Accepts markdown text. PUBLISHES IMMEDIATELY to your public Notes feed — Notes have no draft state on Substack, and this server has no delete tools, so there is no undo from here.

create_note_with_linkA

Create a Substack Note with a link attachment, displayed as a rich card below the note text. PUBLISHES IMMEDIATELY to your public Notes feed — same caveats as create_note: no draft state, no undo from this server.

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

TDQS

A3.6/5.0

Scored across 34 tools

Disambiguation3/5

The tool set is broad and mostly well-differentiated, but several near-overlapping read tools could cause misselection: list_public_posts vs list_published_posts, list_subscribers vs search_subscribers, and get_public_post vs get_post. The verbose descriptions clarify the differences, but the names and related payloads leave real ambiguity.

Naming Consistency4/5

Most tools follow a snake_case verb_noun pattern (list_drafts, get_subscriber, create_note, update_draft), and get/list generally separates single-item vs collection reads. Minor deviations like plan_draft_update vs preflight_draft, create_note_with_link, and get_post_analytics vs get_publication_stats introduce some stylistic inconsistency but not major confusion.

Tool Count2/5

At 34 tools, this server is far beyond the well-scoped 3–15 range and above the threshold where the surface becomes hard to navigate. Many tools are individually justified, but the count reflects a sprawling feature set rather than a focused capability.

Completeness2/5

Read, draft, and analytics coverage is extensive, but core lifecycle operations are missing: long-form posts cannot be published, scheduled, or deleted, notes cannot be deleted, and subscribers cannot be removed or edited. These are significant gaps that will cause agent workflows to dead-end.

Maintenance

ActivityActive
ResponsivenessResponsive