instagram-mcp-server
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| PORT | No | Port for the HTTP transport, for example `3000`. | |
| TRANSPORT | No | Transport to use. Set to `http` for a remote HTTP deployment. | stdio |
| IG_USER_ID | No | Instagram account ID. Required when using `facebook_login` mode. | |
| IG_AUTH_MODE | No | Authentication mode: `instagram_login` (default) or `facebook_login`. | instagram_login |
| IG_API_VERSION | No | Graph API version to pin, for example `v26.0`. | v26.0 |
| IG_ACCESS_TOKEN | Yes | Instagram Graph API access token. Required; the server exits if it is missing. |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
Tools for a single Instagram professional (Business or Creator) account through Meta's official Graph API.
Start with instagram_get_account: it returns the account ID other tools need and reports which capabilities this auth mode supports. Running in instagram_login mode, so hashtag search, business discovery and product tagging are NOT available — those endpoints exist only on the Facebook Login path.
Constraints worth knowing before you act:
Publishing is capped at 100 posts per rolling 24 hours; a carousel counts as one. Check instagram_get_publishing_limit before a batch.
All media must be at a public HTTPS URL that serves the file directly. Images must be JPEG. Meta fetches server-side, so local paths never work.
Direct messages need the messaging permission with Advanced Access, and a reply is only allowed within 24 hours of the user's last message.
Insight metric names change between Graph API versions. Call instagram_list_metric_catalog instead of guessing metric names.
Reads are billed against a Business Use Case limit of 4800 x impressions per 24 hours, so prefer one call with a field list over many small calls.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| instagram_get_accountA | Get the authenticated Instagram professional account's profile, and the server's active capabilities. Returns username, account type, follower/following/media counts, bio and website, plus which Meta auth mode this server is running in and what that mode cannot do. Call this first in a new session: it tells you the account ID to pass to other tools and whether hashtag search and business discovery are available at all. |
| instagram_get_publishing_limitA | Check how much of the 100-post / 24-hour API publishing quota the account has used. Meta counts a carousel as one post. Call this before a bulk publish run, and after a publish failure that might be quota exhaustion rather than a bad request. |
| instagram_get_token_infoA | Inspect the configured access token: its type, scopes, and expiry. On the facebook_login path this calls debug_token and needs IG_APP_ID and IG_APP_SECRET. On the instagram_login path Meta exposes no debug endpoint, so this verifies the token by reading the account and reports what it can. Use it to diagnose a permission error before assuming a tool is broken. |
| instagram_refresh_tokenA | Extend the configured long-lived access token and return the new one. instagram_login: refreshes an Instagram long-lived token for another 60 days. The token must be at least 24 hours old and not yet expired. facebook_login: exchanges the token for a fresh long-lived one; needs IG_APP_ID and IG_APP_SECRET. This does NOT write the new token anywhere. Save the returned value into IG_ACCESS_TOKEN yourself, or the next restart uses the old one. |
| instagram_publish_imageA | Publish a single image to the Instagram feed. The image must be a JPEG served from a public HTTPS URL that returns the file directly — no redirects, no authentication, no HTML wrapper. Meta fetches it server-side, so a local path or a signed URL that expires will fail. For two or more images in one post use instagram_publish_carousel. For video use instagram_publish_reel. Example: "post this photo with the caption 'new drop'" -> image_url="https://cdn.example.com/a.jpg", caption="new drop" |
| instagram_publish_reelA | Publish a video as a Reel. Reels are the only way to publish video to the feed — Instagram converted standalone feed video to Reels. The video must be at a public HTTPS URL; Meta downloads and transcodes it, which takes time, so this tool waits for processing before publishing. If processing outlasts wait_seconds the tool returns the container ID instead of failing; finish with instagram_get_container_status then instagram_publish_container. Specs: MOV or MP4, 3s-15min, aspect ratio 0.01:1 to 10:1, up to 1GB. |
| instagram_publish_storyA | Publish an image or video story. Stories expire after 24 hours. Supply exactly one of image_url or video_url. Stickers, polls, links and music cannot be added through the API — Meta exposes none of those on the publishing endpoint. |
| instagram_publish_carouselA | Publish a carousel of 2-20 images and/or videos as one post. This creates a child container per item, waits for any video children to process, then creates and publishes the carousel container. The whole carousel counts as a single post against the publishing quota. Example: "post these three photos as one post" -> items=[{image_url:"..."},{image_url:"..."},{image_url:"..."}] |
| instagram_create_containerA | Low-level escape hatch: create a media container with arbitrary parameters, without publishing it. Prefer instagram_publish_image / _reel / _story / _carousel, which run the whole flow. Use this when you need a parameter those tools do not expose, or when assembling a carousel from children that were created earlier. Returns the container ID. Publish it with instagram_publish_container. |
| instagram_get_container_statusA | Check whether a media container is ready to publish. status_code is one of: EXPIRED — not published within 24 hours of creation ERROR — processing failed FINISHED — ready to publish IN_PROGRESS — still processing PUBLISHED — already published Meta advises polling about once a minute for no more than five minutes. |
| instagram_publish_containerA | Publish a media container that is already FINISHED. Use this to complete a publish that was interrupted, or after assembling a carousel with instagram_create_container. Publishing a container that is still IN_PROGRESS fails; check instagram_get_container_status first. |
| instagram_list_mediaA | List the account's published media, newest first. Returns one page with a cursor. Covers feed posts, reels and carousels; stories are separate (instagram_list_stories) because they expire. Use this to find media IDs for instagram_get_media_insights, instagram_list_comments, or instagram_get_media. Example: "what did we post recently" -> limit=10 |
| instagram_get_mediaA | Get one media object by ID, with caption, type, engagement counts and permalink. Use instagram_list_media to find IDs. For a carousel, instagram_get_media_children lists the individual photos and videos inside it. |
| instagram_get_media_childrenA | List the individual images and videos inside a carousel post. Only meaningful for media whose media_type is CAROUSEL_ALBUM. Note that Meta serves no per-child insights — insights exist only for the carousel as a whole. |
| instagram_list_storiesA | List the account's currently live stories. Stories expire 24 hours after posting, so this returns only what is live right now. Story insights come from instagram_get_media_insights with media_product_type="STORY". |
| instagram_list_tagged_mediaA | List media published by other accounts in which this account was tagged. Different from mentions: this is the photo-tag edge, not @mentions in captions or comments. Use instagram_list_mentions for those. |
| instagram_list_commentsA | List top-level comments on one of the account's media objects. Replies are nested one level deeper — use instagram_list_comment_replies for a specific thread. Hidden comments are included with hidden=true. Example: "what are people saying on our latest post" -> first instagram_list_media, then this with that media_id |
| instagram_get_commentA | Get one comment by ID, including its text, author, like count and hidden state. |
| instagram_list_comment_repliesA | List the replies to one top-level comment. Instagram supports exactly one level of nesting, so replies have no replies of their own. |
| instagram_create_commentA | Post a new top-level comment on one of the account's own media objects. To answer someone else's comment use instagram_reply_to_comment instead — a reply threads under theirs and notifies them. |
| instagram_reply_to_commentA | Reply to a comment, threaded underneath it. Only top-level comments accept replies — Instagram allows a single level of nesting. Replying to a reply fails; reply to its parent_id instead. |
| instagram_hide_commentA | Hide a comment from public view, or unhide one. Hiding is reversible and leaves the comment visible to its author — prefer it to deletion for borderline content. Use instagram_delete_comment only for comments on the account's own media that should be gone for good. |
| instagram_delete_commentA | Permanently delete a comment. Works on comments the account authored, and on comments by others on the account's own media. This cannot be undone — instagram_hide_comment is the reversible alternative. |
| instagram_toggle_media_commentsA | Turn commenting on or off for one of the account's media objects. Disabling hides the whole comment thread and blocks new comments. Existing comments return when it is re-enabled. |
| instagram_list_conversationsA | List Instagram Direct conversations in the account's inbox, most recently updated first. Each entry carries the participant IGSIDs needed to send a message. Requires the messaging permission with Advanced Access. Example: "any new DMs?" -> limit=10, then instagram_get_conversation on the ones with unread_count > 0 |
| instagram_get_conversationA | Read the messages in one conversation, newest first. Note the 24-hour rule before replying: a standard reply is only allowed within 24 hours of the user's most recent message. Check the newest created_time to see whether the window is still open. |
| instagram_send_messageA | Send a text message to an Instagram user. Meta allows this only within 24 hours of that user's last message to the account. Outside the window, set tag="HUMAN_AGENT" to use the 7-day human-agent allowance; unsolicited messages are not possible at all. Requires the messaging permission with Advanced Access. Get the recipient IGSID from instagram_list_conversations — a public username will not work. |
| instagram_send_media_messageB | Send an image, video, or audio attachment as a direct message. The URL must be public HTTPS; Meta fetches it server-side. The same 24-hour window applies as for text messages. |
| instagram_mark_seenA | Mark the conversation with a user as read, which clears its unread badge. Use it after processing an inbox so the same messages are not reprocessed as new. |
| instagram_list_metric_catalogA | List every insight metric this server can request, which surface it applies to, and which breakdowns each one accepts. Makes no API call. Read this before calling instagram_get_account_insights or instagram_get_media_insights with custom metrics — an unsupported metric or an invalid metric/breakdown pair is a hard API error, not an empty result. |
| instagram_get_account_insightsA | Get account-level insights: reach, views, interactions, likes, comments, shares, saves, follows and profile link taps. Defaults to the full interaction metric set for the last 7 days as totals. Narrow with metrics, or split a metric with breakdown (one metric at a time when using a breakdown). For audience age/city/country/gender use instagram_get_audience_demographics instead. Examples:
|
| instagram_get_media_insightsA | Get per-post insights for one media object. The valid metric set depends on the media type, so pass media_product_type and the right defaults are used: FEED for posts and carousels, REELS for reels, STORY for stories. Carousel albums report only whole-post figures — Meta serves no per-child insights. Example: "how did the latest reel do" -> media_id from instagram_list_media, media_product_type="REELS" |
| instagram_get_audience_demographicsA | Get the follower or engaged-audience breakdown by age, city, country or gender. These are lifetime metrics and a breakdown is mandatory. Meta withholds demographics for accounts under roughly 100 followers, which comes back as an empty result rather than an error. Example: "where are our followers" -> metric="follower_demographics", breakdown="country" |
| instagram_get_story_insightsA | Read insights for every story currently live on the account, in one call. Stories vanish after 24 hours and their insights go with them, so this is the tool for catching a day's story performance before it is lost. Equivalent to instagram_list_stories followed by instagram_get_media_insights on each result. |
| instagram_get_mentioned_commentA | Read a comment in which the account was @mentioned, by comment ID. Meta provides no endpoint that lists mentions — they are delivered to your app through the "mentions" webhook field, and this tool reads one once you have its ID. If you have no webhook configured, you cannot discover mentions through the API at all. Reply with instagram_reply_to_mention. |
| instagram_get_mentioned_mediaA | Read a media object whose caption @mentions the account, by media ID. As with mentioned comments, Meta delivers these through the "mentions" webhook rather than a list endpoint. Photo tags are a different edge — use instagram_list_tagged_media for those, which does support listing. |
| instagram_reply_to_mentionA | Reply to a mention of the account on someone else's media. Pass comment_id to reply to the specific comment that mentioned you; omit it to leave a new top-level comment on the media instead. The media's owner must allow comments. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 37 tools
Most tools have clearly distinct resource+action targets, and descriptions explicitly differentiate close pairs (hide vs delete comment, reply_to_comment vs create_comment, tagged media vs mentions). The main overlaps are the low-level container escape hatch (create_container/publish_container) versus the high-level publish_* tools, and get_media_insights versus get_story_insights, both of which are called out in the descriptions rather than left ambiguous.
Every tool uses the instagram_ prefix with snake_case verb_noun structure (publish_reel, list_comments, get_media_insights, reply_to_comment). Variants like mark_seen and toggle_media_comments still follow the same verb-first convention, so an agent can predict names reliably.
37 tools is heavy for a single-account publishing/insights server, well above the 25-tool threshold. Several groups could be consolidated (five publish variants, three token/auth tools, three mention tools), and the surface is large enough that an agent must read carefully to pick correctly.
Coverage is broad and coherent across publishing, media reading, comments, DMs, insights, mentions and auth — essentially the full Graph API surface for a professional account, including quota and token diagnostics. Minor gaps: instagram_list_tagged_media references an instagram_list_mentions tool that does not exist in this set, and there is no media-deletion capability (though Meta's API largely forbids it).