create_article_image_upload
Create Article Image Upload. Get one-time upload URLs on writestack.io for an article image that has no public URL, such as a local file, an attachment you can read, or a private Google Drive file.
| Group | Access | MCP name |
|---|---|---|
| Articles | Writes data | create_article_image_upload |
Description
The description the MCP server sends to the model, word for word. Its rules apply to every call.
Get one-time upload URLs on writestack.io for an article image that has no public URL, such as a local file, an attachment you can read, or a private Google Drive file. Use it only when you can send the file's bytes yourself with an HTTP request (for example curl in a shell or code sandbox); if you cannot, ask the user for a public image URL instead. Pass the articleId, the file name, its exact contentType, and its exact size in bytes (max 10 MB). Within 15 minutes, send each entry in parts as an HTTP PUT of the raw bytes for that range (offset, sizeBytes) with the Content-Type header set to that contentType. A file up to 4 MB has one part: curl -X PUT -H 'Content-Type: <contentType>' --data-binary @<file> '<uploadUrl>'. A bigger file has 2 or 3 parts of 4 MB (4194304 bytes, the last one shorter); send them IN ORDER and wait for each 2xx, e.g. dd if=<file> bs=4194304 skip=<part> count=1 2>/dev/null | curl -X PUT -H 'Content-Type: <contentType>' --data-binary @- '<part url>'. Do not use multipart/form-data and do not base64-encode the body. An error response says what did not match (type, size, order, or an expired link); fix it or call this tool again. After the last part succeeds, pass the returned imageUrl to update_article (imageUrl array to append it, or  in bodyMarkdown to place it) or to create_article. Uploading alone does not add the image to the article. An imageUrl whose upload never finished is skipped.
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
articleId | string | Yes | The article the image is for. |
contentType | string | Yes | Exact MIME type of the file. Send the same value as the Content-Type header on the PUT. One of: "image/png", "image/jpeg", "image/gif", "image/webp", "image/avif", "image/heic". |
fileName | string | Yes | Original file name, e.g. cover.png. Used for the extension. Min length 1. Max length 200. |
sizeBytes | integer | Yes | Exact file size in bytes (for example from wc -c < file or stat). The upload must be exactly this size. Max 10 MB. Files over 4 MB are sent in parts. Max 10485760. |
Example call
The smallest valid request: required arguments only, with placeholder values.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "create_article_image_upload",
"arguments": {
"articleId": "…",
"fileName": "…",
"contentType": "image/png",
"sizeBytes": 1
}
}
}Result
The result has structuredContent that matches this schema. The same data is also in content as JSON text.
| Field | Type | Required | Description |
|---|---|---|---|
articleId | string | Yes | |
expiresAt | string | Yes | |
headers | object | Yes | |
headers.Content-Type | string | Yes | |
imageUrl | string | Yes | |
maxBytes | number | Yes | |
message | string | Yes | |
method | string | Yes | |
parts | object[] | Yes | |
parts[].offset | number | Yes | |
parts[].part | number | Yes | |
parts[].sizeBytes | number | Yes | |
parts[].url | string | Yes | |
uploadUrl | string | Yes |
Annotations
| Hint | Value |
|---|---|
readOnlyHint | false |
destructiveHint | false |
openWorldHint | false |
The full input and output JSON Schemas for every tool are in /mcp-tools.json.
update_article
Update Article. Update an article's title, subtitle, markdown body, imageUrl (remote images fetched to S3 and appended), or Substack settings (audience, comments, SEO, slug, social title, publishAt).
push_article_to_substack
Push Article To Substack. Push or update a WriteStack article as a Substack draft through the user's Chrome extension.