The Problem
Every time I wanted to connect with someone on LinkedIn,
I'd spend way too long staring at a blank message box.
Generic messages like "I'd love to add you to my network"
get ignored 90% of the time.
Personalized messages work — but writing them manually
for every single person is exhausting.
So I automated it.
What I Built
ConnectAI — a Chrome Extension that scrapes any
LinkedIn profile and generates 3 personalized connection
request messages using the Anthropic Claude API.
Live on Chrome Web Store: [YOUR LINK]
How It Works
1. Content Script — LinkedIn Scraper
The content script runs on any linkedin.com/in/* page
and scrapes:
- Full name
- Current role / headline
- Current company
- Top 3 skills
- Recent activity snippet
- Profile URL
function scrapeProfile() {
const profileData = {};
profileData.fullName = getMultipleTexts([
"h1.text-heading-xlarge",
"h1[class*='heading']",
"h1"
]);
profileData.currentRole = getMultipleTexts([
".text-body-medium.break-words",
".pv-top-card .text-body-medium"
]);
// Store in chrome.storage.local
chrome.storage.local.set({ profileData });
}
The trickiest part? LinkedIn is a React SPA that
constantly changes its class names. I had to use
multiple selector fallbacks for every field.
2. Background Service Worker — Claude API Call
I route all API calls through the background service
worker instead of calling directly from the popup.
Why? CORS. The popup can't make direct API calls
to external URLs in MV3.
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === "GENERATE_MESSAGES") {
handleGenerateMessages(message.payload)
.then(result => sendResponse({ success: true, data: result }))
.catch(err => sendResponse({ success: false, error: err.message }));
return true; // Keep channel open for async
}
});
3. Claude Prompt Engineering
Getting Claude to return consistent JSON under
300 characters per message took some iteration.
The system prompt enforces strict rules:
- Never use "I came across your profile"
- Always reference something specific
- Sound like a peer, not a fan
- Strict JSON output format
const systemPrompt = `
You are a LinkedIn outreach specialist.
STRICT RULES:
1. Each message MUST be under 300 characters
2. NEVER use "I came across your profile"
3. Reference something SPECIFIC about the target
4. Sound human — not AI-generated
OUTPUT: strict JSON only, no markdown
`;
4. Popup UI
The popup is 420px wide with a dark theme and shows:
- Scraped target profile info
- Your saved sender info (editable inline)
- 4 purpose pills (networking, referral, etc.)
- Generate button
- 3 message cards with character count badges
- One-click copy
Character count badge logic:
- 🟢 Green = under 300 chars (LinkedIn limit)
- 🔴 Red = over 300 chars
Tech Stack
| Part | Tech |
|---|---|
| Extension | Manifest V3 |
| Language | Vanilla JS (no bundler) |
| AI | Anthropic Claude API |
| Storage | Chrome Storage API |
| Scraping | Content Scripts |
| Styling | Pure CSS, dark theme |
Biggest Challenges
1. LinkedIn DOM Scraping
LinkedIn changes class names constantly and uses
React under the hood. Had to write multiple selector
fallbacks and re-scrape on SPA navigation using
a MutationObserver.
2. CORS in MV3
Popup can't make external API calls directly.
Solved by routing everything through the background
service worker using chrome.runtime.sendMessage.
3. Consistent JSON from Claude
Claude sometimes wraps JSON in markdown code fences.
Added a cleaning step to strip backticks before
JSON.parse() with a fallback regex extraction.
What's Next
- [ ] Auto-detect purpose from profile context
- [ ] Message history per profile
- [ ] Custom tone settings
- [ ] Firefox support
Try It
Chrome Web Store: https://chromewebstore.google.com/detail/cjfnhjpheldgcfmipcmibbmlfmpflfij?utm_source=item-share-cb
GitHub: https://github.com/sujalmeena7/Connect-AI
You need a free Anthropic API key to use it.
New accounts get $5 free credits — enough for
~500 message generations.
If you've built Chrome Extensions before, I'd love
to know how you handle LinkedIn's DOM changes.
Drop your approach in the comments 👇
Top comments (0)