loading…
Search for a command to run...
loading…
Enables AI assistants to manage Instagram and Threads accounts — publish content, handle comments, view insights, search hashtags, and manage DMs through the Me
Enables AI assistants to manage Instagram and Threads accounts — publish content, handle comments, view insights, search hashtags, and manage DMs through the Meta Graph API.
npm version License: MIT meta-mcp MCP server
Enables AI assistants to manage Instagram and Threads accounts — publish content, handle comments, view insights, search hashtags, and manage DMs through the Meta Graph API.
Add to your MCP client config:
{
"mcpServers": {
"meta": {
"command": "npx",
"args": ["-y", "@exileum/meta-mcp"],
"env": {
"INSTAGRAM_ACCESS_TOKEN": "your_ig_token",
"INSTAGRAM_USER_ID": "your_ig_user_id",
"THREADS_ACCESS_TOKEN": "your_threads_token",
"THREADS_USER_ID": "your_threads_user_id"
}
}
}
}
Only set the variables for the platforms you use.
git clone https://github.com/exileum/meta-mcp.git
cd meta-mcp
npm install
npm run build
{
"mcpServers": {
"meta": {
"command": "node",
"args": ["/path/to/meta-mcp/dist/index.js"],
"env": {
"INSTAGRAM_ACCESS_TOKEN": "your_ig_token",
"INSTAGRAM_USER_ID": "your_ig_user_id",
"THREADS_ACCESS_TOKEN": "your_threads_token",
"THREADS_USER_ID": "your_threads_user_id"
}
}
}
}
| Variable | Required | Description |
|---|---|---|
INSTAGRAM_ACCESS_TOKEN |
For Instagram | Instagram Graph API access token |
INSTAGRAM_USER_ID |
For Instagram | Instagram Business/Creator account ID (numeric string, or "me" for the authenticated user) |
THREADS_ACCESS_TOKEN |
For Threads | Threads API access token |
THREADS_USER_ID |
For Threads | Threads user ID (numeric string, or "me" for the authenticated user) |
META_APP_ID |
For token/webhook tools | Meta App ID (numeric string) |
META_APP_SECRET |
For token/webhook tools | Meta App Secret |
META_API_VERSION |
Optional | Meta Graph API version for Instagram and Facebook endpoints — defaults to v25.0 (verified 2026-05-06). Override only when Meta deprecates a version before meta-mcp ships a new release. Format: vMAJOR.MINOR (e.g., v26.0); malformed values fall back to the default with a stderr warning. OAuth token endpoints are unversioned and unaffected by this setting |
THREADS_API_VERSION |
Optional | Threads API version — defaults to v1.0 (verified 2026-05-06). Threads runs a separate single-major-version track and is not bumped in lockstep with the Graph API. Same vMAJOR.MINOR format and fallback behavior as META_API_VERSION |
MCP_TRANSPORT |
Optional | Transport to serve MCP over — stdio (default) or http. See HTTP Transport |
MCP_HTTP_PORT |
Optional (http) | TCP port for the HTTP transport — defaults to 3000. Must be an integer 1–65535 |
MCP_HTTP_HOST |
Optional (http) | Bind address for the HTTP transport — defaults to 127.0.0.1 (loopback only). Set to 0.0.0.0 to accept connections from other hosts (e.g. in Docker), but only behind a TLS/auth reverse proxy |
MCP_HTTP_ALLOWED_HOSTS |
Optional (http) | Comma-separated host:port allowlist for DNS-rebinding protection. Defaults to loopback variants at the bound port; set this when binding to a non-loopback host so legitimate requests pass the Host check |
The server validates these at startup. Malformed values for INSTAGRAM_USER_ID, THREADS_USER_ID, or META_APP_ID cause the process to exit with Invalid meta-mcp configuration: …. Setting only one half of a credential pair (e.g., INSTAGRAM_ACCESS_TOKEN without INSTAGRAM_USER_ID) prints a stderr warning and continues; related tool invocations still fail at call time.
By default the server speaks MCP over stdio — the right choice for local clients (Claude Desktop, Claude Code, etc.). Set MCP_TRANSPORT=http to instead serve the SDK's Streamable HTTP transport, which enables remote/web-based MCP clients, cloud deployments, and multiple concurrent client sessions.
MCP_TRANSPORT=http MCP_HTTP_PORT=3000 npx @exileum/meta-mcp
The server then listens on http://127.0.0.1:3000/mcp: POST to send messages, GET for the server→client SSE stream, DELETE to end a session. Each client gets an isolated session keyed by the Mcp-Session-Id header, so multiple clients can connect at once.
Security. The transport binds to 127.0.0.1 (loopback) by default and enables DNS-rebinding protection scoped to localhost, so it is not reachable off-host out of the box. To expose it to other machines (e.g. a container), set MCP_HTTP_HOST=0.0.0.0 and run it behind a reverse proxy that terminates TLS and handles authentication — the server itself performs no auth. When bound to a non-loopback address, set MCP_HTTP_ALLOWED_HOSTS to the host:port values clients will use so the Host-header check passes (otherwise rebinding protection is disabled with a stderr warning).
| Platform | Account Type | Notes |
|---|---|---|
| Business or Creator | Personal accounts cannot use the Graph API. Free to switch in settings | |
| Threads | Any account | Instagram link no longer required since Sep 2025 |
| Meta (token/webhook) | Meta Developer App | Create at developers.facebook.com |
x-app-usage header — and automatic client-side throttling at 80% (1s slowdown) / 90% (5s backoff) so a burst of tool calls stays under Meta's per-app quota429/500/502/503/504, network errors, fetch timeouts) with exponential backoff and Retry-After honoring; tunable via MetaClient's maxRetries option (default 3, set to 0 to disable)error_type (auth, validation, rate_limit, server, network, internal), HTTP status, Meta API code/subcode/type, and a remediation hint where actionable — see CHANGELOG.md for the JSON shapeinstructions sent during initialize so clients know required env vars, the two-step publish flow, expected video processing times, and the _rateLimit envelope without re-reading the READMEnotifications/progress emitted while polling container status during publishing — attach a progressToken to ig_publish_* / threads_publish_image|video|carousel calls and the server reports each poll attemptnotifications/message channel (the server declares the logging capability) — each API call logs debug (method + path, never the token-bearing URL), terminal failures log error (status/code/sanitized message), rate-limit pressure logs warning, and DELETE/publish operations log an info audit line. Clients can raise the floor with logging/setLevel (default emits all levels, including debug)MCP_TRANSPORT=http to serve the MCP Streamable HTTP transport (stateful multi-session, localhost-bound by default) instead of stdio, for remote/cloud deployments — see HTTP Transport| Tool | Description |
|---|---|
meta_exchange_token |
Exchange short-lived token for long-lived token (~60 days). Requires platform (instagram or threads) |
meta_refresh_token |
Refresh a long-lived token before expiration. Requires platform (instagram or threads) |
meta_debug_token |
Inspect token validity, expiration, and scopes |
meta_get_app_info |
Get Meta App information |
meta_subscribe_webhook |
Subscribe to webhook notifications |
meta_get_webhook_subscriptions |
List current webhook subscriptions |
| Tool | Description |
|---|---|
ig_publish_photo |
Publish a photo post (supports alt_text, collaborators) |
ig_publish_video |
[DEPRECATED] Use ig_publish_reel — publishes as a Reel (legacy VIDEO media_type retired by Meta Nov 9, 2023; supports collaborators) |
ig_publish_carousel |
Publish a carousel/album (2-10 items, supports alt_text per IMAGE item, collaborators) |
ig_publish_reel |
Publish a Reel (supports collaborators) |
ig_publish_story |
Publish a Story (24hr) |
ig_get_container_status |
Check media container processing status |
| Tool | Description |
|---|---|
ig_get_media_list |
List published media |
ig_get_media |
Get media details |
ig_delete_media |
Delete a media post (requires Facebook Login) |
ig_get_media_insights |
Get media analytics (default views, reach — override metric per media type) |
ig_toggle_comments |
Enable/disable comments on a post |
| Tool | Description |
|---|---|
ig_get_comments |
Get comments on a post |
ig_get_comment |
Get comment details |
ig_post_comment |
Post a comment |
ig_get_replies |
Get replies to a comment |
ig_reply_to_comment |
Reply to a comment |
ig_hide_comment |
Hide/unhide a comment |
ig_delete_comment |
Delete a comment |
| Tool | Description |
|---|---|
ig_get_profile |
Get account profile info |
ig_get_account_insights |
Get account-level analytics (views, reach, follower_count). Optional metric_type (total_value or time_series) controls aggregation shape |
ig_business_discovery |
Look up another business account |
ig_get_collaboration_invites |
Get pending collaboration invites |
ig_respond_collaboration_invite |
Accept/decline a collaboration invite by media_id |
| Tool | Description |
|---|---|
ig_search_hashtag |
Search hashtag by name |
ig_get_hashtag |
Get hashtag info |
ig_get_hashtag_recent |
Get recent media for a hashtag |
ig_get_hashtag_top |
Get top media for a hashtag |
| Tool | Description |
|---|---|
ig_get_mentioned_comment |
Get details of a specific comment mentioning you (by comment_id from a mention webhook) |
ig_get_tagged_media |
Get media you're tagged in |
| Tool | Description |
|---|---|
ig_get_conversations |
List DM conversations |
ig_get_messages |
Get messages in a conversation |
ig_send_message |
Send a DM (optional messaging_type = RESPONSE/UPDATE/MESSAGE_TAG and tag = HUMAN_AGENT for the 7-day human-agent window) |
ig_get_message |
Get message details |
| Tool | Description |
|---|---|
threads_publish_text |
Publish a text post in a single API call (auto_publish_text=true, default; set auto_publish=false for the legacy two-step flow). Supports polls, GIFs, link attachments, topic tags, quote posts, cross-share to IG Stories, geo-gating via allowlisted_country_codes, location tagging via location_id, text attachments up to 10K chars with styling |
threads_publish_image |
Publish an image post (supports alt_text, topic tags, spoiler flag, cross-share to IG Stories, geo-gating via allowlisted_country_codes, location tagging via location_id) |
threads_publish_video |
Publish a video post (supports alt_text, topic tags, spoiler flag, cross-share to IG Stories, geo-gating via allowlisted_country_codes, location tagging via location_id) |
threads_publish_carousel |
Publish a carousel (2-20 items, supports alt_text per item, cross-share to IG Stories, geo-gating via allowlisted_country_codes and location tagging via location_id on the parent container) |
threads_delete_post |
Delete a post (max 100/day) |
threads_get_container_status |
Check container processing status (unpublished containers only) |
threads_get_publishing_limit |
Check remaining publishing quota (250 posts/day) |
threads_repost |
Repost an existing thread to your profile (requires threads_content_publish) |
threads_search_locations |
Search Threads-supported locations by query (q) or coordinates (latitude+longitude) to obtain a location_id for the four threads_publish_* tools (requires threads_location_tagging permission) |
| Tool | Description |
|---|---|
threads_get_posts |
List published posts (includes topic_tag, poll, GIF fields; optional fields param to override the default field list) |
threads_get_post |
Get post details |
threads_search_posts |
Search public posts by keyword or tag (requires threads_keyword_search permission) |
| Tool | Description |
|---|---|
threads_get_replies |
Get replies to a post (mode='top_level' default, or mode='full_tree' for the entire conversation flattened) |
threads_reply |
Reply to a post (supports image/video attachments) |
threads_hide_reply |
Hide a reply |
threads_unhide_reply |
Unhide a reply |
| Tool | Description |
|---|---|
threads_get_mentions |
List posts where the user was @mentioned (requires threads_manage_mentions) |
| Tool | Description |
|---|---|
threads_get_profile |
Get Threads profile info (includes is_verified and is_eligible_for_geo_gating) |
| Tool | Description |
|---|---|
threads_get_post_insights |
Get post analytics (views, likes, replies, reposts, quotes, shares) |
threads_get_user_insights |
Get account-level analytics (period: day/lifetime; since/until required for day) |
| Resource URI | Description |
|---|---|
meta-mcp://instagram/profile |
Instagram account profile data |
meta-mcp://threads/profile |
Threads account profile data (includes is_verified and is_eligible_for_geo_gating) |
| Prompt | Description | Arguments (all optional) |
|---|---|---|
content_publish |
Cross-post content to Instagram and Threads | platform (instagram | threads | both), content_type (text | image | video | carousel), media_url, caption |
analytics_report |
Generate combined analytics report | platform (instagram | threads | both), time_range (7d | 30d | 90d), focus (engagement | growth | content) |
Your META_APP_ID and META_APP_SECRET are in App Settings -> Basic.
Requires an Instagram Business or Creator account. Switch for free in Instagram app -> Settings -> Account type. No Facebook Page linking required — this uses the Instagram API with Instagram Login.
INSTAGRAM_ACCESS_TOKEN.meta_refresh_token tool with platform: "instagram", or:GET https://graph.instagram.com/refresh_access_token
?grant_type=ig_refresh_token
&access_token=LONG_LIVED_TOKEN
GET https://graph.instagram.com/v25.0/me?fields=user_id,username&access_token=YOUR_TOKEN
The user_id is your INSTAGRAM_USER_ID.instagram_business_basic — required for all operationsinstagram_business_content_publish — publishing photos, reels, carouselsinstagram_business_manage_comments — reading and managing commentsinstagram_business_manage_messages — DM conversations and messagingWorks with any Threads account. Instagram link no longer required since Sep 2025.
https://threads.net/oauth/authorize
?client_id=YOUR_APP_ID
&redirect_uri=YOUR_REDIRECT_URI
&scope=threads_basic,threads_content_publish,threads_manage_insights,threads_manage_replies,threads_read_replies,threads_share_to_instagram,threads_manage_mentions,threads_keyword_search
&response_type=code
For local testing, use https://localhost/ as redirect URI (configure in App Settings -> Threads API -> Redirect URIs).POST https://graph.threads.net/oauth/access_token
Content-Type: application/x-www-form-urlencoded
client_id=YOUR_APP_ID
&client_secret=YOUR_APP_SECRET
&grant_type=authorization_code
&redirect_uri=YOUR_REDIRECT_URI
&code=AUTHORIZATION_CODE
GET https://graph.threads.net/access_token
?grant_type=th_exchange_token
&client_secret=YOUR_APP_SECRET
&access_token=SHORT_LIVED_TOKEN
GET https://graph.threads.net/v1.0/me?fields=id,username&access_token=YOUR_TOKEN
The id field is your THREADS_USER_ID.Access tokens expire after ~60 days. Refresh before expiration (token must be at least 24h old):
meta_refresh_token with platform: "instagram", or call:GET https://graph.instagram.com/refresh_access_token
?grant_type=ig_refresh_token
&access_token=CURRENT_LONG_LIVED_TOKEN
meta_refresh_token with platform: "threads", or call:GET https://graph.threads.net/refresh_access_token
?grant_type=th_refresh_token
&access_token=CURRENT_LONG_LIVED_TOKEN
When you rotate a token through meta_refresh_token or meta_exchange_token, the new token is automatically applied in-memory to the running MCP server — subsequent tool calls use it immediately, no server restart needed. The new token is still returned in the response so you can persist it in your environment for the next process restart. A single [meta-mcp] <Platform> access token updated in-memory after <tool>… line is logged to stderr when this happens.
Check token status anytime with meta_debug_token.
Tool failures return isError: true with a JSON body in content[0].text matching the envelope documented in CHANGELOG.md: { error: true, error_type, http_status, code, subcode, type, step, container_id, message, remediation, fbtrace_id, raw }. The fastest path to a fix is to read error_type and the Meta API code, then jump to the matching subsection below. The full code reference is the Meta Graph API error handling guide.
On the publish tools (ig_publish_*, threads_publish_*, threads_reply), errors also include step (container creation / processing / publishing, plus child container creation / child processing / parent container creation / parent processing on carousels) and container_id when one was created. The message mirrors them: "Publish photo failed at processing (container: 17889615324): Container processing timed out after 30s". Use these to decide whether to retry the publish, clean up an orphaned container, or treat the existing container as still reusable.
error_type: "auth" — expired, revoked, or under-scoped tokenTriggered by Meta API codes 190, 10, 102, HTTP 401, or type: "OAuthException". Common messages:
Error validating access token: Session has expired — long-lived tokens expire ~60 days after issue.Application does not have permission for this action — the token is missing a scope, or the account is not eligible (e.g., a Personal Instagram account on Graph API endpoints).What to do:
meta_debug_token to inspect expires_at, is_valid, and scopes.meta_refresh_token (platform: "instagram" or "threads") — this extends the lifetime by another ~60 days. If the token is already expired, the refresh endpoint will reject it; regenerate a short-lived token from the Meta App dashboard and exchange it via meta_exchange_token (or run a full re-authorization for Threads).instagram_business_basic (always required) plus instagram_business_content_publish, instagram_business_manage_comments, instagram_business_manage_messages per feature.threads_basic, threads_content_publish, threads_manage_insights, threads_manage_replies, threads_read_replies, threads_share_to_instagram, threads_manage_mentions, threads_keyword_search per feature.error_type: "rate_limit" — application or user quota exhaustedTriggered by Meta API codes 4, 17, 32, 341, 613, the business-use-case range 80001–80008, or HTTP 429. Includes any OAuthException with code 4 / 17 (these are surfaced as error_type: "rate_limit", not "auth", despite the type field). MetaClient automatically retries HTTP 429 up to 3 times with exponential backoff and honors any Retry-After header — a rate_limit error reaching the caller means the retry budget was exhausted.
What to do:
_rateLimit field on prior successful tool responses. callCount, totalCpuTime, and totalTime come from Meta's x-app-usage header; when any approaches 100 you are near the per-app threshold.max(callCount, totalCpuTime, totalTime) crosses 80% (1s slowdown) or 90% (5s backoff) — watch for the warning-level MCP log message (logger: "meta-client", with usage_pct and delay_ms) the server emits before each throttled call. Profile reads (ig_get_profile, threads_get_profile, and the matching meta-mcp://*/profile resources) and hashtag-name lookups (ig_search_hashtag) are also cached in-process for 5 minutes / 7 days respectively, with cache hits skipping the network entirely. If you are still hitting rate_limit errors despite all that, reduce request volume further.threads_get_publishing_limit before bulk operations.error_type: "validation" — bad parameter, wrong ID, or unsupported fieldTriggered by Meta API codes 100, 200, 803, or any unmapped 4xx HTTP status. Common pitfalls:
INSTAGRAM_USER_ID and THREADS_USER_ID must be the numeric ID returned by GET /me?fields=user_id (Instagram) or GET /me?fields=id (Threads), or the literal "me" for the authenticated user. The Instagram username is not accepted.(#100) Messaging is not supported on ig_send_message / ig_get_conversations / ig_get_messages — the account does not have the messaging API enabled. Grant instagram_business_manage_messages on your token and ensure DMs are enabled in the Instagram app (Settings → Privacy → Messages).ig_publish_video was retired by Meta on Nov 9, 2023; use ig_publish_reel for video posts. ig_publish_story is required for Stories.threads_publish_text post can carry only one of text_attachment, poll_options, link_attachment, or gif_attachment; combining them is rejected at the schema level.metric / fields for the resource — see the per-tool Meta docs (ig_get_media_insights lists per-media_type valid metrics in its description).error_type: "server" (codes 1, 2, HTTP 5xx) — transient Meta outage. MetaClient already retried 500/502/503/504 up to 3 times with exponential backoff before surfacing this; check metastatus.com if it persists.error_type: "network" — fetch timed out or failed before reaching Meta. MetaClient already retried thrown network errors up to 3 times; verify outbound connectivity if the error keeps reappearing.error_type: "internal" — unexpected condition that did not map to a Meta error code. The raw field carries the sanitized original message; access_token, client_secret, and input_token values are scrubbed to *** before reporting.meta-mcp is consumed as an MCP server runtime, not as a library. The supported entry points are:
npx @exileum/meta-mcp (recommended for end users)node dist/index.js (manual installation)The single programmatic export from the package root, createSandboxServer(): McpServer, exists for the Smithery sandbox runner and is the only stable JavaScript/TypeScript API.
zod and other transitive runtime dependencies are internal and not part of meta-mcp's public API. No zod symbols, types, or schemas flow through dist/index.d.ts, so zod's version may change in any release — including major version bumps — without a corresponding meta-mcp major bump.
@modelcontextprotocol/sdk is the one exception: McpServer (the return type of createSandboxServer()) is imported from that package, so a breaking change to McpServer's public interface would also be a breaking change for meta-mcp's programmatic API. In practice the MCP SDK follows semver, so consumers can treat @modelcontextprotocol/sdk as an implicit peer dependency of the createSandboxServer export.
Only the package root (@exileum/meta-mcp) is a supported import target. Deep imports into the published dist/ tree (e.g. @exileum/meta-mcp/dist/schemas.js) are blocked by the package.json exports map for any spec-compliant resolver and are not part of the public API; they may be renamed, removed, or restructured in any release.
Contributions are welcome. See CONTRIBUTING.md for the dev setup, project layout, the tool-registration recipe, testing, commit conventions, the CHANGELOG flow, and the CI gates. Bug reports and feature requests use the issue templates; pull requests use the PR template.
See CHANGELOG.md for release history.
Run in your terminal:
claude mcp add meta-mcp -- npx CSA PROJECT - FZCO © 2026 IFZA Business Park, DDP, Premises Number 31174 - 001
Security
Low riskAutomated heuristic from public metadata — not a security guarantee.