Fetch public X posts and threads; count and split text with X's real rules. No login, no tracking.
Runs on your machine from its package; the steps are below.

Not assessed yet.
Odel charges nothing per call. The publisher may charge for its own service.
Runs locally over stdio
This server isn't hosted — your MCP client launches it from a package registry. Use one of the commands below, or drop the config into your client (e.g. Claude Desktop).
Run
npx -y twittertools-mcp
MCP client config
{
"mcpServers": {
"twittertools-mcp": {
"command": "npx",
"args": [
"-y",
"twittertools-mcp"
]
}
}
}A free and open-source suite of Twitter tools for X (Twitter). Ten tools, no login, no tracking, no paid X API. Most features run directly in your browser; the server only provides a stateless media proxy when a download needs one.
TwitterTools is an independent project and is not affiliated with X Corp.
TwitterTools MCP — a Model Context Protocol
server (npx twittertools-mcp) that lets AI assistants fetch posts and threads,
count characters, and split text with X's real rules. No login, no tracking.
Bookmark Manager is the most distinctive tool in the suite — a local-first X bookmark library:
https://twittertools.com — every tool works without an account.
Twitter tools often demand accounts, OAuth permissions, or opaque black-box services. TwitterTools is open source so you can verify the opposite: nothing is tracked, nothing is stored, and every claim on the site can be checked against the code.
Frontend: Astro + TypeScript + Tailwind CSS (a scoped service worker powers the installable Bookmark Manager)
Backend: Python 3.8+ (3.11+ recommended) + aiohttp. Public posts are resolved through X's free syndication endpoint (the same one that powers embeds on publisher sites) — no developer account, no OAuth, no per-read billing.
Tests: vitest (frontend units) + Playwright (page smoke tests) + an offline mock suite for the backend — all run in CI on every push.
The site is a static build (dist/) plus one small API service. Any host that can
reach cdn.syndication.twimg.com, pbs.twimg.com and video.twimg.com can run it.
npm install
npm run build # -> dist/
cd server
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
.venv/bin/python downloader_server.py # serves 127.0.0.1:8787
Point a web server at dist/ and reverse-proxy /api/ to 127.0.0.1:8787.
A ready-made Apache vhost example lives in deploy/.
Two rules matter:
dist/, never at the repo root — that would
expose server/ and deploy/ to the web.video.twimg.com 403s hotlink requests that carry a non-X Referer; the
backend already sends none — don't add one back.For production, run the API under the provided systemd unit
(server/twittertools-api.service) and create
its state directory first (install -d -o www-data -g www-data /var/lib/twittertools)
so the tweet cache can persist across restarts. If persistence fails, the backend
logs a one-time "staying memory-only" warning and keeps working.
| Variable | Default | Purpose |
|---|---|---|
TT_CACHE_FILE | next to the script (dev) | Disk path for the 10-minute tweet cache; "" disables persistence |
TT_GLOBAL_TWEET_RATE | 300/60 | Global lookup limiter, limit/window-seconds |
TT_GLOBAL_DOWNLOAD_RATE | 120/60 | Global download limiter |
TT_THREAD_DOWNWALK | 0 | 1 enables best-effort conversation down-walk (guest GraphQL) |
TT_MEDIA_HOSTS | — | Extra allowed media hosts (for tests) |
TT_PROXY | — | Optional outbound proxy (e.g. socks5h://127.0.0.1:1080) for hosts that can't reach X directly |
Per-IP limits (60 lookups/min, 40 downloads/min) are built in. Aggregate, anonymous
counters are served at GET /api/healthz.
git pull && npm install && npm run build
# rsync dist/ to your web root; rsync server/ and restart the service when it changes
npm run dev # site on :4321, API auto-wired to :8787
cd server && .venv/bin/python mock_test.py # offline integration suite (no network)
Honest limitation: the syndication endpoint is undocumented and has no SLA. If X
changes it, only the fetch layer in
server/downloader_server.py
(fetch_syndication() / candidate_tokens()) needs updating — GET /api/healthz
shows the upstream status distribution.