x-agent-mcp
The x-agent-mcp server provides an unofficial client for X (Twitter) using session cookies (no API key required), enabling AI agents to automate actions. It supports:
Posting: tweets (including note-tweets over 280 chars), replies, quotes, and deletion.
Engagement: like/unlike, retweet/unretweet, and bookmark/unbookmark.
Users: follow/unfollow by ID, profile lookup, and fetching tweets, likes, followers, and following.
Search & timelines: search with operators (Top/Latest/People/Media), home timeline, get tweet/thread with engagement, mentions, and notifications.
Direct Messages: send and inbox retrieval.
Pagination: use next_cursor to fetch more pages from timelines.
Multi-account: optional account parameter when X_ACCOUNTS is configured.
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-agent-mcpsearch for tweets from @elonmusk"
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-agent-sdk
Unofficial X (Twitter) client — for your own code and for AI agents alike.
Use it as a typed TypeScript library, or drop in its MCP server so agents connect directly. All through the same private GraphQL API the web app uses.
Cookie auth · no OAuth · no paid API · automatic x-client-transaction-id · multi-account · cookie-free public reads · built-in MCP server
Use at your own risk. This is an unofficial tool that talks to X's private API with your session cookies, which may breach X's Terms of Service. It can get your account rate-limited, shadow-banned, suspended, or permanently terminated. The author takes no responsibility for any bans, damages, or losses. You alone are responsible for how you use it and for complying with X's ToS and applicable law. Educational and personal use only.
Agent setup guide
This package ships a SKILL.md with operating instructions for AI agents:
MCP setup, credential handling, tool choice, and known X API quirks. Use the prompt below to
configure the MCP server on this machine.
Set up x-agent on this machine.
The package is on npm:
https://www.npmjs.com/package/x-agent-sdk
The source and agent guide live at:
https://github.com/alarok/x-agent-sdk
Workflow:
1. Read the full agent guide first:
https://github.com/alarok/x-agent-sdk/blob/main/SKILL.md
2. Start the MCP server without installing anything:
npx -y x-agent-mcp
Or install once and run it:
npm install -g x-agent-sdk && x-agent-mcp
3. Register the MCP server with the MCP client already installed on this
machine. If the client is Claude Code, use:
claude mcp add x-agent --env AUTH_TOKEN=... --env CT0=... -- npx -y x-agent-mcp
For Hermes or other clients, follow the config blocks in the README.
If the client is unclear, inspect the local configuration and ask me
before making assumptions.
4. x-agent requires these private environment variables:
- AUTH_TOKEN: the auth_token cookie from a logged-in x.com session
- CT0: the ct0 cookie from that same session
- For multiple accounts, use X_ACCOUNTS instead (see "Multiple accounts").
Ask me for them if they are not already configured. Never print, log,
commit, expose, or include either value in command output.
5. Verify setup with a read-only action, such as get_user for username "x".
Do not post, reply, like, repost, follow, bookmark, delete, or send a DM
without first showing me the intended action and receiving explicit approval.
If any step fails, inspect the actual error and propose the smallest safe fix.
At the end, summarize the MCP configuration, the command used to run it, and
the available read-only tools.Related MCP server: Twitter/X MCP Server
What is this?
X killed its free public API in 2023. But the web client at x.com still talks to
a private GraphQL API using nothing but your session cookies. x-agent drives that
API directly — post, reply, like, retweet, search, read timelines — with two ways in:
As a library — a typed TypeScript client you call from your own code, to build a bot, a scheduler, a scraper, or any automation you want.
As an MCP server — drop it into any Model Context Protocol agent (Claude Desktop, Cursor, iris, ...) and it discovers the tools automatically.
Same engine underneath; pick whichever fits.
Why a plain HTTP client fails
Hitting X's private API with a plain HTTP client returns a misleading
error 344 "You have reached your daily limit" even on a fresh account with 2
tweets. That is not a quota — it's anti-bot. The browser signs every request
with a per-request x-client-transaction-id header. x-agent generates that
header for you, in-process, on every call — with zero third-party crypto.
Install
bun add x-agent-sdk # or: npm install x-agent-sdkInstalling from source instead (git clone): run bun install && bun run build first.
Runs on Node 18+ and Bun. Dependencies are all generic infrastructure:
@modelcontextprotocol/sdk (the MCP server), node-html-parser (reads X's home
page), zod (tool schemas). No transaction-id package — that algorithm lives in
this repo.
Credentials
Two cookies from a logged-in x.com session:
Cookie | What it is |
| Your session token |
| CSRF token (sent as both cookie and header) |
Copy them from your browser's dev tools while logged in at x.com:
Browser | Where |
Chrome / Edge / Brave | F12 → Application → Cookies → |
Firefox | F12 → Storage → Cookies → |
Safari | Develop → Show Web Inspector → Storage → Cookies (enable the Develop menu first: Settings → Advanced → Show features for web developers) |
Provide via env (AUTH_TOKEN, CT0) or pass to the constructor.
Need more than one account? Every XClient holds one account's cookies, so
create one client per account — see Multiple accounts.
Quick start (library)
import { XClient } from "x-agent-sdk";
const x = new XClient(); // reads AUTH_TOKEN / CT0 from env
// const x = new XClient({ authToken: "...", ct0: "..." });
// Post
const id = await x.postTweet("hello from an agent");
console.log(`https://x.com/i/web/status/${id}`);
// Reply
await x.reply(id, "and a threaded reply");
// Read a tweet's engagement + replies (clean shape)
const { root, replies } = await x.getThread(id);
console.log(`${root.likes} likes, ${root.replies} replies, ${root.views} views`);
for (const r of replies) console.log(` @${r.author}: ${r.text} (${r.likes} likes)`);
// Search
for (const t of await x.search("typescript", 10, "Latest")) {
console.log(t.url, "-", t.text);
}
// Resolve a handle, then read their tweets
const user = await x.getUser("x");
const tweets = await x.getUserTweets(user.rest_id, 20);
// Engage
await x.like(id);
await x.retweet(id);
await x.bookmark(id);API
Method | Returns | Notes |
| tweet id |
|
| tweet id | Shorthand for |
| tweet id | Quote-tweet; resolves the handle if given a bare id |
|
| |
|
| |
|
| |
|
| |
|
| Numeric id — uses X's REST v1.1 endpoint |
|
|
|
| user object | Has |
|
| Needs numeric id — resolve via |
|
| Tweets a user liked |
|
| A user's followers |
|
| Who a user follows |
|
| "For You" |
| raw | DM a user (your own id auto-resolved) |
| raw | Conversations + recent messages |
| raw | Likes, follows, replies |
| raw | Tweets mentioning you |
|
| Your account's numeric id, cached |
|
| Last |
| raw response | Full |
| parsed tweet | | No cookies needed — reads via the public FxTwitter API. |
|
| Parsed likes/replies/views — use this over |
Pagination — every timeline has a *Page variant returning { items, next_cursor }:
Method | Returns | Notes |
|
|
|
|
| |
|
| |
|
| |
|
| |
|
|
let cursor: string | undefined;
do {
const { items, next_cursor } = await x.searchPage("typescript", 10, "Latest", cursor);
for (const t of items) console.log(t.url, "-", t.text);
cursor = next_cursor ?? undefined;
} while (cursor);new XClient({ retries }) sets the retry budget for 344/429 (default 3).
Two more options for agent-facing code:
new XClient({ fetch })injects a custom HTTP client — plug a curl-impersonate-style transport here to mimic Chrome's TLS fingerprint.new XClient({ onRateLimit })fires with{ remaining, reset, limit }after each response that carries X's rate-limit headers;getLastRateLimit()returns the last one. Call it before a burst to check the remaining budget.
Use it as an MCP server (recommended for agents)
Connect the MCP server and the agent auto-discovers all 25 tools — names, descriptions, JSON schemas. No glue code.
Config
Any MCP client (Claude Desktop, Cursor, Windsurf, ...) — add to the MCP config:
{
"mcpServers": {
"x": {
"command": "npx",
"args": ["-y", "x-agent-mcp"],
"env": {
"AUTH_TOKEN": "your_auth_token_cookie",
"CT0": "your_ct0_cookie"
}
}
}
}Claude Code — one command, no JSON file:
claude mcp add x-agent \
--env AUTH_TOKEN=your_auth_token_cookie \
--env CT0=your_ct0_cookie \
-- npx -y x-agent-mcpHermes Agent — add to ~/.hermes/config.yaml:
mcp_servers:
x:
command: "npx"
args: ["-y", "x-agent-mcp"]
env:
AUTH_TOKEN: "..."
CT0: "..."Restart the agent. It now has these tools:
Tool | Does |
| Publish a tweet (or reply via |
| Quote another tweet with your commentary |
| Delete a tweet by id |
| Like / unlike |
| Retweet / undo |
| Save / remove a bookmark |
| Follow / unfollow by user id |
| Search ( |
| Resolve |
| Recent tweets by user id |
| Tweets a user liked |
| List followers / following |
| Raw tweet + thread |
| Read a tweet without cookies (public FxTwitter API). |
| Clean |
| The For You timeline |
| Send a direct message by user id |
| Conversations + recent messages |
| Recent notifications |
| Tweets mentioning you |
Now you can tell the agent: "search X for the latest posts about Bun and reply to
the top one with a question" — it calls search_tweets then post_tweet by itself.
Timeline tools (search_tweets, get_user_tweets, get_likes, get_followers,
get_following, home_timeline) return a next_cursor; pass it back as cursor
to fetch the next page.
Try the MCP server by hand
AUTH_TOKEN=... CT0=... npx -y x-agent-mcp
# speaks MCP over stdio; connect any MCP client to list/call toolsMultiple accounts
One XClient holds one account's cookies. Use the library with several
accounts by creating one client per account:
const work = new XClient({ authToken: "...", ct0: "..." });
const personal = new XClient({ authToken: "...", ct0: "..." });
await work.postTweet("from the work account");
await personal.postTweet("from the personal account");The MCP server takes it further: pass X_ACCOUNTS — a JSON object mapping an
account name to its cookies — and every tool gains an optional account
param to pick which account to use. Omit account to use the default
account (or the only configured one). Unknown names fail with the list of
available accounts.
{
"mcpServers": {
"x": {
"command": "npx",
"args": ["-y", "x-agent-mcp"],
"env": {
"X_ACCOUNTS": "{\"default\":{\"authToken\":\"...\",\"ct0\":\"...\"},\"work\":{\"authToken\":\"...\",\"ct0\":\"...\"}}"
}
}
}
}Claude Code:
claude mcp add x-agent \
--env 'X_ACCOUNTS={"default":{"authToken":"...","ct0":"..."},"work":{"authToken":"...","ct0":"..."}}' \
-- npx -y x-agent-mcpHermes Agent (~/.hermes/config.yaml):
mcp_servers:
x:
command: "npx"
args: ["-y", "x-agent-mcp"]
env:
X_ACCOUNTS: '{"default":{"authToken":"...","ct0":"..."},"work":{"authToken":"...","ct0":"..."}}'X_ACCOUNTS wins over AUTH_TOKEN / CT0 when both are set. Write actions
are public — when an agent manages several accounts, tell it explicitly which
account to use before it posts.
Watch mode (for agents)
X has no push channel (no webhooks, no streaming) on the cookie API — the web
app itself polls. So watch means: poll with the *Page methods, diff tweet
ids, and report only what is new. A scheduled script beats a long-lived loop:
same result, a fraction of the rate-limit cost.
// watch.mjs — prints only new tweets for a search; run on a schedule.
// A copy lives in the repo at examples/watch.mjs.
import { readFileSync, appendFileSync, writeFileSync } from "node:fs";
import { XClient } from "x-agent-sdk";
const x = new XClient(); // AUTH_TOKEN / CT0 from env
const QUERY = process.env.WATCH_QUERY ?? "typescript"; // search to watch
const STATE = process.env.WATCH_STATE ?? ".watch-state"; // dedup state
const LOG = process.env.WATCH_LOG; // optional: append to a markdown log
// State file: first line is the last known rate-limit budget ("rl:N").
let body = "";
try {
body = readFileSync(STATE, "utf8");
} catch {
/* first run: no state yet */
}
const [head = "", ...rest] = body.split("\n");
const lastRemaining = Number(head.replace(/^rl:/, ""));
if (lastRemaining > 0 && lastRemaining <= 5) process.exit(0); // low budget
const seen = new Set(rest.filter(Boolean));
const { items } = await x.searchPage(QUERY, 10, "Latest");
const fresh = items.filter((t) => t.id && !seen.has(t.id));
for (const t of fresh) {
console.log(`[${t.author ?? "unknown"}] ${t.text}\n${t.url}`);
if (LOG) appendFileSync(LOG, `- **${t.author ?? "unknown"}** — ${t.text}\n ${t.url}\n`);
seen.add(t.id);
}
const rl = x.getLastRateLimit();
writeFileSync(STATE, `rl:${rl?.remaining ?? "?"}\n${[...seen].join("\n")}`);In Hermes this is a cron job with no_agent: the script runs on schedule and
its stdout is delivered verbatim — silent when nothing is new, so you only
hear about fresh tweets:
mkdir -p ~/.hermes/scripts
cp examples/watch.mjs ~/.hermes/scripts/watch-x.mjs
hermes cron add watch-x --schedule "every 15m" \
--script ~/.hermes/scripts/watch-x.mjs \
--no-agent --deliver origin
# exact flags: hermes cron add --helpSet AUTH_TOKEN / CT0 in the environment the Hermes scheduler runs in.
Intervals: 10-15 minutes is the safe 24/7 default; 1-5 minutes works for
near-real-time (the web app itself polls every ~30-60 s). The example skips a
tick silently when the rate-limit budget runs low — tune it with
getLastRateLimit(). Set WATCH_LOG to append every new tweet to a markdown
file: that file becomes the long-running conversation. To keep every cron
delivery in one chat thread instead, enable the job's attach_to_session
option (see hermes cron add --help). STATE is relative to the job's
working directory.
Use the tool defs with the Vercel AI SDK (or any framework)
The tool definitions are exported runtime-agnostic (Zod schema + execute), so you
can use them in any agent runtime, not just MCP:
import { XClient } from "x-agent-sdk";
import { tools } from "x-agent-sdk/tools";
import { tool } from "ai";
import { z } from "zod";
const x = new XClient();
const aiTools = Object.fromEntries(
tools.map((t) => [
t.name,
tool({
description: t.description,
parameters: z.object(t.inputSchema),
execute: (args) => t.execute(x, args),
}),
]),
);
// generateText({ model, tools: aiTools, prompt: "post a tweet about ..." })Reliability notes
344/429handling. On344(anti-bot) the client rebuilds the transaction generator and backs off exponentially; on HTTP429it honorsx-rate-limit-reset. The344daily-limit message is almost always the header, not a real quota — tweet length is irrelevant.queryIds in
QIDcan go stale; if a call starts returning400, re-capture the current ones from X's web bundle.TweetDetailneeds full variables +fieldTogglesor X returns"...must be defined".getTweetalready sends the validated set.TLS fingerprint.
fetchdoesn't mimic Chrome's JA3. The transaction-id clears the common blocks, but for very high sustained volume a real browser (Playwright) remains the safest transport — or pass a curl-impersonate wrapper via thefetchoption to speak with Chrome's TLS fingerprint.
Project layout
src/
transaction.ts x-client-transaction-id generator (zero third-party crypto)
accounts.ts MCP account loading (X_ACCOUNTS / AUTH_TOKEN+CT0)
client.ts XClient — typed methods, 344/429 backoff
tools.ts 25 runtime-agnostic tool defs (Zod schemas)
mcp.ts MCP stdio server (bin: x-agent-mcp), multi-account routing
index.ts public exports
dist/ compiled output (what actually runs)License & disclaimer
MIT — see LICENSE, which includes a use-at-your-own-risk notice.
Unofficial, not affiliated with X Corp. This software uses X's private API with your session cookies and may violate X's Terms of Service. It can result in your account being rate-limited, suspended, or permanently banned. The author accepts no responsibility for any bans, damages, or losses — you use it entirely at your own risk and are solely responsible for complying with X's ToS and applicable law. For educational and personal use only.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- FlicenseNot gradedqualityNot gradedmaintenanceConnects AI assistants to Twitter/X using cookie-based authentication to read timelines, search tweets, and perform actions like posting and liking. It leverages Twitter's internal GraphQL API to provide full functionality without requiring a developer account.12
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to interact with Twitter/X through Playwright browser automation without requiring an official API key. It provides tools for posting content, searching tweets, reading feeds, and managing social interactions like follows and likes.2MIT
- AlicenseNot gradedqualityBmaintenanceProvides programmatic X (Twitter) engagement via MCP, offering 24 tools for search, timelines, notifications, bookmarks, profiles, and tweet actions through a headless browser.MIT
- AlicenseBqualityAmaintenanceEnables AI agents to search, read user profiles, timelines, media, follow threads, track trends, and manage accounts on X/Twitter via GraphQL, without browser automation or paid API keys.1009MIT
Related MCP Connectors
X (formerly Twitter) posts, profiles, and search for AI agents. Free key, self-minted, no signup.
Connect any AI agent to 11+ social platforms: schedule, publish & track posts via hosted MCP.
Hosted MCP with 91 agent tools: X, domains, SEO, Maps, Trends, Search, YouTube, TikTok, and more.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- 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/alarok/x-agent-sdk'
If you have feedback or need assistance with the MCP directory API, please join our Discord server