{
  "markdown": "<!-- mcp-name: io.github.jsilets/twscrape-twitter-mcp -->\n\n# twscrape-twitter-mcp\n\n[![PyPI](https://img.shields.io/pypi/v/twscrape-twitter-mcp)](https://pypi.org/project/twscrape-twitter-mcp/)\n[![CI](https://github.com/jsilets/twscrape-twitter-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/jsilets/twscrape-twitter-mcp/actions/workflows/ci.yml)\n[![Python](https://img.shields.io/pypi/pyversions/twscrape-twitter-mcp)](https://pypi.org/project/twscrape-twitter-mcp/)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE)\n[![MCP](https://img.shields.io/badge/MCP-server-black)](https://modelcontextprotocol.io)\n\nAn [MCP](https://modelcontextprotocol.io) server that reads X (Twitter): posts,\nthreads, replies, quotes, and search. It wraps [twscrape](https://github.com/vladkens/twscrape)\nand uses your own logged-in session, so there's no paid X API and no developer\naccount. Tools return clean markdown shaped for an agent to read, including\nimage, video, and GIF URLs and any external links in a post.\n\nWorks with any MCP client over the two standard transports — local **stdio** and\nhosted **Streamable HTTP**.\n\n## Tools\n\n| Tool | Returns |\n|---|---|\n| `read_tweet(url_or_id)` | One post as markdown. |\n| `read_thread(url_or_id, max_replies=50)` | Root post + the author's self-thread + top replies. |\n| `read_replies(url_or_id, limit=50)` | Replies to a post. |\n| `read_quotes(url_or_id, limit=30)` | Quote-tweets (best-effort, search-based). |\n| `user_timeline(username, limit=40, include_replies=False)` | A user's recent posts, newest first. Set `include_replies=True` to include replies. |\n| `search(query, limit=20, product=\"Latest\")` | Search results. Supports `from:`, `has:media`, `min_faves:`, etc. |\n| `user_profile(username)` | A user's profile as markdown: bio, location, follower/following/tweet counts, join date. |\n\n## Install\n\n```bash\nuv tool install twscrape-twitter-mcp     # or: pipx install twscrape-twitter-mcp\n```\n\nThen authenticate once (next section) and verify:\n\n```bash\ntwscrape-twitter-mcp smoke               # reads one public tweet end-to-end\n```\n\n## Authenticate\n\nReads run against your own X session. Pick one path:\n\n**1. Launch a dedicated browser (recommended).** Opens a separate Chrome/Brave\nprofile with a DevTools port, you sign in to X once, and the session is captured.\nIt does not touch your daily browser or automate X's login flow.\n\n```bash\ntwscrape-twitter-mcp login --launch-browser chrome   # or: brave\n```\n\n**2. Attach to a dedicated browser profile.** Only use this when you already run\nan isolated Chromium profile with a debug port. Browser debugging exposes browser\ndata, and recent Chrome versions do not enable it for the default profile.\n\n```bash\n# macOS\n\"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome\" --remote-debugging-port=9222 --user-data-dir=/tmp/x-mcp-browser\n# Linux:   google-chrome --remote-debugging-port=9222 --user-data-dir=/tmp/x-mcp-browser\n# Windows: \"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe\" --remote-debugging-port=9222 --user-data-dir=%TEMP%\\x-mcp-browser\n\ntwscrape-twitter-mcp login --attach                  # add --cdp-url for a non-default port\n```\n\n**3. Headless / CI (raw cookies).** A server can't open your desktop browser, so\nadd a session from `auth_token` + `ct0` cookies. Run this without cookie flags to\nenter values through hidden prompts, rather than leaving secrets in shell history:\n\n```bash\ntwscrape-twitter-mcp init\n```\n\nThe captured session is reused across restarts. Run `login --launch-browser chrome` again when it expires,\nor to add burner sessions for rate-limit rotation. `twscrape-twitter-mcp accounts`\nlists the pool.\n\n> Use burner accounts, not your main — see [Legal](#legal).\n\n## Connect your client\n\nThe server runs locally over stdio. Most MCP clients take a JSON block like this:\n\n```json\n{\n  \"mcpServers\": {\n    \"x\": {\n      \"command\": \"twscrape-twitter-mcp\",\n      \"args\": [\"serve\", \"--transport\", \"stdio\"]\n    }\n  }\n}\n```\n\nClient-specific equivalents:\n\n<details>\n<summary><b>Claude Code</b></summary>\n\n```bash\nclaude mcp add x --scope user -- twscrape-twitter-mcp serve --transport stdio\n```\n</details>\n\n<details>\n<summary><b>Claude Desktop</b></summary>\n\nAdd the JSON block above to `claude_desktop_config.json`\n(Settings → Developer → Edit Config).\n</details>\n\n<details>\n<summary><b>Codex</b> — <code>~/.codex/config.toml</code></summary>\n\n```toml\n[mcp_servers.x]\ncommand = \"twscrape-twitter-mcp\"\nargs = [\"serve\", \"--transport\", \"stdio\"]\n```\n</details>\n\n<details>\n<summary><b>Cursor</b> — <code>~/.cursor/mcp.json</code></summary>\n\n```json\n{\n  \"mcpServers\": {\n    \"x\": {\n      \"command\": \"twscrape-twitter-mcp\",\n      \"args\": [\"serve\", \"--transport\", \"stdio\"]\n    }\n  }\n}\n```\n</details>\n\n<details>\n<summary><b>VS Code</b> — <code>.vscode/mcp.json</code></summary>\n\n```json\n{\n  \"servers\": {\n    \"x\": {\n      \"command\": \"twscrape-twitter-mcp\",\n      \"args\": [\"serve\", \"--transport\", \"stdio\"]\n    }\n  }\n}\n```\n</details>\n\n<details>\n<summary><b>Remote (Streamable HTTP)</b></summary>\n\nFor a hosted private instance (see [Deploy](#deploy)), point a preconfigured\nclient at the HTTP endpoint with a bearer token. This is static bearer auth, not\nan OAuth sign-in flow.\n\n```json\n{\n  \"mcpServers\": {\n    \"x\": {\n      \"url\": \"https://YOUR-APP.example.com/mcp\",\n      \"headers\": { \"Authorization\": \"Bearer YOUR_TOKEN\" }\n    }\n  }\n}\n```\n</details>\n\nThen ask, e.g. *\"read this thread: &lt;url&gt;\"*.\n\n## Deploy\n\nRun the server always-on and reachable over HTTP (`POST /mcp`). It ships as a\ncontainer; Cloudflare Workers won't work because twscrape is Python with native\ndeps.\n\nA headless container can't open your desktop browser, so authenticate **locally**\nfirst (`login --attach` writes `storage_state.json` under\n`TWSCRAPE_TWITTER_MCP_HOME`), then ship that session to the host — copy the file to\nthe mounted volume, or run the cookie-based `init` over SSH. The server reloads a\npersisted session on boot.\n\n**Always set a token when exposing HTTP** — anyone who can reach the endpoint can\nuse your X session:\n\n```bash\nexport TWSCRAPE_TWITTER_MCP_AUTH_TOKEN=$(openssl rand -hex 32)\n```\n\n<details>\n<summary><b>Fly.io</b></summary>\n\n```bash\nfly launch --no-deploy\nfly volumes create twscrape_twitter_mcp_data --size 1\nfly secrets set TWSCRAPE_TWITTER_MCP_AUTH_TOKEN=$(openssl rand -hex 32)\nfly deploy\n# seed a session onto the volume through hidden prompts:\nfly ssh console -C \"twscrape-twitter-mcp init\"\n```\n</details>\n\n<details>\n<summary><b>Railway</b></summary>\n\nPoint Railway at this repo (it reads `railway.json` + `Dockerfile`), add a volume\nmounted at `/data`, set `TWSCRAPE_TWITTER_MCP_AUTH_TOKEN`, and seed a session via\nthe Railway shell with `twscrape-twitter-mcp init`.\n</details>\n\nClients send `Authorization: Bearer <token>`.\n\n## Configuration\n\n| Env var | Default | Purpose |\n|---|---|---|\n| `TWSCRAPE_TWITTER_MCP_HOME` | `~/.config/twscrape-twitter-mcp` | Where the sqlite account pool lives. Point at a volume in prod. |\n| `TWSCRAPE_TWITTER_MCP_DB` | `$TWSCRAPE_TWITTER_MCP_HOME/accounts.db` | Override the pool path directly. |\n| `TWSCRAPE_TWITTER_MCP_AUTH_TOKEN` | _(unset)_ | Required bearer token for HTTP transport. |\n| `TWSCRAPE_TWITTER_MCP_PROXY` | _(unset)_ | Global proxy for every account. |\n| `TWSCRAPE_TWITTER_MCP_CDP_URL` | `http://127.0.0.1:9222` | Browser DevTools endpoint for `login --attach`. |\n| `TWSCRAPE_TWITTER_MCP_DEFAULT_LIMIT` | `40` | Default timeline and search result count (1–100). |\n| `PORT` | `8080` | HTTP port (Railway injects this). |\n\n## How it works\n\nThe hard part of reading X — GraphQL signing, the `x-client-transaction-id`\nheader, TLS fingerprinting — lives entirely in `twscrape`, which is pinned.\nThis package is a read-only MCP layer on top and never\ntouches that machinery. When X changes something and reads break, the fix is a\nversion bump, not reverse-engineering.\n\n## Limits\n\n- X can expire, rate-limit, or suspend the account behind your session.\n- Protected, deleted, geo-blocked, or otherwise restricted posts may not be readable.\n- Quote-tweet coverage is search-based and incomplete.\n- Search results depend on X's current search behavior and can vary by session.\n- It does not decrypt browser cookie stores — use browser attach or cookie `init`.\n\n## Legal\n\nReading X with your own logged-in session may violate X's Terms of Service, and\naccounts used for scraping can be rate-limited or suspended. Use burner accounts,\nnot your main. Provided as-is for research and personal use; you are responsible\nfor how you use it.\n\n## License\n\nMIT.\n\n## Credits\n\nThe hard scraping work is [`twscrape`](https://github.com/vladkens/twscrape) by\nvladkens. This is a read-only MCP layer on top — go star it.\n</content>\n",
  "bytes": 8738,
  "sha": "cc457ba1a04e7789eda84ca57817d2398dd8f0fbe5a18c7c1d544d647d7672ed",
  "repo_slug": "jsilets/twscrape-twitter-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_jsilets_twscrape_twitter_mcp_201643d9/readme"
}