mcpField
B
Blacktwist

Blacktwist

Local
github.com/blacktwistSocial Media3Updated 4w ago

About Blacktwist

Blacktwist is an MCP server for managing a BlackTwist account through AI assistants, including social posts, drafts, and analytics. It supports OAuth or API-key authentication and team-scoped operations.

Key features

  1. 01Create and schedule posts or threads
  2. 02Manage drafts and scheduled posts
  3. 03Review social account analytics
  4. 04Manage teams and connected accounts
  5. 05Attach media and configure spoiler content

Use cases

  1. 01Schedule a post or thread
  2. 02Edit or review a draft
  3. 03Check analytics for a date range
  4. 04Manage content for a selected team

MCP support

It exposes BlackTwist account operations to MCP clients over Streamable HTTP. Authentication uses OAuth 2.1 or an API key.

Configuration

Local server config is the same for every client. Copy it into your client’s MCP config file.

mcp.json · Universal config
{
"mcpServers": {
"blacktwist": {
"command": "npx",
"args": [
"-y",
"mcp-remote"
],
"env": {
"BLACKTWIST_TOKEN": "<BLACKTWIST_TOKEN>"
}
}
}
}
Run commandnpx -y mcp-remote
Environment variables to set
BLACKTWIST_TOKENSecret

Local servers need a runtime on your machine (e.g. Node.js 18+). Config file locations differ by client; see your client’s docs.

Packages
npmmcp-remote

Tools

No tool list yet

Local servers expose tools only once running on your machine, and the maintainer has not provided a tool list.

Details

From the project README

BlackTwist MCP Server

The BlackTwist MCP (Model Context Protocol) server allows AI assistants like Claude, Cursor, and other MCP-compatible clients to interact with your BlackTwist account — creating posts, checking analytics, managing drafts, and more.

Quick Start

There are two ways to connect to the BlackTwist MCP server: OAuth (recommended for claude.ai) or API Key (for desktop clients and CLI tools).

Option A: Connect via OAuth (claude.ai)

If you're using claude.ai as a custom connector, OAuth is the easiest option — no API key needed.

  1. Go to claude.ai > Settings > Integrations (or add a custom connector)
  2. Enter the MCP server URL: https://blacktwist.app/api/mcp
  3. Claude will automatically discover the OAuth endpoints, register itself, and redirect you to sign in with your BlackTwist account
  4. Once signed in, the connection is established — Claude can now use your BlackTwist account

OAuth tokens refresh automatically, so you won't need to re-authenticate unless you revoke access.

Option B: Connect via API Key

For Claude Desktop, Claude Code, Cursor, and other MCP clients that don't support OAuth, use an API key.

1. Generate an API Key
  1. Open BlackTwist and go to Settings (gear icon)
  2. Click the MCP tab in the sidebar
  3. Click Create API Key, give it a name (e.g. "Claude Desktop"), and click Create Key
  4. Copy the key immediately — it won't be shown again
2. Configure Your MCP Client

Add the following to your MCP client configuration:

Claude Desktop (claude_desktop_config.json):

{
  "blacktwist": {
    "command": "npx",
    "args": [
      "-y",
      "mcp-remote@latest",
      "https://blacktwist.app/api/mcp",
      "--header",
      "Authorization:${BLACKTWIST_TOKEN}"
    ],
    "env": {
      "BLACKTWIST_TOKEN": "Bearer YOUR_API_KEY"
    }
  }
}

You need node.js v22, if you are using nvm be sure to use the correct version:

{
  "blacktwist": {
    "command": "/Users/<your_user>/.nvm/versions/node/v22.22.0/bin/npx",
    "args": [
      "-y",
      "mcp-remote@latest",
      "https://blacktwist.app/api/mcp",
      "--header",
      "Authorization:${BLACKTWIST_TOKEN}"
    ],
    "env": {
      "BLACKTWIST_TOKEN": "Bearer YOUR_API_KEY",
      "PATH": "/Users/<your_user>/.nvm/versions/node/v22.22.0/bin:/usr/local/bin:/usr/bin:/bin",
      "NODE_PATH": "/Users/<your_user>/.nvm/versions/node/v22.22.0/lib/node_modules"
    }
  }
}

Claude Code

Run in the terminal:

claude mcp add --transport http blacktwist https://blacktwist.app/api/mcp --header "Authorization: Bearer YOUR_API_KEY"

Or edit the file .mcp.json in project root:

{
  "mcpServers": {
    "blacktwist": {
      "type": "url",
      "url": "https://blacktwist.app/api/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY"
      }
    }
  }
}

Cursor (Settings > MCP):

  • Name: blacktwist
  • Type: url
  • Server URL: https://blacktwist.app/api/mcp
  • Headers: Authorization: Bearer YOUR_API_KEY
3. Start Using It

Once connected, you can ask your AI assistant things like:

  • "List my connected social accounts"
  • "Schedule a post on Threads for tomorrow at 9am saying: Just shipped a new feature!"
  • "Edit my latest draft to say: Updated copy with better hook"
  • "Add a second post to my thread about product launches"
  • "Show me my analytics for the last 7 days"
  • "What are my best posting times?"
  • "List my upcoming scheduled posts"
  • "Tag my latest draft with growth and product launch"

Server Details

PropertyValue
URLhttps://blacktwist.app/api/mcp
AuthOAuth 2.1 (PKCE) or Bearer token (API key)
TransportStreamable HTTP (stateless)
ProtocolMCP via JSON-RPC over HTTP POST

Team Context (teamId)

Most tools accept an optional teamId parameter to scope operations to a specific team.

Behavior:

  1. If teamId is a team ID, the tool operates in that team's context (membership is verified).
  2. If teamId is "personal", the tool operates in personal mode (user's own data only, no team).
  3. If teamId is omitted, the tool falls back to the user's currently active team (from user settings). If no team is active, this is equivalent to "personal".

Tools that support teamId: list_providers, list_posts, list_drafts, create_post, get_thread, delete_thread, reschedule_thread, list_time_slots, list_tags, get_subscription, get_follow_up_templates, and all analytics tools.

Tools without teamId: list_teams (lists all teams), get_user_settings (personal settings), list_viral_templates / list_viral_template_categories (account-wide content library), edit_post / edit_thread / get_thread_follow_up / set_thread_follow_up / get_thread_tags / set_thread_tags / set_thread_auto_repost (thread-based access handles team auth internally via userCanAccessThread).

Subscription access: Analytics tools that require a paid plan will check the user's own subscription first. If the user doesn't have one, the system also checks whether any team owner the user belongs to has an active plan. This means team members can access paid features through their team owner's subscription.


Available Tools

Providers
list_providers

List all connected social media accounts (Threads and Bluesky).

ParameterTypeRequiredDescription
teamIdstringNoTeam ID. If not provided, uses the active team. Pass "personal" for the personal account. When set to a team, shows only providers linked to that team.

Returns: Array of providers with id, provider, providerUserId, providerUsername, displayName, profilePicture.

Note: Many other tools require a providerId (the id field) or providerUserId from this response.


Teams
list_teams

List all teams the user belongs to, including their role and which team is currently active. Always includes a "personal" entry representing the user's personal (non-team) account.

Parameters: None

Returns: Array of teams with id, name, role (OWNER, ADMIN, MEMBER, GUEST), isActive, createdAt.

The personal entry has id: "personal" and is marked isActive: true when no team is currently selected.

Note: The team id can be passed as teamId to other tools (list_posts, list_drafts, create_post, etc.). Pass "personal" to explicitly use the personal account. When teamId is omitted, those tools automatically use the user's currently active team.


Posts & Threads
create_post

Create a new post or thread. A thread is multiple posts linked together.

ParameterTypeRequiredDescription
providerIdstringYesProvider ID from list_providers
postsarrayYesArray of post objects (see below)
scheduleAtstringNoISO 8601 datetime. If omitted, saves as draft. Times without a timezone offset (e.g. 2025-03-15T09:00:00) are interpreted in the user's configured timezone.
autoRepostobjectNo{ enabled: boolean, delays: string[] } where delays are hours, e.g. ["168", "336"] for 7 and 14 days. If omitted, the user's default auto-repost setting is applied.
teamIdstringNoTeam ID. If not provided, uses the active team. Pass "personal" for the personal account.

Default behaviors applied automatically:

  • Auto-repost: If autoRepost is not provided and the user has auto-repost enabled in settings, the default delays are applied to the first post. This happens for drafts too, so a draft still carries its delays once it is scheduled — reschedule_thread only moves scheduledAt and does not add them retroactively.
  • Follow-up (auto-plug): If the provider has a default follow-up template enabled, it is automatically copied and attached to the thread. The response includes autoPlugApplied: true when this happens.
  • Timezone: Times without an offset are interpreted in the user's notificationsTimezone setting. Times with an offset (Z, +02:00 or +0200) are used as that exact instant, with no timezone lookup. This applies to reschedule_thread and list_posts too.

Each post object:

FieldTypeRequiredDescription
textstringYesPost content
topicstringNoTopic tag
mediaobject[]NoMedia attachments via public URL (see Media Attachments)
spoilerRangesobject[]NoText spoiler ranges (see Spoilers)
isSpoilerMediabooleanNoIf true, all media in this post is marked as a spoiler (see Spoilers)

Example — single post:

{
  "providerId": "clx...",
  "posts": [{ "text": "Hello world!" }],
  "scheduleAt": "2025-03-15T09:00:00"
}

Example — thread:

{
  "providerId": "clx...",
  "posts": [
    { "text": "Thread time! Here's what I learned this week 🧵" },
    { "text": "1/ First lesson: consistency beats perfection" },
    { "text": "2/ Second lesson: engage with your community daily" }
  ],
  "scheduleAt": "2025-03-15T09:00:00"
}
Media Attachments

Tools that accept media (create_post, edit_post, edit_thread, set_thread_follow_up) let you attach images and videos via public URLs. The server downloads, validates against Threads specs, and uploads them automatically.

Each media object:

FieldTypeRequiredDescription
urlstringYesPublic URL of the image or video
altTextstringNoAlt text for accessibility

Threads image specs:

  • Formats: JPEG, PNG
  • Max file size: 8 MB
  • Max aspect ratio: 10:1

Threads video specs:

  • Formats: MP4, MOV
  • Max file size: 1 GB
  • Duration: up to 5 minutes
  • Max width: 1920px

Example — post with an image:

{
  "providerId": "clx...",
  "posts": [
    {
      "text": "Check out this view!",
      "media": [
        {
          "url": "https://example.com/photo.jpg",
          "altText": "Sunset over the mountains"
        }
      ]
    }
  ],
  "scheduleAt": "2025-03-15T09:00:00"
}
Spoilers (Threads only)

Spoilers allow you to hide portions of text or media behind a blur effect on Threads. Viewers tap to reveal the hidden content.

Text spoilers are defined as an array of character ranges:

FieldTypeRequiredDescription
offsetnumberYesCharacter offset (0-indexed)
lengthnumberYesNumber of characters in the range
  • Maximum 10 spoiler ranges per post
  • Ranges refer to character positions in the text field

Media spoilers are a single boolean flag per post. When isSpoilerMedia is true, all media (images and videos) in the post is blurred until the viewer taps to reveal.

Text and media spoilers are independent — a post can have both, either, or neither.

Example — post with text spoiler:

{
  "providerId": "clx...",
  "posts": [
    {
      "text": "The winner is John!",
      "spoilerRanges": [{ "offset": 14, "length": 5 }]
    }
  ]
}

Example — post with media spoiler:

{
  "providerId": "clx...",
  "posts": [
    {
      "text": "Spoiler alert! Check the image",
      "media": [{ "url": "https://example.com/reveal.jpg" }],
      "isSpoilerMedia": true
    }
  ]
}

In responses (list_posts, list_drafts, get_thread), each post includes:

  • spoilerRanges: array of { offset, length } (empty if no text spoilers)
  • isSpoilerMedia: boolean (true if media is spoilered)

list_posts

List scheduled and published posts within a date range.

ParameterTypeRequiredDescription
fromstringYesStart date (ISO 8601). Times without a timezone offset are interpreted in the user's configured timezone.
tostringYesEnd date (ISO 8601). Times without a timezone offset are interpreted in the user's configured timezone.
providerIdstringNoProvider ID (from list_providers). If provided, only returns posts for this provider.
teamIdstringNoTeam ID. If not provided, uses the active team. Pass "personal" for the personal account.
list_drafts

List all draft posts (not yet scheduled).

ParameterTypeRequiredDescription
teamIdstringNoTeam ID. If not provided, uses the active team. Pass "personal" for the personal account.
get_thread

Get a specific thread with all its posts, media, and analytics.

ParameterTypeRequiredDescription
threadIdstringYesThe thread ID
teamIdstringNoTeam ID. If not provided, uses the active team. Pass "personal" for the personal account.
edit_post

Edit the text content, topic, or media of a single post. Only posts with status READY (drafts or scheduled) can be edited — published, processing, or failed posts are rejected. Use get_thread first to retrieve post IDs.

ParameterTypeRequiredDescription
postIdstringYesThe post ID (from get_thread)
contentstringNoNew text content
topicstringNoNew topic tag. Pass null to remove the topic.
mediaobject[]NoReplace all media via public URL (see Media Attachments). Omit to keep existing media unchanged.
spoilerRangesobject[]NoReplace text spoiler ranges (see Spoilers). Omit to keep unchanged. Pass [] to remove all.
isSpoilerMediabooleanNoSet media spoiler flag. true = blurred, false = remove spoiler. Omit to keep unchanged.

At least one of content, topic, media, spoilerRanges, or isSpoilerMedia must be provided.

Returns: postId, threadId, updated (object indicating which fields were changed).

Example — update text:

{
  "postId": "clx...",
  "content": "Updated post content"
}

Example — update text and clear topic:

{
  "postId": "clx...",
  "content": "New content",
  "topic": null
}
edit_thread

Edit a thread by adding, removing, or reordering its posts. Provide the full desired list of posts — existing posts (with id) are updated, new posts (without id) are created, and existing posts not in the list are removed. Only threads where all posts have status READY can be edited.

ParameterTypeRequiredDescription
threadIdstringYesThe thread ID
postsarrayYesOrdered array of posts (min 1). Order determines postOrder (0-indexed). See below.

Each post object:

FieldTypeRequiredDescription
idstringNoPost ID for existing posts. Omit to create a new post in this position.
textstringYesPost content
topicstringNoTopic tag
mediaobject[]NoMedia attachments via public URL (see Media Attachments)
spoilerRangesobject[]NoText spoiler ranges (see Spoilers)
isSpoilerMediabooleanNoIf true, all media in this post is marked as a spoiler

New posts inherit scheduledAt, provider, providerUserId, and teamId from the existing thread.

Returns: threadId, postsUpdated, postsCreated, postsDeleted, totalPosts.

Example — reorder and add a post:

{
  "threadId": "abc123",
  "posts": [
    { "id": "existing-post-2", "text": "Now this is first" },
    { "id": "existing-post-1", "text": "Now this is second" },
    { "text": "Brand new third post" }
  ]
}

Example — remove a post (omit it from the list):

{
  "threadId": "abc123",
  "posts": [{ "id": "existing-post-1", "text": "Keep only this one" }]
}
delete_thread

Permanently delete a thread and all its posts.

ParameterTypeRequiredDescription
threadIdstringYesThe thread ID
teamIdstringNoTeam ID. If not provided, uses the active team. Pass "personal" for the personal account.
reschedule_thread

Change the scheduled date/time for a thread.

ParameterTypeRequiredDescription
threadIdstringYesThe thread ID
scheduledAtstringYesNew datetime (ISO 8601). Times without a timezone offset are interpreted in the user's configured timezone.
teamIdstringNoTeam ID. If not provided, uses the active team. Pass "personal" for the personal account.

Note: This changes scheduledAt only. It does not add auto-repost delays or a follow-up to a thread that has none — use set_thread_auto_repost and set_thread_follow_up for that.

set_thread_auto_repost

Set or remove auto-repost on an existing thread. Auto-repost republishes the thread's first post after each delay has elapsed since publishing.

ParameterTypeRequiredDescription
threadIdstringYesThe thread ID
enabledbooleanYesWhether auto-repost is enabled. When false, all delays are removed from the thread.
delaysstring[]NoDelays in hours as strings, e.g. ["168", "336", "720"] for 7, 14 and 30 days. Required when enabled is true. Replaces any existing delays — not additive.

Delays must be positive whole numbers of hours. Enabling with an empty or missing delays array is rejected rather than silently clearing the thread.

Returns: threadId, enabled, delays.

Use get_user_settings to read the user's default delays (autoRepostDelays), and get_thread to see a thread's current autoRepostDelay. New posts pick up the default automatically — this tool is for changing a thread after the fact.


Tags

Tags organize posts (drafts, scheduled, and published) and can be used to filter the Posts Analytics view in the app. Tags are scoped per workspace: team tags are shared with the whole team, personal tags are private. Tags apply to a whole thread, not to individual posts within it.

list_tags

List the tags available in the workspace for organizing posts.

ParameterTypeRequiredDescription
teamIdstringNoTeam ID. If not provided, uses the active team. Pass "personal" for the personal account.

Returns: { tags } — array of tags with id, name, color.

get_thread_tags

Get the tags currently on a thread.

ParameterTypeRequiredDescription
threadIdstringYesThe thread ID to get tags for

Returns: { threadId, tags } where tags is an array of { id, name, color }.

set_thread_tags

Set the tags on a thread. This single tool covers setting, updating, and removing tags: it replaces the thread's existing tags with the exact set you pass in.

ParameterTypeRequiredDescription
threadIdstringYesThe thread ID to set tags for
tagNamesstring[]YesThe full desired set of tag names for the thread. Replaces existing tags — not additive. Names that don't exist yet are created automatically. Pass [] to remove all tags.

Behavior:

  • Names are trimmed, empty entries dropped, and duplicates removed case-insensitively ("Growth" and "growth" are the same tag).
  • New tag names are created automatically in the thread's workspace scope (team thread → team tag, personal thread → personal tag) with an auto-assigned color.
  • Tag names are limited to 40 characters.

Returns: { threadId, tags } — the final tag set on the thread.

Example — tag a thread:

{
  "threadId": "abc123",
  "tagNames": ["growth", "product launch"]
}

Example — remove all tags:

{
  "threadId": "abc123",
  "tagNames": []
}

Analytics

All analytics tools require a providerUserId (from list_providers) and accept an optional teamId to scope access.

Paid plan required: get_metric_timeseries, get_post_analytics, get_follower_growth, and get_best_time_to_post require a paid plan (user or team owner). Free-plan users will receive: "A paid plan is required to access the analytics. Subscribe at https://blacktwist.app#pricing". The remaining tools (get_live_metrics, get_consistency, get_daily_recap, get_recommendations) are available on all plans.

Date ranges: The from and to dates are both inclusive. For example, from: "2026-03-01" and to: "2026-03-08" includes all data from March 1 through the end of March 8.

get_live_metrics

Get engagement metrics with percentage change vs. the previous period.

ParameterTypeRequiredDescription
providerUserIdstringYesProvider user ID
fromstringNoStart date (defaults to 7 days ago)
tostringNoEnd date (defaults to now)
teamIdstringNoTeam ID. If not provided, uses the active team. Pass "personal" for the personal account.

Returns: views, likes, replies, reposts, quotes — each with value and percentageChange.

get_metric_timeseries

Get daily data points for a specific metric over a date range.

ParameterTypeRequiredDescription
providerUserIdstringYesProvider user ID
metricenumYesOne of: VIEWS, LIKES, REPLIES, REPOSTS, QUOTES, ENGAGEMENT_RATE, FOLLOWERS_COUNT
fromstringYesStart date (ISO 8601)
tostringYesEnd date (ISO 8601)
teamIdstringNoTeam ID. If not provided, uses the active team. Pass "personal" for the personal account.
get_post_analytics

Get per-post engagement metrics for posts within a date range. Each post also carries its thread continuation parts (self-replies), so multi-post threads are returned in full.

ParameterTypeRequiredDescription
providerUserIdstringYesProvider user ID
fromstringYesStart date (ISO 8601)
tostringYesEnd date (ISO 8601)
includeThreadPartsbooleanNoInclude each post's thread continuation parts (self-replies) in a thread array. Defaults to true; pass false to omit.
sortByenumNoRank posts before the 100-post cap. One of: timestamp, views, likes, replies, reposts, quotes, engagementRate. Defaults to timestamp.
sortOrderenumNoasc or desc. Defaults to desc.
searchstringNoOnly return posts whose text contains this string (case-insensitive, max 200 characters).
tagNamesstring[]NoOnly return posts carrying at least one of these tags (names from list_tags, case-insensitive, max 50). Unknown names return an error.
teamIdstringNoTeam ID. If not provided, uses the active team. Pass "personal" for the personal account.

Returns: posts, count (posts returned) and total (posts in the range matching the filters). Sorting and filters apply to the whole date range before the 100-post cap, so sortBy: "views" returns the actual top posts even when the range holds thousands; without sortBy, the 100 most recent posts come back. total > count means the list was capped.

Each post has mediaId, text (truncated to 200 characters), timestamp, permalink, provider, and analytics (views, likes, replies, reposts, quotes, engagementRate). Each post also includes a thread array of its continuation parts, reconstructed from the user's self-replies, so a multi-post thread comes back with all of its posts. Each part has replyId, text (truncated to 200 characters), permalink, timestamp, partOrder (starting at 2; the root post is part 1), and its own analytics (views, likes, replies, reposts, quotes, engagementRate). Single posts return an empty thread. Pass includeThreadParts: false to skip reconstruction entirely and return empty thread arrays.

Examples:

  • Top 10 posts of the last 6 months: sortBy: "views", then take the first 10.
  • Posts about a topic: search: "morning routine".
  • Best posts in a tag: tagNames: ["Evergreen"], sortBy: "engagementRate".
get_follower_growth

Get daily follower count over time.

ParameterTypeRequiredDescription
providerUserIdstringYesProvider user ID
fromstringYesStart date (ISO 8601)
tostringYesEnd date (ISO 8601)
teamIdstringNoTeam ID. If not provided, uses the active team. Pass "personal" for the personal account.

Returns: Daily followers data points and totalGrowth.

get_consistency

Get a 365-day posting consistency heatmap.

ParameterTypeRequiredDescription
providerUserIdstringYesProvider user ID
teamIdstringNoTeam ID. If not provided, uses the active team. Pass "personal" for the personal account.

Returns: days (array of { date, count }), totalPosts, daysWithPosts, currentStreak, recordStreak.

get_daily_recap

Get yesterday's summary.

ParameterTypeRequiredDescription
providerUserIdstringYesProvider user ID
teamIdstringNoTeam ID. If not provided, uses the active team. Pass "personal" for the personal account.

Returns: date, newFollowers, postsCount.

get_recommendations

Get posting recommendations based on your analytics patterns.

ParameterTypeRequiredDescription
providerUserIdstringYesProvider user ID
teamIdstringNoTeam ID. If not provided, uses the active team. Pass "personal" for the personal account.

Returns: bestPostingHoursUtc, topPerformingPosts, currentStreak, recordStreak.

get_best_time_to_post

Get the best times to post based on historical engagement over the last 10 weeks. Mirrors the "Best time to post" analytics chart — engagement bucketed by hour of day, both averaged across the whole week and broken down per day of the week. Hours are reported in the user's own timezone.

Active subscription required: This tool requires an active subscription — the user must have their own paid plan, or be part of a team whose owner has an active subscription. Otherwise it returns: "The best time to post requires an active subscription. You need your own paid plan, or to be part of a team whose owner has an active subscription. Subscribe at https://blacktwist.app#pricing"

Timezone: Resolved automatically server-side from the user's notificationsTimezone setting (defaulting to UTC). MCP callers normally cannot know the user's timezone, so they should leave timezoneOffset unset and let it be derived.

ParameterTypeRequiredDescription
providerUserIdstringYesProvider user ID
timezoneOffsetnumberNoOverride for the timezone used to bucket hours. Leave unset to use the user's configured account timezone (recommended). If set, use minutes matching JavaScript's Date.getTimezoneOffset() (behind UTC: UTC+2 is -120).
viewenumNo"average", "daily", or "both" (default). Selects which breakdown to return.
teamIdstringNoTeam ID. If not provided, uses the active team. Pass "personal" for the personal account.

Returns: provider, timezone (IANA name, or "custom" when an override offset is used), timezoneOffsetMinutes, timezoneInfo (a human-readable note naming the timezone used and linking to https://blacktwist.app/write?settings=notifications so the user can correct it if it's wrong), and (depending on view) averageView (bestHours + 24 hourly averageEngagement values across the whole week) and dailyView (7 days, each with dayName, bestHour, and 24 hourly averageEngagement values).


Follow-Up (Auto-Plug)

The follow-up system automatically replies to your post after it reaches engagement thresholds (likes, replies, reposts) or a time delay.

Timing: The follow-up job only evaluates posts published within the last 24 hours. Attaching a follow-up to a thread published more than a day ago has no effect — it will never fire. Attach follow-ups before the post goes out, or within the first day after.

get_follow_up_templates

List saved follow-up templates for a provider.

ParameterTypeRequiredDescription
userProviderIdstringYesProvider id from list_providers
teamIdstringNoTeam ID. If not provided, uses the active team. Pass "personal" for the personal account. Scopes which team members' templates are included.
get_thread_follow_up

Get the follow-up configuration for a specific thread.

ParameterTypeRequiredDescription
threadIdstringYesThe thread ID
set_thread_follow_up

Set or update the follow-up for a thread. This replaces the thread's follow-up configuration wholesale — fields you omit are reset to their defaults rather than left as they were.

ParameterTypeRequiredDescription
threadIdstringYesThe thread ID
isEnabledbooleanYesEnable/disable the follow-up
textstringNoFollow-up reply text
imagesstring[]NoImage URLs
delayTimeMinutesnumberNoTime delay trigger (minutes)
delayTimeMinutesEnabledbooleanNoEnable time delay trigger
numberOfLikesnumberNoLikes threshold
numberOfLikesEnabledbooleanNoEnable likes trigger
numberOfRepliesnumberNoReplies threshold
numberOfRepliesEnabledbooleanNoEnable replies trigger
numberOfRepostsnumberNoReposts threshold
numberOfRepostsEnabledbooleanNoEnable reposts trigger
mediaobject[]NoMedia attachments via public URL (see Media Attachments)

Scheduling
list_time_slots

List configured posting time slots for a provider. Time slots define preferred scheduling times.

ParameterTypeRequiredDescription
providerIdstringYesProvider id from list_providers
teamIdstringNoTeam ID. If not provided, uses the active team. Pass "personal" for the personal account.

Returns: Array of { timeSlotHour, timeSlotMinute, weekDays } where weekDays is [0-6] (0 = Sunday).


Viral Templates

Browse the library of viral post templates extracted from high-performing posts. Each template includes the structure, usage instructions, and the original example post with its engagement metrics.

Paid plan required: Both tools below require an active paid plan (user-owned plan, Lifetime Deal, or membership in a workspace whose owner has a paid plan). Free-plan users will receive: "A paid plan is required to access viral post templates".

list_viral_templates

List viral post templates with filtering, free-text search, sorting, and pagination.

ParameterTypeRequiredDescription
searchstringNoFree-text search. Matches against the example post text (case-insensitive).
categorystringNoFilter to a single category. Use a value returned by list_viral_template_categories or the categories field of a previous response.
sortenumNoSort by example-post engagement: views (default), likes, replies, or engagementRate. Descending.
pagenumberNoPage number, 1-indexed. Defaults to 1.
pageSizenumberNoItems per page (1–50). Defaults to 20. Values above 50 are clamped to 50.

Returns: { templates, categories, totalCount, page, pageSize }.

Each template:

FieldTypeDescription
idstringTemplate ID
categorystringTemplate category
templatestringThe template structure
instructionsstringHow to use the template
exampleTextstringThe original viral post text
viewsnumberViews on the example post
likesnumberLikes on the example post
repliesnumberReplies on the example post
engagementRatenumberEngagement rate of the example post (likes+replies/views)
list_viral_template_categories

List all available template categories with the number of templates in each. Useful as a discovery step before calling list_viral_templates with a category filter.

Parameters: None

Returns: { categories: [{ category, templateCount }] }, ordered alphabetically by category.


Account
get_subscription

Get your current plan, remaining post limits, and account quotas.

ParameterTypeRequiredDescription
teamIdstringNoTeam ID. If not provided, uses the active team. Pass "personal" for the personal account.

Returns: planType, planName, isFreePlan, remainingPosts, limits (maxTeams, maxMembers, maxAccounts).

get_user_settings

Get your user settings including timezone, date format, and auto-repost config.

Parameters: None

Returns: notificationsTimezone, dateFormat, autoRepostEnabled, autoRepostDelays, and more.


Common Workflows

Schedule a post at your next free time slot
  1. list_providers → get your provider ID
  2. list_time_slots → find available slots
  3. create_post → schedule with the next slot time
Check weekly performance
  1. list_providers → get your provider user ID
  2. get_live_metrics → see this week vs. last week
  3. get_recommendations → get improvement suggestions
Edit an existing post
  1. get_thread → retrieve the thread and its post IDs
  2. edit_post → update the text, topic, or media of a specific post
Add a post to an existing thread
  1. get_thread → retrieve current posts with their IDs
  2. edit_thread → pass all existing posts (with IDs) plus the new post (without ID)
Create a post with follow-up
  1. create_post → create and schedule the post (returns threadId)
  2. set_thread_follow_up → attach a promotional reply

If the provider already has a default follow-up template enabled, create_post attaches it for you and step 2 is only needed to override it.

Add a follow-up or auto-repost to an existing thread
  1. list_posts or list_drafts → find the thread and its threadId
  2. get_follow_up_templates → read the provider's saved template values
  3. set_thread_follow_up → attach the reply, copying the fields from the template
  4. set_thread_auto_repost → set the delays (use get_user_settings for the user's defaults)

This works on threads that haven't been published yet, or published within the last 24 hours. Beyond that, the follow-up job no longer looks at the post.

Organize posts with tags
  1. create_post → create the post (returns threadId)
  2. set_thread_tags → tag it (e.g. ["growth", "tips"]) — new tag names are created automatically
  3. list_tags / get_thread_tags → discover existing tags or check what's on a thread

Prompts

The MCP server includes built-in prompts — reusable templates that guide your AI assistant through multi-step workflows. In Claude Desktop, access them by typing / in the chat. In Claude Code, run claude prompts to list them.

weekly-report

Generate a weekly performance report with engagement metrics, top posts, follower growth, and recommendations.

Arguments:

ParameterRequiredDescription
providerUserIdYesYour provider user ID

Example: "Run the weekly-report prompt for my Threads account"

content-ideas

Generate post ideas based on your best-performing content and posting patterns.

Arguments:

ParameterRequiredDescription
providerUserIdYesYour provider user ID
topicNoTopic to focus on (e.g. "productivity", "startups")

Example: "Give me content ideas about marketing"

optimize-schedule

Analyze your posting patterns and suggest an optimal posting schedule for the week.

Arguments:

ParameterRequiredDescription
providerUserIdYesYour provider user ID
providerIdYesYour provider ID

Example: "Help me optimize my posting schedule"

draft-review

Review your current drafts and get suggestions to improve them before publishing.

Arguments: None

Example: "Review my drafts and tell me how to improve them"

monthly-recap

Generate a comprehensive monthly recap with key metrics, top posts, and growth trends.

Arguments:

ParameterRequiredDescription
providerUserIdYesYour provider user ID

Example: "Generate my monthly recap"


API Key Management

  • Create keys: Settings > MCP > Create API Key
  • View keys: Settings > MCP (shows prefix, creation date, last used)
  • Delete keys: Click the trash icon next to any key
  • Endpoint: GET/POST/DELETE /api/user/mcp-keys

Keys are hashed with SHA-256 before storage. The raw key is only shown once at creation time.


Authentication

The MCP server supports two authentication methods. Both use the Authorization: Bearer <token> header.

OAuth 2.1 (for claude.ai and OAuth-capable clients)

The server implements the MCP Authorization specification with OAuth 2.1 and PKCE (S256). OAuth-capable clients like claude.ai handle the flow automatically.

OAuth endpoints:

EndpointDescription
GET /.well-known/oauth-protected-resourceResource metadata (RFC 9728)
GET /.well-known/oauth-authorization-serverAuthorization server metadata (RFC 8414)
POST /api/mcp/oauth/registerDynamic client registration (RFC 7591)
GET /api/mcp/oauth/authorizeAuthorization endpoint
POST /api/mcp/oauth/tokenToken exchange and refresh
POST /api/mcp/oauth/revokeToken revocation (RFC 7009)

How it works:

  1. The client discovers the OAuth endpoints via the well-known metadata
  2. The client registers itself using dynamic client registration
  3. The user is redirected to sign in with their BlackTwist account (uses the existing BlackTwist login)
  4. After sign-in, an authorization code is issued and exchanged for access + refresh tokens
  5. Access tokens expire after 1 hour and are refreshed automatically using the refresh token (30-day lifetime)
API Keys (for desktop clients and CLI tools)

API keys are static tokens prefixed with bt_mcp_. They don't expire by default and are suitable for clients that don't support OAuth (Claude Desktop, Cursor, Claude Code CLI).

See API Key Management for how to create and manage keys.


Technical Details

  • Endpoint: POST /api/mcp (JSON-RPC)
  • Transport: WebStandardStreamableHTTPServerTransport from @modelcontextprotocol/sdk
  • Mode: Stateless (no session management)
  • Auth: OAuth 2.1 with PKCE (S256) or API key via Authorization: Bearer <key> header
  • Unauthenticated discovery: initialize and tools/list can be called without authentication, so MCP registries (e.g. Glama) can inspect available tools
  • Rate limiting: Authenticated requests are rate-limited to 60 req/min per IP. Unauthenticated discovery requests are limited to 10 req/min per IP.
  • Source code: src/libs/mcp/ (server, auth, oauth, tools), src/app/api/mcp/ (route handler, OAuth endpoints)

Comments

Markdown supported

New to MCP?

From what the protocol is to installing your first server, these answers cover most first questions.

An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.