x-api-mcp
Manages multiple authorized X accounts and performs X API v2 actions: posting, media upload, interactions, reading/search, Lists, and trends.
Account management: list/add/remove accounts, OAuth PKCE authorization, set default, check auth status, save client ID.
Posting: create posts, replies, quotes, polls, threads; delete posts; attach uploaded media or local files.
Media: upload images/GIFs/videos (chunked for large/video), with alt text; check processing status.
Interactions: like/unlike, repost/unrepost, bookmark/unbookmark, hide/unhide replies, follow/unfollow, mute/unmute, block/unblock.
Reading/search: search posts/users, look up posts/users, read timelines (home, posts, mentions, liked, bookmarks), engagement details, followers/following/muting/blocking.
Lists: create/update/delete Lists, add/remove members, read Lists and List posts.
Trends/profile: get personalized trends and authenticated user profile.
Multi-account support: most tools accept an
accountparameter; default account used if omitted.No DM tools are present in this schema.
Deploys the MCP server as a self-hosted Cloudflare Worker for remote use, using Cloudflare KV, Durable Objects, and R2 for token storage and media uploads.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@x-api-mcp post 'Just shipped v2! ๐' from my main X account"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
x-api-mcp
A local stdio MCP server for the official X API v2, built around multi-account posting: posts, replies, threads, deletes, and image/GIF/video uploads (including chunked upload). It also covers likes, reposts, bookmarks, follows, timelines, search, DMs, Lists and trends.
It runs on your own computer, started by your AI client as a child process. OAuth tokens stay in ~/.x-api-mcp/tokens.json (mode 0600) and never pass through a third-party server.
For claude.ai on the web and phone, which can't start local processes, the same tools also run as a self-hosted remote server on your own Cloudflare account.
Why x-api-mcp
X offers two MCP servers of its own (see X's MCP docs). Neither is built for posting from several accounts day to day:
x-api-mcp | X hosted MCP ( | xmcp (X's self-hosted server) | |
Post, reply, quote, threads | Yes | No; writes are limited to bookmarks and Articles | Yes, one endpoint per tool |
Upload images, GIFs, video | Yes, chunked upload waits for processing | No | Raw upload endpoints |
DMs, follows, likes, Lists | Yes | No (lookups only) | Yes |
Several X accounts | Yes, pick one per call | Through | One per server process |
Stays signed in | Yes, tokens saved and refreshed | Via | No, re-authorize on every restart |
Install | One plugin or |
| Python, |
Tool count | 26, grouped by action | Curated set | About 120, generated from the API spec |
Web and phone chat | Yes, with your own remote server | Yes | Only if you host it yourself |
Safety rails | Agent skill and server instructions (confirm before posting, name the account), tool annotations, optional | None beyond read-only access | Tool allowlist |
Use the hosted MCP when you only need to read and search X. Use x-api-mcp when an agent should post, reply, upload media or manage interactions for one or more accounts, with tokens kept on your computer. All three use your own X App and its pay-per-use credits.
Related MCP server: x-mcp
Quick start
Create an X App and copy its Client ID.
Install x-api-mcp in your AI client.
Ask the agent to add your X account, then start posting.
You don't need to clone this repository or build anything; every install method runs the published npm package.
Create an X App
Each user brings their own X App, and API usage is billed to that App's credits.
Create an App at console.x.com and add pay-per-use credits.
Under User authentication settings:
App permissions: Read and write (add Direct Messages if you want DM tools)
Type of App: Native App (public client, no secret needed, recommended). A Web App also works; set
X_CLIENT_SECRETas well.Callback URI:
http://127.0.0.1:8765/callback(must exactly matchX_REDIRECT_URI)
Copy the OAuth 2.0 Client ID.
Install
Client | How to install | Where the Client ID goes |
Claude Code | Plugin settings form | |
Claude desktop app (Cowork) |
| |
Codex CLI, ChatGPT desktop app |
| |
Cursor | Plugin settings, or | |
Grok Bot |
| |
Claude desktop chat, Windsurf, Cline and other MCP clients |
| |
claude.ai on the web and phone, other remote MCP clients | A secret on your Worker |
All plugin installs include the same MCP server plus a skill that tells the agent how to use it safely: confirm before acting, always name the account, mind the costs, and how to authorize. Everything runs locally, so plugins work only where the client runs on your computer. For web and mobile chat, deploy the remote server.
Wherever the Client ID isn't asked for at install time, either ask the agent to call set_client_id with it, or run npx x-api-mcp config --client-id your-client-id once in a terminal. It is saved in ~/.x-api-mcp/app.json; an X_CLIENT_ID environment variable wins when both are set.
Claude Code
/plugin install x-api-mcp --marketplace fatwang2/x-api-mcpChoose a scope, then fill in your Client ID and options in the settings dialog (reopen it with /plugin configure x-api-mcp@x-api-mcp). From a shell: claude plugin install x-api-mcp --marketplace fatwang2/x-api-mcp.
Claude desktop app (Cowork)
In Customize > Plugins, add the GitHub marketplace fatwang2/x-api-mcp and install x-api-mcp. It runs in Cowork sessions on your computer. Cowork doesn't show plugin settings, so set the Client ID with set_client_id. For regular desktop chat, use the MCP config instead.
Codex
codex plugin marketplace add fatwang2/x-api-mcpThen install X API from that marketplace in the Plugins Directory (Codex CLI or the ChatGPT desktop app), and set the Client ID with set_client_id.
Cursor
Install x-api-mcp from Customize once it is listed, or have a Teams/Enterprise admin import this repository as a team marketplace (Dashboard > Plugins & MCPs > Add Marketplace > Import from Repo). Then fill in the Client ID and options under Configure on the plugin. Until then, the MCP config works in Cursor too (.cursor/mcp.json).
Grok Bot
Open the X API MCP template and select Add to Grok Bot. It runs this MCP server with the same safety rules (it confirms every post with you). Then give it your Client ID, which it saves with set_client_id, and add your X account.
Any other MCP client
Add this to your client's MCP config (claude_desktop_config.json, .cursor/mcp.json, ...):
{
"mcpServers": {
"x": {
"command": "npx",
"args": ["-y", "x-api-mcp"],
"env": { "X_CLIENT_ID": "your-client-id", "X_REQUIRE_ACCOUNT": "1" }
}
}
}Or, for Claude Code without the plugin:
claude mcp add x -e X_CLIENT_ID=your-client-id -- npx -y x-api-mcpRequires Node.js 20 or later.
Remote server (self-hosted)
The remote server is the same set of tools on a Cloudflare Worker, added to Claude as a custom connector. Use it when you want to post from claude.ai on the web or your phone. There is no shared instance: you deploy your own, on your own Cloudflare account, with your own X App, and only the X accounts you allow can sign in.
Local server | Remote server | |
Runs on | Your computer, started by the client | A Worker on your Cloudflare account (free plan is enough to start) |
Works in | Claude Code, desktop apps, Codex, Cursor and other local clients | claude.ai on the web, phone and desktop, and any client that supports remote MCP with OAuth |
X App | Native App (no secret) | A separate Web App with a Client Secret |
Tokens |
| A Durable Object in your account |
Who can use it | Whoever runs it | X accounts on |
Media |
| Public |
Extra tools |
|
|
1. Create an X Web App
Use a separate App from the one your local server uses, so each can be revoked on its own.
At console.x.com, create an App and add pay-per-use credits.
Under User authentication settings: App permissions Read and write (plus Direct Messages if you want DM tools), Type of App Web App, Callback URI
https://<your-worker-host>/x/callback.Copy the OAuth 2.0 Client ID and Client Secret.
<your-worker-host> is either x-api-mcp.<your-subdomain>.workers.dev or your own domain. You can create the App first with your best guess and fix the Callback URI after deploying.
2. Deploy
With the button: it copies this repository into your GitHub account, creates the KV namespace, Durable Object and R2 bucket, and asks for the four secrets below.
By hand (Node.js 20 or later):
git clone https://github.com/fatwang2/x-api-mcp && cd x-api-mcp
npm install
npx wrangler login
cp .dev.vars.example .dev.vars # fill in the four secrets (git ignores this file)
npx wrangler deploy --secrets-file .dev.vars
npx wrangler r2 bucket lifecycle add x-api-mcp-media expire-uploads uploads/ --expire-days 7The first deploy creates the KV namespace and the R2 bucket. You can delete .dev.vars afterwards; the secrets live on the Worker, and npx wrangler secret put <NAME> changes one later.
The secrets:
Secret | Value |
|
|
| From the X Web App |
| Numeric X user IDs allowed to sign in, comma separated. Empty allows nobody. If you don't know yours, sign in once: the refusal page shows it |
Your own domain: keep it out of the repository. Copy wrangler.jsonc to wrangler.local.jsonc (git ignores it), add "routes": [{ "pattern": "x-api-mcp.example.com", "custom_domain": true }] with your domain, and deploy with npx wrangler deploy -c wrangler.local.jsonc. Then set X_PUBLIC_BASE_URL and the X Callback URI to that domain.
Settings in wrangler.jsonc: X_TOOLS (all or read), X_REQUIRE_ACCOUNT (on; write tools must name their account), X_MEDIA_WAIT_SECONDS (how long an upload waits for X to process a video, default 20). X_ENABLE_DM, X_SCOPES and X_DEFAULT_ACCOUNT work as on the local server.
3. Add the connector to Claude
In claude.ai, open Settings โ Connectors โ Add custom connector, enter https://<your-worker-host>/mcp, and connect. You approve the connection on your server's page, then sign in with an allowed X account. That account is saved as the first one.
Set the write tools (create_post, create_thread, delete_post, post_action, user_action, list_action, send_dm) to ask for approval every time in the connector's tool settings. The server also tells Claude to confirm each visible action, and every write is recorded; ask Claude for get_write_log to see what was done.
Before relying on it, try it with a test account: post once, check get_write_log, then delete the post.
Using it
More X accounts: ask Claude to
add_account. It gives you an X sign-in link; open it in a browser signed in to that account and approve. Accounts added this way don't need to be on the allowlist, since only a signed-in owner can start one.Images and video: give Claude a public https link (
media_url), or ask for an upload link:create_upload_linkreturns a page that takes one file from your phone or computer within 15 minutes, and Claude attaches it by itsr2_key. Videos up to 100 MB go through that page (Cloudflare's request size limit on Free and Pro plans). If X is still processing a video, Claude checksget_media_statusand posts once it's ready.Updating: pull the latest code and deploy again (with the button, push to the copied repository).
Add X accounts
On the local server, call add_account from chat (or run npx x-api-mcp login in a terminal) and open the returned URL. (On the remote server, see Using it.) Repeat to add more accounts. How the callback comes back depends on where the browser is:
Loopback (browser on the same computer as the server): a listener on
127.0.0.1:8765saves the token automatically.Paste (browser on another device, as with chat bots, remote agents or SSH): after approving, the browser lands on an error page at
http://127.0.0.1:8765/callback?.... Copy that full URL and paste it back right away; the agent passes it tocomplete_authorization. X codes expire about 30 seconds after approval.
X_AUTH_MODE=auto (the default) uses paste over SSH and loopback otherwise. Set X_AUTH_MODE=paste explicitly for chat-bot hosts.
The first authorized account becomes the default. Set X_REQUIRE_ACCOUNT=1 to make every write name its account, which is recommended with several accounts. Access tokens are refreshed automatically. X rotates refresh tokens, so concurrent refreshes from several MCP processes are serialized with a file lock.
Tools
Endpoints are grouped by action rather than one tool per endpoint, so agents can choose easily. X_TOOLS=write registers only the account and write tools; DM tools appear only with X_ENABLE_DM=1. Every tool takes an optional account (defaults to the default account). Users can be given as @username, username or a numeric ID.
Group | Tool | What it does |
Accounts |
| App setup, multi-account OAuth and management; local except for the OAuth exchange |
| Profile of the authorized account (read) | |
Posting |
| Post, reply, quote, poll, attach media (local files can be passed directly) |
| Publish a thread in order; stops at the first failure and returns what was posted | |
| Delete a post | |
| like / unlike / repost / unrepost / bookmark / unbookmark / hide_reply / unhide_reply | |
Media |
| Images up to 5 MB in one request; GIFs, videos and large files via chunked upload, waiting for processing; optional alt text |
Reading |
|
|
| Look up posts by ID | |
| home / posts / mentions / liked / bookmarks | |
| Liking users / reposted by / quote posts | |
Users |
| Look up profiles by username or ID |
| followers / following, plus your own muting / blocking lists | |
| follow / unfollow / mute / unmute / block / unblock | |
DMs |
| Send a DM to a user or conversation (with media) / read DMs. Only with |
Lists |
| Create, update, delete Lists and manage members / read owned Lists, List info, members and posts |
Trends |
| Personalized trends |
Almost every call is billed per use; see the pricing page and your console for current rates. Posts containing a URL cost noticeably more. X documents quote_post_id and blocking as Enterprise-only.
By default authorization requests every scope these tools use except DM scopes, because an App without Direct Messages permission rejects the whole request when they are asked for. If an account was authorized with fewer scopes, calls that need a missing scope fail before reaching X with a hint to run add_account again.
The remote server leaves out set_client_id and complete_authorization, has no file_path media source, and adds get_write_log, create_upload_link and list_uploads.
Not covered: Spaces, bookmark folders, DM deletion, streaming endpoints, Ads and compliance endpoints.
Configuration
All settings are optional environment variables. Plugin installs fill them from the plugin's settings form where the host has one.
Variable | Default | Notes |
| none | Your App's Client ID. Can be saved with |
| none | Only for Web Apps (confidential clients) |
|
|
|
|
|
|
| off |
|
| off |
|
|
| Must match the App settings |
| All non-DM scopes the tools use (see | Set to narrow permissions |
|
| Storage directory for tokens and the saved Client ID |
| none | Used when no default account is set locally |
CLI
x-api-mcp Run the MCP server on stdio
x-api-mcp config --client-id <id> [--client-secret <s>]
Save your App's Client ID locally
x-api-mcp login Authorize a new account in the browser
x-api-mcp accounts List accounts (* marks the default)
x-api-mcp default <username> Set the default account
x-api-mcp logout <username> Remove an account and revoke its tokens
x-api-mcp post [--account a] <text> Publish a quick test postPrivacy Policy
x-api-mcp runs entirely on your computer. It has no server of its own and the author collects no data.
What it stores: your X App's Client ID (and secret, if you use one) in
~/.x-api-mcp/app.json, and OAuth access and refresh tokens plus basic account details (username, user ID, display name, granted scopes) in~/.x-api-mcp/tokens.json. Both files are readable only by your user account (mode 0600). Nothing else is stored.Where data goes: requests go directly from your computer to the X API (
api.x.com) and X's OAuth pages, using your own App. Post text, media files, DMs and other content you ask the agent to send are transmitted only to X. No analytics or telemetry are collected.Third parties: your use of the X API is governed by the X Developer Agreement and Policy and X's Privacy Policy. Content returned by the tools is passed to the AI agent you run them in, under that agent provider's terms.
Remote server: if you deploy it, the same data lives in your own Cloudflare account instead: X tokens and account details in a Durable Object, a log of writes (last 1000), and the connector's OAuth grants in KV. Files uploaded through
create_upload_linkare kept in your R2 bucket until your lifecycle rule removes them. Nothing goes to the author of this project.Removal:
remove_account(orx-api-mcp logout <username>) revokes an account's tokens at X and deletes them locally. Deleting~/.x-api-mcpremoves everything.Contact: open an issue at https://github.com/fatwang2/x-api-mcp/issues.
Development
This section is for contributors. To use x-api-mcp you only need Install.
git clone https://github.com/fatwang2/x-api-mcp && cd x-api-mcp
npm install
npm test # builds, then runs end-to-end tests against a local mock of the X APITo try a local build in a client, point its MCP config at node /absolute/path/to/x-api-mcp/dist/index.js instead of npx -y x-api-mcp.
npm run test:worker runs the remote server locally with wrangler dev against the same kind of mock (no Cloudflare account needed); npm run worker:typecheck and npm run worker:build check and bundle it without deploying.
The tests cover OAuth (loopback and paste), refresh-token rotation, the concurrency lock, threads, chunked upload, interactions, DMs, tool-group switches and the stdio protocol.
Layout: src/oauth.ts (PKCE and token endpoint), src/auth.ts (authorization flow and loopback callback), src/store.ts (token file and lock), src/client.ts (API client with auto refresh and scope checks), src/posts.ts (posting), src/social.ts (interactions, relationships, timelines, DMs, Lists), src/shape.ts (response trimming), src/media.ts, src/server.ts (MCP tool registration), src/index.ts (entry point and CLI). The remote server lives in worker/ (see worker/README.md) and reuses everything in src/ except the file store and the local auth flow.
Plugin files
The repository root is the plugin for every host; they share the agent skill in skills/x-api-mcp/SKILL.md and launch npx -y x-api-mcp@<version>.
File | Read by |
| Claude Code and Cowork ( |
| Cursor ( |
| Codex and other Agent Plugins hosts; Codex listing details live under |
Validate the Claude plugin with claude plugin validate .claude-plugin/plugin.json (and the marketplace with claude plugin validate .). npm version keeps the version in all of these files in step with package.json (scripts/sync-version.mjs).
Releasing
Releases are published by GitHub Actions when a version tag is pushed:
npm version patch # or minor / major; also updates the version pinned in the plugin files
git push --follow-tags.github/workflows/release.yml checks that the tag matches package.json, runs the tests, publishes to npm with provenance and creates a GitHub release. It runs in the npm GitHub environment and authenticates with npm trusted publishing (GitHub repository fatwang2/x-api-mcp, workflow release.yml, environment npm), so no npm token is stored. A version that is already on npm is skipped.
References
License
MIT
Available Tools
24 toolsadd_accountA
Start OAuth 2.0 (PKCE) to authorize another X account. Returns authorize_url for the user to open in a browser logged in to that account, and a note to relay to them. mode=loopback: a local listener saves the token automatically; confirm with auth_status. mode=paste: ask the user to paste the callback URL from the address bar right after approving and pass it to complete_authorization at once. Tokens are stored only on this machine.
Inputs: wait_seconds.
auth_statusARead-only
Check whether an authorization started with add_account has finished. Optionally wait for it.
Inputs: state (required), wait_seconds.
complete_authorizationA
Fallback when the automatic callback listener is unavailable: finish add_account with the full callback URL from the browser address bar (contains code and state). X codes expire ~30 seconds after approval.
Inputs: redirect_url (required).
create_postA
Publish a post as the given account; also used to reply (reply_to_post_id). Attach media via media_files (uploads for you) or media_ids. Spends pay-per-use credits (roughly $0.015 per post, much more if the text contains a URL; check current pricing in the X console). Only call when the user has confirmed the exact text and account.
Inputs: poll, text, account, media_ids, +6 more.
create_threadA
Publish a thread: each post replies to the previous one. Stops at the first failure and reports what was posted. Each post is billed like create_post.
Inputs: posts (required), account, reply_to_post_id.
delete_postADestructiveIdempotent
Delete a post owned by the given account (billed per request).
Inputs: post_id (required), account.
get_listsCRead-only
Read Lists. type: owned (lists a user owns, default the account), info, members, or posts of list_id.
Inputs: type (required), user, account, list_id, +2 more.
get_meARead-only
Fetch the authenticated user's profile via GET /2/users/me (billed as a user read).
Inputs: account.
get_media_statusCRead-only
Check processing status of an uploaded video/GIF.
Inputs: media_id (required), account.
get_post_engagementARead-only
Who engaged with a post. type: liking_users, reposted_by (users), or quotes (quote posts).
Inputs: type (required), post_id (required), account, max_results, +1 more.
get_postsARead-only
Look up posts by ID (1-100), with author, metrics, media and referenced posts. Billed per post.
Inputs: ids (required), account.
get_timelineARead-only
Read a timeline. type: home (your reverse-chronological home feed), posts (a user's posts), mentions, liked (posts a user liked), bookmarks (yours). user defaults to the account itself. Billed per post returned.
Inputs: type (required), user, account, exclude, +4 more.
get_trendsCRead-only
Personalized trends for the account.
Inputs: account.
get_user_relationsARead-only
List followers or following of a user (default: the account itself), or the account's own muting/blocking list.
Inputs: type (required), user, account, max_results, +1 more.
get_usersARead-only
Look up user profiles by usernames or by numeric IDs (up to 100). Billed per user.
Inputs: ids, account, usernames.
list_accountsARead-only
List X accounts authorized on this machine and which one is the default. Local only, no API cost.
Inputs: no named parameters.
list_actionC
Manage X Lists: create, update, delete, add_member, remove_member.
Inputs: action (required), name, user, account, +3 more.
post_actionBIdempotent
Interact with a post as the given account: like, unlike, repost, unrepost, bookmark, unbookmark, hide_reply or unhide_reply (replies to your own posts).
Inputs: action (required), post_id (required), account.
remove_accountBDestructive
Forget an authorized account on this machine and revoke its tokens at X.
Inputs: account (required), revoke.
searchARead-only
Search X. type=posts: posts from the last 7 days (billed per post returned, keep max_results small). type=users: search accounts by keyword.
Inputs: query (required), type, account, end_time, +4 more.
set_client_idAIdempotent
Save the OAuth 2.0 Client ID of the user's own X App (from console.x.com) on this machine, for hosts that cannot pass the X_CLIENT_ID environment variable. Needed once before add_account. No API call.
Inputs: client_id (required), client_secret.
set_default_accountAIdempotent
Choose which authorized account is used when a tool call omits account.
Inputs: account (required).
upload_mediaA
Upload an image, GIF or video and return its media_id for create_post. Images <= 5 MB go in one request; GIFs, videos and larger files use chunked upload and wait for processing. Optional alt text.
Inputs: base64, account, alt_text, file_path, +2 more.
user_actionAIdempotent
Follow, unfollow, mute, unmute, block or unblock a user as the given account. X documents block as Enterprise-only, so it may be refused on pay-per-use apps.
Inputs: user (required), action (required), account.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelogโฆ
TDQS
Scored across 24 tools
Most tools target distinct resource+action combinations, and the auth cluster (add_account, auth_status, complete_authorization) is well-differentiated by description. Minor overlap exists between get_posts, get_timeline, and search for retrieving posts, but descriptions clarify the boundaries.
The large majority follow a clear verb_noun pattern (get_posts, create_post, delete_post, get_users, set_default_account). Deviations are the noun_action bundles (list_action, post_action, user_action), the bare 'search', and 'auth_status', but these remain readable and the *_action group is internally consistent.
24 tools is slightly heavy but justified by the broad X API surface spanning auth, posting, media, timelines, search, lists, users, and relations. Each tool maps to a meaningful capability with little redundancy.
Core workflows are well covered: OAuth account lifecycle, posting/threads/deletes, engagement actions, media upload, timelines, search, lists, and user relations. Gaps like direct messages, profile editing, and pinned-post management exist but are outside the stated posting/reading focus.
Maintenance
Related MCP Connectors
Search Bluesky posts and feeds; read profiles, threads, and trends. STDIO or Streamable HTTP.
Search X (Twitter) posts and read profiles, followers, trends and X Articles. OAuth sign-in.
Read public Twitter/X data: tweets, profiles, followers, search. No X developer account.
X/Twitter reads, search, monitors and posting. Pay-per-call in USDC โ no signup, no API keys.
Related MCP Servers
- AlicenseBqualityCmaintenanceEnables interaction with X (formerly Twitter), allowing for posting tweets, searching content, managing accounts, and organizing lists.1119 npm3MIT
- FlicenseAqualityDmaintenanceEnables interaction with the X (Twitter) API through MCP tools for managing users, posts, bookmarks, likes, and lists. Supports markdown or JSON output and uses OAuth 2.0 PKCE authentication.22-
- AlicenseNot gradedqualityCmaintenanceEnables AI assistants and local operators to publish approved posts, linear threads, replies, and Community posts to X through a Playwright-driven authenticated browser session, without X API credits. Also exposes read-only tools to check session status and list Communities available to the account, via stdio or bearer-protected Streamable HTTP.MIT
- AlicenseBqualityBmaintenanceEnables MCP clients such as Claude and Cursor to read timelines, search statuses, and look up users or conversations, plus optionally post, delete, favorite, and follow with explicit confirmation. It supports both local stdio launches and remote HTTP hosting with per-user authorization.15MIT