Skip to main content
Glama

Create draft

create_draft

Create a new draft owned by the authenticated user. To give an article its cover from ChatGPT, pass the image the user attached in the chat in coverFile; do not transcribe image bytes or pass a sandbox path. Returns the created draft id and title. To schedule or publish this article later, pass that id as draftId to schedule_article; do not send its title and contentHtml again, which would save a second copy. When reviewing a Substack article with the writer, briefly mention once per article that they can ask for a Subscribe button after a named paragraph, a caption beneath it, a paywall before a named section (a paywall makes the article paid-only), or a Freewall before a named section (everyone reads the opening; free and paid subscribers keep reading, and readers without an email are invited to subscribe free). Use the requested position; ask where only if it is ambiguous.

Tool call template

Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "arguments": {
      "title": "<title>"
    },
    "name": "create_draft"
  }
}

Input Schema

NameRequiredDescriptionDefault
titleYesDraft title.
coverFileNoThe approved cover image attached in ChatGPT, passed by the client. Use this OR coverImage, never both. If the generated image is not available as an attachment, ask the user to download and attach the PNG, or upload it in the Narrareach editor. Never substitute another image.
coverImageNoAttach or replace an article cover using a public URL, data URI, or actual base64 image bytes. For an image attached in ChatGPT, use coverFile instead. Protected source hosts may reject URL ingestion.
contentHtmlNoOptional HTML body. If omitted, the draft starts empty. Plain text is also accepted. Keep audience-specific match/else wrappers intact. Native Substack controls are markup, never placeholder text: a paid-only paywall is <hr data-type="paywall"> at the paragraph boundary; a Freewall (free and paid subscribers keep reading) is <hr data-type="paywall" data-access="subscribers">; a Subscribe button is <div data-type="button" data-kind="subscribe" data-text="Subscribe"></div>, or with a caption <div data-type="button" data-kind="subscribeCaption" data-text="Subscribe" data-caption="Your caption"></div>, with HTML-escaped attribute values. To make the whole article paid-only, schedule it with isPaidContent true instead of adding a paywall. Check the saved content and get_draft substackControls before confirming what was added and where, then show the updated draft for review before scheduling.

Output Schema

NameRequiredDescriptionDefault
idYes
titleYes
nextStepNo
createdAtYes
coverImageUrlNo
connectedAccountNo

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changed
    • addedOutput schema / properties / nextStep
      Added value: +{
      +  "type": "string"
      +}
  2. Changed1 schema field changed
    • changedInput schema / properties / contentHtml / description
      Previous value: -"Optional HTML body. If omitted, the draft starts empty. Plain text is also accepted. Keep audience-specific match/else wrappers intact. Native Substack controls are markup, never placeholder text: a paid-only paywall is <hr data-type=\"paywall\"> at the paragraph boundary; a Freewall (free and paid subscribers keep reading) is <hr data-type=\"paywall\" data-access=\"subscribers\">; a Subscribe button is <div data-type=\"button\" data-kind=\"subscribe\" data-text=\"Subscribe\"></div>, or with a caption <div data-type=\"button\" data-kind=\"subscribeCaption\" data-text=\"Subscribe\" data-caption=\"Your caption\"></div>, with HTML-escaped attribute values. To make the whole article paid-only, schedule it with isPaidContent true instead of adding a paywall. When reviewing a Substack article, briefly tell the writer they can ask for a Subscribe button after a named paragraph, a caption beneath it, or a paywall before a named section. Use the requested position; ask where only if it is ambiguous. Check the saved content and get_draft substackControls before confirming what was added and where, then show the updated draft for review before scheduling."New value: +"Optional HTML body. If omitted, the draft starts empty. Plain text is also accepted. Keep audience-specific match/else wrappers intact. Native Substack controls are markup, never placeholder text: a paid-only paywall is <hr data-type=\"paywall\"> at the paragraph boundary; a Freewall (free and paid subscribers keep reading) is <hr data-type=\"paywall\" data-access=\"subscribers\">; a Subscribe button is <div data-type=\"button\" data-kind=\"subscribe\" data-text=\"Subscribe\"></div>, or with a caption <div data-type=\"button\" data-kind=\"subscribeCaption\" data-text=\"Subscribe\" data-caption=\"Your caption\"></div>, with HTML-escaped attribute values. To make the whole article paid-only, schedule it with isPaidContent true instead of adding a paywall. Check the saved content and get_draft substackControls before confirming what was added and where, then show the updated draft for review before scheduling."
  3. Changed1 schema field changed
    • changedInput schema / properties / contentHtml / description
      Previous value: -"Optional HTML body. If omitted, the draft starts empty. Plain text is also accepted. Keep audience-specific match/else wrappers intact. Native Substack controls are markup, never placeholder text: a paid-only paywall is <hr data-type=\"paywall\"> at the paragraph boundary; a Freewall (free and paid subscribers keep reading) is <hr data-type=\"paywall\" data-access=\"subscribers\">; a Subscribe button is <div data-type=\"button\" data-kind=\"subscribe\" data-text=\"Subscribe\"></div>, or with a caption <div data-type=\"button\" data-kind=\"subscribeCaption\" data-text=\"Subscribe\" data-caption=\"Your caption\"></div>, with HTML-escaped attribute values. To make the whole article paid-only, schedule it with isPaidContent true instead of adding a paywall. Check the result with get_draft substackControls before scheduling."New value: +"Optional HTML body. If omitted, the draft starts empty. Plain text is also accepted. Keep audience-specific match/else wrappers intact. Native Substack controls are markup, never placeholder text: a paid-only paywall is <hr data-type=\"paywall\"> at the paragraph boundary; a Freewall (free and paid subscribers keep reading) is <hr data-type=\"paywall\" data-access=\"subscribers\">; a Subscribe button is <div data-type=\"button\" data-kind=\"subscribe\" data-text=\"Subscribe\"></div>, or with a caption <div data-type=\"button\" data-kind=\"subscribeCaption\" data-text=\"Subscribe\" data-caption=\"Your caption\"></div>, with HTML-escaped attribute values. To make the whole article paid-only, schedule it with isPaidContent true instead of adding a paywall. When reviewing a Substack article, briefly tell the writer they can ask for a Subscribe button after a named paragraph, a caption beneath it, or a paywall before a named section. Use the requested position; ask where only if it is ambiguous. Check the saved content and get_draft substackControls before confirming what was added and where, then show the updated draft for review before scheduling."
  4. Changed1 schema field changed
    • changedInput schema / properties / contentHtml / description
      Previous value: -"Optional HTML body. If omitted, the draft starts empty. Plain text is also accepted. For a subscriber-only Freewall use <hr data-type=\"paywall\" data-access=\"subscribers\">; for paid-only use <hr data-type=\"paywall\">. Keep audience-specific match/else wrappers intact."New value: +"Optional HTML body. If omitted, the draft starts empty. Plain text is also accepted. Keep audience-specific match/else wrappers intact. Native Substack controls are markup, never placeholder text: a paid-only paywall is <hr data-type=\"paywall\"> at the paragraph boundary; a Freewall (free and paid subscribers keep reading) is <hr data-type=\"paywall\" data-access=\"subscribers\">; a Subscribe button is <div data-type=\"button\" data-kind=\"subscribe\" data-text=\"Subscribe\"></div>, or with a caption <div data-type=\"button\" data-kind=\"subscribeCaption\" data-text=\"Subscribe\" data-caption=\"Your caption\"></div>, with HTML-escaped attribute values. To make the whole article paid-only, schedule it with isPaidContent true instead of adding a paywall. Check the result with get_draft substackControls before scheduling."
  5. Changed3 schema fields changed
    • addedInput schema / properties / coverFile
      Added value: +{
      +  "description": "The approved cover image attached in ChatGPT, passed by the client. Use this OR coverImage, never both. If the generated image is not available as an attachment, ask the user to download and attach the PNG, or upload it in the Narrareach editor. Never substitute another image.",
      +  "properties": {
      +    "download_url": {
      +      "type": "string"
      +    },
      +    "file_id": {
      +      "type": "string"
      +    },
      +    "file_name": {
      +      "type": "string"
      +    },
      +    "mime_type": {
      +      "type": "string"
      +    }
      +  },
      +  "required": [
      +    "download_url",
      +    "file_id"
      +  ],
      +  "type": "object"
      +}
    • addedInput schema / properties / coverImage
      Added value: +{
      +  "description": "Attach or replace an article cover using a public URL, data URI, or actual base64 image bytes. For an image attached in ChatGPT, use coverFile instead. Protected source hosts may reject URL ingestion.",
      +  "properties": {
      +    "data": {
      +      "type": "string"
      +    },
      +    "fileName": {
      +      "type": "string"
      +    },
      +    "mimeType": {
      +      "type": "string"
      +    },
      +    "sourceType": {
      +      "enum": [
      +        "url",
      +        "data_uri",
      +        "base64"
      +      ],
      +      "type": "string"
      +    },
      +    "url": {
      +      "type": "string"
      +    }
      +  },
      +  "type": "object"
      +}
    • addedOutput schema / properties / coverImageUrl
      Added value: +{
      +  "type": "string"
      +}
  6. Changed1 schema field changed
    • changedInput schema / properties / contentHtml / description
      Previous value: -"Optional HTML body. If omitted, the draft starts empty. Plain text is also accepted."New value: +"Optional HTML body. If omitted, the draft starts empty. Plain text is also accepted. For a subscriber-only Freewall use <hr data-type=\"paywall\" data-access=\"subscribers\">; for paid-only use <hr data-type=\"paywall\">. Keep audience-specific match/else wrappers intact."
  7. First observed

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare a non-read-only, non-idempotent, non-destructive write, and the description adds real context beyond that: the return payload ('the created draft id and title'), the duplicate-copy risk that explains idempotentHint=false, and the rule against substituting or re-encoding images. The warning that a paywall makes an article paid-only, plus the alternative of scheduling with isPaidContent, further clarifies consequences of what is created.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Creation, cover handling, return value, and handoff to schedule_article are front-loaded in a logical order. The closing paragraph about when to mention Subscribe buttons, paywalls, and Freewalls to the writer is on-topic but long and only indirectly about invoking this tool, so the definition is slightly heavier than it needs to be.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a four-parameter creation tool with nested cover objects, an output schema, and full annotation coverage, nothing essential is missing: input sourcing rules, duplicate-creation avoidance, return values, and downstream scheduling are all addressed. The output schema carries the response shape, so the description is not obligated to detail it further.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds meaning the schema does not: pass the attached image object in coverFile rather than transcribing bytes or a sandbox path, and use isPaidContent at schedule time instead of embedding a paywall for a wholly paid article. The contentHtml markup semantics are largely restated from the schema description, which keeps this from a 5.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence gives a specific verb and resource ('Create a new draft owned by the authenticated user') and explicitly separates it from the scheduling path via schedule_article. It does not differentiate from other creation siblings such as create_reader_reply_draft or create_article_from_note, so an agent still has to infer which creator applies, keeping this just short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Guidance is explicit and conditional: use coverFile for a ChatGPT-attached image versus coverImage for URL/base64, and never both. It also states the follow-on path ('pass that id as draftId to schedule_article') and an explicit prohibition ('do not send its title and contentHtml again, which would save a second copy').

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources