LinkedIn — Engagement via chrome-bridge MCP
LinkedIn is a React SPA on top of a logged-in Chrome session. The bridge profile has cookies. Do not attempt programmatic login. If redirected to /login or /checkpoint, 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.
Capability map
| Task | Pre-condition | Script | Notes | |
|---|---|---|---|---|
| Login state check | navigate('https://www.linkedin.com/feed/') + 5s | scripts/login_check.js | Logged in → url stays at /feed/, title is "Feed \ | LinkedIn". |
| Profile articles via schema | navigate('https://www.linkedin.com/in/<handle>/') + 5s | use page_schema() directly | Returns Article entries with headline, datePublished, like counts. Cheapest enumeration of Pulse articles. Does NOT include short feed posts. | |
| Lazy-load the feed | navigated to a feed page | scripts/scroll_for_feed.js | LinkedIn renders nothing until ~1000px scroll. Call then wait ~3s. | |
| Enumerate visible feed posts | scroll_for_feed.js then ~3s | scripts/enumerate_posts.js | Up to 10 {index, urn, author, excerpt, liked, reactionLabel}. | |
| Like first un-liked post in view | feed is loaded | scripts/like_first_unliked.js | Idempotent — checks aria-pressed first. Verify after ~1s by re-querying. | |
| Open the Reaction picker (non-Like reaction) | feed post in view | scripts/reaction_hover.js | After ~1s: dom_click('button[aria-label="Celebrate"]') (or Like / Support / Love / Insightful / Funny). | |
| Open the comment editor | feed post in view | scripts/comment_open.js | After ~2s for editor to mount: dom_type('[role="textbox"][aria-label*="comment" i]', '<text>'). | |
| Submit a comment | typed via dom_type, waited ~1s | scripts/comment_submit.js | Walks up from the editor to find the inner submit (not the action-bar Comment button). | |
| Open Repost menu | feed post in view | scripts/repost_open.js | Wait ~1s then repost_select.js. | |
| Pick "Repost" / "Share with your thoughts" | menu open | scripts/repost_select.js | ||
| Expand "...see more" truncation | feed post in view | scripts/expand_truncated.js | ||
| Dismiss notification / Premium banners | anywhere | scripts/dismiss_banners.js | Matches "Not now / No thanks / Dismiss / Close / Maybe later". | |
| Scroll feed to expose next batch | acted on visible posts | scripts/scroll_step.js | +600px; wait ~2s. |
Profile overview via schema (fast path)
Before scrolling the activity feed, hit the profile page — LinkedIn embeds a JSON-LD array of the user's recent Pulse articles with headlines, publish dates, and like counts. Zero DOM scraping.
navigate(url='https://www.linkedin.com/in/<handle>/')
# wait ~5s
page_schema()
# → { schemas: [{ "@graph": [{ "@type": "Article", author, headline, datePublished,
# interactionStatistic: { userInteractionCount: 7139, ... },
# url }, ...] }] }This is the cheapest way to enumerate a profile's recent article posts. It does not include feed-style short posts — for those, walk the activity page below.
View a profile's recent activity feed
navigate(url='https://www.linkedin.com/in/<handle>/recent-activity/all/')
# wait ~5s
execute_script(code=<contents of scripts/scroll_for_feed.js>)
# wait ~3s
posts = execute_script(code=<contents of scripts/enumerate_posts.js>)Like a post
LinkedIn calls the like button the "Reaction button". Its aria-pressed reflects state.
result = execute_script(code=<contents of scripts/like_first_unliked.js>)
# wait ~1s, verify by re-querying the same button's aria-pressedReact with a specific emotion (not just Like)
execute_script(code=<contents of scripts/reaction_hover.js>)
# wait ~1s for picker to appear
dom_click('button[aria-label="Celebrate"]') # or Like / Support / Love / Insightful / FunnyComment on a post (ProseMirror)
LinkedIn's comment editor is ProseMirror. dom_fill will not work — you must use dom_type.
execute_script(code=<contents of scripts/comment_open.js>)
# wait ~2s for the editor to mount
dom_type('[role="textbox"][aria-label*="comment" i]', 'Great insight — thanks for sharing.')
# wait ~1s so LinkedIn's "empty comment" check unlatches
execute_script(code=<contents of scripts/comment_submit.js>)Repost
execute_script(code=<contents of scripts/repost_open.js>)
# wait ~1s for the menu
execute_script(code=<contents of scripts/repost_select.js>)Expand truncated post text
execute_script(code=<contents of scripts/expand_truncated.js>)Dismiss banners and popups
LinkedIn throws occasional "Turn on notifications", "Upgrade to Premium", or messaging dropdowns over the feed:
execute_script(code=<contents of scripts/dismiss_banners.js>)Rate limiting
- Likes: 3–5s between
- Comments: 10–30s between
- Follows / connects: 5–10s between
- Per session: 20–30 actions max, then pause 5–10 minutes
If you see "You've reached the weekly invitation limit" or "We've limited your activity", stop immediately.
Critical rules
- Scroll before interacting. LinkedIn's feed lazy-loads — no posts exist in the DOM until scroll ~1000px down. Use
scroll_for_feed.js. - Check
aria-pressedbefore clicking Reaction. Clicking when already pressed will unlike.like_first_unliked.jsdoes this for you. - Use
dom_typefor comments. Anything else silently fails. - Submit comments via
comment_submit.js— it walks up from the editor to find the correct inner submit. The action-bar Comment button is a different element that re-opens the editor. - Skip Promoted / Suggested posts —
[data-urn*="sponsored"]or look for "Promoted" in the card. Engaging with these wastes rate-limit budget. - Close tabs when done.
Example: like the 3 latest posts of a profile
navigate(url='https://www.linkedin.com/in/williamhgates/recent-activity/all/')
# wait ~5s
execute_script(code=<contents of scripts/scroll_for_feed.js>)
# wait ~3s
# Like the first 3 unliked posts in order
for _ in range(3):
r = execute_script(code=<contents of scripts/like_first_unliked.js>)
if r == 'all posts already liked': break
# wait 4s (rate limit)
execute_script(code=<contents of scripts/scroll_step.js>)
# wait 2sCommon failures
| Signal | Cause | Fix |
|---|---|---|
| Zero posts found | Forgot to scroll ~1000px | Run scroll_for_feed.js first, then wait, then enumerate |
| Comment text appears but submit is disabled | ProseMirror state didn't catch the input | Wait 1s after dom_type before running comment_submit.js |
| Submit click does nothing | Clicked the action-bar Comment button instead of the editor's inner one | Use comment_submit.js — it walks up from the editor element |
| DOM empty on activity page | Tab opened in background | Activate with action('tabs.update',...) first |
| "Restricted" warning | Rate-limited | Stop immediately; wait out the cooldown outside this session |
| Login wall | Session expired | Stop; tell the user to re-authenticate in Chrome |