Skip to main content
Glama
aayushjain1811

Instagram MCP Server

📸 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/appsCreate 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.

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.

-
license - not tested
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/aayushjain1811/instagram-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server