Skip to main content
Glama
osAlhaddad1

instagram-mcp

by osAlhaddad1

instagram-mcp

An MCP server that exposes instagrapi — Instagram's private mobile API — as 49 tools an agent can call.

Reading is enabled out of the box. Anything that changes the account (posting, liking, following, commenting, DMing, deleting) is refused until you explicitly turn writes on.

Setup

cp .env.example .env

Then fill in .env with either a username and password, or a sessionid cookie copied from a browser where you are already signed in (DevTools → Application → Cookies → instagram.com). The sessionid route is less likely to trigger a login challenge.

If the account uses two-factor auth, paste the authenticator "setup key" into INSTAGRAM_TOTP_SEED and codes are generated for you. Otherwise, when Instagram asks for a code, call instagram_login with verification_code.

Verify the install without touching Instagram:

.venv/Scripts/python smoke_test.py

Related MCP server: Instagram MCP Server

Registering the server

Already registered for this project in ../.mcp.json. To use it elsewhere:

claude mcp add instagram -- "C:\Users\osami\OneDrive\Documents\GitHub\ayham project 2\instagram-mcp\.venv\Scripts\instagram-mcp.exe"

The executable works from any directory — it always reads .env and writes session.json next to this README.

Enabling write actions

INSTAGRAM_ALLOW_WRITES=true

Restart the server afterwards. While this is false, write tools fail with an explanation rather than doing anything, so the read-only tools stay usable.

Tools

Group

Tools

Writing DMs

instagram_prepare_dm, instagram_find_person, instagram_build_style_profile, instagram_get_style_profile

Persona search

instagram_search_start, instagram_search_recall, instagram_search_gate, instagram_search_expand, instagram_search_enrich, instagram_search_signals, instagram_search_shortlist, instagram_search_judge, instagram_search_results, instagram_search_list

Session

instagram_login_status, instagram_login, instagram_account_info

Users

instagram_get_user, instagram_search_users, instagram_get_followers, instagram_get_following, instagram_get_user_medias, instagram_get_user_stories

Posts

instagram_get_media, instagram_get_media_comments, instagram_get_media_likers, instagram_download_media

Discovery

instagram_get_timeline_feed, instagram_get_hashtag_info, instagram_get_hashtag_medias, instagram_search_locations, instagram_get_location_medias, instagram_search_posts, instagram_similar_accounts, instagram_account_about

Direct messages

instagram_list_direct_threads, instagram_get_direct_thread, instagram_send_direct_message *

Engagement

instagram_like_media *, instagram_unlike_media *, instagram_comment_media *, instagram_follow_user *, instagram_unfollow_user *

Publishing

instagram_upload_photo *, instagram_upload_video *, instagram_upload_reel *, instagram_upload_album *, instagram_upload_story *, instagram_delete_media *

* requires INSTAGRAM_ALLOW_WRITES=true.

Users are addressed by username or user_id. Posts are addressed by a media argument that accepts a post URL, a shortcode, or a numeric media id.

Writing DMs in your own voice

This is what the server is mainly for. The problem with letting a model write your messages is that it writes correctly — punctuated, capitalised, polite — and everyone who knows you can tell instantly.

So instagram_build_style_profile measures how you actually write, from your own sent DMs: message length, capitalisation, terminal punctuation, emoji rate, which emoji, how you spell laughter, shorthand, language mixing, and whether you send bursts of short messages instead of one composed one. It records this globally and per contact, because nobody writes to their mother the way they write to their closest friend.

Run it once:

.venv/Scripts/python -c "import asyncio,json;from instagram_mcp.server import server;print(asyncio.run(server.call_tool('instagram_build_style_profile',{})).content[0].text[:400])"

After that, instagram_prepare_dm(person="sarah") returns — in one call — the recent conversation, the measured rules of your voice, and samples of how you write to that specific person. That single call is the whole interface for drafting; there is no need to stitch together the raw thread tools.

The profile is cached in style_profile.json and never sent to Instagram. Refresh it occasionally as your writing drifts.

The skill

~/.claude/skills/instagram-dm/SKILL.md drives the whole workflow in normal conversation — "reply to ahmed", "what should I say back to her", "check my ig messages". It handles finding the person, loading your voice, drafting, and holding the draft for your approval before anything sends.

Nothing sends without you seeing the exact words first.

Finding people who match a persona

The other thing the server is for. You describe someone — female, Amsterdam, fitness, mid-twenties, blonde — and get back ranked profiles with a confidence figure per attribute.

The hard part is that Instagram has no index for any of that. It indexes four things: handle and name text, hashtags, place geotags, and the follow graph. A persona is none of them. So every attribute is either compiled into a probe against one of those four, or inferred afterwards from what came back — which makes this a funnel that trades recall for precision, not a query.

recall   hundreds of candidates, mostly wrong, from many cheap probes
gate     free: drops private accounts and shops
expand   chaining off the best survivors — the highest-precision channel
enrich   ~3 API calls each. The expensive stage, so it runs on a ranked subset
signals  free: name, pronouns, geotag clusters, captions, category, birth years
judge    vision, on the shortlist only, from one contact sheet per candidate
results  ranked, with every piece of evidence attached

What makes it work is instagram_similar_accounts, which reads Instagram's own "Suggested for you" graph, built from co-follow behaviour it already models. Once you have one good match, chaining outward from it beats any keyword search by a wide margin — which is why the text and hashtag probes exist mainly to find that first foothold.

Location is the other thing worth knowing about. Instagram's city field is almost always null and occasionally wrong — one live probe returned a place called "Hollanda" carrying coordinates in Alexandria, Egypt — and place names fragment badly, with one city arriving as "Amsterdam, Netherlands", "Amsterdam Canal District", "Red Light District, Amsterdam" and "Amsterdam Canal River". Coordinates are always present, so geotags are clustered by position rather than by name: the variants merge, and the mislabelled entry excludes itself.

One thing the design originally leaned on turned out not to exist. Instagram generates alt text for photos ("may be an image of 1 person, blonde hair, standing"), which would have been free coarse vision on every post — but it is only exposed to the web client, and came back empty on all thirty-two posts of a live probe. Appearance therefore costs a real look at real images, and the ceilings reflect that rather than pretending otherwise.

A search is a job on disk, not a function call: a real one is several hundred API calls over ten or twenty minutes against an account Instagram will rate limit, so it runs stage by stage, survives a crash, and lets you fix a bad probe plan after twenty calls instead of three hundred.

instagram_search_start(persona={"gender": {"value": "female", "required": true},
                                "city": "Amsterdam", "niche": ["fitness"],
                                "age_band": [24, 32], "hair": "blonde"})
instagram_search_recall(search_id, probes={"hashtags": [{"tag": "fitgirlnl"}],
                                           "places":   [{"query": "Amsterdam gym"}],
                                           "accounts": [{"query": "amsterdam fitness"}]})
instagram_search_gate(search_id)      # free
instagram_search_expand(search_id)    # chain off the best
instagram_search_enrich(search_id, limit=40)
instagram_search_signals(search_id)   # free, and resolves most personas outright
instagram_search_results(search_id, limit=20)

Judging the pictures

Appearance is the one thing no free signal reaches, so it has to be looked at. instagram_search_shortlist(download_images=true) fetches each candidate's profile picture and recent thumbnails and composes them into a single numbered contact sheet, rather than handing over a dozen loose files.

That is not just tidier. It costs a twelfth of the attention, the numbers let a judgement cite the tile it came from, and it makes the hardest question answerable: which of these faces is the account holder? Feeds are full of friends, partners and clients, and a judgement made on the wrong face arrives sounding exactly as confident as a right one. Seeing every picture side by side turns that into something you can just look at - find the recurring face, check it against the tile marked avatar, which is the only picture certain to be them, and report the result as owner_face_confidence. A low value there weakens how firmly every vision reading is held, rather than pretending the person fits worse than they do.

A screenshot of the profile page would show much the same thing, but that page needs a logged-in browser to render at all, while these thumbnails have already been fetched and paid for.

Reading the confidence

Every attribute carries two numbers, never one: match is how well the evidence agrees, certainty is how far that evidence can be trusted. A model that reports a single "85%" has silently multiplied them and thrown away which one was weak.

Certainty is capped per attribute and per source, so the system cannot overclaim. Hair colour read off one avatar caps at 0.45; read off several daylight posts, 0.80. Country from Instagram's own "account based in" reaches 0.95. Height caps at 0.15 — a photograph carries no scale reference — and height, ethnicity and build are advisory: reported, never allowed to move a ranking, and rejected outright if you mark them required.

Unknown is not "no". An attribute nobody could observe lowers coverage, not match, and the ranking shrinks toward the prior by how little was verified — so a 0.9 scored on two observed attributes loses to a 0.75 on six. Anything labelled unverified scored well on too little to act on.

Results are for public accounts. Private ones are dropped at the gate because they cannot be verified. Nobody under 18 is ever returned: age is read from a stated birth year and from Instagram's join date, the bottom of that estimate decides, and the check runs when age first becomes readable and again at every exit - an audit found the original gate-only version protected nothing, because the gate runs before any age has been read. Every candidate keeps full provenance — which probes found them, and what each conclusion rests on. Search jobs live in searches/ and are gitignored: they hold other people's profiles and photos.

Staying unblocked

instagrapi drives the private API that the phone app uses. Instagram detects and blocks automated behaviour, and the account it blocks is yours — so:

  • Sessions are cached in session.json and reused. Logging in from scratch repeatedly is the single fastest way to get flagged. Keep that file.

  • Requests are spaced out by a random INSTAGRAM_DELAY_MININSTAGRAM_DELAY_MAX second pause. Raise it if Instagram starts asking you to wait.

  • Back off on warnings. "Please wait a few minutes" and "action blocked" mean stop, not retry. The tools say so in their error messages.

  • Bulk reads are risky. Pulling thousands of followers in one go looks nothing like a human using the app.

  • Use a throwaway or secondary account if you are experimenting.

Layout

File

Contains

instagram_mcp/server.py

The 49 tool definitions

instagram_mcp/persona.py

What a persona is, and the confidence maths

instagram_mcp/signals.py

Reading a persona off a profile, free and offline

instagram_mcp/names.py

Given names to a gender prior, offline

instagram_mcp/discovery.py

The recall channels candidates come from

instagram_mcp/search.py

A persona search as a resumable job on disk

instagram_mcp/sheets.py

Candidate pictures composed into one judgeable sheet

instagram_mcp/client.py

Login, session persistence, the write guard, threading

instagram_mcp/serialize.py

Compact JSON views of instagrapi's models

instagram_mcp/errors.py

Instagram exceptions turned into actionable advice

smoke_test.py

Offline check: schemas, guards, serializers

.env and session.json hold credentials and live auth cookies, and searches/ holds other people's profiles and photos. All three are gitignored — keep them that way.

Install Server
F
license - not found
Not graded
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.

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables AI applications to interact with Instagram Business accounts through the Graph API, supporting profile management, media publishing, insights retrieval, and direct messaging capabilities.
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables AI assistants to manage Instagram Business accounts by automating content publishing, scheduling posts, and analyzing performance metrics. Supports posts, stories, reels, and carousels with detailed audience insights and hashtag discovery.
  • A
    license
    A
    quality
    C
    maintenance
    Enables AI assistants to manage Instagram and Threads accounts — publish content, handle comments, view insights, search hashtags, and manage DMs through the Meta Graph API.
    59
    46
    10
    MIT

View all related MCP servers

Related MCP Connectors

  • Instagram for AI agents: publish, read comments and DMs, insights, and engage from your account.

  • 60+ Meta Ads tools for AI agents: audits, campaign management, audiences and CAPI tracking.

  • Give your agent live data from Twitter, Reddit, the web and GitHub. No API keys, no scraping stack.

View all MCP Connectors

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/osAlhaddad1/instagram-mcp'

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