create_note_with_link
Create and publish a Substack note with an attached link that displays as a rich card below the text. Use this tool to share content with embedded URL previews.
Instructions
Create a Substack Note with a link attachment. The link is displayed as a rich card below the note text. Publishes immediately.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| body | Yes | Note content in markdown format | |
| url | Yes | URL to attach as a link card |
Implementation Reference
- src/server.ts:300-334 (registration)Tool registration for 'create_note_with_link' using server.tool() with Zod schema (body: string, url: string.url()) and async handler that orchestrates attachment creation and note publishing
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"), }, async ({ body, url }) => { const attachment = await client.createNoteAttachment(url); const bodyJson = { type: "doc" as const, attrs: { schemaVersion: "v1" as const }, content: markdownToProseMirrorContent(body), }; const note = await client.createNote(bodyJson, [attachment.id]); return { content: [ { type: "text", text: JSON.stringify( { id: note.id, body: note.body, date: note.date, attachment_id: attachment.id, message: "Note with link published successfully.", }, null, 2, ), }, ], }; }, ); - src/server.ts:307-333 (handler)Handler function that: (1) creates link attachment via client.createNoteAttachment(url), (2) converts markdown to ProseMirror JSON format, (3) creates note with attachment via client.createNote(bodyJson, [attachment.id]), (4) returns JSON result with note id, body, date, and attachment_id
async ({ body, url }) => { const attachment = await client.createNoteAttachment(url); const bodyJson = { type: "doc" as const, attrs: { schemaVersion: "v1" as const }, content: markdownToProseMirrorContent(body), }; const note = await client.createNote(bodyJson, [attachment.id]); return { content: [ { type: "text", text: JSON.stringify( { id: note.id, body: note.body, date: note.date, attachment_id: attachment.id, message: "Note with link published successfully.", }, null, 2, ), }, ], }; }, - src/api/client.ts:204-212 (handler)createNoteAttachment method - makes POST request to /api/v1/comment/attachment with {url, type: 'link'} payload, returns NoteAttachment with id
async createNoteAttachment(url: string): Promise<NoteAttachment> { return this.request<NoteAttachment>( `${this.publicationUrl}/api/v1/comment/attachment`, { method: "POST", body: JSON.stringify({ url, type: "link" }), }, ); } - src/api/client.ts:182-202 (handler)createNote method - makes POST request to /api/v1/comment/feed with bodyJson, tabId, surface, replyMinimumRole, and optional attachmentIds
async createNote( bodyJson: NoteCreatePayload["bodyJson"], attachmentIds?: string[], ): Promise<SubstackNote> { const payload: NoteCreatePayload = { bodyJson, tabId: "for-you", surface: "feed", replyMinimumRole: "everyone", }; if (attachmentIds?.length) { payload.attachmentIds = attachmentIds; } return this.request<SubstackNote>( `${this.publicationUrl}/api/v1/comment/feed`, { method: "POST", body: JSON.stringify(payload), }, ); } - markdownToProseMirrorContent helper function - converts markdown text to ProseMirror content array format used for Notes
export function markdownToProseMirrorContent(markdown: string): PMNode[] { const doc = JSON.parse(markdownToProseMirror(markdown)); return doc.content; }