Instagram MCP Server

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.

Category
Visit Server

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)

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 fresh McpServer + 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-style getRecentMentions / getRecentCommentEvents tools (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 GraphClient that adds appsecret_proof, retries transient/rate-limited failures with backoff, and normalizes Meta's error shapes into typed AppError subclasses.
  • 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

  1. Go to https://developers.facebook.com/apps → Create App.
  2. Choose type Business.
  3. In the App Dashboard, add the Instagram Graph API product (search "Instagram" in "Add Product").
  4. 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:

  1. Convert your Instagram account to a Professional Account (Business or Creator) in the Instagram app: Settings → Account type.
  2. Create or use an existing Facebook Page.
  3. Link them: Instagram app → Settings → Account → Linked accounts → Facebook, or Facebook Page → Settings → Instagram → Connect Account.
  4. 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):

  1. Go to https://developers.facebook.com/tools/explorer/.

  2. Select your app, select your Page, and add the permissions listed below.

  3. 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"
    
  4. Put the resulting token in IG_LONG_LIVED_ACCESS_TOKEN.

  5. 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 id in IG_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):

  1. Set META_REDIRECT_URI to https://your-domain.com/auth/callback and add it as a Valid OAuth Redirect URI in the Meta App Dashboard (Facebook Login product → Settings).
  2. GET /auth/authorize returns a URL — open it in a browser, approve the permissions.
  3. Meta redirects to /auth/callback, which exchanges the code for a long-lived token and stores it encrypted automatically.
  4. GET /auth/status confirms 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

  1. Push this repo to GitHub.
  2. In Render: New → Blueprint, point at your repo — render.yaml is auto-detected.
  3. Fill in the secret env vars flagged sync: false in the Render dashboard.
  4. 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:

  1. In claude.ai: Settings → Connectors → Add custom connector.
  2. Enter your MCP URL: https://your-domain.com/mcp.
  3. If you set MCP_AUTH_TOKEN, add it as a Bearer token / custom header (Authorization: Bearer <token>) in the connector's auth settings.
  4. Claude will call initialize and tools/list automatically 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:

  1. Set META_WEBHOOK_VERIFY_TOKEN to a random string.
  2. In the Meta App Dashboard → Webhooks → Instagram, subscribe to comments, mentions (and others as needed) with callback URL https://your-domain.com/webhooks and the same verify token.
  3. Meta will GET /webhooks once to verify (handled automatically), then POST /webhooks on every event.
  4. Call the getRecentMentions / getRecentCommentEvents MCP 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.
  • .env is git-ignored; only .env.example (no real secrets) is committed.
  • appsecret_proof is 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-256 using your App Secret — wire verifySignature into webhookRoutes.ts if you want to reject unsigned/forged payloads outright (currently logged; enable strict rejection for production hardening).
  • Set MCP_AUTH_TOKEN in 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

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.

Official
Featured
TypeScript
Magic Component Platform (MCP)

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.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

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.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

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.

Official
Featured
TypeScript
Kagi MCP Server

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.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

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.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured