mcp-server-x
MCP server for X (Twitter)
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
| Tool | Description |
|---|---|
| `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
cargo build --releaseProduces `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):
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):
mkdir -p ~/.config/mcp-server-xCreate `~/.config/mcp-server-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.
- `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:
chmod 700 ~/.config/mcp-server-x
chmod 600 ~/.config/mcp-server-x/config.tomlSee Getting credentials below for how to obtain these.
3. Add to your MCP client
Claude Code (`~/.claude.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
| Param | Type | Required | Description |
|---|---|---|---|
| `account` | string | no | Account to use (omit for default) |
| `text` | string | yes | Tweet text (max 280 characters) |
| `media` | array | no | Media to upload and attach. Each item: `{ path, alt_text? }`. Max 4 images, or 1 video, or 1 GIF. |
| `media_ids` | array | no | Pre-uploaded media IDs to attach (max 4). Mutually exclusive with `media`. |
| `reply_to` | string | no | Tweet ID to reply to |
post_thread
| Param | Type | Required | Description |
|---|---|---|---|
| `account` | string | no | Account to use (omit for default) |
| `tweets` | array | yes | Array of tweets (max 25). Each: `{ text, media? }` |
delete_tweet / like_tweet / unlike_tweet / retweet / unretweet / bookmark_tweet / unbookmark_tweet
| Param | Type | Required | Description |
|---|---|---|---|
| `account` | string | no | Account to use (omit for default) |
| `tweet_id` | string | yes | Tweet ID or full tweet URL |
All accept URLs like `https://x.com/user/status/123456` — the ID is extracted automatically.
upload_media
| Param | Type | Required | Description |
|---|---|---|---|
| `account` | string | no | Account to use (omit for default) |
| `path` | string | yes | Local file path. Supported: jpeg/png/webp (max 5MB), gif (max 15MB), mp4 (max 512MB) |
| `alt_text` | string | no | Alt 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.
| Param | Type | Required | Description |
|---|---|---|---|
| `account` | string | no | Account to use (omit for default) |
| `description` | string | no* | New bio/description (max 160 chars; empty string clears it) |
| `name` | string | no* | New display name (1-50 chars) |
| `location` | string | no* | New location (max 30 chars; empty string clears it) |
| `url` | string | no* | 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
| Param | Type | Required | Description |
|---|---|---|---|
| `account` | string | no | Account to use (omit for default) |
| `path` | string | yes | Local file path to banner image (JPEG/PNG/WebP only, max 5MB). X recommends 1500x500 pixels. |
| `width` | integer | no | Width of the image (for cropping) |
| `height` | integer | no | Height of the image (for cropping) |
| `offset_left` | integer | no | Left offset (pixels) for crop start |
| `offset_top` | integer | no | Top 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
| Param | Type | Required | Description |
|---|---|---|---|
| `account` | string | no | Account to use (omit for default) |
| `query` | string | yes | Search query. Supports: `from:user`, `#hashtag`, `@mention`, `"exact phrase"`, `-exclude`, `lang:en` |
| `max_results` | integer | no | 10-100 (default 10) |
| `sort_order` | string | no | `recency` or `relevancy` |
| `pagination_token` | string | no | Next page token from previous response |
get_trends
| Param | Type | Required | Description |
|---|---|---|---|
| `account` | string | no | Account to use (omit for default; determines which app's rate limit is used) |
| `woeid` | integer | no | WOEID for location (default: 1 = Worldwide). See common values in tool description. |
Returns trend names and approximate post volumes.
get_timeline
| Param | Type | Required | Description |
|---|---|---|---|
| `account` | string | no | Account to use (omit for default) |
| `max_results` | integer | no | 1-100 (default 20) |
| `exclude` | string | no | `replies`, `retweets`, or both comma-separated |
| `pagination_token` | string | no | Next page token |
get_bookmarks
| Param | Type | Required | Description |
|---|---|---|---|
| `account` | string | no | Account to use (omit for default) |
| `max_results` | integer | no | 1-100 (default 20) |
| `pagination_token` | string | no | Next page token |
lookup_user / follow_user / unfollow_user
| Param | Type | Required | Description |
|---|---|---|---|
| `account` | string | no | Account to use (omit for default) |
| `user` | string | yes | Username (with or without `@`) or numeric user ID |
get_followers / get_following
| Param | Type | Required | Description |
|---|---|---|---|
| `account` | string | no | Account to use (omit for default) |
| `max_results` | integer | no | 1-100 (default 20) |
| `pagination_token` | string | no | Next page token |
get_all_followers / get_all_following
| Param | Type | Required | Description |
|---|---|---|---|
| `account` | string | no | Account to use (omit for default) |
| `max_users` | integer | no | Safety 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
| Param | Type | Required | Description |
|---|---|---|---|
| `account` | string | no | Account to use (omit for default) |
| `max_results` | integer | no | 1-100 (default 20) |
| `pagination_token` | string | no | Next page token |
send_dm
| Param | Type | Required | Description |
|---|---|---|---|
| `account` | string | no | Account to use (omit for default) |
| `conversation_id` | string | yes | DM conversation ID (get from `get_dm_events`) |
| `text` | string | yes | Message text |
get_me
| Param | Type | Required | Description |
|---|---|---|---|
| `account` | string | no | Account 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.shImportant: 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
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 binaryTechnical 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)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
an open source, extensible AI agent that goes beyond code suggestions - install, execute, edit, and test with any LLM
Search infrastructure for AI
YC (S26) | Open Computer History | Record your screen continuously locally and provide context to your agents (Claude, Codex, Openclaw, Hermes, Runner...)
The fastest and the most accurate file search SDK for AI agents, Neovim, Rust, C, Python, Bun and NodeJS
Semantic version control => entity-level diffs, blame, and impact analysis on top of git. 28 languages via tree-sitter. Built for coding agents.
Fast, local-first web content extraction for LLMs. Scrape, crawl, extract structured data — all from Rust. CLI, REST API, and MCP server.
Run your own MCP server? See who uses it and what to fix.
Measure it with TrackMCP