Post X MCP

MCP server for posting to X (Twitter) with multi-account support

Dokumentation

mcp-server-post-x

An MCP (Model Context Protocol) server for X (Twitter). Built in Rust using OAuth 1.0a and the X API v2. Supports multiple accounts.

Communicates via stdio using JSON-RPC 2.0.

Tools

ToolDescription
list_accountsList available accounts and which is the default
post_tweetPost a tweet with optional media (up to 4 images, 1 video, or 1 GIF)
post_threadPost a thread of up to 25 tweets, each with optional media
delete_tweetDelete a tweet by ID or URL
upload_mediaUpload media for later attachment (returns a media_id)
update_profileUpdate your bio/description, display name, location, and/or website URL (legacy v1.1 endpoint)
update_profile_bannerUpdate profile header/banner image (legacy v1.1 endpoint)
search_tweetsSearch recent tweets (last 7 days) with Twitter operators
get_timelineGet your home timeline in reverse chronological order
get_bookmarksGet your bookmarked tweets (paginated)
get_meGet the authenticated user's profile
lookup_userLook up any user by @username or numeric ID
get_followersList your followers (paginated)
get_followingList who you follow (paginated)
get_all_followersFetch ALL your followers in a single call (auto-paginates)
get_all_followingFetch ALL accounts you follow in a single call (auto-paginates)
like_tweetLike a tweet by ID or URL
unlike_tweetUnlike a tweet by ID or URL
retweetRetweet a tweet by ID or URL
unretweetUndo a retweet by ID or URL
bookmark_tweetBookmark a tweet by ID or URL
unbookmark_tweetRemove a bookmark by ID or URL
get_trendsGet current trending topics for a WOEID location (default: worldwide)
get_dm_eventsGet recent direct messages across all conversations
send_dmSend a direct message to a conversation
follow_userFollow a user by username or ID
unfollow_userUnfollow a user by username or ID

All tools accept an optional account parameter to select which X account to use. Omit it to use the default account.

Quick Start

1. Build

cargo build --release

Produces target/release/post-x (optimized with LTO, stripped).

2. Configure credentials

The server looks for the config at the first of:

  • $XDG_CONFIG_HOME/mcp-server-post-x/config.toml
  • ~/.config/mcp-server-post-x/config.toml

You can also run without any config file by providing credentials via environment variables (great for containers/CI):

export POST_X_API_KEY=...
export POST_X_API_KEY_SECRET=...
export POST_X_ACCESS_TOKEN=...
export POST_X_ACCESS_TOKEN_SECRET=...
# Optional:
# export POST_X_ACCOUNT_NAME=myaccount

Create the config file (classic approach):

mkdir -p ~/.config/mcp-server-post-x

Create ~/.config/mcp-server-post-x/config.toml:

Single account (no default_account needed):

[accounts.myaccount]
api_key = "your-api-key"
api_key_secret = "your-api-key-secret"
access_token = "your-access-token"
access_token_secret = "your-access-token-secret"

Multiple accounts:

default_account = "myaccount"

[accounts.myaccount]
api_key = "your-api-key"
api_key_secret = "your-api-key-secret"
access_token = "your-access-token"
access_token_secret = "your-access-token-secret"

[accounts.otheraccount]
api_key = "your-api-key"
api_key_secret = "your-api-key-secret"
access_token = "other-access-token"
access_token_secret = "other-access-token-secret"

Notes:

  • Account keys are X usernames (e.g. [accounts.codechap])
  • If you have multiple accounts, default_account is required
  • If you have one account, default_account is optional (auto-detected)
  • Multiple accounts can share the same api_key/api_key_secret (same X app). Only the access_token/access_token_secret differ per account.

Secure it:

chmod 700 ~/.config/mcp-server-post-x
chmod 600 ~/.config/mcp-server-post-x/config.toml

See Getting credentials below for how to obtain these.

3. Add to your MCP client

Claude Code (~/.claude.json):

{
  "mcpServers": {
    "post-x": {
      "command": "/path/to/post-x"
    }
  }
}

Then ask Claude things like:

  • "Post a tweet saying hello world"
  • "Post a tweet as securechap saying hello world"
  • "Search for tweets about Rust"
  • "Show me my timeline"
  • "Like this tweet: https://x.com/someone/status/123456"
  • "Who are my followers?"
  • "Look up @elonmusk"
  • "List my accounts"

Tool Reference

list_accounts

No required parameters. Returns available account names, which is the default, and cached usernames.

post_tweet

ParamTypeRequiredDescription
accountstringnoAccount to use (omit for default)
textstringyesTweet text (max 280 characters)
mediaarraynoMedia to upload and attach. Each item: { path, alt_text? }. Max 4 images, or 1 video, or 1 GIF.
media_idsarraynoPre-uploaded media IDs to attach (max 4). Mutually exclusive with media.
reply_tostringnoTweet ID to reply to

post_thread

ParamTypeRequiredDescription
accountstringnoAccount to use (omit for default)
tweetsarrayyesArray of tweets (max 25). Each: { text, media? }

delete_tweet / like_tweet / unlike_tweet / retweet / unretweet / bookmark_tweet / unbookmark_tweet

ParamTypeRequiredDescription
accountstringnoAccount to use (omit for default)
tweet_idstringyesTweet ID or full tweet URL

All accept URLs like https://x.com/user/status/123456 — the ID is extracted automatically.

upload_media

ParamTypeRequiredDescription
accountstringnoAccount to use (omit for default)
pathstringyesLocal file path. Supported: jpeg/png/webp (max 5MB), gif (max 15MB), mp4 (max 512MB)
alt_textstringnoAlt text (images and GIFs only, not video)

Returns a media_id to use with post_tweet's media_ids param.

update_profile

Update the authenticated user's profile text fields. At least one field must be provided; only the fields you pass are changed, and passing an empty string clears that field.

ParamTypeRequiredDescription
accountstringnoAccount to use (omit for default)
descriptionstringno*New bio/description (max 160 chars; empty string clears it)
namestringno*New display name (1-50 chars)
locationstringno*New location (max 30 chars; empty string clears it)
urlstringno*New website URL shown on the profile (max 100 chars; empty string clears it)

* At least one of description, name, location, or url is required.

Uses the legacy POST /1.1/account/update_profile.json endpoint (no v2 equivalent). Requires the app to have Read and Write permission; without it the endpoint returns 403.

update_profile_banner

ParamTypeRequiredDescription
accountstringnoAccount to use (omit for default)
pathstringyesLocal file path to banner image (JPEG/PNG/WebP only, max 5MB). X recommends 1500x500 pixels.
widthintegernoWidth of the image (for cropping)
heightintegernoHeight of the image (for cropping)
offset_leftintegernoLeft offset (pixels) for crop start
offset_topintegernoTop offset (pixels) for crop start

Uses the legacy POST /1.1/account/update_profile_banner.json endpoint (base64 banner param; no v2 equivalent). Success returns HTTP 200 with no body.

search_tweets

ParamTypeRequiredDescription
accountstringnoAccount to use (omit for default)
querystringyesSearch query. Supports: from:user, #hashtag, @mention, "exact phrase", -exclude, lang:en
max_resultsintegerno10-100 (default 10)
sort_orderstringnorecency or relevancy
pagination_tokenstringnoNext page token from previous response

get_trends

ParamTypeRequiredDescription
accountstringnoAccount to use (omit for default; determines which app's rate limit is used)
woeidintegernoWOEID for location (default: 1 = Worldwide). See common values in tool description.

Returns trend names and approximate post volumes.

get_timeline

ParamTypeRequiredDescription
accountstringnoAccount to use (omit for default)
max_resultsintegerno1-100 (default 20)
excludestringnoreplies, retweets, or both comma-separated
pagination_tokenstringnoNext page token

get_bookmarks

ParamTypeRequiredDescription
accountstringnoAccount to use (omit for default)
max_resultsintegerno1-100 (default 20)
pagination_tokenstringnoNext page token

lookup_user / follow_user / unfollow_user

ParamTypeRequiredDescription
accountstringnoAccount to use (omit for default)
userstringyesUsername (with or without @) or numeric user ID

get_followers / get_following

ParamTypeRequiredDescription
accountstringnoAccount to use (omit for default)
max_resultsintegerno1-100 (default 20)
pagination_tokenstringnoNext page token

get_all_followers / get_all_following

ParamTypeRequiredDescription
accountstringnoAccount to use (omit for default)
max_usersintegernoSafety cap (default 5000, max 10000). Prevents huge responses for high-follower accounts.

Auto-paginates through results (100 per page) with a 200ms delay between pages. For accounts with tens of thousands of followers, prefer the paginated get_followers / get_following tools instead.

get_dm_events

ParamTypeRequiredDescription
accountstringnoAccount to use (omit for default)
max_resultsintegerno1-100 (default 20)
pagination_tokenstringnoNext page token

send_dm

ParamTypeRequiredDescription
accountstringnoAccount to use (omit for default)
conversation_idstringyesDM conversation ID (get from get_dm_events)
textstringyesMessage text

get_me

ParamTypeRequiredDescription
accountstringnoAccount to use (omit for default)

Returns your user ID, display name, and @username.

Adding Additional Accounts

To add another X account to an existing app (without a separate developer account), use the included OAuth authorization script:

export X_API_KEY="your-app-api-key"
export X_API_KEY_SECRET="your-app-api-key-secret"
./oauth-authorize.sh

Important: The script no longer contains any hardcoded credentials. You must provide your own app's Consumer Keys via the two environment variables shown above.

This runs the 3-legged OAuth 1.0a PIN-based flow:

  1. Opens a URL where the new account authorizes your app
  2. You paste the PIN back into the terminal
  3. It outputs the [accounts.username] config block to add to your config.toml

All accounts you authorize share the same X App (and its rate limits + billing). This is the normal pattern for multi-account usage.

Getting Credentials

  1. Go to developer.x.com and sign up for a developer account
  2. Create a Project and an App in the Developer Console
  3. In your App settings, set up User authentication:
    • App permissions: Read and write (and Direct Messages if you want DM support)
    • Type: Web App, Automated App or Bot
    • Callback URL: https://example.com (not used, but required)
    • Website URL: any valid URL
  4. Go to Keys and tokens and generate:
    • API Key and API Key Secret (under Consumer Keys)
    • Access Token and Access Token Secret (under Authentication Tokens)
  5. Copy all four values into your config.toml under [accounts.yourusername]

The server validates credentials at startup. If you get persistent 401 errors, regenerate your tokens at developer.x.com.

Development

cargo build              # debug build
cargo run                # run in dev mode
RUST_LOG=debug cargo run # debug logging (credentials are redacted)

cargo test               # run unit tests
cargo clippy -- -D warnings   # strict lint check (must pass)
cargo build --release    # optimized binary

Technical Details

  • Auth: OAuth 1.0a with HMAC-SHA1 signatures (RFC 5849, RFC 3986 percent-encoding)
  • Multi-account: Multiple X accounts per server instance, selectable per tool call
  • Tweet API: X API v2 (api.x.com/2/)
  • Media upload: v1.1 chunked upload (upload.twitter.com/1.1/media/upload.json) — INIT/APPEND/FINALIZE/STATUS flow for video/GIF, simple multipart for images
  • Media limits: JPEG/PNG/WebP up to 5MB, GIF up to 15MB, MP4 up to 512MB
  • Media validation: Max 4 images OR 1 video OR 1 GIF per tweet (no mixing)
  • Thread posting: 500ms delay between tweets, chained via in_reply_to_tweet_id
  • Retry logic: Automatic retry with exponential backoff on 503 errors
  • Rate limits: 429 responses include reset timestamp in error message (no auto-retry — the caller decides)
  • Safety: get_all_followers / get_all_following are capped at 10k users by default to avoid destroying LLM context windows
  • Rust edition: 2021 (broad compatibility)

Project Structure

src/
  main.rs    — entry point, config loading, tracing, stdio transport
  server.rs  — MCP tool handlers, response formatting, multi-account routing
  api.rs     — X API client: OAuth signing, tweet/media/user/DM endpoints
  params.rs  — tool parameter types (serde + JSON Schema)