MCP server, localhost bridge, HTTP action API, SQLite mirror and CLI for LinkedIn Toolkit. Drives a user's own logged-in Chrome through a local extension to search, read and act on LinkedIn.
Strong toolkit with 31 well-defined tools, consistent naming patterns, and comprehensive descriptions. All tools follow verb_noun naming convention (get_, send_, list_, search_). Descriptions are detailed and context-aware, averaging 150-200 characters and explaining WHAT, WHEN, and HOW to use each tool. Input schemas are complete with proper JSON Schema types, enums, and constraints. However, output schemas are not explicitly documented in the source code, tool descriptions mention what is returned but formal output schema documentation is absent. Error handling is mentioned in descriptions (e.g., 'run this first when a tool returns LINKEDIN_ERROR') but recovery guidance is generic. All 31 tools have fully specified input parameters with types and descriptions. Naming is highly consistent and contextually clear (linkedin_search_people vs linkedin_get_profile vs linkedin_send_invite). Parameter dependencies are documented (e.g., dry_run affects queueing behavior). The server implements tool annotations (tool risk classification as READ_ONLY or WRITE), which aligns with current MCP patterns.
Approve one message or invite in the queue. Use it to whitelist an item the engine drafted that passed your review. The extension will send it on the next run. Returns the approved queue item.
Comment on a post. Use it to engage publicly with industry peers or thought leaders. By default comments queue for approval; pass dry_run to preview. Returns the comment, including the original body.
Decline one message or invite in the queue. Use it to stop the engine from sending something. The extension will not retry it. Returns the declined queue item.
Self-test every LinkedIn endpoint the extension uses; run this first when a tool returns LINKEDIN_ERROR. One read-only call per endpoint reports ok, failed, unverified or skipped, plus the LinkedIn client version the endpoint table was captured against, so you can tell "LinkedIn moved" from "the toolkit is broken". The search and profile reads it makes count against the normal daily caps.
Add a person to a campaign in the extension. Use it to launch automation at scale. Pass the publicId and a campaign key (one of the campaigns in linkedin_list_campaigns). Returns the enrollment record with campaignKey, publicId and enrolledAt.
Output schemas not formally documented in source code. Descriptions mention what is returned (e.g., 'Returns profiles', 'Returns engagers plus nextStart') but structured output schema definitions are not visible in the source artifacts provided.
Parameter descriptions are present but some are minimal (e.g., 'The project key' level brevity missing). For example, linkedin_query_sql 'params' parameter lacks format guidance for array element types, and linkedin_sync 'since' lacks clarification on timestamp format (epoch seconds vs milliseconds).
Error handling guidance is implicit rather than explicit. While descriptions mention 'run this first when a tool returns LINKEDIN_ERROR', there is no centralized error taxonomy or recovery guide documenting what errors each tool can raise and how to recover (e.g., rate limit vs auth failure vs malformed input).
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 68 | 2025-06-18+ | v2 |
Fetch many profiles in one call from a list of LinkedIn URLs. Use it to hydrate a list you already have URLs for. Returns profiles plus a failed array of {url, error}; each profile counts against the 500 visits/day cap, so keep batches modest.
Follow a company on LinkedIn. Use it to stay updated on a prospect. Counts against the daily visit bucket. Returns the company record.
Fetch a company page by URL or universalName. Use it for account research before outreach. Returns name, industry, size, HQ, website, description and follower count.
List people who work at a company, by universalName. Use it for account-based sourcing once you know the company. Returns a page of profiles plus nextStart; results count against the daily search cap.
Check whether the user is already connected to, or has a pending invite with, each of the given publicIds. Always call this before sending invites so you do not re-invite existing connections. Returns a map publicId to connected | pending | none.
List the user's own first-degree connections. Use it to work an existing network rather than sending new invites. Returns a page of profiles plus nextStart.
List LinkedIn inbox threads, optionally only those since a timestamp or only unread. Use it to triage replies. Returns threads with participants, snippet, unread flag and sentiment when the extension has an AI provider configured.
List attendees of a LinkedIn event you can see. Use it to source people around a conference or webinar. Returns a page of profiles plus nextStart.
List members of a LinkedIn group you belong to. Use it for niche sourcing. Returns a page of profiles plus nextStart.
Fetch the messages in one thread by threadId. Use it after linkedin_get_conversations to read the full exchange before replying. Returns messages with sender publicId, body and sentAt.
List the people who liked or commented on a LinkedIn post. Use it to source warm leads who have shown intent. Returns engagers (a profile plus reaction or comment text) and nextStart.
Fetch one profile by URL or publicId. Use it before writing an invite or message so the copy can reference real detail. Returns the Profile; with full=true it also captures the rendered page text, photo and experience/education, which costs one profile visit against the 500/day cap.
List the messages and invites queued for approval. Use it to monitor what the engine is about to send. Returns queue items with action, profile and draftText.
Check that the Chrome extension is connected and the user is logged in to LinkedIn. Call this first in any session and again after a rate-limit error; returns extension version, autopilot on/off, business-hours flag, per-quota usage (invite, message, visit, search), pending approval-queue size and campaign counts.
List the campaigns the user has defined in the extension. Use it before enrolling people. Returns campaign records with name, key, createdAt and step count.
Query the local SQLite mirror with raw SQL. Use it to work over captures from linkedin_sync instead of hitting LinkedIn again. The schema is public; introspect it with PRAGMA table_info(table_name). Results are limited to 10,000 rows.
Like or react to a post. Use it to engage with people who might engage back. Pass 'like' or one of 'celebrate', 'insightful', 'curious', 'support'. By default reactions go out immediately. Returns the reaction record.
Cancel a research job by jobId. Use it when a job is taking too long or you do not need the results anymore. Returns the cancelled job record.
Poll a research job by jobId. Use it when linkedin_research_pack timed out and you need to check if the packs are ready. Returns the same shape as linkedin_research_pack: either status running with an eta or the finished packs.
Start a research job and wait for it to finish, then return the packs. Use it to gather structured data on a list of people in bulk. Pass an array of publicIds, a template for the fields you want, and optionally a focus to bias the AI toward certain skills or industries. Returns packs—one per publicId—with the fields you asked for, plus a notes array. If the job outlives the timeout (default 600 seconds), returns the jobId to poll with linkedin_research_get.
Search LinkedIn people and return structured profiles. Use it to build a candidate or prospect list from keywords plus optional title, company and location filters. Returns up to 100 profiles per call with nextStart for paging; the extension caps search results at 1,000 per day.
Send one or many InMails to the given publicIds. Use it to reach out to people outside your network. Pass an array of {publicId, subject, body} items. By default they queue for approval; pass dry_run to preview. Returns the InMails sent, including the original subject and body.
Send a connection invite with an optional note. Use it to launch a sourcing campaign. By default the invite queues for approval in the extension instead of sending immediately; pass dry_run to preview, or set the extension to non-autopilot mode to send without approval. Returns the invite record, including the original note text it sent.
Send a direct message to one person. Use it to follow up a sourcing campaign. By default the message queues for approval; pass dry_run to preview. Returns the message, including the original body it sent.
Pull LinkedIn Toolkit events since a timestamp and upsert them into the local SQLite mirror. Use it to build an offline snapshot of invites sent, replies received, campaigns, queue items and research jobs. Returns counts of each type added or updated.
Record a profile view on LinkedIn. Use it to draw attention when you find someone interesting. Counts against the daily visit bucket. Returns the profile record.
Dry-run pattern is inconsistently applied. Tools like linkedin_send_invite, linkedin_send_message, linkedin_send_inmails, linkedin_react_to_post, and linkedin_comment_on_post support dry_run, but other write operations (linkedin_visit_profile, linkedin_follow_company) do not. This creates asymmetry in confirmation capabilities.
Pagination guidance incomplete. While many tools mention 'nextStart' in descriptions, there is no documentation of what 'nextStart' actually contains (is it an offset? a cursor? opaque?), how to pass it back, or when pagination is exhausted (is it when result count < limit?).