Agent Skill · browser-use MCP · Playwright MCP · MIT

Edit LinkedIn profiles
without breaking things.

LinkedIn is a React SPA. input.value = is silently ignored. Typeaheads need timed multi-step calls. This skill uses browser-use MCP for navigation and clicks, and Playwright MCP for React form filling — so you don't have to debug the DOM every time.

claude — ~/0-projects

$ claude "add Python to my cocode.dk LinkedIn entry"

Loading linkedin-profile-editor skill...

browser_navigate → experience edit form

browser_get_state → found Save button [idx 42]

Running add-skill.js → added: "Python"

browser_click [42] → saved

✓ Done. Profile updated.

01 Why standard Playwright fails

Navigation redirects

Playwright's browser_navigate causes redirects on LinkedIn edit URLs. browser-use MCP's browser_navigate handles this correctly — it's the primary navigation driver in this skill.

React ignores .value =

React maintains internal fiber state. Setting input.value directly has no effect. You need nativeInputValueSetter from the prototype chain plus dispatched synthetic events.

Typeahead timing

The company field fetches suggestions async. Type → wait → check → click. One synchronous call returns nothing. Scripts split this into 3 separate timed steps.

Identical-looking inputs

The skill input and title input are visually identical in the DOM. Targeting the wrong one does nothing — or silently overwrites the wrong field. Scripts use precise placeholder selectors.

02 Scripts

Script Purpose Params
inspect-dialog.js Dump all fields, IDs, values, and buttons in the open dialog none
navigate-to-edit.js Open the edit dialog for an existing experience entry USERNAME ENTRY_ID
open-add-position.js Navigate to experience page and open Add Position dialog USERNAME
fill-position-form.js Fill title, employment type, checkbox, and description TITLE TYPE working DESC
fill-company-typeahead.js Type company name and select from suggestions — 3 steps COMPANY_NAME
fill-dates.js Set start and end date selects START_M START_Y END_M END_Y
add-skill.js Add one skill to an entry — run once per skill SKILL_NAME
save.js Save the dialog — tries XPath, text match, aria-label none
delete-entry.js Delete an entry via the edit form — 3 steps USERNAME ENTRY_ID
post-company-update.js Post a text update to a company page — 4 steps COMPANY_ID POST_TEXT

Employment type values

LinkedIn stores these as numeric IDs. Pass the number as a string to fill-position-form.js.

2 Contract
12 Full-time
11 Part-time
3 Self-employed
20 Freelance
18 Internship

03 Workflows

Add new experience entry

  1. browser_navigate → /details/experience/
  2. browser_get_state + browser_click click + button
  3. browser_get_state + browser_click click "Add position"
  4. inspect-dialog.js verify dialog opened
  5. fill-position-form.js title, type, description
  6. fill-company-typeahead.js 3 steps with wait
  7. fill-dates.js start and end dates
  8. browser_get_state + browser_click click Save
  9. add-skill.js × N — once per skill

Edit existing entry

  1. browser_navigate → /edit/forms/ENTRY_ID/
  2. inspect-dialog.js verify fields
  3. run whichever fill scripts you need
  4. browser_get_state + browser_click click Save

Delete an entry

  1. browser_navigate → /edit/forms/ENTRY_ID/
  2. delete-entry.js step 2 — click Delete
  3. delete-entry.js step 3 — confirm

Finding entry IDs

Run this on the experience page to list all entry IDs:

(() => {
  return Array.from(document.querySelectorAll('a[href*="/edit/forms/"]'))
    .map(l => l.href).join('\n');
})()

Or take a screenshot — pencil buttons link to URLs that contain the ID.

04 Install as an agent skill

Works with Claude Code, Codex, Cursor, OpenCode, GitHub Copilot, Roo, and more. Installs to all detected agents automatically:

npx add-skill cocodedk/linkedin-profile-editor

Or target a specific agent:

npx add-skill cocodedk/linkedin-profile-editor --agent claude
npx add-skill cocodedk/linkedin-profile-editor --agent codex

Then just ask your agent: "add Python to my LinkedIn cocode.dk entry" — no boilerplate, no DOM debugging.

R1
browser-use for navigation

Use browser-use MCP's browser_navigate for all URLs. It handles LinkedIn redirects correctly. Playwright's browser_navigate does not.

R2
Get state before every click

Run browser_get_state immediately before browser_click. Element indices shift after any DOM mutation — dialog open, typeahead appearing, etc.

R3
Always use the scripts for forms

Never write React form logic inline. Scripts handle nativeInputValueSetter + synthetic events. Inline code will be silently ignored by React.

Built by Babak Bandpey while automating a job search workflow with Claude Code. LinkedIn breaks enough standard Playwright patterns that the fixes are worth sharing.