MCP tools reference

Substack MCP tools

14 tools for Substack on the Cotlet MCP server. Your agent picks them from tools/list.

MCP server
https://cotlet.dev/mcp/v1
Connects with
A Substack account that can publish to the publication
Integration page
Substack
Substack Endpoints
Endpoint Access Description
substack_list_posts read Posts of your publication, newest first, with status and audience.
substack_get_post read Full post: title, subtitle, body as Markdown, audience, URL and dates.
substack_get_post_stats read Recipients, opens, open rate, views, likes, restacks and subscribers gained.
substack_get_publication_stats read Total, paid, new and churned subscribers and revenue for a period.
substack_list_subscribers read Subscribers with plan, start date and activity rating.
substack_export_subscribers read The full subscriber list as a CSV download link, valid for 1 hour.
substack_list_comments read Comments on a post, with author, text and whether you replied.
substack_create_draft write New draft from Markdown. Nothing is sent until you publish or schedule it.
substack_update_draft write Change the title, subtitle, body or audience of a draft.
substack_schedule_post write Publish a draft at a set time, on the web and by email.
substack_publish_post write Publish a draft now.
substack_publish_note write Short post in Substack Notes, now or at a set time, optionally with a link card to one of your posts.
substack_reply_to_comment write Reply as you under a comment on your post.
substack_add_subscriber write Add an email as a free subscriber or give a complimentary paid plan.

List posts

read

MCP tool substack_list_posts

Lists posts of your publication, newest first. Filter by status to see drafts or scheduled posts. Scheduled posts include their publish time. Pages hold up to 50 posts; pass next_cursor back as cursor for the next page.

Parameters

Parameters: list_posts
NameTypeDescription
status enum published, draft or scheduled. Default: published.
limit integer 1 to 50. Default: 20.
cursor string Next page cursor from the previous call.

tools/call

JSON-RPC

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "substack_list_posts",
    "arguments": {}
  }
}

Example response

{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "posts": [
      {
        "post_id": "148213977",
        "title": "The quiet launch",
        "status": "published",
        "audience": "everyone",
        "published_at": "2026-10-06T12:00:00Z",
        "url": "https://yourname.substack.com/p/the-quiet-launch"
      },
      {
        "post_id": "148420031",
        "title": "Notes on pricing",
        "status": "draft",
        "audience": "only_paid",
        "published_at": null,
        "url": null
      }
    ],
    "next_cursor": "eyJvZmZzZXQiOjIwfQ"
  },
  "isError": false
}

Get post

read

MCP tool substack_get_post

Returns one post with its full body as Markdown, including the paywalled part of your own posts. Use it to edit a published post or to quote it in a Note. Images come back as URLs.

Parameters

Parameters: get_post
NameTypeDescription
post_id required string Post ID from list_posts.

tools/call

JSON-RPC

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "substack_get_post",
    "arguments": {
      "post_id": "post_8c41d2"
    }
  }
}

Example response

{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "post_id": "148213977",
    "title": "The quiet launch",
    "subtitle": "What waiting taught me",
    "body_markdown": "Last month I shipped without telling anyone...",
    "audience": "everyone",
    "status": "published",
    "published_at": "2026-10-06T12:00:00Z",
    "url": "https://yourname.substack.com/p/the-quiet-launch"
  },
  "isError": false
}

MCP tool substack_get_post_stats

Returns the engagement numbers of one published post, the same as its stats page in the dashboard. Email numbers cover the send; views include the web. Numbers keep growing for days after a send, so check again later.

Parameters

Parameters: get_post_stats
NameTypeDescription
post_id required string Post ID from list_posts.

tools/call

JSON-RPC

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "substack_get_post_stats",
    "arguments": {
      "post_id": "post_8c41d2"
    }
  }
}

Example response

{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "post_id": "148213977",
    "recipients": 4120,
    "opens": 2011,
    "open_rate": 0.488,
    "views": 3560,
    "likes": 142,
    "restacks": 18,
    "subscribers_gained": 37
  },
  "isError": false
}

MCP tool substack_get_publication_stats

Returns subscriber and revenue numbers for a period, the same figures as the publisher dashboard: total, paid and new subscribers, churned subscribers and revenue. Periods are 7, 30 or 90 days.

Parameters

Parameters: get_publication_stats
NameTypeDescription
period enum 7d, 30d or 90d. Default: 30d.

tools/call

JSON-RPC

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "substack_get_publication_stats",
    "arguments": {
      "period": "7d"
    }
  }
}

Example response

{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "period": "7d",
    "subscribers_total": 4380,
    "subscribers_paid": 412,
    "subscribers_new": 96,
    "subscribers_churned": 11,
    "revenue_usd": 3150
  },
  "isError": false
}

MCP tool substack_list_subscribers

Lists subscribers, newest first. Filter by plan or by the date they joined. Activity rating is the 0 to 5 score Substack shows in the subscriber dashboard. Pages hold up to 500 subscribers; pass next_cursor back as cursor.

Parameters

Parameters: list_subscribers
NameTypeDescription
plan enum all, free, paid, comp or founding. Default: all.
since date Only subscribers who joined on or after this date.
limit integer 1 to 500. Default: 100.
cursor string Next page cursor from the previous call.

tools/call

JSON-RPC

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "substack_list_subscribers",
    "arguments": {}
  }
}

Example response

{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "subscribers": [
      {
        "email": "maya@example.com",
        "plan": "free",
        "joined_at": "2026-10-04T09:12:00Z",
        "activity_rating": 4
      },
      {
        "email": "jon@example.com",
        "plan": "paid",
        "joined_at": "2026-10-02T17:40:00Z",
        "activity_rating": 5
      }
    ],
    "next_cursor": null
  },
  "isError": false
}

MCP tool substack_export_subscribers

Builds the same CSV as Export in the subscriber dashboard and returns a link to it. Use it for the full list; list_subscribers is better for filtered pages. The link stops working after 1 hour.

Parameters

No parameters.

tools/call

JSON-RPC

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "substack_export_subscribers",
    "arguments": {}
  }
}

Example response

{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "download_url": "https://files.cotlet.example/exports/subscribers-2026-10-11.csv",
    "rows": 4380,
    "expires_at": "2026-10-11T13:00:00Z"
  },
  "isError": false
}

MCP tool substack_list_comments

Returns the comments of one post, oldest first, with replies nested under their parent. Set unanswered_only to skip threads you already replied to. Pass comment_id from an item to reply_to_comment.

Parameters

Parameters: list_comments
NameTypeDescription
post_id required string Post ID from list_posts.
unanswered_only boolean Skip threads you already replied to.

tools/call

JSON-RPC

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "substack_list_comments",
    "arguments": {
      "post_id": "post_8c41d2"
    }
  }
}

Example response

{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "comments": [
      {
        "comment_id": "c_90417",
        "author": "Maya Chen",
        "body": "Does the next part cover pricing?",
        "created_at": "2026-10-07T08:20:00Z",
        "replied": false,
        "replies": []
      }
    ]
  },
  "isError": false
}

Create draft

write

MCP tool substack_create_draft

Creates a draft in your publication and returns its post_id. The draft is visible only to your team. Sending is a separate step: schedule_post or publish_post. Creating a draft reaches no one and update_draft or the Substack editor changes it, so it runs without asking.

Parameters

Parameters: create_draft
NameTypeDescription
title required string Post title.
body_markdown required string Post body. Images by URL.
subtitle string Shown under the title and in the email preview.
audience enum everyone, only_paid or founding. Default: everyone.

tools/call

JSON-RPC

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "substack_create_draft",
    "arguments": {
      "title": "title",
      "body_markdown": "body markdown"
    }
  }
}

Example response

{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "post_id": "148420031",
    "status": "draft",
    "audience": "everyone",
    "edit_url": "https://yourname.substack.com/publish/post/148420031"
  },
  "isError": false
}

Update draft

write

MCP tool substack_update_draft

Changes fields of a draft and leaves the others as they are. It works on drafts only; a published post is not changed. Nothing reaches your subscribers, so it runs without asking.

Parameters

Parameters: update_draft
NameTypeDescription
post_id required string Post ID from list_posts.
fields required object Any of title, subtitle, body_markdown, audience.

tools/call

JSON-RPC

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "substack_update_draft",
    "arguments": {
      "post_id": "post_8c41d2",
      "fields": "fields"
    }
  }
}

Example response

{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "post_id": "148420031",
    "status": "draft",
    "updated": [
      "title",
      "body_markdown"
    ]
  },
  "isError": false
}

Schedule post

write Confirm

MCP tool substack_schedule_post

Schedules a draft to go live at the time you set, on the web and, unless send_email is false, by email to your subscribers. The time must be in the future. A scheduled post stays editable until it goes out. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: schedule_post
NameTypeDescription
post_id required string Post ID from list_posts.
publish_at required datetime ISO 8601 with time zone.
send_email boolean Email it to subscribers. Default: true.

tools/call

JSON-RPC

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "substack_schedule_post",
    "arguments": {
      "post_id": "148420031",
      "publish_at": "2026-10-13T08:00:00-04:00",
      "send_email": true
    }
  }
}

Example response

{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "post_id": "148420031",
    "status": "scheduled",
    "publish_at": "2026-10-13T08:00:00-04:00",
    "audience": "everyone",
    "url": "https://yourname.substack.com/p/notes-on-pricing"
  },
  "isError": false
}

Publish post

write Confirm

MCP tool substack_publish_post

Publishes a draft immediately, on the web and, unless send_email is false, by email to your subscribers. A sent email cannot be recalled. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: publish_post
NameTypeDescription
post_id required string Post ID from list_posts.
send_email boolean Email it to subscribers. Default: true.

tools/call

JSON-RPC

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "substack_publish_post",
    "arguments": {
      "post_id": "post_8c41d2"
    }
  }
}

Example response

{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "post_id": "148420031",
    "status": "published",
    "published_at": "2026-10-11T10:02:00Z",
    "emailed": true,
    "url": "https://yourname.substack.com/p/notes-on-pricing"
  },
  "isError": false
}

Publish Note

write Confirm

MCP tool substack_publish_note

Posts a Note under your name. Without publish_at it goes out now; with it, the Note waits in your scheduled Notes and goes out at that time. Text is up to 1,000 characters. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: publish_note
NameTypeDescription
body required string Note text, up to 1,000 characters.
link_post_id string Attach one of your posts as a card.
publish_at datetime ISO 8601 with time zone. Omit to post now.

tools/call

JSON-RPC

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "substack_publish_note",
    "arguments": {
      "body": "body"
    }
  }
}

Example response

{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "note_id": "n_5521098",
    "status": "published",
    "published_at": "2026-10-11T10:05:00Z",
    "url": "https://substack.com/@yourname/note/c-5521098"
  },
  "isError": false
}

Reply to comment

write Confirm

MCP tool substack_reply_to_comment

Posts a reply under one comment, under your name, visible to everyone who can read the post. The author gets Substack's usual reply notification. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: reply_to_comment
NameTypeDescription
comment_id required string Comment ID from list_comments.
body required string Reply text.

tools/call

JSON-RPC

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "substack_reply_to_comment",
    "arguments": {
      "comment_id": "c_90417",
      "body": "Thank you, Maya. Pricing is the whole of part two, out on Thursday."
    }
  }
}

Example response

{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "comment_id": "c_90432",
    "parent_id": "c_90417",
    "created_at": "2026-10-11T10:07:00Z"
  },
  "isError": false
}

Add subscriber

write Confirm

MCP tool substack_add_subscriber

Adds one email to your list. Substack sends the new subscriber its usual welcome email. With plan set to comp the person gets a complimentary paid plan until comp_until; for someone who already pays, the time is added after their current billing cycle. Add only people who agreed to hear from you. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: add_subscriber
NameTypeDescription
email required string Subscriber email.
plan enum free or comp. Default: free.
comp_until date End of the complimentary plan.

tools/call

JSON-RPC

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "substack_add_subscriber",
    "arguments": {
      "email": "jane@example.com"
    }
  }
}

Example response

{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "email": "maya@example.com",
    "plan": "free",
    "status": "added",
    "joined_at": "2026-10-11T10:09:00Z"
  },
  "isError": false
}