trackmcp
Back to directory
codeChap

mcp-server-x

View on GitHub

MCP server for X (Twitter)

0 stars RustOthers Updated Aug 26, 2026

Documentation

mcp-server-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_accounts`List available accounts and which is the default
`post_tweet`Post a tweet with optional media (up to 4 images, 1 video, or 1 GIF)
`post_thread`Post a thread of up to 25 tweets, each with optional media
`delete_tweet`Delete a tweet by ID or URL
`upload_media`Upload media for later attachment (returns a media_id)
`update_profile`Update your bio/description, display name, location, and/or website URL (legacy v1.1 endpoint)
`update_profile_banner`Update profile header/banner image (legacy v1.1 endpoint)
`search_tweets`Search recent tweets (last 7 days) with Twitter operators
`get_timeline`Get your home timeline in reverse chronological order
`get_bookmarks`Get your bookmarked tweets (paginated)
`get_me`Get the authenticated user's profile
`lookup_user`Look up any user by @username or numeric ID
`get_followers`List your followers (paginated)
`get_following`List who you follow (paginated)
`get_all_followers`Fetch ALL your followers in a single call (auto-paginates)
`get_all_following`Fetch ALL accounts you follow in a single call (auto-paginates)
`like_tweet`Like a tweet by ID or URL
`unlike_tweet`Unlike a tweet by ID or URL
`retweet`Retweet a tweet by ID or URL
`unretweet`Undo a retweet by ID or URL
`bookmark_tweet`Bookmark a tweet by ID or URL
`unbookmark_tweet`Remove a bookmark by ID or URL
`get_trends`Get current trending topics for a WOEID location (default: worldwide)
`get_dm_events`Get recent direct messages across all conversations
`send_dm`Send a direct message to a conversation
`follow_user`Follow a user by username or ID
`unfollow_user`Unfollow 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

bash
cargo build --release

Produces `target/release/mcp-server-x` (optimized with LTO, stripped).

2. Configure credentials

The server looks for the config at the first existing of:

  • `$XDG_CONFIG_HOME/mcp-server-x/config.toml`
  • `~/.config/mcp-server-x/config.toml`
  • `$XDG_CONFIG_HOME/mcp-server-post-x/config.toml` (legacy)
  • `~/.config/mcp-server-post-x/config.toml` (legacy)

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

bash
export X_API_KEY=...
export X_API_KEY_SECRET=...
export X_ACCESS_TOKEN=...
export X_ACCESS_TOKEN_SECRET=...
# Optional:
# export X_ACCOUNT_NAME=myaccount

`POST_X_*` / `POST_X_ACCOUNT_NAME` are still accepted if the `X_*` vars are unset.

Create the config file (classic approach):

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

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

Single account (no `default_account` needed):

toml
[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:

toml
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.
  • `bookmark_tweet` / `unbookmark_tweet` require OAuth 2.0 User Context (`bookmark.write`). Add optional `oauth2_client_id`, `oauth2_client_secret`, `oauth2_access_token`, and `oauth2_refresh_token` on the account that needs bookmarks. OAuth 1.0a stays in use for every other tool. Generate the user token in the X Developer Console (App → Keys & Tokens → OAuth 2.0 Access Token) with `tweet.read`, `users.read`, `bookmark.read`, `bookmark.write`, and `offline.access`. Access tokens last ~2 hours; the server refreshes them with `oauth2_refresh_token` (and writes the rotated tokens back to `config.toml`).

Secure it:

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

See Getting credentials below for how to obtain these.

3. Add to your MCP client

Claude Code (`~/.claude.json`):

json
{
  "mcpServers": {
    "x": {
      "command": "/path/to/mcp-server-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
`account`stringnoAccount to use (omit for default)
`text`stringyesTweet text (max 280 characters)
`media`arraynoMedia to upload and attach. Each item: `{ path, alt_text? }`. Max 4 images, or 1 video, or 1 GIF.
`media_ids`arraynoPre-uploaded media IDs to attach (max 4). Mutually exclusive with `media`.
`reply_to`stringnoTweet ID to reply to

post_thread

ParamTypeRequiredDescription
`account`stringnoAccount to use (omit for default)
`tweets`arrayyesArray of tweets (max 25). Each: `{ text, media? }`

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

ParamTypeRequiredDescription
`account`stringnoAccount to use (omit for default)
`tweet_id`stringyesTweet ID or full tweet URL

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

upload_media

ParamTypeRequiredDescription
`account`stringnoAccount to use (omit for default)
`path`stringyesLocal file path. Supported: jpeg/png/webp (max 5MB), gif (max 15MB), mp4 (max 512MB)
`alt_text`stringnoAlt 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
`account`stringnoAccount to use (omit for default)
`description`stringno*New bio/description (max 160 chars; empty string clears it)
`name`stringno*New display name (1-50 chars)
`location`stringno*New location (max 30 chars; empty string clears it)
`url`stringno*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
`account`stringnoAccount to use (omit for default)
`path`stringyesLocal file path to banner image (JPEG/PNG/WebP only, max 5MB). X recommends 1500x500 pixels.
`width`integernoWidth of the image (for cropping)
`height`integernoHeight of the image (for cropping)
`offset_left`integernoLeft offset (pixels) for crop start
`offset_top`integernoTop 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
`account`stringnoAccount to use (omit for default)
`query`stringyesSearch query. Supports: `from:user`, `#hashtag`, `@mention`, `"exact phrase"`, `-exclude`, `lang:en`
`max_results`integerno10-100 (default 10)
`sort_order`stringno`recency` or `relevancy`
`pagination_token`stringnoNext page token from previous response
ParamTypeRequiredDescription
`account`stringnoAccount to use (omit for default; determines which app's rate limit is used)
`woeid`integernoWOEID for location (default: 1 = Worldwide). See common values in tool description.

Returns trend names and approximate post volumes.

get_timeline

ParamTypeRequiredDescription
`account`stringnoAccount to use (omit for default)
`max_results`integerno1-100 (default 20)
`exclude`stringno`replies`, `retweets`, or both comma-separated
`pagination_token`stringnoNext page token

get_bookmarks

ParamTypeRequiredDescription
`account`stringnoAccount to use (omit for default)
`max_results`integerno1-100 (default 20)
`pagination_token`stringnoNext page token

lookup_user / follow_user / unfollow_user

ParamTypeRequiredDescription
`account`stringnoAccount to use (omit for default)
`user`stringyesUsername (with or without `@`) or numeric user ID

get_followers / get_following

ParamTypeRequiredDescription
`account`stringnoAccount to use (omit for default)
`max_results`integerno1-100 (default 20)
`pagination_token`stringnoNext page token

get_all_followers / get_all_following

ParamTypeRequiredDescription
`account`stringnoAccount to use (omit for default)
`max_users`integernoSafety 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
`account`stringnoAccount to use (omit for default)
`max_results`integerno1-100 (default 20)
`pagination_token`stringnoNext page token

send_dm

ParamTypeRequiredDescription
`account`stringnoAccount to use (omit for default)
`conversation_id`stringyesDM conversation ID (get from `get_dm_events`)
`text`stringyesMessage text

get_me

ParamTypeRequiredDescription
`account`stringnoAccount 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:

bash
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:

    4. Go to Keys and tokens and generate:

      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

      bash
      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

      code
      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)

      Frequently asked questions

      What is mcp-server-x?

      mcp-server-x is MCP server for X (Twitter)

      How do I install mcp-server-x?

      Open the GitHub repository and follow its README. Most MCP servers are added to your client's MCP config, then called by your agent.

      Is mcp-server-x open source?

      Yes — it is hosted on GitHub at https://github.com/codeChap/mcp-server-x.

      Related MCP tools

      Run your own MCP server? See who uses it and what to fix.

      Measure it with TrackMCP