Threads MCP
An MCP server that gives Claude (or any MCP client) access to Threads through your own logged-in browser. Search posts by keyword and get the top results with their likes, replies and reposts, read your feed, profiles and replies, work with your DMs, and draft posts, all from your AI assistant.
Everything runs on your machine. There is no Meta developer app, no API key, and no third-party server: the tool drives a dedicated Chrome profile that only you log in to.
Independent open-source project, not affiliated with Meta or Threads. Threads is a trademark of Meta Platforms, used here only to identify the service this software works with. Automated use of Threads may conflict with its terms; use it at a low, personal volume and at your own risk.
What you can do
| Tool | What it does |
|---|---|
search_posts |
Keyword search returning the top posts, ranked by engagement. Top or recent, minimum likes, last N days, and export to JSON, CSV or Markdown. |
keyword_insights |
Analyze a keyword: engagement totals and medians, top authors, best posting hours, media mix, top posts. |
search_profiles |
Find accounts by keyword (name or bio), by relevance or follower count. |
get_trending |
Trending topics on Threads with their AI summary and post counts. |
get_feed |
Posts from your home feed. |
get_profile |
A user's profile: name, bio, followers, links, verified badge. |
get_user_posts |
A user's most recent posts. |
get_post |
One post with its replies. |
list_conversations |
Your DM inbox: who, last message, time, unread. |
get_conversation |
The recent messages in a DM thread, including shared posts. |
send_message |
Send a DM, after you confirm the preview. |
create_post |
Publish a thread (up to 500 characters), after you confirm the preview. |
reply_to_post |
Reply to a post, after you confirm the preview. |
session_status |
Which account the browser is logged in as. |
close_session |
Close the browser without logging out. |
Post-returning tools (search_posts, get_feed, get_user_posts, get_post replies) default to format="compact": text trimmed to 280 characters and ids and media links left out, about a third smaller than format="full", which returns every field. Exports always contain full data.
With format="full", every post comes back with the same fields: url, author (username, name, verified), text, created_at (UTC), likes, replies, reposts, quotes, score, media_type, media_urls, is_reply. The score is likes + 2 x replies + 3 x reposts + 2 x quotes.
Requirements
- macOS, Linux or Windows
- uv
- Google Chrome (recommended). Without it, run
uvx --from git+https://github.com/sunnycho100/threads-mcp patchright install chromiumonce and the server uses Chromium instead. - A Threads account
Setup
1. Log in once
uvx threads-mcp@latest --login
@latest installs from PyPI and picks up fixes automatically, which matters because Threads changes its pages from time to time. To run the newest unreleased code instead, use uvx --from git+https://github.com/sunnycho100/threads-mcp threads-mcp wherever this README says uvx threads-mcp@latest.
A Chrome window opens on threads.com. Log in (Instagram login works) and the window closes by itself. The session is saved in ~/.threads-mcp/profile, separate from your everyday Chrome.
Check it:
uvx threads-mcp@latest --status
logged in as @your_username
profile: /Users/you/.threads-mcp/profile
2. Add it to your MCP client
Claude Code
claude mcp add threads -- uvx threads-mcp@latest
Or, inside a clone of this repo, the included .mcp.json registers the server automatically.
Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %APPDATA%\Claude\claude_desktop_config.json on Windows)
{
"mcpServers": {
"threads": {
"command": "uvx",
"args": ["threads-mcp@latest"],
"env": { "UV_HTTP_TIMEOUT": "300" }
}
}
}
Cursor, Windsurf and other clients use the same command and args in their MCP settings.
Claude Desktop, one-click bundle
git clone https://github.com/sunnycho100/threads-mcp && cd threads-mcp
npx @anthropic-ai/mcpb pack . threads-mcp.mcpb
Open threads-mcp.mcpb with Claude Desktop (double-click, or Settings > Extensions > Install Extension). The install dialog offers a read-only toggle. Run --login once in a terminal first, as above.
Docker
Log in from inside the container with the built-in viewer:
docker build -t threads-mcp https://github.com/sunnycho100/threads-mcp.git
docker run --rm -it -p 127.0.0.1:6080:6080 -v ~/.threads-mcp-docker:/data threads-mcp --login-viewer
Open the printed http://localhost:6080/vnc.html, enter the one-time password it prints, and log in to Threads in the window that appears. The container exits once the session is saved. The viewer listens on localhost only.
Or log in on your computer and move the session in:
uvx --from git+https://github.com/sunnycho100/threads-mcp threads-mcp --export-session ~/threads-session.json
docker run --rm -v ~/.threads-mcp-docker:/data -v ~/threads-session.json:/session.json:ro threads-mcp --import-session /session.json
rm ~/threads-session.json
The exported file holds your login cookies and is created readable only by you; delete it after importing. Then point your client at the container:
{
"mcpServers": {
"threads": {
"command": "docker",
"args": ["run", "-i", "--rm", "-v", "/Users/you/.threads-mcp-docker:/data", "threads-mcp"]
}
}
}
Restart the client, then ask something like "search Threads for posts about rust async and show me the top 10".
Examples
Ask in plain language; the assistant picks the tool. The calls below show what it sends.
Top posts for a keyword
search_posts(query="rust async", limit=10)
{
"query": "rust async", "sort": "top", "count": 10,
"results": [
{"url": "https://www.threads.com/@someone/post/DZ6Wq...", "author": {"username": "someone", "full_name": "Some One", "verified": false},
"text": "...", "created_at": "2026-06-23T02:33:30Z", "likes": 2692, "replies": 334, "reposts": 377, "quotes": 12, "score": 4848,
"media_type": "image", "media_urls": ["..."], "is_reply": false}
]
}
Only popular posts from the last week, saved to a spreadsheet
search_posts(query="indie hacker", min_likes=100, since_days=7, limit=30, export="csv")
The response includes export_path, for example ~/.threads-mcp/exports/search-indie-hacker-20261011-091500.csv. CSV and Markdown files are UTF-8, so Korean, Japanese and emoji come through intact.
Newest posts first
search_posts(query="rust async", sort="recent", limit=20)
What works for a topic
keyword_insights(query="vibe coding", limit=50)
Returns engagement medians, the authors with the most total engagement, the hours (your local time) when the best-performing posts went up, and the media mix.
Who to follow on a topic
search_profiles(query="ios developer", sort="followers", limit=10)
A post and its replies
get_post(url="https://www.threads.com/@someone/post/DZ6WqOxlC-t", reply_limit=20)
DMs
list_conversations()
get_conversation(username="friend_name", limit=20)
Writing, with a confirmation step
create_post(text="Shipping a new side project today")
{"confirm_required": true, "preview": {"action": "create_post", "text": "Shipping a new side project today", "length": 33},
"note": "Show this preview to the user and call again with confirm=true to proceed."}
Nothing is posted until the assistant shows you the preview and calls again with confirm=true. The same applies to send_message and reply_to_post.
Usernames are accepted as name, @name or a profile URL. Post links work with threads.com or threads.net.
How it works
Threads' web app ships its data as JSON: posts and profiles are embedded in each page, and more arrive through the page's own GraphQL requests as you scroll. The server opens the page in your logged-in profile, collects that JSON, and picks out anything shaped like a post or a user, wherever it sits. It does not replay Meta's internal API calls; it reads what the page itself loads, the way you would by scrolling.
DMs are loaded over a live connection rather than page JSON, so the DM tools read the rendered conversation instead. Posting and replying use the normal composer.
MCP client --stdio--> server.py tools, confirmations, error messages
service.py search, feed, profiles, posts
session.py one Chrome profile, one page, one task at a time
extract.py finds posts and users in the page JSON
ranking.py score, sort, filter, insights, export
dm.py inbox and conversations
compose.py new posts and replies
Safety
- Writes need confirmation.
send_message,create_postandreply_to_postreturn a preview and do nothing until called again withconfirm=true. - Human pace. Scrolls and typing use randomized delays, and only one browser task runs at a time, even when the client calls tools in parallel.
- Low volume. Every list is capped at 100 items, and a call is a few page loads, not a crawl.
- Account checks. If Threads shows a checkpoint or account warning, the server stops all browser activity and tells you to resolve it in the app.
- No social actions. There are no tools for likes, follows, reposts or deletes.
- Local data. The browser profile and exports live in
~/.threads-mcp. Nothing is sent anywhere except threads.com.
Command line
threads-mcp run the MCP server over stdio (what clients launch)
threads-mcp --login open a browser window and sign in
threads-mcp --status show the logged-in account (exit code 1 if not logged in)
threads-mcp --logout delete the saved browser profile
threads-mcp --export-session FILE save the login cookies (owner-only file) for Docker or another machine
threads-mcp --import-session FILE load cookies saved by --export-session
threads-mcp --no-headless run the server with the browser window visible
threads-mcp --version
Options for the server and login:
| Flag | Default | Meaning |
|---|---|---|
--transport {stdio,streamable-http} |
stdio |
Serve over HTTP instead of stdio. |
--host, --port |
127.0.0.1, 8000 |
HTTP address; the endpoint is /mcp. |
--user-data-dir PATH |
~/.threads-mcp/profile |
Browser profile to use. |
--login-timeout SECONDS |
900 |
How long --login waits for you to sign in. |
--browser-idle-timeout SECONDS |
600 |
Close an idle browser after this long. |
--viewport WxH |
1280x900 |
Browser window size. |
--proxy-server URL |
none | Route the browser through a proxy (http://, socks5://). |
--slow-mo MS |
0 |
Delay between browser actions, for debugging. |
--chrome-path PATH |
Google Chrome | Use a specific Chrome or Chromium executable. |
Environment variables:
| Variable | Default | Meaning |
|---|---|---|
THREADS_MCP_HOME |
~/.threads-mcp |
Where the profile and exports live. |
THREADS_MCP_HEADLESS |
1 |
Set to 0 to show the browser window. |
THREADS_MCP_READONLY |
0 |
Set to 1 to refuse confirmed writes; previews still work. Handy for demos and testing. |
The browser closes itself after 10 idle minutes and reopens on the next call.
Troubleshooting
"Not logged in to Threads. Run threads-mcp --login"
The saved session expired or was never created. Run threads-mcp --login (with the same uvx --from ... prefix you use in your client config), log in, then retry.
"The browser profile is in use by another threads-mcp process"
Only one process can use the profile at a time. Another MCP client, a second Claude window, or a --login window has it open. Close that, or call close_session from the client that holds it. Idle servers release it after 10 minutes.
Replies fail with "private profiles can only reply to their followers"
That is a Threads rule. If your profile is private, you can only reply to people who follow you. Reply to a follower's post, or switch your profile to public in the Threads app.
"Threads page error" or empty results
Threads may have changed its page layout. Run threads-mcp --no-headless, call the tool again and watch the window, then open an issue with the tool name and what you saw. Updating (uvx --refresh ...) picks up fixes.
Fewer results than the limit
Threads loads about 7 more posts per scroll after the first 20, and some keywords or profiles simply have fewer posts. Filters like min_likes and since_days are applied after loading, so they can return fewer items than limit.
The first call is slow
The first call starts Chrome (a few seconds). A 30-post search usually takes 10 to 20 seconds. If your client times out, raise its MCP tool timeout.
Chrome is not installed
Run uvx --from git+https://github.com/sunnycho100/threads-mcp patchright install chromium once. The server falls back to that Chromium automatically.
Development
git clone https://github.com/sunnycho100/threads-mcp
cd threads-mcp
uv sync
uv run pytest -q # unit tests, no browser needed
uv run threads-mcp --login # once
uv run python scripts/e2e.py # every tool against your live account
- Unit tests run against scrubbed fixtures captured from real pages (
tests/fixtures/).scripts/scrub_fixture.pyturns a raw capture into a fixture, replacing usernames, names, bios, captions and media links. scripts/e2e.pystarts the server over stdio with the MCP client and calls every tool. Writes run in dry-run mode: the composer is opened, filled, checked and cancelled. Put your own test keywords in.local/keywords.txt(gitignored, one per line); they are never written to the report. The latest results are in docs/E2E_REPORT.md.- Releasing:
uv run python scripts/bump_version.py X.Y.Z, commit, then push avX.Y.Ztag. The release workflow tests, builds and publishes to PyPI through trusted publishing. - Design notes: docs/superpowers/specs/2026-10-11-threads-mcp-design.md.
- How this compares with linkedin-mcp-server, including what is not built yet: docs/LINKEDIN_PARITY.md.
Acknowledgements
The browser-session approach follows stickerdaniel/linkedin-mcp-server, and reading the page's own JSON follows xpzouying/xiaohongshu-mcp.
Metadata
Release files for threads-mcp 0.1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| threads_mcp-0.1.3.tar.gz | 25.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| threads_mcp-0.1.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 55.1 kB
Release files / threads_mcp-0.1.3.tar.gz
| Download URL | threads_mcp-0.1.3.tar.gz |
|---|---|
| Size | 25.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b80ecf562ff08f960df993431c05bde238b4d578758c572a59d123f262bdaf6d
|
|
BLAKE2b-256 checksum How to use checksums |
024c6834f7c360ea47f9aeea9ad5a2b15f10c5d0fae506d08748afb72642ba92
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / threads_mcp-0.1.3-py3-none-any.whl
| Download URL | threads_mcp-0.1.3-py3-none-any.whl |
|---|---|
| Size | 29.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
79f2fb40cccc44be18c4d4ad884cbb1d5676f96ce2f0044f4c437364d636159e
|
|
BLAKE2b-256 checksum How to use checksums |
9ce56f5afc62d64220c8ad636a7da158a50de81fbca64c36cd846e70c52b292a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|