Instagram — Engagement via chrome-bridge MCP
Instagram is a client-rendered React SPA. The logged-in Chrome session is assumed — the bridge profile has cookies. Do not attempt programmatic login. If you hit a login wall, stop and tell the user.
How this skill is laid out
Each capability has a focused JS file under scripts/. To use one:
Readthe script file to load it as a string.- Pass the contents as the
codeargument tomcp__chrome-bridge__execute_script. - The script returns a structured object — see the header comment at the top of each file for the exact return shape.
Some scripts have a value embedded inline (e.g. the comment text in comment_textarea.js) — edit before passing.
Capability map
| Task | Pre-condition | Script | Notes |
|---|---|---|---|
| Profile metadata (followers, picture) | navigate('https://www.instagram.com/<handle>/') + 5s | scripts/profile_meta.js | Reads OG meta tags. page_schema() returns [] on profile pages. |
| Profile posts grid | same as above | scripts/profile_posts.js | Up to 12 latest posts/reels, scoped to <main> to skip sidebar/suggestions. |
| Like a post (idempotent) | navigate('https://www.instagram.com/p/<shortcode>/') + 4s | scripts/like_post.js | Returns {state} — already_liked / clicked / button_not_found. Scoped to section so comment-likes don't get matched. |
| Verify a like landed | ran like_post.js, waited ~1s | scripts/like_verify.js | Returns boolean. |
| Type a comment | comment box visible (click svg[aria-label="Comment"] first if needed, wait ~1s) | scripts/comment_textarea.js | Edit the comment text inline. Native-setter pattern fires the input event React needs. |
| Submit a comment | ran comment_textarea.js | scripts/comment_submit.js | Matches Post button by innerText (button or div[role=button]). |
| Follow / Follow Back | navigate('https://www.instagram.com/<handle>/') + 5s | scripts/follow_button.js | Idempotent. Returns followed / already_following / follow button not found. |
| Expand truncated caption | on a post page | scripts/caption_expand.js | Click the "... more" link. |
| Read caption text | after caption_expand.js (or if no truncation) | scripts/caption_read.js | Reads article h1. |
Profile overview workflow
navigate(url='https://www.instagram.com/natgeo/')
# wait ~5s — Instagram takes a moment to hydrate
meta = execute_script(code=<contents of scripts/profile_meta.js>)
posts = execute_script(code=<contents of scripts/profile_posts.js>)The post grid is under <main>. Don't query a[href*="/p/"] globally — the sidebar and suggestions use the same pattern.
Like a post workflow
Instagram renders action icons as svg[aria-label]. The actual click target is the ancestor <button> (or nearest clickable parent). After clicking Like, the aria-label flips to "Unlike" — like_post.js checks for that first so re-running the script is safe.
navigate(url='https://www.instagram.com/p/<shortcode>/')
# wait ~4s
result = execute_script(code=<contents of scripts/like_post.js>)
# wait ~1s
liked = execute_script(code=<contents of scripts/like_verify.js>)Comment on a post workflow
Instagram uses a <textarea> (not contentEditable) for the comment box, which is why dom_fill usually works — but the submit button only enables after input dispatches correctly, so the native-setter trick in comment_textarea.js is safer.
# Open the comment field by clicking the comment icon (some layouts auto-focus, some need the click)
dom_click('svg[aria-label="Comment"]') # or the post-specific scoped variant
# wait ~1s
# Edit the comment text inside scripts/comment_textarea.js, then:
execute_script(code=<contents of scripts/comment_textarea.js>)
execute_script(code=<contents of scripts/comment_submit.js>)Read full caption workflow
Captions truncate with a "... more" link. Expand before extracting:
execute_script(code=<contents of scripts/caption_expand.js>)
# wait ~1s
caption = execute_script(code=<contents of scripts/caption_read.js>)Rate limiting
Instagram aggressively throttles automation. Hard rules:
- Likes: wait 3–5s between
- Comments: wait 10–30s between
- Follows: wait 5–10s between
- Per session: cap at 20–30 actions, then pause 5–10 minutes
If you see "Try Again Later", "Action Blocked", or a rate-limit dialog, stop immediately and tell the user. Don't retry.
Critical rules
- Scope selectors to
main/article/section— Instagram's DOM has look-alikes in the sidebar and suggestions. - Check state before acting. Clicking "Like" when already liked will *unlike*. The
like_post.jsscript does this for you. - Wait after navigation — Instagram needs 4–6s after
navigatebefore the DOM is stable. - Never log in. If you hit
/accounts/login, tell the user. - Close tabs when done. Instagram tabs are heavy.
- One action per pass. Don't chain like + comment + follow in a single script — Instagram's client state can lag.
Example: like the 3 latest posts of a profile
# 1. Discover
navigate(url='https://www.instagram.com/natgeo/')
# wait ~5s
posts = execute_script(code=<contents of scripts/profile_posts.js>)[:3]
# 2. Per post — open, like, verify
for p in posts:
navigate(url=p['url'])
# wait ~4s
result = execute_script(code=<contents of scripts/like_post.js>)
# wait ~1s
liked = execute_script(code=<contents of scripts/like_verify.js>)
# wait ~3-5s before next post (rate limit)Common failures
| Signal | Cause | Fix |
|---|---|---|
svg[aria-label="Like"] matches but click does nothing | Matched a comment-like, not the post-like | like_post.js already scopes to section; if it still happens, the layout changed |
| Comment posted but didn't show up | Submit button was still disabled when clicked | Wait ~1s between comment_textarea.js and comment_submit.js so React state catches up |
DOM empty after navigate | Page not hydrated | Wait 5–6s; or activate tab if it was opened in background |
| "Try Again Later" | Rate limited | Stop; tell the user; don't retry in the same session |
| Login wall | Session expired | Stop; tell the user to re-authenticate in Chrome manually |