threads-mcp
The threads-mcp server lets you automate your own Meta Threads account via a real browser session. It provides structured output for all reads and rate-limited write actions.
Read / Lookup:
whoami– Get signed-in account info (handle, ID, follower/following counts)get_profile– Fetch any user's profile (bio, follower count, verified status, recent posts)get_user_threads– List a user's recent posts (up to 50)get_thread– Retrieve a single post with like, reply, repost, quote countsget_thread_replies– Fetch replies under a postget_timeline– Read your "For You" home feedsearch– Search posts or users by keywordget_followers/get_following– Partial samples of a user's followers / who they followget_notifications– Activity feed (follows, replies, mentions, likes, reposts, suggestions), filterable by kind
Write (acts on your real account):
create_thread– Post a new thread with text and/or media (images, video from local files or URLs); supports multi-post chains and carouselsreply_to_thread– Reply to any post with text and/or mediaquote_thread– Quote a post with your own comment and optional mediadelete_thread– Permanently delete one of your own postslike_thread/unlike_thread– Like or remove like from a postrepost_thread/unrepost_thread– Repost or remove repostfollow_user/unfollow_user– Follow or unfollow a user
Scheduling:
schedule_thread– Queue a post with text/media for later; specify an absolute time (at) or relative delay (in). (Server must be running at publication time.)list_scheduled– View all scheduled posts and statuscancel_scheduled– Cancel a pending scheduled post
Diagnostics:
doctor– Health-check session validity and UI selectors; optionally deep-check post pages and activity feed.
Allows posting, replying, quoting, liking, reposting, following, scheduling, and reading profiles, posts, replies, timeline, search, and notifications on Meta's Threads platform.
Click on "Deploy 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., "@threads-mcp show my timeline"
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.
threads-mcp
An MCP server for Meta's Threads that acts as your own account — read profiles, posts, replies, your timeline & search, and post / reply / quote / like / repost / follow / schedule — from any MCP client (Claude Desktop, Claude Code, etc.).
No developer account. Unlike the official Threads Graph API approach (which needs an app, OAuth, and an Instagram Business account), this server drives a real logged-in browser session using your own cookies.
Contents: Rate limits · Tools · Structured output · Media · Scheduling · Setup · Run as a daemon · Config · How it works · When things break · Troubleshooting
Full reference: Documentation · Changelog: CHANGELOG.md · Versioning & releases: docs/RELEASES.md
⚠️ Behave for rate limits
You are automating a real Threads account. Meta rate-limits aggressively and can restrict or ban accounts that behave like bots — bursty posting, rapid follow/unfollow, like loops. This server helps, but the discipline is on you:
Writes are spaced ≥
THREADS_MIN_ACTION_INTERVAL_MS(default 8s) apart, enforced server-side.Treat
create_thread,follow_user,like_threadas scarce actions, not loops.If you hit a
🐢 rate-limitedmessage, stop for several minutes — don't retry immediately.Reads are cheaper but still hit a real session; results are cached briefly.
Related MCP server: meta-threads-mcp
Tools
24 tools. Posts are identified by a full url or handle + code (the shortcode in .../@user/post/CODE).
Read
Tool | What it returns |
| Which account you're signed in as (handle, user id, name, follower/following). |
| A user's bio, follower count, verified status + recent posts. Omit |
| A user's recent posts (their profile feed). |
| A single post with its like/reply/repost counts. |
| Replies under a post. |
| Your "For you" home feed. |
| Search Threads for |
| A partial sample of a user's followers. |
| A partial sample of who a user follows. |
| Your Activity feed — follows, replies, mentions, suggestions. Filterable by |
Write (rate-limited — real account)
Tool | Action |
| Post a new thread — text and/or media, optionally a multi-post |
| Reply to a post — text and/or media. |
| Quote-post (repost with your own comment + optional media). |
| Delete one of your own posts (permanent). |
| Like / remove a like. |
| Repost / remove a repost. |
| Follow / unfollow a user. |
Schedule
Tool | Action |
| Queue a text/media post to publish later ( |
| List scheduled posts and their status. |
| Cancel a pending scheduled post by id. |
Note: reposting your own post is a no-op on Threads (it silently does nothing) — that's Threads' behavior, not a bug.
Tool annotations
Per the MCP annotations spec — side effects at a glance. Write tools act on your real account.
Tool | Read-only | Idempotent | Destructive |
| ✓ | ✓ | – |
| – | – | – |
| – | ✓ | – |
| – | ✓ | ✓ |
| – | – | – |
| ✓ | ✓ | – |
| – | ✓ | – |
Structured output
Every read tool returns both a rendered text block and machine-readable
structuredContent, described by an outputSchema the client can inspect. Text
clients are unaffected; anything that understands structured output gets typed
fields instead of parsing prose.
This matters for chaining. Recovering a shortcode from rendered markdown works right up until a post's own text contains something shortcode-shaped:
// search → structuredContent
{
"posts": [
{
"code": "DbUd7C8iR7A", // pass straight to like_thread / get_thread
"url": "https://www.threads.com/@someone/post/DbUd7C8iR7A",
"author": "someone",
"text": "…",
"created_at": "2026-07-28T05:12:44.000Z", // ISO, not a relative "3h"
"likes": 15,
"replies": 2,
"reposts": 0,
"quotes": 0,
"media": "image", // none | image | video
"is_reply": false,
"quoted": { "author": "other", "text": "…" }, // when it quotes a post
},
],
}get_profile and get_followers / get_following return profile / users
in the same spirit; get_notifications returns notifications with a
normalised kind plus Threads' own label.
The shapes are a small surface this project owns — deliberately not Meta's
raw payloads, which reshuffle between app builds. Failures set isError and
carry no structured content, so a failed call is never mistaken for an empty
result.
Media
create_thread, reply_to_thread, quote_thread, and schedule_thread take an optional media array — local file paths and/or http(s) URLs (URLs are downloaded to a temp file first, then cleaned up). Supported: images (jpg/png/webp/avif) and video (mp4/mov/webm). Multiple images post as a carousel. Either text or media is required.
// text + single image
create_thread { "text": "hello", "media": ["/path/to/pic.jpg"] }
// carousel (multiple images, mix local + URL)
create_thread { "text": "trip 🧵", "media": ["a.jpg", "b.jpg", "https://…/c.jpg"] }
// image-only reply
reply_to_thread { "handle": "someone", "code": "ABC123", "media": ["reaction.png"] }
// quote with a comment + image
quote_thread { "url": "https://www.threads.com/@x/post/ABC", "text": "this 👇", "media": ["chart.png"] }Multi-post threads
create_thread takes an optional chain — extra posts published as one
connected thread, the format Threads calls "Add to thread". Posting them
separately instead produces unlinked standalone threads.
create_thread {
"text": "Three things I learned shipping this 🧵",
"chain": ["1. Meta detects headless.", "2. Cache invalidation is still hard.", "3. Ship it."]
}On your profile a chain appears as a single entry; the later parts are
reachable via get_thread_replies on the first post.
Scheduling
Threads' web UI has no native scheduling (it's a mobile / Meta Business Suite feature), so this server runs its own scheduler: jobs are persisted to ~/.threads-mcp/scheduled.json and a poll loop publishes them when due, through the same code path as create_thread.
// absolute time (local timezone unless you add an offset like +07:00 or Z)
schedule_thread { "text": "launch 🚀", "at": "2026-07-20T09:00" }
// relative delay
schedule_thread { "text": "in a bit", "in": "2h", "media": ["teaser.jpg"] }
list_scheduled {} // → ids + status (pending / done / failed / canceled)
cancel_scheduled { "id": "b9ec…" }The one hard limit
A cookie/browser approach can only post while this server process is running — there's no Threads-side scheduler to hand the job to. So:
Short horizons / same session — works while your MCP client keeps the server alive.
Past-due jobs — fire on the next startup (better late than never).
Long horizons (days out) — run the server as an always-on daemon so it's alive when the job is due.
Local media paths must still exist when the job fires (URLs are re-downloaded at fire time).
Setup
From npm (recommended)
npm install -g @bintangtimurlangit/threads-mcp # downloads the CloakBrowser binary (~200 MB, cached)This puts two commands on your PATH: threads-mcp (the server) and threads-mcp-login (one-time login). Or run without installing: npx -y @bintangtimurlangit/threads-mcp.
From source
git clone https://github.com/bintangtimurlangit/threads-mcp.git
cd threads-mcp
npm install # also downloads the CloakBrowser binary (~200 MB, cached)
npm run build1. Log in once
threads-mcp-login # global install — or, from a source checkout: npm run loginOpens a CloakBrowser window — log into Threads, then press Enter. Saves your session to ~/.threads-mcp/chrome-profile. Re-run only when it expires.
2. Register with your MCP client
The server launches a headed browser, so it needs a display. On a headless machine wrap it with xvfb-run:
{
"mcpServers": {
"threads": {
"command": "xvfb-run",
"args": ["-a", "threads-mcp"]
}
}
}On a machine with a real display, drop xvfb-run: "command": "threads-mcp", "args": []. From a source checkout, use "command": "node", "args": ["/absolute/path/to/threads-mcp/build/index.js"] (wrapped in xvfb-run on a headless box).
Running as a persistent daemon
For reliable scheduling (and to avoid re-launching the browser each session), run the server always-on under a virtual display. Example with systemd on Linux:
# ~/.config/systemd/user/threads-mcp.service
[Unit]
Description=threads-mcp (Threads MCP server)
After=network-online.target
[Service]
ExecStart=/usr/bin/xvfb-run -a /usr/bin/node /absolute/path/to/threads-mcp/build/index.js
Restart=on-failure
Environment=DEBUG=false
[Install]
WantedBy=default.targetsystemctl --user enable --now threads-mcp
loginctl enable-linger "$USER" # keep it running after logoutOr with pm2: pm2 start "xvfb-run -a node build/index.js" --name threads-mcp.
Signing in without a display
npm run login needs a visible browser, which is the main obstacle to running
this anywhere without a desktop — a VPS, a container, CI. The session is only
cookies, so move it instead of trying to log in headlessly:
On a machine with a display, sign in to Threads normally.
Open devtools → Application → Cookies →
threads.comand copysessionidandds_user_id.On the server:
THREADS_SESSIONID=… THREADS_DS_USER_ID=… npx threads-mcp-import-session
npm run test:live # confirm it worked⚠️ A
sessionidis a bearer credential for your entire account — whoever holds it is you. Prefer the environment-variable form so it stays out of shell history, never commit it, and revoke it by logging out of Threads if it leaks.
Chromium system libraries
On a fresh server Chromium needs system libraries that are not installed by
default. If the browser fails to launch with a missing .so:
npx playwright install-deps chromium
# or, Debian/Ubuntu, without Playwright's helper:
sudo apt-get install -y libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 \
libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 \
libgbm1 libasound2 libpango-1.0-0 libcairo2Why not a Docker image? It would mainly pin those libraries — the one line above. Against that, MCP over stdio means the client owns the process, so a container turns
npx threads-mcpintodocker run -iwith a volume for the profile; and a containerised Chromium on a virtual display has no GPU and reports renderer strings that match no real desktop, which cuts directly against the fingerprint work this server depends on. Running it natively underxvfbis both simpler and less detectable.
Note: MCP over stdio expects the client to own the process. Running a standalone daemon is specifically for the scheduler to survive between client sessions — the scheduled-post queue is shared via
~/.threads-mcp/scheduled.json.
Configuration
All optional — see .env.example, copy to .env to override.
Variable | Default | Purpose |
|
| Threads domain ( |
|
| Where the saved login lives. |
|
| Keep |
|
| Minimum gap between write actions. Raise to be safer. |
|
| In-memory read-cache lifetime. |
|
| Ceiling on one browser operation; on timeout the page resets. |
|
| Largest accepted media file (64 MB). |
|
| Per-download timeout for |
|
| Log startup, captured GraphQL op names, and scheduler activity to stderr. |
State lives under ~/.threads-mcp/: chrome-profile/ (your login) and scheduled.json (the post queue).
How it works
Threads' web app talks to Meta's Relay GraphQL gateway with per-session tokens (fb_dtsg, lsd) and anti-automation fingerprinting. A hand-rolled fetch gets rejected, and operation IDs churn. So this server drives CloakBrowser — a fingerprint-patched Chromium — against a persistent profile you log into once, and:
Reads — collects the data the app renders: the server-side JSON embedded in each page's
<script>tags, plus every/api/graphqland/graphql/queryresponse (the home feed uses the latter). A defensive walker pulls posts/users out of whatever comes back, so it survives Meta renaming operations.Writes — drive the real composer and action buttons so Meta's own client mints the tokens. Icon buttons are clicked at the DOM level (a humanized pointer click misses them). Replies use the inline composer's Ctrl+Enter; reply-with-media promotes it to the full dialog via "Expand composer".
Scheduling — a persisted queue + poll loop, delegating to the same publish path as
create_thread.
The browser runs headed (Meta detects headless); on a server use a virtual display (xvfb).
Development
npm run typecheck
npm test # unit tests (no browser, no login — runs in CI)
npm run test:live # live READ-only smoke test (needs login + a display)
npm run dev # tsx watchDEBUG=true logs every GraphQL operation name the app fires and each scheduler tick — useful if Meta reshuffles a surface and a reader comes back empty.
When things break
Meta ships UI changes without notice. Because writes drive the real interface, a moved button shows up as a vague "couldn't confirm" from whichever tool happened to use it — not as an obvious failure.
Run doctor first. It checks the session and every DOM anchor the write tools
depend on, and tells you what each failure breaks:
✅ session — session cookie present
✅ composer-add-to-thread — present
❌ post-repost — NOT FOUND
**Impact:**
- `post-repost` → repost_thread / quote_thread / unrepost_threadAnchors are declared in src/browser/selectors.ts;
update the ones that moved. doctor { "deep": true } also checks a real post
page and the activity feed.
Troubleshooting
Symptom | Likely cause / fix |
| No/expired session → run |
A read returns empty for a public account | Try again (feed/timeline is lazy-loaded); run with |
A write says it couldn't find its button | Meta changed the UI, or a promo interstitial got in the way (the server tries to dismiss those). Retry; if persistent, the selector needs updating. |
| Stop for several minutes, then slow down. |
Scheduled post never fired | The server wasn't running when it was due — see Run as a daemon. It'll fire on next startup. |
Headless / server has no display | Wrap the command in |
Caveats
Login required. No session → tools return a friendly "run
npm run login" prompt.Anti-bot is a moving target. The free CloakBrowser binary can go stale as Meta updates detection; CloakBrowser Pro ships newer patches. Writes rely on UI selectors Meta can change.
Reads are resilient to GraphQL renames (they parse whatever the app fetches), but a private/blocked account yields nothing, and just-posted content can be briefly stale on read-back.
Scheduling only fires while the server runs (see above).
Respect Threads' Terms of Service and the rate-limit guidance above. This is for personal use of your own account, not scraping or automation at scale.
Contributing & security
CONTRIBUTING.md · SECURITY.md · Code of Conduct
License
Disclaimer
This is an unofficial project. It is not affiliated with, authorized, maintained, sponsored, or endorsed by Meta, Threads, or Instagram.
It works by driving a real logged-in browser session against Threads' web app, which can change without notice — a tool may break when Meta updates its site or anti-bot behavior. It automates your own account and performs only the actions you invoke.
You are responsible for using this software in compliance with Threads' / Meta's Terms of Service and applicable law. Keep request and write volumes reasonable. All product names, logos, and brands are property of their respective owners.
Available Tools
24 toolscancel_scheduledCancel a scheduled postAIdempotent
Cancel a pending scheduled post by its id (from schedule_thread / list_scheduled).
Inputs: id (required).
create_threadPost a threadA
Post a new thread to YOUR Threads account — text and/or media (images & video). ⚠️ Real account — use sparingly; the server enforces a minimum gap between writes to avoid rate-limit/automation flags.
Inputs: text, chain, media.
delete_threadDelete a postADestructiveIdempotent
Delete one of YOUR OWN Threads posts. Provide the post url, or handle + code. This is permanent.
Inputs: url, code, handle.
doctorHealth checkARead-onlyIdempotent
Check that this server can still drive Threads: session validity, and whether each DOM anchor the write tools depend on is still present. Run it when tools start failing oddly — Meta ships UI changes without notice, and a moved anchor otherwise shows up as a vague "could not confirm" from whichever tool happened to use it. Read-only: it opens the composer to inspect it but never posts, and discards any draft it would create.
Inputs: deep. Returns: checks, healthy
follow_userFollow a userAIdempotent
Follow a Threads user by @handle. ⚠️ Real account — follow/unfollow bursts are a classic ban trigger, so this is rate-limited.
Inputs: handle (required).
get_followersGet followersARead-onlyIdempotent
Get a sample of a user's followers (opens the followers list and reads what loads). Threads does not expose a full follower dump; expect a partial list.
Inputs: handle (required), limit. Returns: users
get_followingGet followingARead-onlyIdempotent
Get a sample of the accounts a user follows (opens their Following list and reads what loads). Threads does not expose a full dump; expect a partial list.
Inputs: handle (required), limit. Returns: users
get_notificationsGet notificationsARead-onlyIdempotent
Read your Threads Activity feed — who followed you, replies and mentions, and suggestions. This is how you find out what happened on your account; every other read tool looks outward. Optionally filter by kind.
Inputs: kind, limit. Returns: notifications
get_profileGet profileARead-onlyIdempotent
Get a Threads user's profile: display name, bio, follower count, verified status, and a few recent posts. Omit handle to get your own profile.
Inputs: handle. Returns: profile, recent_posts
get_threadGet a postARead-onlyIdempotent
Get a single Threads post with its stats. Provide the post url, or handle + code.
Inputs: url, code, handle. Returns: post
get_thread_repliesGet post repliesARead-onlyIdempotent
Get the replies to a Threads post. Provide the post url, or handle + code.
Inputs: url, code, limit, handle. Returns: replies
get_timelineGet home timelineARead-onlyIdempotent
Get your Threads home feed (the "For you" timeline).
Inputs: limit. Returns: posts
get_user_threadsGet user postsBRead-onlyIdempotent
Get a user's recent Threads posts (their profile feed).
Inputs: handle (required), limit. Returns: posts
like_threadLike a postAIdempotent
Like a Threads post. Provide the post url, or handle + code. ⚠️ Real account — rate-limited.
Inputs: url, code, handle.
list_scheduledList scheduled postsARead-onlyIdempotent
List scheduled posts and their status (pending / done / failed / canceled).
Inputs: no named parameters.
quote_threadQuote a postA
Quote-post a Threads post — repost it with your own comment (text and/or media). Provide the post url, or handle + code, plus your text and/or media. ⚠️ Real account — rate-limited.
Inputs: url, code, text, media, +1 more.
reply_to_threadReply to a postA
Reply to a Threads post with text and/or media. Provide the post url, or handle + code. ⚠️ Real account — rate-limited.
Inputs: url, code, text, media, +1 more.
repost_threadRepost a postAIdempotent
Repost a Threads post to your followers. Provide the post url, or handle + code. ⚠️ Real account — rate-limited.
Inputs: url, code, handle.
schedule_threadSchedule a postA
Schedule a text/media post to publish later. Give at (ISO datetime, e.g. "2026-07-14T20:00", interpreted in the server's local timezone unless you add an offset like "+07:00" or "Z") OR in (a duration like "30m", "2h", "1d"). ⚠️ The post only fires while THIS server is running; past-due jobs fire on the next startup. For long horizons, run the server as an always-on daemon. Local media file paths must still exist when the job fires.
Inputs: at, in, text, media.
searchSearch ThreadsARead-onlyIdempotent
Search Threads for posts or users matching a query.
Inputs: query (required), type, limit. Returns: posts, users
unfollow_userUnfollow a userAIdempotent
Unfollow a Threads user by @handle. ⚠️ Real account — follow/unfollow bursts are a classic ban trigger, so this is rate-limited.
Inputs: handle (required).
unlike_threadUnlike a postAIdempotent
Remove your like from a Threads post. Provide the post url, or handle + code. ⚠️ Real account — rate-limited.
Inputs: url, code, handle.
unrepost_threadRemove a repostAIdempotent
Remove your repost of a Threads post. Provide the post url, or handle + code. ⚠️ Real account — rate-limited.
Inputs: url, code, handle.
whoamiWho am IARead-onlyIdempotent
Show which Threads account this server is signed in as (your @handle, user id, name, follower/following counts). Use this to confirm the active session before posting.
Inputs: no named parameters. Returns: profile, signed_in
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 24 tools
Each tool targets a distinct action: scheduling, reading profiles/posts, interactions (like/repost/quote), follow/unfollow, etc. No two tools have overlapping purposes despite the large number.
Nearly all tools follow a verb_noun pattern (e.g., list_scheduled, get_profile, create_thread). Two exceptions: 'whoami' (lowercase, no underscore) and 'doctor' (single word), which are minor deviations from the otherwise consistent schema.
24 tools is slightly above the typical well-scoped range, but the server covers a broad domain (scheduling, posts, interactions, notifications, followers) without feeling bloated—each tool has a clear purpose.
The surface covers core CRUD for posts, scheduling, interactions, and account details. Minor gaps include no tool to update scheduled posts or edit profile information, but these are not critical for the primary use case.
Maintenance
Related MCP Connectors
Hosted X (Twitter) MCP server for Claude, ChatGPT and Cursor, with each action switched per account.
Social media MCP server: your Instagram, TikTok, YouTube, LinkedIn and Threads history for your AI.
An MCP server that provides read access to your cloud storage providers, bank accounts and more.
Hosted Web Crawler MCP server for Claude, ChatGPT and Cursor, with each action switched per account.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA stdio MCP server for the official Threads API, enabling publishing, reading, moderation, insights, discovery, locations, and setup diagnostics.2MIT
- AlicenseAqualityDmaintenanceUnofficial MCP server for Meta's Threads API. Enables LLMs like Claude to publish posts, manage replies, and track insights through the Model Context Protocol.15MIT
- FlicenseAqualityDmaintenanceMCP server for the Threads API, enabling profile management, content reading, publishing, replies, and discovery through 26 tools.26-
- AlicenseAqualityCmaintenanceCustom MCP server for Threads (Meta) — post, reply, and read insights via the official free Threads API.59 npmMIT