# Medium MCP tools

> Every Medium MCP tool on the Cotlet server: tool names, input schemas, a tools/call example and an example result for each tool.

Source: https://cotlet.dev/docs/mcp/medium

Docs sections: [Get started](https://cotlet.dev/docs.md) · [REST API](https://cotlet.dev/docs/api.md) · [MCP](https://cotlet.dev/docs/mcp.md) · [SDKs & CLI](https://cotlet.dev/docs/sdks.md) · [Integrations](https://cotlet.dev/docs/integrations.md)

13 tools for Medium on the Cotlet MCP server. Your agent picks them from tools/list.

- MCP server: `https://cotlet.dev/mcp/v1`
- Connects with: Your own Medium account (writer or editor role in a publication to submit or schedule there)
- Integration page: https://cotlet.dev/integrations/medium-api-mcp-server

## Endpoints

| Endpoint | Access | Description |
|---|---|---|
| `medium_list_stories` | read | Your stories, newest first, with status, publication, tags and URL. |
| `medium_get_story` | read | Full story: title, subtitle, body as Markdown, tags, canonical link, paywall and dates. |
| `medium_get_story_stats` | read | Views, reads, member read ratio, claps, responses and earnings for paywalled stories. |
| `medium_get_account_stats` | read | The account-level figures Medium shows on your stats page, for one month. |
| `medium_list_responses` | read | Responses to a story, with author, text and whether you replied. |
| `medium_list_publications` | read | Publications you own, edit or write for, with your role. |
| `medium_create_draft` | write | New draft from Markdown. Nothing is public until you publish or schedule it. |
| `medium_import_story` | write | Medium's "Import a story": copies a post from a URL into a draft, backdated, with a canonical link. |
| `medium_update_story` | write | Change the title, subtitle, body, tags, canonical link or paywall of a draft or a published story. |
| `medium_publish_story` | write | Publish a draft now. |
| `medium_schedule_story` | write | Publish a draft at a set time. |
| `medium_submit_to_publication` | write | Send a draft to the editors of a publication you write for. |
| `medium_reply_to_response` | write | Reply as you under a response to your story. |

## List stories

MCP tool: `medium_list_stories`

Access: read.

Lists your own stories, newest first. Filter by status to find drafts or scheduled stories. Each item has the story_id used by the other tools. Pages hold up to 50 stories; pass next_cursor back as cursor for the next page.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `status` | enum | no | published, draft or scheduled. Default: published. |
| `limit` | integer | no | 1 to 50. Default: 20. |
| `cursor` | string | no | Next page cursor from the previous call. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "medium_list_stories",
    "arguments": {}
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "stories": [
      {
        "story_id": "3f9a1c27b84e",
        "title": "Shipping small",
        "status": "published",
        "publication": null,
        "tags": [
          "Python",
          "Programming"
        ],
        "published_at": "2026-10-02T09:00:00-04:00",
        "url": "https://medium.com/@yourname/shipping-small-3f9a1c27b84e"
      }
    ],
    "next_cursor": null
  },
  "isError": false
}
```

## Get story

MCP tool: `medium_get_story`

Access: read.

Returns one story with its body as Markdown. Use it before update_story so your agent changes only the part you asked for. Works for drafts, scheduled and published stories.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `story_id` | string | yes | Story ID from list_stories. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "medium_get_story",
    "arguments": {
      "story_id": "story_8c41d2"
    }
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "story_id": "3f9a1c27b84e",
    "title": "Shipping small",
    "subtitle": "Why small releases win",
    "body_markdown": "Small releases beat big ones.\n\nHere is why.",
    "tags": [
      "Python",
      "Programming"
    ],
    "canonical_url": "https://yourname.dev/blog/shipping-small",
    "paywalled": false,
    "status": "published"
  },
  "isError": false
}
```

## Get story stats

MCP tool: `medium_get_story_stats`

Access: read.

Returns the numbers from the story's detailed stats page. Member read ratio is member reads divided by member views, the figure Medium uses to adjust earnings. Earnings appear only for stories behind the paywall.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `story_id` | string | yes | Story ID from list_stories. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "medium_get_story_stats",
    "arguments": {
      "story_id": "story_8c41d2"
    }
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "story_id": "3f9a1c27b84e",
    "views": 1840,
    "reads": 912,
    "member_read_ratio": 0.42,
    "claps": 143,
    "responses": 6,
    "earnings": {
      "amount": 18.4,
      "currency": "USD"
    }
  },
  "isError": false
}
```

## Get account stats

MCP tool: `medium_get_account_stats`

Access: read.

Returns the account-level figures Medium shows on your stats page for one month. Use list_stories and get_story_stats to find which stories drove them.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `month` | string | no | YYYY-MM. Default: current month. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "medium_get_account_stats",
    "arguments": {
      "month": "2026-09"
    }
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "month": "2026-09",
    "views": 9420,
    "reads": 4310
  },
  "isError": false
}
```

## List responses

MCP tool: `medium_list_responses`

Access: read.

Lists the responses under one story, with the author, text and whether you already replied. Hidden responses are not returned. Pass response_id to reply_to_response.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `story_id` | string | yes | Story ID from list_stories. |
| `unanswered_only` | boolean | no | Skip responses you already replied to. |
| `cursor` | string | no | Next page cursor from the previous call. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "medium_list_responses",
    "arguments": {
      "story_id": "story_8c41d2"
    }
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "responses": [
      {
        "response_id": "r_41b9c2",
        "author": "Dana Whitfield",
        "body": "The third example has a typo.",
        "created_at": "2026-10-03T11:20:00Z",
        "replied": false
      }
    ],
    "next_cursor": null
  },
  "isError": false
}
```

## List publications

MCP tool: `medium_list_publications`

Access: read.

Returns every publication you belong to with your role. Use the publication_id with submit_to_publication. Each publication sets its own rules for what it accepts.

### Parameters

No parameters.

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "medium_list_publications",
    "arguments": {}
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "publications": [
      {
        "publication_id": "pub_7d21",
        "name": "Better Programming",
        "role": "writer"
      }
    ]
  },
  "isError": false
}
```

## Create draft

MCP tool: `medium_create_draft`

Access: write.

Creates a draft from Markdown and returns its story_id. The draft is private. It reaches no one and is deleted or edited in one call, so it asks for no confirmation.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `title` | string | yes | Story title. |
| `body_markdown` | string | yes | Story body. Images by URL. |
| `subtitle` | string | no | Shown under the title and in previews. |
| `tags` | string[] | no | Up to 5 tags. |
| `canonical_url` | string | no | Original URL if the story was first published elsewhere. |
| `paywalled` | boolean | no | Member-only story (Partner Program). Default: false. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "medium_create_draft",
    "arguments": {
      "title": "Shipping small",
      "subtitle": "Why small releases win",
      "body_markdown": "Small releases beat big ones.\n\n## Here is why",
      "tags": [
        "Python",
        "Programming",
        "Software Engineering"
      ],
      "paywalled": true
    }
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "story_id": "3f9a1c27b84e",
    "status": "draft",
    "url": "https://medium.com/p/3f9a1c27b84e/edit"
  },
  "isError": false
}
```

## Import story

MCP tool: `medium_import_story`

Access: write.

Runs Medium's importer on a public URL. The draft keeps the original date and gets a canonical link to the source. If Medium cannot read the page, the call fails and create_draft is the fallback. The draft is private, so no confirmation.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `url` | string | yes | Public URL of the original post. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "medium_import_story",
    "arguments": {
      "url": "https://yourname.dev/blog/shipping-small"
    }
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "story_id": "3f9a1c27b84e",
    "status": "draft",
    "canonical_url": "https://yourname.dev/blog/shipping-small"
  },
  "isError": false
}
```

## Update story

MCP tool: `medium_update_story`

Access: write. Confirm: waits for the user's OK before it runs.

Changes only the fields you send. On a published story the change is visible to readers at once. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `story_id` | string | yes | Story ID from list_stories. |
| `fields` | object | yes | Any of title, subtitle, body_markdown, tags, canonical_url, paywalled. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "medium_update_story",
    "arguments": {
      "story_id": "3f9a1c27b84e",
      "fields": {
        "body_markdown": "Small releases beat big ones.\n\nMore on [yourname.dev](https://yourname.dev)."
      }
    }
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "story_id": "3f9a1c27b84e",
    "status": "published",
    "updated_fields": [
      "tags"
    ]
  },
  "isError": false
}
```

## Publish story

MCP tool: `medium_publish_story`

Access: write. Confirm: waits for the user's OK before it runs.

Publishes a draft under your name, or in the publication it belongs to. Readers and followers can see it at once. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `story_id` | string | yes | Story ID from list_stories. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "medium_publish_story",
    "arguments": {
      "story_id": "story_8c41d2"
    }
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "story_id": "3f9a1c27b84e",
    "status": "published",
    "url": "https://medium.com/@yourname/shipping-small-3f9a1c27b84e"
  },
  "isError": false
}
```

## Schedule story

MCP tool: `medium_schedule_story`

Access: write. Confirm: waits for the user's OK before it runs.

Schedules a draft. Medium publishes it at the set time, and the story stays in your drafts until then. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `story_id` | string | yes | Story ID from list_stories. |
| `publish_at` | datetime | yes | ISO 8601 with time zone. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "medium_schedule_story",
    "arguments": {
      "story_id": "3f9a1c27b84e",
      "publish_at": "2026-10-15T09:00:00-04:00"
    }
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "story_id": "3f9a1c27b84e",
    "status": "scheduled",
    "publish_at": "2026-10-15T09:00:00-04:00"
  },
  "isError": false
}
```

## Submit to publication

MCP tool: `medium_submit_to_publication`

Access: write. Confirm: waits for the user's OK before it runs.

Submits a draft to a publication. Its editors review it and decide when to publish. A publication can accept only drafts, only published stories, or both. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `story_id` | string | yes | Story ID from list_stories. |
| `publication_id` | string | yes | Publication ID from list_publications. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "medium_submit_to_publication",
    "arguments": {
      "story_id": "story_8c41d2",
      "publication_id": "publication_8c41d2"
    }
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "story_id": "3f9a1c27b84e",
    "publication_id": "pub_7d21",
    "status": "submitted"
  },
  "isError": false
}
```

## Reply to response

MCP tool: `medium_reply_to_response`

Access: write. Confirm: waits for the user's OK before it runs.

Posts a reply under one response, under your name. By default you confirm each call; you can switch that off for this tool.

### Parameters

| Name | Type | Required | Description |
|---|---|---|---|
| `response_id` | string | yes | Response ID from list_responses. |
| `body` | string | yes | Reply text. |

### tools/call

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "medium_reply_to_response",
    "arguments": {
      "response_id": "response_8c41d2",
      "body": "body"
    }
  }
}
```

### Example response

```json
{
  "content": [
    {
      "type": "text",
      "text": "{ ... same JSON as structuredContent ... }"
    }
  ],
  "structuredContent": {
    "response_id": "r_41b9c2",
    "reply_id": "r_41ba07",
    "status": "published"
  },
  "isError": false
}
```
