instagram-mcp-ai@0.0.1 Node 22+ · MIT
Model Context Protocol · Instagram

Give your AI assistant an Instagram account it can operate safely.

instagram-mcp-ai is a locally-run TypeScript MCP server that turns an Instagram professional account into 28 well-annotated tools — talking only to the official Meta Graph API, with writes that preview before they touch anything.

Preview-by-default writes Node.js 22+ TypeScript · ESM MIT 3 runtime deps
instagram-mcp-ai live
28MCP tools
6packages
2auth paths
3runtime deps
v25.0Graph API

See it in action #

You describe the outcome in plain language; the model picks the right tool and arguments. Here are three real tool calls the server exposes.

Publish an image — safely

instagram_post_image is a composite write: it builds a media container and publishes it in one call. It previews by default — nothing is posted until you add apply: true.

Single URL posts one photo; 2–10 URLs post a carousel. Only public HTTPS URLs — the server never uploads or fetches your media itself.

// 1) Preview — changes nothing
instagram_post_image {
  "imageUrls": ["https://cdn.example.com/lake.jpg"],
  "caption": "Golden hour at the lake 🌅 #travel"
}
// → { "preview": true, "wouldPost": "IMAGE",
//     "caption_chars": 33, "hashtags": 1 }

// 2) Apply — actually publishes
instagram_post_image {
  "imageUrls": ["https://cdn.example.com/lake.jpg"],
  "caption": "Golden hour at the lake 🌅 #travel",
  "apply": true
}
// → { "id": "1789…", "permalink": "https://…/p/Cx…/" }

What is MCP? #

The Model Context Protocol is an open standard that lets AI assistants call external tools through a uniform interface. An MCP server advertises a set of tools — each with a name, a typed input schema, and safety annotations — and the assistant's client decides which to call and with what arguments.

instagram-mcp-ai is one such server. It runs locally on your machine, holds your Instagram access token, and turns Graph API operations into tools the model can invoke. Because it runs where you run it, your token never leaves your environment.

Typed tools

Every tool declares a zod input schema, so arguments are validated before any API call is spent.

Honest annotations

Tools are tagged read-only, idempotent, or destructive, so clients can gate risky actions.

Runs anywhere MCP does

Speaks MCP over stdio to Claude Desktop, Cursor, and other MCP-capable clients.

Capabilities #

One server, the full lifecycle of a professional account — read, publish, engage, measure, discover.

Account & media

Read the operated profile, list and inspect media (including carousel children), and check the linked accounts and token status.

Publishing

Post images, carousels, reels, and stories via the container → publish flow, or use the composite one-call helpers.

Comment management

List, reply, create, hide, unhide, and (double-gated) delete comments; toggle comments on a media object.

Insights

Post-2025 views-based account and media insights, audience demographics, and online-follower timing.

Discovery

Resolve hashtags, read public hashtag media, and profile other public businesses — Path B, dark by default.

Safety built in

Preview-by-default writes, a destructive double-gate, host allow-listing, and untrusted-text fencing.

Quickstart #

Three steps to a working server. You will need Node.js 22+ and an Instagram professional account.

Authenticate once

Run the login helper to obtain and persist a long-lived token (choose ig or fb).

npx instagram-mcp-ai login --path ig

Verify your setup

The doctor prints the resolved profile, packages, write mode, and token expiry.

npx instagram-mcp-ai doctor

Wire it into your client

Point your MCP client at the command. The server speaks MCP over stdio by default.

npx instagram-mcp-ai

~/Library/Application Support/Claude/claude_desktop_config.json

{
  "mcpServers": {
    "instagram": {
      "command": "npx",
      "args": ["-y", "instagram-mcp-ai"],
      "env": {
        "IG_ENV_FILE": "~/.config/instagram-mcp-ai/.env"
      }
    }
  }
}

Writes preview by default. Even after setup, the server changes nothing on Instagram until you pass apply: true and run with IG_WRITE_MODE=apply. Start in preview mode and promote to apply when you trust the calls.

Credentials #

The server authenticates with a standard Meta Graph API access token. There is no scraping and no password automation — a live login requires a registered Meta app (app id + secret and a whitelisted OAuth redirect URI).

You needWhyWhere it goes
Instagram professional accountBusiness or Creator — personal accounts aren't exposed by the Graph APIIG_ACCOUNT_ID (optional; auto-resolved when possible)
A Meta app (id + secret)Required to run OAuth and, on Path B, to sign requestsIG_APP_ID, IG_APP_SECRET
An access tokenAuthorizes every Graph API callIG_ACCESS_TOKEN (Path A) or IG_FB_ACCESS_TOKEN (Path B)

The login subcommand writes the token to your XDG / APPDATA env file (chmod 0600 on POSIX) and never prints it. Secrets are also registered with a redactor so they are masked in all log output.

Authentication — two paths #

The server supports two official OAuth paths. It auto-detects which you configured; you can also pin it with IG_AUTH_MODE.

 Path A — Instagram LoginPath B — Facebook Login
Modeig-loginfb-login
Endpointgraph.instagram.comgraph.facebook.com (pinned v25.0)
Request signingNoneappsecret_proof (HMAC-SHA256 of the token)
Token lifetime60-day long-lived, refreshableCan be a never-expiring system-user token
Discovery packageNot availableAvailable (subject to Meta feature access)
Best forThe simplest publishing & engagement setupDiscovery, or a token that never expires

Not sure? Start with Instagram Login (Path A). Switch to Facebook Login (Path B) only when you need the discovery package or a never-expiring token. The tool surface is otherwise the same — discovery tools are simply absent on Path A.

Command-line interface #

The instagram-mcp-ai binary is both the MCP server and a small operator CLI. Run it with no subcommand to start the configured transport; the three subcommands below never start a transport — they act and exit.

CommandWhat it does
login --path <ig|fb> Interactive browser OAuth to obtain and persist a long-lived token. Options: --profile, --app-id, --app-secret, --redirect-uri, --account-id, --scopes. Requires a registered Meta app.
doctor Health-check the active profile: prints auth path, transport, write mode, destructive gate, active packages, and a token-expiry verdict (valid / never / expiring_soon / expired / unknown). Exit code reflects health.
refresh Refresh the active profile's long-lived token, rewrite the credentials file, and print the new expiry (token itself never printed).
npx instagram-mcp-ai login --path ig --profile default
npx instagram-mcp-ai doctor
npx instagram-mcp-ai refresh

Configuration #

Everything is configured through IG_* environment variables (loaded from the process env and, optionally, an env file via IG_ENV_FILE). A * marks a value you'll usually need to set.

VariableDefaultPurpose
IG_AUTH_MODEautoPin the auth path: ig-login or fb-login. Auto-detected from which token is present.
IG_ACCESS_TOKEN *Path A (Instagram Login) access token.
IG_FB_ACCESS_TOKENPath B (Facebook Login) access token.
IG_ACCOUNT_IDresolvedThe Instagram professional-account id to operate.
IG_APP_ID / IG_APP_SECRETMeta app credentials — required for login and for Path B request signing.
IG_ENV_FILEPath to an env file to load before resolving settings.
IG_ACTIVE_PROFILEdefaultWhich named profile is active.
IG_PROFILE_<NAME>_*Per-profile overrides (token, account id, app id/secret) for multi-account setups.
IG_TOOL_PACKAGEScoreA profile (core/reader/publisher/all) or a comma/space list of package names.
IG_PACKAGES_DENYPackages to remove from the selection.
IG_PACKAGES_READONLYPackages forced to expose only their read tools.
IG_WRITE_MODEpreviewpreview refuses to apply writes; apply allows them (still needs apply: true per call).
IG_ALLOW_DESTRUCTIVEfalseSecond gate required to delete a comment.
IG_TRANSPORTstdioTransport: stdio or http.
IG_HTTP_HOST / IG_PORT127.0.0.1 / 3000Bind address for the optional loopback HTTP transport.
IG_HTTP_TOKENBearer token required by the HTTP transport.
IG_MAX_CONCURRENT4Max concurrent Graph API requests.
IG_MAX_ITEMS200Server-side cap on items returned from paginated reads.
IG_REFRESH_AFTER_DAYS45When the doctor starts warning that a token is expiring soon.
IG_TIMEOUT_MS30000Per-request timeout in milliseconds.
IG_LOG_LEVELinfoLog verbosity (debugerror). Secrets are always redacted.
IG_PRETTY_JSONfalsePretty-print JSON in tool results.

Write mode & the destructive gate

Two independent switches guard mutations. IG_WRITE_MODE is the server-wide gate (default preview); each write tool additionally needs apply: true. Deleting a comment is double-gated — it requires apply: true and IG_ALLOW_DESTRUCTIVE=true.

# A read-mostly reviewer that can publish but never delete
IG_TOOL_PACKAGES=publisher
IG_WRITE_MODE=apply
IG_ALLOW_DESTRUCTIVE=false

Packages & profiles #

The 28 tools are grouped into six packages. You choose which to expose with IG_TOOL_PACKAGES — either a named profile or an explicit list. The default is core, which deliberately leaves discovery dark.

  • Profilescore (default): account, media, publishing, comments, insights (25 tools). reader: account, media, insights, comments, discovery. publisher: account, media, publishing, comments. all: every package (28 tools).
  • Packagesaccount, media, publishing, comments, insights, discovery.
# Named profile …
IG_TOOL_PACKAGES=reader

# … or an explicit package list
IG_TOOL_PACKAGES=account,media,insights

The discovery package is not in core. It reads the public content graph, is Path B only, and needs Meta's "Instagram Public Content Access" — so it ships dark and you opt in explicitly (e.g. the reader or all profile).

Tools #

All 28 tools, grouped by package. read tools never mutate; write tools preview by default and act only with apply: true. Path B marks tools available only on Facebook Login.

No tools match your filter.

account — profile, linked accounts & token status (3 tools)

instagram_get_account readRead the operated professional account's profile (username, name, biography, website, counts). Free-text fields are returned fenced.

No parameters.

instagram_list_linked_accounts readPath BList the Facebook Pages and Instagram accounts linked to the token. Facebook Login only.

No parameters.

instagram_token_status readReport the access token's type and expiry. Path B resolves expiry via debug_token; on Path A expiry may be reported as unknown.

No parameters.

media — list & inspect media, toggle comments (3 tools)

instagram_list_media readList the account's media objects, newest first, with paging.
ParameterTypeNotes
limitnumberoptionalPage-size hint, 1–100.
afterstringoptionalPaging cursor from a previous page.
fetchAllbooleanoptionalFollow paging up to the server item cap.
instagram_get_media readFetch one media object; carousel albums include their children.
ParameterTypeNotes
mediaIdstringrequiredThe media object id.
instagram_set_comments_enabled writeEnable or disable comments on a media object. Idempotent.
ParameterTypeNotes
mediaIdstringrequiredThe media object to update.
enabledbooleanrequiredtrue enables comments; false disables them.
applybooleanoptionalSet true to perform the write; omitted/false previews only.

publishing — containers, publish & composite posters (7 tools)

instagram_create_media_container writeCreate a media container (step 1 of publish). Supports images, reels, stories, carousels, and carousel items.
ParameterTypeNotes
mediaTypeenumoptionalOne of: REELS · STORIES · CAROUSEL. Omit for a single image.
imageUrlstringoptionalPublic HTTPS image URL.
videoUrlstringoptionalPublic HTTPS video URL (reels/stories).
captionstringoptional≤2200 chars, ≤30 hashtags, ≤20 mentions.
childrenstring[]optional2–10 carousel-item container ids.
locationIdstringoptionalPage/location id to tag.
userTagsobject[]optionalUsers to tag in the media.
coverUrl / thumbOffsetstring / numberoptionalReel cover image or thumbnail frame offset.
shareToFeedbooleanoptionalAlso share a reel to the main feed.
isCarouselItembooleanoptionalMark this container as a carousel child.
applybooleanoptionalSet true to perform the write; omitted/false previews only.
instagram_get_container_status readPoll a container's processing status before publishing.
ParameterTypeNotes
containerIdstringrequiredThe container (creation) id to inspect.
instagram_publish_media writePublish a ready container (step 2). Never auto-retried, to avoid accidental duplicate posts.
ParameterTypeNotes
creationIdstringrequiredThe container id to publish.
applybooleanoptionalSet true to perform the write; omitted/false previews only.
instagram_get_publishing_limit readReport how much of the account's rolling publishing quota is used.

No parameters.

instagram_post_image writeComposite: build the container(s) and publish a photo or carousel in one call.
ParameterTypeNotes
imageUrlsstring[]required1 URL = single photo; 2–10 = carousel. Public HTTPS only.
captionstringoptional≤2200 chars, ≤30 hashtags, ≤20 mentions.
locationIdstringoptionalLocation/page id to tag.
userTagsobject[]optionalUsers to tag.
resumeContainerIdstringoptionalResume a previously-created container instead of rebuilding.
applybooleanoptionalSet true to perform the write; omitted/false previews only.
instagram_post_reel writeComposite: build and publish a reel from a public video URL in one call.
ParameterTypeNotes
videoUrlstringrequiredPublic HTTPS video URL.
captionstringoptional≤2200 chars, ≤30 hashtags, ≤20 mentions.
coverUrl / thumbOffsetstring / numberoptionalCover image URL or thumbnail frame offset.
shareToFeedbooleanoptionalAlso share the reel to the feed.
locationIdstringoptionalLocation/page id to tag.
resumeContainerIdstringoptionalResume a previously-created container.
applybooleanoptionalSet true to perform the write; omitted/false previews only.
instagram_post_story writeComposite: publish a story from one image XOR one video. Stories expire after 24 hours.
ParameterTypeNotes
imageUrlstringoptionalPublic HTTPS image URL (mutually exclusive with videoUrl).
videoUrlstringoptionalPublic HTTPS video URL (mutually exclusive with imageUrl).
resumeContainerIdstringoptionalResume a previously-created container.
applybooleanoptionalSet true to perform the write; omitted/false previews only.

comments — read & moderate comments (8 tools)

instagram_list_comments readList a media object's comments, with threaded replies inlined. Text is returned fenced.
ParameterTypeNotes
mediaIdstringrequiredThe media object to read comments for.
limitnumberoptionalPage-size hint, 1–100.
afterstringoptionalPaging cursor.
fetchAllbooleanoptionalFollow paging up to the server item cap.
instagram_get_comment readFetch a single comment by id.
ParameterTypeNotes
commentIdstringrequiredThe comment id.
instagram_list_tagged_media readList media the account is tagged in. Note: tags are not @mentions.
ParameterTypeNotes
limitnumberoptionalPage-size hint, 1–100.
afterstringoptionalPaging cursor.
fetchAllbooleanoptionalFollow paging up to the server item cap.
instagram_reply_to_comment writeReply to a comment.
ParameterTypeNotes
commentIdstringrequiredThe comment to reply to.
messagestringrequiredThe reply text.
applybooleanoptionalSet true to perform the write; omitted/false previews only.
instagram_create_comment writePost a top-level comment on a media object.
ParameterTypeNotes
mediaIdstringrequiredThe media object to comment on.
messagestringrequiredThe comment text.
applybooleanoptionalSet true to perform the write; omitted/false previews only.
instagram_hide_comment writeHide a comment. Idempotent.
ParameterTypeNotes
commentIdstringrequiredThe comment to hide.
applybooleanoptionalSet true to perform the write; omitted/false previews only.
instagram_unhide_comment writeUnhide a previously hidden comment. Idempotent.
ParameterTypeNotes
commentIdstringrequiredThe comment to unhide.
applybooleanoptionalSet true to perform the write; omitted/false previews only.
instagram_delete_comment writedestructivePermanently delete a comment. Irreversible, and double-gated: needs apply:true AND IG_ALLOW_DESTRUCTIVE=true.
ParameterTypeNotes
commentIdstringrequiredThe comment to delete. This cannot be undone.
applybooleanoptionalSet true to perform the write; also requires IG_ALLOW_DESTRUCTIVE=true.

insights — post-2025 views-based analytics (4 tools)

instagram_get_account_insights readAccount-level insights using the post-2025 metric set. Ranges are bounded by Meta's 90-day retention.
ParameterTypeNotes
metricsenum[]optionalPost-2025 metrics; defaults to all. Legacy names (impressions, profile_views, video_views) are rejected.
periodenumoptionalAggregation period; defaults to day.
metric_typeenumoptionaltotal_value (default) or time_series.
since / untilnumberoptionalUnix seconds. Fully-expired ranges are rejected; partial ranges are clamped and flagged.
instagram_get_media_insights readInsights for one media object. The valid metric set depends on the media product type.
ParameterTypeNotes
media_idstringrequiredThe media object to measure.
metricsenum[]optionalDefaults to views, reach, likes, comments, saved, shares, total_interactions. navigation/replies are story-only.
media_product_typestringoptionalHint (FEED / REELS / STORY) to reject invalid metric combinations client-side.
instagram_get_audience_demographics readFollower / engaged-audience demographics. Requires a breakdown, a timeframe, and ≥100 followers.
ParameterTypeNotes
metricsenum[]optionalDefaults to follower_demographics; engaged_audience_demographics also available.
breakdownenumrequiredThe single dimension to break down by.
timeframeenumrequiredThe window demographics are computed over. Needs an account with ≥100 followers.
instagram_get_online_followers readHourly distribution of when followers are online (last 30 days). On Meta's deprecation watch-list.

No parameters.

discovery — public content graph · dark by default (3 tools)

instagram_search_hashtag readPath BResolve a hashtag name to its hashtag id(s). Budget: 30 unique hashtags per account per rolling 7 days.
ParameterTypeNotes
hashtagstringrequired1–150 chars, with or without a leading #. Counts against the 30/7-day budget.
instagram_get_hashtag_media readPath BList public media under a hashtag id, top or recent. Captions are fenced; owners are not disclosed.
ParameterTypeNotes
hashtagIdstringrequiredFrom instagram_search_hashtag.
edgeenumrequiredOne of: top · recent.
limitnumberoptionalPage-size hint, 1–100.
instagram_discover_business readPath BFetch another public business/creator profile and recent media by handle. Free text is fenced.
ParameterTypeNotes
usernamestringrequired1–30 chars, without a leading @. Private/personal/unknown handles error.
mediaLimitnumberoptional0–100 recent media; defaults to min(25, server cap).

Use cases #

A few workflows the tool set makes natural.

📅 Draft, review, then publish

Solo creator / small brand

Compose a carousel and read back exactly what would post before anything goes live, then apply.

Prompt “Draft a 3-photo carousel from these URLs with a short caption, show me the preview, and post it only if it looks right.”
Tools instagram_post_image instagram_get_publishing_limit

💬 Triage the morning's comments

Community manager

Pull the latest comments across recent posts, reply to questions, and hide spam — with deletes kept behind the destructive gate.

Prompt “Summarize new comments on my last post, reply to genuine questions, and hide anything that's obvious spam.”
Tools instagram_list_comments instagram_reply_to_comment instagram_hide_comment

📈 Weekly performance readout

Marketer

Roll up account and per-post insights into a plain-language summary using the post-2025 views-based metrics.

Prompt “How did last week go? Give me reach and views for the account and my three most recent posts.”
Tools instagram_get_account_insights instagram_list_media instagram_get_media_insights

🔎 Scout a competitor

Growth / research (Path B)

Profile another public business and skim their recent posts' engagement — subject to Meta's public-content access.

Prompt “Look up @naturehub and tell me their follower count and how their last few posts performed.”
Tools instagram_discover_business instagram_search_hashtag instagram_get_hashtag_media

Security & write-safety #

The server is built to be handed to an autonomous model without becoming a liability. Safety is layered — no single switch is the only thing standing between the model and an irreversible action.

Preview by default

Every write returns what it would do and changes nothing until apply: true and IG_WRITE_MODE=apply.

Destructive double-gate

Deleting a comment is irreversible, so it also requires IG_ALLOW_DESTRUCTIVE=true — off by default.

Audit journal

Applied writes are appended to a local JSONL journal at ~/.local/state/instagram-mcp-ai/writes.jsonl.

Untrusted-text fencing

Captions, usernames, and bios are fenced before returning, so hostile text can't smuggle instructions to the model.

Official endpoints only

Requests go only to graph.instagram.com and graph.facebook.com — no scraping, no private APIs.

No media fetching (SSRF-safe)

Publishing accepts only public HTTPS URLs; the server never fetches or proxies your media itself.

Treat your token like a password. It authorizes real actions on a real account. The login flow stores it with restrictive permissions and the logger redacts it, but anyone with the token — or your env file — can act as the account.

Architecture #

The codebase is a strict four-layer stack. Dependencies point one way — a tools spec may import api and mcp, but never reaches into core transport directly. The direction is enforced by an ESLint no-restricted-imports rule, so the boundary can't quietly rot.

tools 28 tool specs, grouped into 6 packages (tools-as-data) Layer 3 mcp registry · define · result shaping · fencing Layer 2 api Graph API domain calls (account, media, …) Layer 1 core http · auth · settings · profiles · logging · redaction Layer 0
Four layers, one-way dependencies — enforced by ESLint, not just convention.
client AI assistant tool validate · gate api + core sign · request Meta Graph API graph.*.com fenced, shaped result
A call is validated and gated before it ever reaches the network; the response is shaped and fenced on the way back.

Troubleshooting #

Run npx instagram-mcp-ai doctor first — it diagnoses most of the below in one shot.

SymptomLikely causeFix
"No account profile is configured"No token has been persisted yetRun login --path <ig|fb> to create the default profile.
A write "previews" but never postsServer is in preview mode, or apply was omittedSet IG_WRITE_MODE=apply and pass apply: true.
Delete is refused even with apply: trueThe destructive gate is closedSet IG_ALLOW_DESTRUCTIVE=true (only when you mean it).
Discovery tools aren't listedOn Path A, or discovery not selectedUse Facebook Login and include discovery (e.g. IG_TOOL_PACKAGES=reader).
Discovery calls error with a permissions messageMissing "Instagram Public Content Access"Request the feature for your Meta app; it may be App-Review-gated.
Publishing fails on your media URLURL isn't public HTTPS, or is still processingHost the media at a public HTTPS URL; poll get_container_status before publishing.
Demographics return an errorAccount has fewer than 100 followersDemographics require ≥100 followers, plus a breakdown and timeframe.
Token "expiring_soon" / "expired" in doctorLong-lived token nearing its windowRun refresh before it lapses (Path A) or issue a fresh token.

Still stuck? Set IG_LOG_LEVEL=debug for verbose (redacted) logs, then open an issue with the output on GitHub.

FAQ #

Is this an official Meta or Instagram product?

No. instagram-mcp-ai is an independent, community-built project — not affiliated with, endorsed by, or sponsored by Meta Platforms, Inc. It talks only to the official Meta Graph API; Instagram and Meta are trademarks of Meta Platforms, Inc., used nominatively to indicate compatibility.

What kind of Instagram account do I need?

An Instagram professional account — Business or Creator. The Instagram Platform / Graph API does not expose personal accounts, so a personal profile cannot be operated by this server.

Does it scrape Instagram or automate my password?

No. Every call goes to an official Meta Graph API endpoint (graph.instagram.com or graph.facebook.com). There is no scraping, no private/undocumented API, and no credential or browser automation — authentication uses standard OAuth access tokens.

Which authentication path should I choose?

Instagram Login (Path A) is the simplest setup for publishing and engagement — graph.instagram.com, granular scopes, and a 60-day long-lived token. Facebook Login (Path B) uses graph.facebook.com v25.0 with an appsecret_proof, can issue a never-expiring system-user token, and is the only path that unlocks the discovery package.

Can it post or delete things without my say-so?

No. Every write previews by default and acts only when you pass apply: true (with IG_WRITE_MODE=apply). Deleting a comment is double-gated — it additionally requires IG_ALLOW_DESTRUCTIVE=true — and applied writes are appended to a local JSONL audit journal.

Do I need to build or host anything?

No. Run it locally with npx instagram-mcp-ai on Node.js 22 or newer. It speaks MCP over stdio to your client (Claude Desktop, Cursor, and other MCP-capable apps); an optional loopback HTTP transport is available for local development.