Instagram MCP Server
A production-ready Remote MCP Server that gives Claude direct, tool-based access to your Instagram Business account through the Meta Graph API ā profile data, posts, comments, publishing, insights, analytics, hashtags, messaging, and real-time webhooks.
README
šø Instagram MCP Server
A production-ready Remote MCP (Model Context Protocol) Server that gives Claude direct, tool-based access to your Instagram Business account through the Meta Graph API ā profile data, posts, comments, publishing, insights, analytics, hashtags, messaging, and real-time webhooks.
Built with TypeScript, Node.js, Express, and the official
@modelcontextprotocol/sdk.
Table of Contents
- What this is (and isn't)
- Features / Tool Reference
- Architecture
- Quick Start
- Meta App Setup
- Configuration
- Running Locally
- Deployment
- Connecting Claude
- Webhooks (real-time updates)
- Testing
- Security Notes
- Known API Limitations
- Troubleshooting
- FAQ
- License
What this is (and isn't)
This project is a real, working integration between Claude and the Instagram Graph API for Professional (Business/Creator) accounts. It is not a wrapper around the consumer Instagram app, and it cannot do anything the Graph API itself doesn't support. Where Meta's API has a real limitation (no follower-list endpoint, no general story-history endpoint, no competitor-audience data), this server surfaces a clear error explaining the limitation instead of fabricating data ā see Known API Limitations.
You need:
- An Instagram Professional account (Business or Creator)
- Linked to a Facebook Page
- A Meta Developer App with the Instagram Graph API product added
Features / Tool Reference
Every feature is exposed as an MCP tool Claude can call directly.
| Category | Tools |
|---|---|
| Profile | getProfile, getUsername, getBiography, getFollowers, getFollowing, getProfilePicture, getMediaCount |
| Media | getRecentPosts, getReels, getStories*, getMediaMetadata, getMediaUrl, getCaption, getTaggedUsers, getLocation |
| Comments | getComments, replyComment, deleteComment, hideComment |
| Publishing | publishPost, publishCarouselPost, publishReel, createMediaContainer, schedulePost, getPublishingStatus |
| Insights | getFollowerGrowth, getReach, getImpressions, getProfileVisits, getWebsiteClicks, getEngagement, getAccountsReached, getAccountsEngaged, getMediaInsights |
| Analytics | getTopPerformingPosts, getTopPerformingReels, getAverageEngagement, getBestPostingTime, weeklyAnalytics, monthlyAnalytics, comparePosts, compareReels, generateAnalyticsSummary |
| Messaging | getMessages, getConversationHistory, sendMessage, replyMessage |
| Hashtags | searchHashtag, getHashtags |
| Business | getAccountInformation, businessDiscovery, competitorAnalysis* |
| Webhooks | getRecentMentions, getRecentCommentEvents |
* getStories and competitorAnalysis intentionally return an explanatory
error ā see Known API Limitations.
54 tools total. Run the server and call tools/list over MCP, or see
examples/api-requests.md, for the live list.
Architecture
instagram-mcp/
āāā src/
ā āāā index.ts # process bootstrap (listen, graceful shutdown)
ā āāā server.ts # Express app assembly (middleware + routes)
ā āāā config/ # env validation (Zod) + constants
ā āāā routes/ # /mcp, /auth, /webhooks, /health
ā āāā controllers/ # OAuth setup controller
ā āāā services/
ā ā āāā graph/ # low-level Graph API HTTP client (auth, retries, error mapping)
ā ā āāā instagram/ # profile, media, comments, publishing, insights, analytics, hashtags, messaging, business discovery
ā āāā mcp/
ā ā āāā mcpServer.ts # McpServer factory ā registers every tool
ā ā āāā tools/ # one file per tool category (Zod schemas + handlers)
ā āāā middlewares/ # auth, rate limiting, validation, error handling, logging
ā āāā utils/ # logger, errors, cache, retry, crypto
ā āāā types/ # shared TypeScript types
ā āāā auth/ # OAuth token exchange logic
ā āāā storage/ # encrypted-at-rest token store
ā āāā webhooks/ # signature verification + event router
āāā tests/
ā āāā unit/ # crypto, retry, cache, service-level (mocked via nock)
ā āāā integration/ # full Express app via supertest
āāā examples/ # sample MCP config + curl requests
āāā Dockerfile / docker-compose.yml
āāā render.yaml / railway.json / fly.toml
āāā .github/workflows/ci.yml
Design choices worth knowing about:
- The Remote MCP endpoint (
POST /mcp) uses the SDK's stateless Streamable HTTP transport ā a freshMcpServer+ transport pair per request. This makes the server trivially horizontally scalable. It does not support server-initiated push over a long-lived connection; use the polling-stylegetRecentMentions/getRecentCommentEventstools (fed by the webhook listener) instead. - Tokens are encrypted at rest with AES-256-GCM using a key derived from
TOKEN_ENCRYPTION_KEY. - All Graph API calls go through a single
GraphClientthat addsappsecret_proof, retries transient/rate-limited failures with backoff, and normalizes Meta's error shapes into typedAppErrorsubclasses. - Analytics like "best posting time" and "compare posts" are computed locally from real data pulled via Graph API primitives ā Meta does not expose these as first-class endpoints.
Quick Start
git clone <this-repo-url> instagram-mcp
cd instagram-mcp
npm install
cp .env.example .env
# edit .env with your Meta App credentials (see below)
npm run dev
Server starts on http://localhost:3000. Health check:
curl http://localhost:3000/health.
Meta App Setup
1. Create a Meta App
- Go to https://developers.facebook.com/apps ā Create App.
- Choose type Business.
- In the App Dashboard, add the Instagram Graph API product (search "Instagram" in "Add Product").
- Note your App ID and App Secret (Settings ā Basic) ā these go in
META_APP_ID/META_APP_SECRET.
2. Link a Facebook Page + Instagram Business Account
The Instagram Graph API only works through a Facebook Page connection:
- Convert your Instagram account to a Professional Account (Business or Creator) in the Instagram app: Settings ā Account type.
- Create or use an existing Facebook Page.
- Link them: Instagram app ā Settings ā Account ā Linked accounts ā Facebook, or Facebook Page ā Settings ā Instagram ā Connect Account.
- Note the Facebook Page ID (Page ā About ā Page ID), used as
FACEBOOK_PAGE_ID.
3. Generate an Access Token
Option A ā Graph API Explorer (fastest for personal use):
-
Select your app, select your Page, and add the permissions listed below.
-
Generate a User Access Token, then exchange it for a long-lived token (~60 days) using:
curl -G "https://graph.facebook.com/v21.0/oauth/access_token" \ -d "grant_type=fb_exchange_token" \ -d "client_id=$META_APP_ID" \ -d "client_secret=$META_APP_SECRET" \ -d "fb_exchange_token=$SHORT_LIVED_TOKEN" -
Put the resulting token in
IG_LONG_LIVED_ACCESS_TOKEN. -
Find your Instagram Business Account ID:
curl -G "https://graph.facebook.com/v21.0/$FACEBOOK_PAGE_ID" \ -d "fields=instagram_business_account" \ -d "access_token=$IG_LONG_LIVED_ACCESS_TOKEN"Put the returned
idinIG_BUSINESS_ACCOUNT_ID.
This is the simplest path for a single-account, self-hosted deployment ā set
those three env vars and skip the /auth/* OAuth endpoints entirely.
Option B ā Full OAuth flow (multi-user / production apps):
- Set
META_REDIRECT_URItohttps://your-domain.com/auth/callbackand add it as a Valid OAuth Redirect URI in the Meta App Dashboard (Facebook Login product ā Settings). GET /auth/authorizereturns a URL ā open it in a browser, approve the permissions.- Meta redirects to
/auth/callback, which exchanges the code for a long-lived token and stores it encrypted automatically. GET /auth/statusconfirms the connection.
4. Permissions & App Review
Required scopes (already requested by buildAuthorizationUrl):
instagram_basic
instagram_manage_insights
instagram_manage_comments
instagram_content_publish
instagram_manage_messages
pages_show_list
pages_read_engagement
pages_manage_metadata
business_management
For personal/self-use on your own account, most of these work in Development
Mode with your own account added as a Test User ā no App Review needed.
To let other Instagram accounts authorize your app, Meta requires App
Review for instagram_manage_comments, instagram_content_publish,
instagram_manage_messages, and instagram_manage_insights.
Configuration
See .env.example for the full, documented list. Key
variables:
| Variable | Required | Description |
|---|---|---|
META_APP_ID / META_APP_SECRET |
ā | From your Meta App dashboard |
TOKEN_ENCRYPTION_KEY |
ā | ā„32 chars, encrypts tokens at rest |
IG_LONG_LIVED_ACCESS_TOKEN / IG_BUSINESS_ACCOUNT_ID |
Option A | Skip OAuth entirely |
META_REDIRECT_URI |
Option B | Needed only for the /auth/* OAuth flow |
MCP_AUTH_TOKEN |
Recommended | Bearer token required to call /mcp |
META_WEBHOOK_VERIFY_TOKEN |
Optional | Needed for real-time webhooks |
The server refuses to start if required variables are missing or malformed (validated with Zod on boot) ā check the console output for the exact field that failed.
Running Locally
npm install
npm run dev # tsx watch mode
# or
npm run build && npm start # production build
Other scripts: npm run lint, npm run format, npm run typecheck,
npm test, npm run test:coverage.
Deployment
Docker
docker build -t instagram-mcp-server .
docker run -p 3000:3000 --env-file .env -v $(pwd)/data:/app/data instagram-mcp-server
Or with Compose:
docker compose up -d --build
Render
- Push this repo to GitHub.
- In Render: New ā Blueprint, point at your repo ā
render.yamlis auto-detected. - Fill in the secret env vars flagged
sync: falsein the Render dashboard. - Deploy. Your Remote MCP URL will be
https://<your-service>.onrender.com/mcp.
Railway
railway init
railway up
railway.json configures the Dockerfile build and health check
automatically. Set env vars via railway variables set KEY=value or the
dashboard.
Fly.io
fly launch --no-deploy # detects fly.toml
fly secrets set META_APP_ID=... META_APP_SECRET=... TOKEN_ENCRYPTION_KEY=... MCP_AUTH_TOKEN=...
fly deploy
Self-host (bare metal / VM)
npm ci --omit=dev
npm run build
NODE_ENV=production node dist/index.js
Put it behind a reverse proxy (nginx/Caddy) for TLS termination ā Claude's Remote MCP requires HTTPS in production.
Connecting Claude
Once deployed with a public HTTPS URL:
- In claude.ai: Settings ā Connectors ā Add custom connector.
- Enter your MCP URL:
https://your-domain.com/mcp. - If you set
MCP_AUTH_TOKEN, add it as a Bearer token / custom header (Authorization: Bearer <token>) in the connector's auth settings. - Claude will call
initializeandtools/listautomatically and the Instagram tools become available in your conversation.
For Claude Desktop or other MCP clients that read a JSON config file, see
examples/claude-mcp-config.json.
See examples/api-requests.md for raw
curl-based examples of the MCP JSON-RPC protocol if you want to test
independent of Claude.
Webhooks (real-time updates)
To receive comments/mentions in near-real-time instead of polling:
- Set
META_WEBHOOK_VERIFY_TOKENto a random string. - In the Meta App Dashboard ā Webhooks ā Instagram, subscribe to
comments,mentions(and others as needed) with callback URLhttps://your-domain.com/webhooksand the same verify token. - Meta will
GET /webhooksonce to verify (handled automatically), thenPOST /webhookson every event. - Call the
getRecentMentions/getRecentCommentEventsMCP tools to read the most recent events received (kept in an in-memory ring buffer ā swap for persistent storage if you need durability across restarts).
Testing
npm test # everything
npm run test:unit # unit tests (crypto, retry, cache, services via nock mocks)
npm run test:integration # full Express app via supertest, including a live MCP handshake
npm run test:coverage
27 tests ship with the project, covering encryption round-trips, retry/backoff
logic, caching, comment/publishing service behavior against a mocked Graph
API, and an end-to-end MCP initialize handshake against the real app.
Security Notes
- Tokens are never logged (redacted in the pino logger config) and are encrypted at rest with AES-256-GCM.
.envis git-ignored; only.env.example(no real secrets) is committed.appsecret_proofis sent on every Graph API call by default (META_APP_SECRET_PROOF=true) to prevent token replay if a token leaks.- Webhook payloads are (optionally) signature-verified against
X-Hub-Signature-256using your App Secret ā wireverifySignatureintowebhookRoutes.tsif you want to reject unsigned/forged payloads outright (currently logged; enable strict rejection for production hardening). - Set
MCP_AUTH_TOKENin any environment reachable from the public internet.
Known API Limitations
The Instagram Graph API does not support everything a full "clone" of the Instagram app would need. This server is honest about it:
| Requested feature | Reality |
|---|---|
| List of follower/following usernames | Not exposed ā only aggregate counts, for user privacy. |
| Reading Stories | Only stories this app itself published are readable, and only within 24h. No general story-history endpoint exists. |
| Competitor analysis (ads, demographics, audience overlap) | Business Discovery exposes only public profile/media stats of other accounts ā nothing beyond what's visible on their public profile. |
| Native "schedule for later" | No such flag exists on the Graph API; this server creates the container immediately and defers the media_publish call with an in-process timer (persist this in a real queue for production-grade durability across restarts). |
| DM automation outside the 24h window | Meta restricts free-form messaging to a 24-hour window after the user last messaged you, or requires approved message tags ā this is a platform policy, not a limitation of this code. |
Troubleshooting
"Environment validation failed" on startup Check the console output ā it lists exactly which env var is missing/invalid.
AuthenticationError: Instagram access token is invalid or expired
Long-lived tokens last ~60 days. Call POST /auth/refresh, or re-run the
Graph API Explorer token exchange and update IG_LONG_LIVED_ACCESS_TOKEN.
GraphApiError with code 100 on publishing
Usually means the media URL isn't publicly reachable/valid, or the account
lacks instagram_content_publish permission.
Rate limit errors (RateLimitError)
The client retries automatically with backoff; if it persists, you're
hitting Meta's per-app/per-user call limits ā space out requests or wait.
Claude can't connect to /mcp
Confirm the URL is HTTPS and publicly reachable, and that any
MCP_AUTH_TOKEN you configured matches exactly what's set in Claude's
connector settings.
FAQ
Do I need App Review to use this on my own account? No ā Development Mode + adding your own account as a Test User is enough for personal use of all read/write tools.
Can this manage multiple Instagram accounts?
The current token store holds one account's credentials. For multi-account
support, extend TokenStore to key by account ID and add an accountId
parameter to the relevant tools.
Does this work with Creator accounts, not just Business? Yes ā the Graph API treats Business and Creator accounts the same way for these endpoints.
Is there a database?
No ā token storage is a single encrypted JSON file by default, so the
project runs anywhere without provisioning infrastructure. Swap
src/storage/tokenStore.ts for a Redis/Postgres-backed implementation if you
need multi-instance deployments.
License
MIT ā see LICENSE.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.