Sign In

@conorbronsdon/substack-mcp

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@conorbronsdon/substack-mcp - npm Package Compare versions

Comparing version
0.2.2
to
0.3.0
+40
CLAUDE.md
# substack-mcp
MCP server for Substack — read posts, manage drafts, create notes. Cannot publish or delete posts by design; notes (short-form) publish immediately since Substack has no note-draft state.
## Architecture
- `src/index.ts` — MCP server bootstrap and entry point
- `src/server.ts` — Tool registration, request handlers, Zod schema generation
- `src/annotations.ts` — Tool side-effect classification (read / additive-write / publish) → MCP annotations
- `src/api/client.ts` — HTTP client for Substack API (session cookie auth)
- `src/api/types.ts` — TypeScript interfaces for API responses
- `src/utils/errors.ts` — Error handling utilities
- `src/utils/markdown-to-prosemirror.ts` — Markdown to ProseMirror AST converter (Substack editor format)
- `src/__tests__/` — Vitest tests for client, errors, and markdown conversion
## Key constraints
- Posts are read/draft only — no publish or delete capabilities by design
- Notes publish immediately via `create_note` / `create_note_with_link` — Substack has no note-draft state, so there is no preview step
- Auth sends both `connect.sid` and `substack.sid` cookies set to the same session token (custom domains use `connect.sid`, substack.com uses `substack.sid`)
- Markdown must be converted to ProseMirror format for Substack's editor
## Development
```bash
npm ci
npm run lint # tsc --noEmit (type-check)
npm run build # tsc (outputs to dist/)
npm test # vitest run
```
## Testing
4 test suites:
- `client.test.ts` — API client auth validation
- `errors.test.ts` — Error handling and wrapping
- `markdown-to-prosemirror.test.ts` — Markdown to ProseMirror AST conversion
- `annotations.test.ts` — Annotation mapping + completeness (every registered tool classified)
## Agent workflow
- Always work on a branch. Never push directly to main.
- Create PRs targeting main. CI must pass (build + test on Node 20 and 22). `npm run build` runs `tsc` and fails on type errors.
- Keep changes focused — one feature or fix per PR.
- Run `npm run lint` (fast no-emit type-check) and `npm test` locally before pushing.
import { describe, it, expect } from "vitest";
import { buildAnnotations, TOOL_KINDS } from "../annotations.js";
import { createServer } from "../server.js";
// The server only touches the client inside tool handlers, which these tests
// never invoke — a bare object is enough to register everything.
const server = createServer({});
// SDK-private registry; the McpServer API has no public tool introspection.
const registered = server._registeredTools;
const READ_TOOLS = [
"get_subscriber_count",
"list_published_posts",
"list_drafts",
"get_post",
"get_draft",
"get_post_comments",
];
const ADDITIVE_WRITE_TOOLS = [
"create_draft",
"update_draft",
"upload_image",
];
const PUBLISH_TOOLS = ["create_note", "create_note_with_link"];
describe("buildAnnotations mapping", () => {
it("read -> { readOnlyHint: true } and nothing else", () => {
const a = buildAnnotations("get_post");
expect(a).toEqual({ readOnlyHint: true });
});
it("additive write -> { readOnlyHint: false } with no destructive or open-world hint", () => {
const a = buildAnnotations("create_draft");
expect(a).toEqual({ readOnlyHint: false });
expect(a.destructiveHint).toBeUndefined();
expect(a.openWorldHint).toBeUndefined();
});
it("publish (Notes) -> { readOnlyHint: false, openWorldHint: true }", () => {
const a = buildAnnotations("create_note");
expect(a).toEqual({ readOnlyHint: false, openWorldHint: true });
});
});
describe("tool annotation classifications", () => {
it("completeness: registered tools and the classification registry match exactly", () => {
// Every registered tool must be classified, and every classified tool
// must exist — new tools cannot slip through unannotated, and stale
// registry entries cannot linger.
expect(Object.keys(registered).sort()).toEqual(Object.keys(TOOL_KINDS).sort());
});
it("every registered tool carries the annotations its classification dictates", () => {
for (const name of Object.keys(TOOL_KINDS)) {
expect(registered[name].annotations, `${name} annotations should match buildAnnotations`).toEqual(buildAnnotations(name));
}
});
it("read tools are readOnlyHint:true", () => {
for (const name of READ_TOOLS) {
expect(buildAnnotations(name).readOnlyHint, name).toBe(true);
}
});
it("additive writes are readOnlyHint:false with no destructive or open-world hint", () => {
for (const name of ADDITIVE_WRITE_TOOLS) {
const a = buildAnnotations(name);
expect(a.readOnlyHint, name).toBe(false);
expect(a.destructiveHint, name).toBeUndefined();
expect(a.openWorldHint, name).toBeUndefined();
}
});
it("Note tools (immediate public publish) carry openWorldHint:true", () => {
for (const name of PUBLISH_TOOLS) {
const a = buildAnnotations(name);
expect(a.readOnlyHint, name).toBe(false);
expect(a.openWorldHint, name).toBe(true);
}
});
it("no tool is destructive (this server has no deletes by design)", () => {
for (const name of Object.keys(TOOL_KINDS)) {
expect(buildAnnotations(name).destructiveHint, name).toBeUndefined();
}
});
it("Note tool descriptions say loudly that they publish immediately", () => {
for (const name of PUBLISH_TOOLS) {
expect(registered[name].description, name).toMatch(/publishes immediately/i);
}
});
it("the classification groups above cover the whole registry", () => {
const grouped = [...READ_TOOLS, ...ADDITIVE_WRITE_TOOLS, ...PUBLISH_TOOLS];
expect(grouped.sort()).toEqual(Object.keys(TOOL_KINDS).sort());
});
});
//# sourceMappingURL=annotations.test.js.map
{"version":3,"file":"annotations.test.js","sourceRoot":"","sources":["../../src/__tests__/annotations.test.ts"],"names":[],"mappings":"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"}
/**
* Tool side-effect classification → MCP tool annotations.
*
* Mirrors the gws-mcp-server pattern: every tool declares exactly one
* side-effect class in an exhaustive registry, and `buildAnnotations` maps
* that class to MCP annotation hints so clients can reason about side
* effects and render accurate consent UI. A completeness test asserts the
* registry matches the set of tools actually registered on the server, so
* new tools cannot ship unclassified.
*/
/** Side-effect classes for substack-mcp tools. */
export type ToolKind =
/** Pure read: no side effects on the user's Substack data. */
"read"
/**
* Additive write to PRIVATE state (drafts, CDN image uploads). Reversible
* in Substack's editor; nothing becomes public from these tools.
*/
| "additive-write"
/**
* Write with IMMEDIATE PUBLIC effect: Substack Notes publish the moment
* the tool runs. Notes have no draft state on Substack, and this server
* has no delete tools, so there is no undo from here.
*/
| "publish";
/**
* Exhaustive tool-name → kind registry.
*
* This server has NO destructive tools (no deletes) by design. If one is
* ever added, introduce a "destructive" kind mapping to
* `{ readOnlyHint: false, destructiveHint: true }` per the MCP spec.
*/
export declare const TOOL_KINDS: {
readonly get_subscriber_count: "read";
readonly list_published_posts: "read";
readonly list_drafts: "read";
readonly get_post: "read";
readonly get_draft: "read";
readonly get_post_comments: "read";
readonly create_draft: "additive-write";
readonly update_draft: "additive-write";
readonly upload_image: "additive-write";
readonly create_note: "publish";
readonly create_note_with_link: "publish";
};
export type ToolName = keyof typeof TOOL_KINDS;
/** MCP tool annotations derived from a tool's side-effect class. */
export interface ToolAnnotationHints {
readOnlyHint: boolean;
destructiveHint?: boolean;
openWorldHint?: boolean;
}
/**
* Map a tool's declared kind into MCP `annotations`.
*
* Every tool gets an explicit `readOnlyHint` (true for reads, false for any
* write) so clients always know the side-effect class. Additive writes carry
* `readOnlyHint: false` and no destructive hint (matching gws-mcp-server:
* they create or update reversible private state, never remove data). The
* Note tools additionally carry `openWorldHint: true` because they publish
* public content immediately — clients should not treat them as low-stakes
* writes.
*/
export declare function buildAnnotations(name: ToolName): ToolAnnotationHints;
/**
* Tool side-effect classification → MCP tool annotations.
*
* Mirrors the gws-mcp-server pattern: every tool declares exactly one
* side-effect class in an exhaustive registry, and `buildAnnotations` maps
* that class to MCP annotation hints so clients can reason about side
* effects and render accurate consent UI. A completeness test asserts the
* registry matches the set of tools actually registered on the server, so
* new tools cannot ship unclassified.
*/
/**
* Exhaustive tool-name → kind registry.
*
* This server has NO destructive tools (no deletes) by design. If one is
* ever added, introduce a "destructive" kind mapping to
* `{ readOnlyHint: false, destructiveHint: true }` per the MCP spec.
*/
export const TOOL_KINDS = {
// Reads
get_subscriber_count: "read",
list_published_posts: "read",
list_drafts: "read",
get_post: "read",
get_draft: "read",
get_post_comments: "read",
// Additive writes (private: drafts and uploads — nothing goes public)
create_draft: "additive-write",
update_draft: "additive-write",
upload_image: "additive-write",
// Immediate public publishes (Substack Notes)
create_note: "publish",
create_note_with_link: "publish",
};
/**
* Map a tool's declared kind into MCP `annotations`.
*
* Every tool gets an explicit `readOnlyHint` (true for reads, false for any
* write) so clients always know the side-effect class. Additive writes carry
* `readOnlyHint: false` and no destructive hint (matching gws-mcp-server:
* they create or update reversible private state, never remove data). The
* Note tools additionally carry `openWorldHint: true` because they publish
* public content immediately — clients should not treat them as low-stakes
* writes.
*/
export function buildAnnotations(name) {
switch (TOOL_KINDS[name]) {
case "read":
return { readOnlyHint: true };
case "additive-write":
return { readOnlyHint: false };
case "publish":
return { readOnlyHint: false, openWorldHint: true };
}
}
//# sourceMappingURL=annotations.js.map
{"version":3,"file":"annotations.js","sourceRoot":"","sources":["../src/annotations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAkBH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,QAAQ;IACR,oBAAoB,EAAE,MAAM;IAC5B,oBAAoB,EAAE,MAAM;IAC5B,WAAW,EAAE,MAAM;IACnB,QAAQ,EAAE,MAAM;IAChB,SAAS,EAAE,MAAM;IACjB,iBAAiB,EAAE,MAAM;IACzB,sEAAsE;IACtE,YAAY,EAAE,gBAAgB;IAC9B,YAAY,EAAE,gBAAgB;IAC9B,YAAY,EAAE,gBAAgB;IAC9B,8CAA8C;IAC9C,WAAW,EAAE,SAAS;IACtB,qBAAqB,EAAE,SAAS;CACW,CAAC;AAW9C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAc;IAC7C,QAAQ,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,KAAK,MAAM;YACT,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;QAChC,KAAK,gBAAgB;YACnB,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;QACjC,KAAK,SAAS;YACZ,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;IACxD,CAAC;AACH,CAAC"}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

+1
-1

@@ -19,3 +19,3 @@ import { SubstackAPIError, AuthenticationError } from "../utils/errors.js";

"Content-Type": "application/json",
"User-Agent": "substack-mcp/0.2.2",
"User-Agent": "substack-mcp/0.3.0",
...(options.headers || {}),

@@ -22,0 +22,0 @@ };

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { buildAnnotations } from "./annotations.js";
import { markdownToProseMirror, markdownToProseMirrorContent } from "./utils/markdown-to-prosemirror.js";

@@ -7,6 +8,14 @@ export function createServer(client) {

name: "substack-mcp",
version: "0.2.2",
version: "0.3.0",
});
// Every tool is registered with MCP annotations derived from its declared
// side-effect class (see annotations.ts) so clients can render accurate
// consent UI. Reads are readOnlyHint:true; draft/upload writes are
// additive; the Note tools publish public content immediately.
// --- Read tools ---
server.tool("get_subscriber_count", "Get the current subscriber count for your Substack publication", {}, async () => {
server.registerTool("get_subscriber_count", {
description: "Get the current subscriber count for your Substack publication",
inputSchema: {},
annotations: buildAnnotations("get_subscriber_count"),
}, async () => {
const result = await client.getSubscriberCount();

@@ -19,5 +28,9 @@ return {

});
server.tool("list_published_posts", "List published posts with pagination. Returns title, date, slug, and URL for each post.", {
offset: z.number().optional().default(0).describe("Number of posts to skip"),
limit: z.number().optional().default(25).describe("Max posts to return (1-100)"),
server.registerTool("list_published_posts", {
description: "List published posts with pagination. Returns title, date, slug, and URL for each post.",
inputSchema: {
offset: z.number().optional().default(0).describe("Number of posts to skip"),
limit: z.number().optional().default(25).describe("Max posts to return (1-100)"),
},
annotations: buildAnnotations("list_published_posts"),
}, async ({ offset, limit }) => {

@@ -39,5 +52,9 @@ const { posts, total } = await client.getPublishedPosts(offset, Math.min(limit, 100));

});
server.tool("list_drafts", "List draft posts. Returns title, creation date, and audience for each draft.", {
offset: z.number().optional().default(0).describe("Number of drafts to skip"),
limit: z.number().optional().default(25).describe("Max drafts to return (1-100)"),
server.registerTool("list_drafts", {
description: "List draft posts. Returns title, creation date, and audience for each draft.",
inputSchema: {
offset: z.number().optional().default(0).describe("Number of drafts to skip"),
limit: z.number().optional().default(25).describe("Max drafts to return (1-100)"),
},
annotations: buildAnnotations("list_drafts"),
}, async ({ offset, limit }) => {

@@ -58,4 +75,8 @@ const drafts = await client.getDrafts(offset, Math.min(limit, 100));

});
server.tool("get_post", "Get the full content of a published post by ID. Returns title, body HTML, metadata.", {
post_id: z.number().describe("The post ID to retrieve"),
server.registerTool("get_post", {
description: "Get the full content of a published post by ID. Returns title, body HTML, metadata.",
inputSchema: {
post_id: z.number().describe("The post ID to retrieve"),
},
annotations: buildAnnotations("get_post"),
}, async ({ post_id }) => {

@@ -82,4 +103,8 @@ const post = await client.getPost(post_id);

});
server.tool("get_draft", "Get the full content of a draft post by ID. Returns title, body, metadata.", {
draft_id: z.number().describe("The draft ID to retrieve"),
server.registerTool("get_draft", {
description: "Get the full content of a draft post by ID. Returns title, body, metadata.",
inputSchema: {
draft_id: z.number().describe("The draft ID to retrieve"),
},
annotations: buildAnnotations("get_draft"),
}, async ({ draft_id }) => {

@@ -105,5 +130,9 @@ const draft = await client.getDraft(draft_id);

});
server.tool("get_post_comments", "Get comments on a published post. Returns commenter name, comment body, date, and reaction counts.", {
post_id: z.number().describe("The post ID to get comments for"),
limit: z.number().optional().default(20).describe("Max comments to return (default 20)"),
server.registerTool("get_post_comments", {
description: "Get comments on a published post. Returns commenter name, comment body, date, and reaction counts.",
inputSchema: {
post_id: z.number().describe("The post ID to get comments for"),
limit: z.number().optional().default(20).describe("Max comments to return (default 20)"),
},
annotations: buildAnnotations("get_post_comments"),
}, async ({ post_id, limit }) => {

@@ -123,12 +152,16 @@ const comments = await client.getPostComments(post_id, limit);

});
// --- Write tools ---
server.tool("create_draft", "Create a new draft post. Accepts markdown body which is converted to Substack's format. Does NOT publish — creates a draft only.", {
title: z.string().describe("Post title"),
body: z.string().optional().describe("Post body in markdown format"),
subtitle: z.string().optional().describe("Post subtitle"),
audience: z
.enum(["everyone", "only_paid", "founding", "only_free"])
.optional()
.default("everyone")
.describe("Who can see this post"),
// --- Write tools (additive: drafts and uploads — nothing goes public) ---
server.registerTool("create_draft", {
description: "Create a new draft post. Accepts markdown body which is converted to Substack's format. Does NOT publish — creates a draft only.",
inputSchema: {
title: z.string().describe("Post title"),
body: z.string().optional().describe("Post body in markdown format"),
subtitle: z.string().optional().describe("Post subtitle"),
audience: z
.enum(["everyone", "only_paid", "founding", "only_free"])
.optional()
.default("everyone")
.describe("Who can see this post"),
},
annotations: buildAnnotations("create_draft"),
}, async ({ title, body, subtitle, audience }) => {

@@ -150,11 +183,15 @@ const prosemirrorBody = body ? markdownToProseMirror(body) : undefined;

});
server.tool("update_draft", "Update an existing draft post. Only works on unpublished drafts. Accepts markdown body.", {
draft_id: z.number().describe("The draft ID to update"),
title: z.string().optional().describe("New title"),
subtitle: z.string().optional().describe("New subtitle"),
body: z.string().optional().describe("New body in markdown format"),
audience: z
.enum(["everyone", "only_paid", "founding", "only_free"])
.optional()
.describe("Who can see this post"),
server.registerTool("update_draft", {
description: "Update an existing draft post. Only works on unpublished drafts. Accepts markdown body.",
inputSchema: {
draft_id: z.number().describe("The draft ID to update"),
title: z.string().optional().describe("New title"),
subtitle: z.string().optional().describe("New subtitle"),
body: z.string().optional().describe("New body in markdown format"),
audience: z
.enum(["everyone", "only_paid", "founding", "only_free"])
.optional()
.describe("Who can see this post"),
},
annotations: buildAnnotations("update_draft"),
}, async ({ draft_id, title, subtitle, body, audience }) => {

@@ -184,6 +221,10 @@ const updates = {};

});
server.tool("upload_image", "Upload a base64-encoded image to Substack's CDN. Returns the hosted image URL.", {
image_base64: z
.string()
.describe('Base64-encoded image with data URI prefix (e.g., "data:image/png;base64,...")'),
server.registerTool("upload_image", {
description: "Upload a base64-encoded image to Substack's CDN. Returns the hosted image URL.",
inputSchema: {
image_base64: z
.string()
.describe('Base64-encoded image with data URI prefix (e.g., "data:image/png;base64,...")'),
},
annotations: buildAnnotations("upload_image"),
}, async ({ image_base64 }) => {

@@ -197,4 +238,9 @@ const result = await client.uploadImage(image_base64);

});
server.tool("create_note", "Create a Substack Note (short-form content). Accepts markdown text. Publishes immediately — there is no draft state for notes.", {
body: z.string().describe("Note content in markdown format"),
// --- Note tools (PUBLISH IMMEDIATELY — public the moment they run) ---
server.registerTool("create_note", {
description: "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.",
inputSchema: {
body: z.string().describe("Note content in markdown format"),
},
annotations: buildAnnotations("create_note"),
}, async ({ body }) => {

@@ -221,5 +267,9 @@ const bodyJson = {

});
server.tool("create_note_with_link", "Create a Substack Note with a link attachment. The link is displayed as a rich card below the note text. Publishes immediately.", {
body: z.string().describe("Note content in markdown format"),
url: z.string().url().describe("URL to attach as a link card"),
server.registerTool("create_note_with_link", {
description: "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.",
inputSchema: {
body: z.string().describe("Note content in markdown format"),
url: z.string().url().describe("URL to attach as a link card"),
},
annotations: buildAnnotations("create_note_with_link"),
}, async ({ body, url }) => {

@@ -226,0 +276,0 @@ const attachment = await client.createNoteAttachment(url);

@@ -1,1 +0,1 @@

{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
{
"name": "@conorbronsdon/substack-mcp",
"version": "0.2.2",
"description": "MCP server for Substack — read posts, manage drafts. No publish or delete by design.",
"version": "0.3.0",
"description": "MCP server for Substack — read posts, manage drafts, publish Notes. Long-form posts are draft-only by design (no publish, no delete); Notes publish immediately.",
"type": "module",

@@ -11,2 +11,3 @@ "bin": {

"build": "tsc",
"lint": "tsc --noEmit",
"test": "vitest run",

@@ -13,0 +14,0 @@ "start": "node dist/index.js",

@@ -0,3 +1,24 @@

<div align="center">
# substack-mcp
An MCP server for Substack. Read your publication data and manage drafts from your AI agent. Long-form posts are draft-only by design — no publish, no delete. Short-form Notes publish immediately.
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](LICENSE)
[![Language: TypeScript](https://img.shields.io/badge/TypeScript-3178c6?style=flat-square&logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
[![npm version](https://img.shields.io/npm/v/@conorbronsdon/substack-mcp?style=flat-square)](https://www.npmjs.com/package/@conorbronsdon/substack-mcp)
[![MCP](https://img.shields.io/badge/MCP-Model_Context_Protocol-1f6feb?style=flat-square)](https://modelcontextprotocol.io/)
[![Podcast](https://img.shields.io/badge/Podcast-Chain_of_Thought-purple?style=flat-square)](https://chainofthought.show)
[![X](https://img.shields.io/badge/X-@ConorBronsdon-black?style=flat-square&logo=x)](https://x.com/ConorBronsdon)
</div>
---
![Demo: list_drafts tool call and response](docs/demo.gif)
An MCP server for Substack that lets AI assistants read your publication data and manage drafts. The draft list shown in the demo above is sample data, not real account values.
**Safe by design — with one loud exception:** This server cannot publish or delete long-form posts. Post tools create and edit drafts only; you review and publish manually through Substack's editor. The exception is Substack **Notes**: `create_note` and `create_note_with_link` publish short-form Notes immediately, because Notes have no draft state on Substack. Treat the Note tools as public-publish actions — there is no preview step and no undo from this server.
<a href="https://glama.ai/mcp/servers/conorbronsdon/substack-mcp">

@@ -7,8 +28,14 @@ <img width="380" height="200" src="https://glama.ai/mcp/servers/conorbronsdon/substack-mcp/badge" alt="substack-mcp MCP server" />

An MCP server for Substack that lets AI assistants read your publication data and manage drafts.
## About
**Safe by design:** This server can create and edit drafts but cannot publish or delete posts. You always review and publish manually through Substack's editor.
Built and maintained by [Conor Bronsdon](https://github.com/conorbronsdon) for the [Chain of Thought](https://chainofthought.show) podcast production workflow, where it drafts and reviews newsletter posts before a human hits publish. Conor hosts Chain of Thought, a show about AI infrastructure and how practitioners actually build with it. More tools for creators live in [ai-tools-for-creators](https://github.com/conorbronsdon/ai-tools-for-creators). Find Conor on X at [@ConorBronsdon](https://x.com/ConorBronsdon).
**Sibling MCP servers:**
- [Transistor-MCP](https://github.com/conorbronsdon/Transistor-MCP): manage podcast episodes, analytics, and transcripts on Transistor.fm
- [podcastindex-mcp](https://github.com/conorbronsdon/podcastindex-mcp): search the Podcast Index and track guest appearances
## Tools
Every tool declares MCP [tool annotations](https://modelcontextprotocol.io/docs/concepts/tools#tool-annotations): reads carry `readOnlyHint: true`, draft/upload writes carry `readOnlyHint: false`, and the Note tools additionally carry `openWorldHint: true` so clients don't treat an immediate public publish as a low-stakes write.
### Read

@@ -25,3 +52,3 @@

### Write
### Write (drafts and uploads — nothing goes public)

@@ -33,8 +60,15 @@ | Tool | Description |

| `upload_image` | Upload an image to Substack's CDN |
| `create_note` | Publish a Substack Note (short-form, publishes immediately) |
| `create_note_with_link` | Publish a Note with a link card attachment |
### Publish (Notes — public immediately)
| Tool | Description |
|------|-------------|
| `create_note` | Publish a Substack Note (short-form, **publishes immediately**) |
| `create_note_with_link` | Publish a Note with a link card attachment (**publishes immediately**) |
Notes have no draft state on Substack, so there is no draft-first option for these two tools.
### Intentionally excluded
- **Publish posts** — Publishing long-form posts should be a deliberate human action
- **Publish posts** — Publishing long-form posts should be a deliberate human action (Notes are the documented exception above)
- **Delete** — Too destructive for an AI tool

@@ -139,4 +173,14 @@ - **Schedule** — Use Substack's editor for scheduling

## Contributing
Issues and pull requests are welcome. Because this server uses Substack's unofficial API, the most useful contributions are fixes when an endpoint changes. If a tool stops working, open an issue with the tool name and the error. The safe-by-design boundary stays: no publish, no delete, no schedule for long-form posts. Notes publish immediately by design and must keep saying so loudly in their descriptions.
---
## Disclaimer
*All views, opinions, and statements expressed on this account are solely my own and are made in my personal capacity. They do not reflect, and should not be construed as reflecting, the views, positions, or policies of Modular. This account is not affiliated with, authorized by, or endorsed by Modular in any way.*
## License
MIT

@@ -5,3 +5,3 @@ {

"title": "Substack MCP Server",
"description": "MCP server for Substack — read posts, manage drafts, upload images. Safe by design: cannot publish or delete. Uses unofficial Substack API.",
"description": "MCP server for Substack — read posts, manage drafts, upload images, publish Notes. Long-form posts are draft-only (no publish or delete); Notes publish immediately. Uses unofficial Substack API.",
"repository": {

@@ -11,3 +11,3 @@ "url": "https://github.com/conorbronsdon/substack-mcp",

},
"version": "0.2.2",
"version": "0.3.0",
"packages": [

@@ -17,3 +17,3 @@ {

"identifier": "@conorbronsdon/substack-mcp",
"version": "0.2.2",
"version": "0.3.0",
"runtimeHint": "npx",

@@ -20,0 +20,0 @@ "transport": {