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
| 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
readMCP 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
| Name | Type | Description |
|---|---|---|
| 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
readMCP 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
| Name | Type | Description |
|---|---|---|
| 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
} Get post stats
readMCP 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
| Name | Type | Description |
|---|---|---|
| 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
| Name | Type | Description |
|---|---|---|
| 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
} List subscribers
readMCP 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
| Name | Type | Description |
|---|---|---|
| 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
} Export subscribers
readMCP 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
} List comments
readMCP 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
| Name | Type | Description |
|---|---|---|
| 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
writeMCP 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
| Name | Type | Description |
|---|---|---|
| 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
writeMCP 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
| Name | Type | Description |
|---|---|---|
| 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
} 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
| Name | Type | Description |
|---|---|---|
| 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
} 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
| Name | Type | Description |
|---|---|---|
| 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
} 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
| Name | Type | Description |
|---|---|---|
| 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
} 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
| Name | Type | Description |
|---|---|---|
| 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
} 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
| Name | Type | Description |
|---|---|---|
| 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
}