x-mcp-lite
This server provides read-oriented access to Twitter/X with limited low-risk write operations (liking/bookmarking), built-in anti-rate-limit protection, and cookie-based authentication.
Search & Discovery
Search tweets by query, sorted by "Top" or "Latest" (
search_twitter)Search for users by query (
search_user)Get trending topics (
get_trends)
Tweet Operations
Fetch a tweet by ID (
get_tweet_by_id)Get detailed tweet information (
get_tweet_details)Get a full conversation thread (
get_conversation_thread)Find similar tweets (
get_similar_tweets)See who retweeted or liked a tweet (
get_retweeters,get_favoriters)Like or unlike a tweet (
favorite_tweet,unfavorite_tweet)
Timelines & User Tweets
Browse "For You" or "Following" home timelines (
get_timeline,get_latest_timeline)Get tweets from a specific user (
get_user_tweets)Get highlighted or mentioned tweets for a user (
get_highlights_tweets,get_user_mentions)View scheduled tweets (
get_scheduled_tweets)
User & Account Information
Get your authenticated user ID and profile (
get_user_id,get_user)Look up any user by screen name or ID (
get_user_by_screen_name,get_user_by_id,get_user_profile)
Followers & Following (Read-Only)
List followers or following (
get_user_followers,get_latest_followers,get_user_following,get_latest_friends)Get verified followers, common followers, or subscriptions (
get_user_verified_followers,get_user_followers_you_know,get_user_subscriptions)Fetch follower/friend IDs in bulk (
get_followers_ids,get_friends_ids)
Bookmarks
Retrieve bookmarks, paginated or all at once (
get_bookmarks,get_all_bookmarks)List bookmark folders (
get_bookmark_folders)Add or remove bookmarks, optionally in a folder (
bookmark_tweet,delete_bookmark)
Direct Messages (Read-Only)
Read DM conversation history with a specific user (
get_dm_history)
Miscellaneous
Fetch a community note by ID (
get_community_note)
Notable Limitations
❌ No posting, deleting, or retweeting tweets
❌ No sending DMs or managing DM groups
❌ No follow/unfollow, block/unblock, or mute/unmute
❌ No account updates, media management, or poll voting
Click on "Install 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-mcp-liteshow my bookmarks"
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-mcp-lite
Safety-focused lite fork of lord-dubious/x-mcp.
Note on AI involvement: This fork was authored by Claude Code (Anthropic) under human direction. The human collaborator (
strobekiss) made all product decisions — scope of cuts, naming, design tradeoffs (in-memory vs SQLite, active cooldown vs passive sleep, retry vs no-retry on 429, cookie-based auth flow, set_proxy/sing-box integration, etc.) — and reviewed/verified the code at each step. Claude Code did the source reading, pattern analysis, and mechanical refactoring (commenting decorators, wrapping calls withwith_rate_limit, writing thethrottle.py/twikit_patch.py/singbox.pymodules). The anti-rate-limit design is informed by readingDataWhisker/x-mcp-server's official-API rate-limit module and adapting its "learn from real 429 + active intercept" pattern to twikit's reverse-engineered endpoints.
Keeps the read-only tools + bookmark/like management, cuts the high-risk write tools (post/delete tweets, DM, follow/block/mute, groups, cookie ops), and adds:
An anti-rate-limit layer the original project lacks entirely
twikit 2.3.3 patches for three upstream bugs it hasn't shipped fixes for: the
KEY_BYTE indicesbreakage (x.com changed homepage HTML on 2026-03-18),KeyErrorcrashes when parsing users whose accounts omit optionallegacy.*fields (e.g. no bio link →KeyError: 'urls'), andKeyError: 'itemContent'inget_tweet_by_idfrom x.com's flattened cursor entriesA cookie-based auth flow (
get_cookie) so the server can run on datacenter IPs without triggering Cloudflare blocks
⚠️ Risk warning
This is an unofficial reverse-engineering library (via twikit) that talks to Twitter's internal endpoints. It is not affiliated with Twitter/X. Use of such libraries may violate Twitter's Terms of Service and can lead to:
Account being rate-limited (transient, recovers in ~15 min)
Account being locked (requires Arkose challenge / phone verification)
Account being suspended (permanent)
Use at your own risk. Recommendations:
Use a secondary account, not your main one
Keep call frequency low (the built-in throttler defaults to 2–5s random intervals)
Do not run unattended long-running jobs
If the account gets locked, stop and verify manually before resuming
Related MCP server: x-mcp
What's kept vs cut
Kept (39 tools)
Category | Tools |
Bookmarks |
|
Tweet search/detail |
|
User info |
|
Follower/following lists |
|
Timeline/search |
|
DM read-only |
|
Retweeters/favoriters |
|
Community note |
|
Scheduled (read) |
|
Likes (low-risk write) |
|
Cookie setup |
|
Cut (37 tools, @mcp.tool() decorators commented — function bodies retained for diff clarity)
Post/delete tweets, polls, scheduled tweets, retweets, all DM write operations, all group operations, follow/unfollow/block/unblock/mute/unmute, set_delegate_account, update_user, all cookie management (get_cookies / save_cookies / set_cookies / load_cookies / logout / unlock), delete_all_bookmarks, geo (reverse_geocode / search_geo / get_place), media metadata, bookmark folder create/edit, vote / vote_on_poll.
Architecture
src/x_mcp/
├── __init__.py
├── twikit_patch.py # Three twikit 2.3.3 monkey-patches, applied at import:
│ # - ClientTransaction.get_indices (2026-03-18 x.com
│ # HTML format change)
│ # - User.__init__ (KeyError on optional legacy.*
│ # fields x.com omits, e.g. no-bio-link accounts)
│ # - GQLClient.tweet_detail (KeyError itemContent from
│ # x.com's flattened cursor entries)
│ # Loaded BEFORE `import twikit` via twitter.py.
├── throttle.py # Anti-rate-limit layer (state persisted to
│ # ~/.x-mcp/throttle_state.json):
│ # - Throttler (2-5s random pacing, persisted)
│ # - with_rate_limit (rolling request budget +
│ # active cooldown + bounded 429 backoff/retry;
│ # distinct AccountLocked/Suspended handling;
│ # get_cookie hint on other TwitterException)
│ # - paginate_all (3-8s inter-page delay, budget-aware)
├── singbox.py # Archived: historical sing-box management module.
│ # No longer imported by twitter.py. Kept in the repo
│ # (and git history) as a record of the proxy/sing-box
│ # approach. See ARCHIVE.md for details.
└── twitter.py # 39 MCP tools, all wrapped with throttler.wait()
# + with_rate_limit(). get_twitter_client handles
# cookie loading / login.Anti-rate-limit layer (throttle.py)
Throttler— random-pacing throttler. Every tool call awaitsthrottler.wait()first, which sleeps to enforce a 2–5s random interval since the last call. Randomization avoids fixed-pattern detection. The last-call timestamp is persisted, so pacing holds even when the host spawns the server fresh per call.with_rate_limit(endpoint, fn)— DataWhisker-style active cooldown tracking, plus a request budget and bounded backoff. In order:Rolling budget: if we've already made
X_MCP_MAX_CALLS_PER_WINDOWcalls (default 250) in the last 15 min, refuse with a clear "try again in Ns" error instead of adding fuel to a burst that could trip detection. Set to0to disable.Active cooldown: if a previous 429 recorded a reset time for this endpoint, either sleep it out (if within
X_MCP_MAX_BACKOFF) or refuse with a retry hint (if further out) — active intercept, no wasted request.On
TooManyRequests: reade.rate_limit_reset(from thex-rate-limit-resetresponse header, confirmed in twikit 2.3.3errors.py), record it (persisted), then sleep + retry once only if the wait is withinX_MCP_MAX_BACKOFF(default 60s); otherwise refuse with a retry hint. This caps in-call blocking so a single call can't hang for the full 900s window and blow past your MCP client's timeout — the cooldown is still honored on the next call.On
AccountLocked/AccountSuspended: convert to aRuntimeErrorthat says re-cookieing won't help and the account must be fixed manually in a browser (no misleadingget_cookie()hint).On any other
TwitterExceptionsubtype (BadRequest/Unauthorized/Forbidden/NotFound/ etc): convert toRuntimeErrorwith the short "Call get_cookie()" hint, do not retry.
paginate_all— forget_all_bookmarks. IteratesResult.next()with 3–8s random delay between pages; each page goes throughwith_rate_limitso the budget, cooldown and 429 backoff all apply, and any refusal cleanly stops pagination and returns what was collected so far. Defaultmax_pages=50(~1000 bookmarks).
Persistence: pacing (last_call), per-endpoint 429 cooldowns (resets), and the rolling request log (calls) are stored in a small JSON file (default ~/.x-mcp/throttle_state.json, override with X_MCP_STATE_PATH; atomic writes, tolerant of a missing/corrupt file). This is what lets a cooldown survive across per-session server spawns (e.g. mcphub over stdio), instead of being lost every time the process restarts.
Remaining limitations
Documented so you know what the layer still does not cover:
Budget/pacing count tool calls, not underlying HTTP requests. Some tools issue several requests internally (e.g.
get_userdoessettings+get_user_by_screen_name+get_user_by_id) but count as one. The budget is a coarse safety cap, not an exact request meter.No fingerprint hardening. The client uses twikit's default User-Agent unless you set
USER_AGENT. If you exported cookies from a browser, settingUSER_AGENTto match that browser is the most consistent choice.get_user/get_user_iddepend on a flaky endpoint. They call/1.1/account/settings.json, which x.com currently returns404for intermittently. This is an x.com/twikit issue, not a rate-limit one — retry, or useget_user_by_screen_name/get_bookmarkswhich don't hit that endpoint.Concurrent writers race. The state file is best-effort: two servers running against the same file could lose an update. Fine for the normal single-host case.
twikit patches (twikit_patch.py)
twikit_patch.py applies three independent monkey-patches at import time (must run before from twikit import Client). Remove each once twikit ships a release that fixes the corresponding bug.
1. ClientTransaction.get_indices — Couldn't get KEY_BYTE indices
twikit 2.3.3 raises this because x.com changed its homepage HTML on 2026-03-18 — the ondemand.s filename and its hash are now split into two separate ,<N>:"..." entries instead of one inline "ondemand.s":"<hash>". Upstream issue: d60/twikit#408. Fix is upstream in iSarabjitDhiman/XClientTransaction commit 2ff8438, but twikit hasn't pulled it in. Patched using the regex from @audioeng89's comment.
2. User.__init__ — KeyError on optional legacy.* fields
twikit 2.3.3's User.__init__ hard-indexes many optional legacy.* fields that x.com omits for some accounts, so any call that parses a User object crashes. Observed in the wild:
legacy['entities']['description']['urls']for accounts with no link in their bio (x.com sendsentities == {"description": {}}) →KeyError: 'urls'legacy['withheld_in_countries']→KeyError: 'withheld_in_countries'
This hit get_user, get_user_id, get_bookmarks, timelines, follower lists — anything returning users. Same class of bug as d60/twikit#341 (can_media_tag). Rather than whack-a-mole each field, the patch wraps the incoming data in a recursive lenient dict so a missing key at any depth degrades to an empty value instead of raising; present keys keep their real values, so normal accounts parse unchanged.
3. tweet_detail cursors — KeyError: 'itemContent'
twikit 2.3.3 reads reply/next-page cursors from the tweet-detail response at entries[-1]['content']['itemContent']['value'] and reply['item']['itemContent']['value'] (in Client.get_tweet_by_id and Client._get_more_replies). x.com flattened cursor entries — the value now sits directly on the cursor object ({'entryType': 'TimelineTimelineCursor', 'cursorType': ..., 'value': ...}) with no itemContent nesting → KeyError: 'itemContent'. This broke get_tweet_by_id, get_tweet_details, and get_conversation_thread. All the crash sites parse the response from GQLClient.tweet_detail, so the patch wraps that one method and re-nests each flattened cursor's value back under itemContent, leaving the original code unchanged.
Why there is no proxy support
x-mcp-lite previously supported HTTP/SOCKS5 proxies via X_MCP_PROXY/proxy= and local sing-box forwarding for trojan/anytls/ss/vmess nodes via set_proxy(). In practice, every tested proxy path was blocked by Cloudflare before login could succeed on the deployment machine (datacenter/VPS IP). The sing-box binary download and startup were eventually fixed, but the outbound nodes themselves could not reach x.com from the server environment.
To keep the auth surface simple and reliable, proxy support was removed. Only cookie-based auth remains. You generate cookies on a network that x.com trusts (e.g. a home browser or residential machine) and copy them to the MCP server. See ARCHIVE.md for the historical proxy/sing-box implementation.
Setup
1. Clone
git clone https://github.com/strobekiss/x-mcp-lite
cd x-mcp-lite2. Configure in your MCP host (e.g., Claude Desktop, mcphub, Cursor)
{
"x-mcp-lite": {
"command": "uvx",
"args": ["--from", "git+https://github.com/strobekiss/x-mcp-lite", "x-mcp-lite"],
"env": {
"TWITTER_USERNAME": "your_username",
"TWITTER_PASSWORD": "your_password"
}
}
}Environment variables
Variable | Required | Default | Purpose |
| Yes¹ | — | X username (used by auto-login) |
| Yes¹ | — | X password (used by auto-login) |
| No | — | Email if your account has one; omitted if not set (twikit accepts |
| No |
| Path to cookies file. Set this on both the login machine and the deployment machine. |
| No | twikit default | Custom User-Agent. Recommended: match the browser you exported cookies from. |
| No | — | Capsolver API key (only needed if you hit Arkose challenges) |
| No |
| Where the throttle layer persists pacing / cooldowns / request log |
| No |
| Max seconds a single call will block waiting out a cooldown before it refuses with a retry hint (the cooldown is still persisted) |
| No |
| Client-side request budget per 15-min window. |
¹ Only required if you ever use get_cookie() Strategy 3 (auto-login). If you only ever paste cookies via get_cookie(cookie_json=...), credentials aren't needed.
3. Start
The MCP server starts automatically when your MCP host launches. uvx will pull the latest code from GitHub on each startup.
Cookie setup
This server only supports cookie-based auth. Proxy-based login was removed because Cloudflare blocks every proxy path we tested from datacenter/VPS IPs (residential proxies, datacenter proxies, trojan/vless nodes, etc.).
Credentials (TWITTER_USERNAME / TWITTER_PASSWORD) must be set in the MCP server env at startup — agents can't pass them at runtime (security: don't let LLMs handle plaintext passwords). They are only used when the server performs auto-login, which usually only makes sense from a residential/uncensored IP.
Use the get_cookie MCP tool. It picks a strategy based on what you pass:
Strategy 1: Paste cookie JSON (no login, no credentials needed)
Best when you already have cookies exported from a browser or received from another machine.
Use a browser extension (e.g. EditThisCookie) on x.com while logged in, export as JSON — OR — read the cookies file saved by another x-mcp-lite instance.
Call
get_cookie(cookie_json="<the JSON string>").
Writes directly to X_MCP_COOKIES_PATH. Validates JSON is a non-empty object; atomic write (tmp + rename) won't corrupt an existing file.
Strategy 2: Copy a local cookie file (no login, no credentials needed)
Best when cookies are already saved somewhere on this machine.
Call
get_cookie(cookie_file="/path/to/cookies.json").
Reads, validates, and atomically writes to X_MCP_COOKIES_PATH. Refuses if source equals target.
Strategy 3: Auto-login (needs credentials; only reliable from residential IPs)
Best when you don't have cookies yet and the MCP server is running on a network that x.com trusts (e.g. home, office, or a VPN exit that x.com accepts).
Ensure
TWITTER_USERNAME/TWITTER_PASSWORDare set in MCP server env.Call
get_cookie()with no args.twikit logs in directly (no proxy), saves cookies to
X_MCP_COOKIES_PATH.
If this fails with 403/ConnectTimeout, your server's IP is blocked — use Strategy 1 or 2 instead.
Agent self-service flow
When an agent calls any tool (e.g. get_bookmarks) and cookies are missing/expired, the error message tells the agent to call get_cookie (one-line hint). The agent then either:
Asks the user to paste cookies from elsewhere (browser export or another machine) and calls
get_cookie(cookie_json="<JSON>")Asks the user to copy a local cookies file and calls
get_cookie(cookie_file="<path>")Asks the user to restart the MCP server with
TWITTER_USERNAME/TWITTER_PASSWORDon a trusted network and callsget_cookie()
After cookies are saved
Cookies live at X_MCP_COOKIES_PATH (default ~/.x-mcp/cookies.json). To deploy to another machine:
Copy the cookies file to that machine.
Set
X_MCP_COOKIES_PATHto its absolute path there.No proxy is needed — cookies are reused without proxy.
If cookies later expire (Twitter requires re-verification), API calls fail with AccountLocked or Unauthorized. Re-run get_cookie to refresh.
Default behavior (no get_cookie call)
If you skip the get_cookie flow entirely, the server will attempt auto-login on first run with TWITTER_USERNAME / TWITTER_PASSWORD and save cookies to ~/.x-mcp/cookies.json. This works if the server's IP can reach x.com (residential or trusted VPN); fails with 403 on blocked datacenter IPs — use the cookie flow above for server deployments.
Troubleshooting
Couldn't get KEY_BYTE indices
This means the twikit patch isn't loaded. Check that twikit_patch.py is in src/x_mcp/ and that twitter.py imports it before import twikit:
from . import twikit_patch # noqa: F401 must run before `import twikit`
import twikitForbidden (403) or NotFound (404 "page does not exist") on login
The server's IP is blocked by Cloudflare. x-mcp-lite no longer supports proxy-based login (see ARCHIVE.md for the historical reason). Generate cookies on a network that x.com trusts (home, office, or a VPN exit that x.com accepts) and deploy them via get_cookie(cookie_json=...) or get_cookie(cookie_file=...).
Unauthorized (401) on API calls
Cookies are missing or expired. Call get_cookie() to refresh (see Cookie setup above).
Account locked (Arkose challenge) / Account suspended
The account got flagged by Twitter's anti-automation. Re-cookieing will not help. Stop all automated calls, log into x.com in a browser, complete the challenge (locked) or appeal (suspended), then — only for a lock — re-run get_cookie to refresh cookies.
Client-side request budget exceeded
You've hit the built-in safety cap (X_MCP_MAX_CALLS_PER_WINDOW, default 250 calls / 15 min). Wait the stated number of seconds, or raise/disable the cap via the env var. This is a client-side guard, not an X rate limit.
... is rate-limited; cooldown ends in Ns
X returned a 429 and the reset is further out than X_MCP_MAX_BACKOFF (default 60s), so the call refused instead of blocking. The cooldown is persisted (X_MCP_STATE_PATH) and honored automatically — just retry after the stated time. Raise X_MCP_MAX_BACKOFF if you'd rather have calls block and auto-retry.
KeyError: 'urls' / KeyError while reading users or bookmarks
Fixed by the User.__init__ patch in twikit_patch.py (see twikit patches). If you still see it, the patch isn't loaded — confirm twitter.py imports twikit_patch before import twikit.
get_user / get_user_id return NotFound (404 "page does not exist")
These go through /1.1/account/settings.json, which x.com returns 404 for intermittently. It's not a rate-limit or cookie problem — retry, or use get_user_by_screen_name / get_bookmarks, which don't hit that endpoint.
License
MIT, inherited from the upstream project.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/strobekiss/x-mcp-lite'
If you have feedback or need assistance with the MCP directory API, please join our Discord server