Lhremote

Communauté Outils de développement

LinkedHelper automation CLI: campaign management, messaging and profile queries via the Chrome DevTools Protocol.

Que pouvez-vous faire avec Lhremote ?

  • App management — detect, launch, and quit LinkedHelper instances
  • Account & instance control — list accounts, start/stop instances, check status
  • Campaign automation — create, configure, start, stop, and monitor campaigns with full action-chain management
  • People import — import LinkedIn profile URLs into campaign target lists
  • Profile queries — look up and search cached LinkedIn profiles from the local database
  • Messaging — send direct messages, InMails, and connection requests; query messaging history and check for replies
  • LinkedIn engagement — visit profiles, endorse skills, follow/unfollow, like posts, comment, and react
  • Feed & post intelligence — read the LinkedIn feed, search posts, get post details, engagement stats, and engager lists

Installation

Les étapes d'installation sont dans la documentation ci-dessous. Documentation

Documentation

Tirée du README du projet sur GitHub, actualisée chaque semaine. Les liens ouvrent GitHub.

Sommaire
  1. lhremote: LinkedHelper Automation Toolkit
  2. What It Does
  3. Quick Start
  4. Prerequisites
  5. Installation
  6. Usage with Claude Desktop
  7. CLI Usage
  8. MCP Tools
  9. Known Limitations
  10. Troubleshooting
  11. Disclaimer
  12. Purpose
  13. What This Project Does NOT Do
  14. User Responsibility
  15. Ethical Use
  16. License

lhremote: LinkedHelper Automation Toolkit

CLI and MCP server for LinkedHelper automation.

This project is brought to you by Alexey Pelykh.

What It Does

lhremote lets AI assistants (Claude, etc.) control LinkedHelper through the Model Context Protocol. It can:

  • App management — detect, launch, and quit LinkedHelper instances
  • Account & instance control — list accounts, start/stop instances, check status
  • Campaign automation — create, configure, start, stop, and monitor campaigns with full action-chain management
  • People import — import LinkedIn profile URLs into campaign target lists
  • Profile queries — look up and search cached LinkedIn profiles from the local database
  • Messaging — send direct messages, InMails, and connection requests; query messaging history and check for replies
  • LinkedIn engagement — visit profiles, endorse skills, follow/unfollow, like posts, comment, and react
  • Feed & post intelligence — read the LinkedIn feed, search posts, get post details, engagement stats, and engager lists
  • Profile enrichment — extract emails, phones, socials, and company data from LinkedIn profiles
  • LinkedIn search — build search URLs, resolve entity IDs, and query reference data for search filters
  • Budget & throttle monitoring — check daily action limits and LinkedIn throttling status
  • Action discovery — list available LinkedHelper action types with configuration schemas

Quick Start

npm install -g lhremote        # or: npx lhremote --help
lhremote launch-app            # start LinkedHelper with remote debugging
lhremote list-accounts         # find your LinkedIn account ID
lhremote start-instance        # start an instance (auto-selects single account)
lhremote campaign-create --file my-campaign.yaml   # create a campaign
lhremote import-people-from-urls <campaignId> --urls "https://www.linkedin.com/in/..."
lhremote campaign-start <campaignId> --person-ids <id1,id2,...>
lhremote campaign-status <campaignId>              # monitor progress

Pacing: LinkedIn monitors automated activity. See the Rate Limiting guide for recommended settings.

Prerequisites

  • Node.js >= 24
  • LinkedHelper desktop application (requires a paid subscription)

Installation

npm install -g lhremote

Or run directly with npx:

npx lhremote --help

Usage with Claude Desktop

Add to your Claude Desktop configuration (claude_desktop_config.json):

{
    "mcpServers": {
        "lhremote": {
            "command": "npx",
            "args": ["lhremote", "mcp"]
        }
    }
}

Once configured, Claude can use every registered tool directly. A typical workflow:

  1. find-app — Detect a running LinkedHelper instance (or launch-app to start one)
  2. list-accounts — See available LinkedIn accounts
  3. start-instance — Start an instance for an account
  4. describe-actions — Explore available action types
  5. campaign-create — Create a campaign from YAML/JSON configuration
  6. import-people-from-urls — Import target LinkedIn profiles into the campaign
  7. campaign-start — Run the campaign
  8. campaign-status / campaign-statistics — Monitor progress
  9. query-messages / check-replies — Review messaging results

CLI Usage

The lhremote command provides the same functionality as the MCP server. Every MCP tool has a corresponding CLI command except where its reference section says otherwise.

App Management
lhremote find-app [--json]
lhremote launch-app
lhremote quit-app
Account & Instance
lhremote list-accounts [--all-workspaces] [--cdp-port <port>] [--json]
lhremote list-workspaces [--cdp-port <port>] [--json]
lhremote start-instance <accountId> [--cdp-port <port>]
lhremote stop-instance <accountId> [--cdp-port <port>]
lhremote check-status [--cdp-port <port>] [--json]
Campaigns
lhremote campaign-list [--include-archived] [--json]
lhremote campaign-create --file <path> | --yaml <config> | --json-input <config> [--cdp-port <port>] [--json]
lhremote campaign-get <campaignId> [--cdp-port <port>] [--json]
lhremote campaign-export <campaignId> [--format yaml|json] [--output <path>] [--cdp-port <port>]
lhremote campaign-update <campaignId> [--name <name>] [--description <text>] [--clear-description] [--cdp-port <port>] [--json]
lhremote campaign-delete <campaignId> [--cdp-port <port>] [--json]
lhremote campaign-erase <campaignId> [--cdp-port <port>] [--json]
lhremote campaign-start <campaignId> --person-ids <ids> | --person-ids-file <path> [--cdp-port <port>] [--json]
lhremote campaign-stop <campaignId> [--cdp-port <port>] [--json]
Campaign Status & Retry
lhremote campaign-status <campaignId> [--include-results] [--limit <n>] [--cdp-port <port>] [--json]
lhremote campaign-statistics <campaignId> [--action-id <id>] [--max-errors <n>] [--cdp-port <port>] [--json]
lhremote campaign-retry <campaignId> --person-ids <ids> | --person-ids-file <path> [--cdp-port <port>] [--json]
Campaign Actions
lhremote campaign-add-action <campaignId> --name <name> --action-type <type> [--description <text>] [--cool-down <ms>] [--max-results <n>] [--action-settings <json>] [--cdp-port <port>] [--json]
lhremote campaign-remove-action <campaignId> <actionId> [--cdp-port <port>] [--json]
lhremote campaign-update-action <campaignId> <actionId> [--name <name>] [--description <text>] [--clear-description] [--cool-down <ms>] [--max-results <n>] [--action-settings <json>] [--cdp-port <port>] [--json]
lhremote campaign-reorder-actions <campaignId> --action-ids <ids> [--cdp-port <port>] [--json]
lhremote campaign-move-next <campaignId> <actionId> --person-ids <ids> | --person-ids-file <path> [--cdp-port <port>] [--json]
Campaign Targeting
lhremote campaign-exclude-list <campaignId> [--action-id <id>] [--cdp-port <port>] [--json]
lhremote campaign-exclude-add <campaignId> --person-ids <ids> | --person-ids-file <path> [--action-id <id>] [--cdp-port <port>] [--json]
lhremote campaign-exclude-remove <campaignId> --person-ids <ids> | --person-ids-file <path> [--action-id <id>] [--cdp-port <port>] [--json]
lhremote campaign-list-people <campaignId> [--action-id <id>] [--status <status>] [--limit <n>] [--offset <n>] [--cdp-port <port>] [--json]
lhremote campaign-remove-people <campaignId> --person-ids <ids> | --person-ids-file <path> [--cdp-port <port>] [--json]
lhremote import-people-from-urls <campaignId> --urls <urls> | --urls-file <path> [--cdp-port <port>] [--json]
lhremote collect-people <campaignId> <sourceUrl> [--limit <n>] [--max-pages <n>] [--page-size <n>] [--source-type <type>] [--cdp-port <port>] [--json]
Collections
lhremote list-collections [--json]
lhremote create-collection <name> [--cdp-port <port>] [--json]
lhremote delete-collection <collectionId> [--cdp-port <port>] [--json]
lhremote add-people-to-collection <collectionId> --person-ids <ids> | --person-ids-file <path> [--cdp-port <port>] [--json]
lhremote remove-people-from-collection <collectionId> --person-ids <ids> | --person-ids-file <path> [--cdp-port <port>] [--json]
lhremote import-people-from-collection <collectionId> <campaignId> [--cdp-port <port>] [--json]
Profiles & Messaging
lhremote query-profile --person-id <id> | --public-id <slug> [--include-positions] [--json]
lhremote query-profiles [--query <text>] [--company <name>] [--include-history] [--limit <n>] [--offset <n>] [--json]
lhremote query-profiles-bulk --person-id <id>... | --public-id <slug>... [--include-positions] [--json]
lhremote query-messages [--person-id <id>] [--chat-id <id>] [--search <text>] [--limit <n>] [--offset <n>] [--json]
lhremote check-replies [--since <timestamp>] [--cdp-port <port>] [--json]
lhremote scrape-messaging-history --person-ids <ids> | --person-ids-file <path> [--cdp-port <port>] [--json]
LinkedIn Actions
lhremote visit-profile --person-id <id> | --url <url> [--extract-current-organizations] [--cdp-port <port>] [--json]
lhremote endorse-skills --person-id <id> | --url <url> [--skill-name <name>]... [--limit <n>] [--skip-if-not-endorsable] [--keep-campaign] [--cdp-port <port>] [--json]
lhremote enrich-profile --person-id <id> | --url <url> [--enrich-profile-info] [--enrich-phones] [--enrich-emails] [--enrich-socials] [--enrich-companies] [--keep-campaign] [--cdp-port <port>] [--json]
lhremote follow-person --person-id <id> | --url <url> [--mode <follow|unfollow>] [--skip-if-unfollowable] [--keep-campaign] [--cdp-port <port>] [--json]
lhremote unfollow-profile <profileUrl> [--dry-run] [--cdp-port <port>] [--json]
lhremote like-person-posts --person-id <id> | --url <url> [--number-of-articles <n>] [--number-of-posts <n>] [--max-age-of-articles <days>] [--max-age-of-posts <days>] [--should-add-comment] [--message-template <json>] [--skip-if-not-liked] [--keep-campaign] [--cdp-port <port>] [--json]
lhremote message-person --person-id <id> | --url <url> --message-template <json> [--subject-template <json>] [--reject-if-replied] [--reject-if-messaged] [--keep-campaign] [--cdp-port <port>] [--json]
lhremote send-invite --person-id <id> | --url <url> [--message-template <json>] [--save-as-lead-sn] [--keep-campaign] [--cdp-port <port>] [--json]
lhremote send-inmail --person-id <id> | --url <url> --message-template <json> [--subject-template <json>] [--reject-if-replied] [--proceed-on-out-of-credits] [--keep-campaign] [--cdp-port <port>] [--json]
lhremote remove-connection --person-id <id> | --url <url> [--keep-campaign] [--cdp-port <port>] [--json]
Feed & Posts
lhremote get-feed [--count <n>] [--cursor <token>] [--cdp-port <port>] [--json]
lhremote get-post <postUrl> [--comment-count <n>] [--cdp-port <port>] [--json]
lhremote get-post-stats <postUrl> [--cdp-port <port>] [--json]
lhremote get-profile-activity <profile> [--count <n>] [--cursor <token>] [--cdp-port <port>] [--json]
lhremote search-posts <query> [--count <n>] [--cursor <n>] [--cdp-port <port>] [--json]
lhremote comment-on-post --url <url> --text <text> [--cdp-port <port>] [--json]
lhremote react-to-post <postUrl> [--type <like|celebrate|support|love|insightful|funny>] [--cdp-port <port>] [--json]
lhremote react-to-comment <postUrl> <commentUrn> [--type <like|celebrate|support|love|insightful|funny>] [--dry-run] [--cdp-port <port>] [--json]
lhremote dismiss-feed-post <feedIndex> [--dry-run] [--cdp-port <port>] [--json]
lhremote hide-feed-author <feedIndex> [--dry-run] [--cdp-port <port>] [--json]
lhremote hide-feed-author-profile <profileUrl> [--dry-run] [--cdp-port <port>] [--json]
lhremote unfollow-from-feed <feedIndex> [--dry-run] [--cdp-port <port>] [--json]
LinkedIn Search & Reference
lhremote build-url <sourceType> [--keywords <keywords>] [--current-company <id>]... [--past-company <id>]... [--geo <id>]... [--industry <id>]... [--school <id>]... [--network <code>]... [--profile-language <code>]... [--service-category <id>]... [--filter <spec>]... [--slug <slug>] [--id <id>] [--json]
lhremote resolve-entity <entityType> <query> [--limit <n>] [--json]
lhremote list-reference-data <dataType> [--json]
Utilities
lhremote describe-actions [--category <category>] [--type <type>] [--json]
lhremote get-errors [--cdp-port <port>] [--json]
lhremote dismiss-errors [--cdp-port <port>] [--json]
lhremote get-action-budget [--cdp-port <port>] [--json]
lhremote get-throttle-status [--cdp-port <port>] [--json]

MCP Tools

Common Parameters

Most tools and CLI commands connect to LinkedHelper via the Chrome DevTools Protocol (CDP). In addition to the tool-specific parameters listed below, CDP-connected tools accept the following, except where a tool's own reference section says otherwise. The Default column describes the MCP tools; each CLI command exposes its own subset of these flags, and the notes below give the differences that matter:

ParameterCLI FlagTypeDefaultDescription
cdpPort--cdp-portnumberauto-discoveredCDP debugging port
cdpHost--cdp-hoststring127.0.0.1CDP host address
allowRemote--allow-remotebooleanfalseAllow connections to non-loopback addresses
accountIdvaries — see belownumberauto-select if single account (MCP)LinkedHelper account to act as

cdpPort is discovered, not defaulted. Omit it and lhremote finds the port of the running LinkedHelper itself, whatever that port happens to be; it raises LinkedHelperNotRunningError or LinkedHelperUnreachableError when no instance is reachable. Pass a port only to target one specific instance — you do not need one to reach a LinkedHelper that is not on 9222. Two exceptions: quit-app takes cdpPort alone (no cdpHost, allowRemote or accountId) and does default to 9222; and a port is required whenever cdpHost is non-loopback, because discovery scans local processes only.

accountId is required once more than one account is configured. With a single account the MCP tools resolve it themselves; with several they cannot, and the call fails — most raise AccountResolutionError, while start-instance and stop-instance answer with their own message. Either way, list-accounts gives you the ID. Every tool that connects to a running LinkedHelper accepts accountId; the ones that do not are the app-management tools (find-app, launch-app, quit-app), which act on the application rather than on an account, the reference tools (build-linkedin-url, resolve-linkedin-entity, list-linkedin-reference-data, describe-actions), which open no connection, and the profile-cache readers (query-profile, query-profiles, query-profiles-bulk), which search every account's local database and cannot be scoped to one. The CLI never auto-resolves, at any account count: start-instance / stop-instance take a required positional <accountId>, comment-on-post / react-to-post take --account-id, and no other command exposes it.

Security warning: Enabling allowRemote permits CDP connections to remote hosts. CDP is an unsandboxed protocol that grants full control over the target browser — equivalent to remote code execution. Only enable this when the network path between your machine and the target host is fully secured (e.g., SSH tunnel, VPN, or trusted LAN).

App Management
find-app

Detect running LinkedHelper application instances and their CDP connection details.

No parameters.

launch-app

Launch the LinkedHelper application with remote debugging enabled.

ParameterTypeRequiredDefaultDescription
cdpPortnumberNoauto-selectCDP port to use
forcebooleanNofalseKill existing LinkedHelper processes before launching
quit-app

Quit the LinkedHelper application.

ParameterTypeRequiredDefaultDescription
cdpPortnumberNo9222CDP port
Account & Instance
list-accounts

List available LinkedHelper accounts. Returns account ID, LinkedIn ID, name, and email for each account. Returns accounts in the currently selected workspace by default (LinkedHelper 2.113.x+); set includeAllWorkspaces to enumerate every workspace the current LH user belongs to.

ParameterTypeRequiredDefaultDescription
includeAllWorkspacesbooleanNofalseWhen true, enumerates accounts across every workspace the user belongs to, not just the selected workspace
cdpPortnumberNoauto-discoveredCDP port
list-workspaces

List LinkedHelper workspaces the current LH user belongs to. Each workspace includes the user's role and a selected flag indicating the currently active workspace. Returns an empty list on LinkedHelper versions that predate workspaces (pre-2.113.x).

ParameterTypeRequiredDefaultDescription
cdpPortnumberNoauto-discoveredCDP port
start-instance

Start a LinkedHelper instance for a LinkedIn account.

ParameterTypeRequiredDefaultDescription
accountIdnumberNoauto-select if single accountAccount ID
cdpPortnumberNoauto-discoveredCDP port
stop-instance

Stop a running LinkedHelper instance.

ParameterTypeRequiredDefaultDescription
accountIdnumberNoauto-select if single accountAccount ID
cdpPortnumberNoauto-discoveredCDP port
check-status

Check LinkedHelper connection status, running instances, and database health.

ParameterTypeRequiredDefaultDescription
cdpPortnumberNoauto-discoveredCDP port
Campaigns
campaign-list

List existing campaigns with summary statistics.

ParameterTypeRequiredDefaultDescription
includeArchivedbooleanNofalseInclude archived campaigns
cdpPortnumberNoauto-discoveredCDP port
campaign-create

Create a new campaign from YAML or JSON configuration.

ParameterTypeRequiredDefaultDescription
configstringYes—Campaign configuration in YAML or JSON format
formatstringNoyamlConfiguration format (yaml or json)
cdpPortnumberNoauto-discoveredCDP port
campaign-get

Get detailed campaign information including action chain.

ParameterTypeRequiredDefaultDescription
campaignIdnumberYes—Campaign ID
cdpPortnumberNoauto-discoveredCDP port
campaign-export

Export campaign configuration as YAML or JSON.

ParameterTypeRequiredDefaultDescription
campaignIdnumberYes—Campaign ID
formatstringNoyamlExport format (yaml or json)
cdpPortnumberNoauto-discoveredCDP port
campaign-update

Update a campaign's name and/or description.

ParameterTypeRequiredDefaultDescription
campaignIdnumberYes—Campaign ID
namestringNo—New campaign name
descriptionstringNo—New description (empty string to clear)
cdpPortnumberNoauto-discoveredCDP port
campaign-delete

Delete (archive) a campaign.

ParameterTypeRequiredDefaultDescription
campaignIdnumberYes—Campaign ID
cdpPortnumberNoauto-discoveredCDP port
campaign-erase

Permanently erase a campaign and all related data from the database. This is irreversible — unlike campaign-delete (which archives), this removes all campaign data permanently.

ParameterTypeRequiredDefaultDescription
campaignIdnumberYes—Campaign ID
cdpPortnumberNoauto-discoveredCDP port
campaign-start

Start a campaign with specified target persons. Returns immediately (async execution).

ParameterTypeRequiredDefaultDescription
campaignIdnumberYes—Campaign ID
personIdsnumber[]Yes—Person IDs to target
cdpPortnumberNoauto-discoveredCDP port
campaign-stop

Stop a running campaign.

ParameterTypeRequiredDefaultDescription
campaignIdnumberYes—Campaign ID
cdpPortnumberNoauto-discoveredCDP port
Campaign Status & Retry
campaign-status

Check campaign execution status and results.

ParameterTypeRequiredDefaultDescription
campaignIdnumberYes—Campaign ID
includeResultsbooleanNofalseInclude execution results
limitnumberNo20Max results to return
cdpPortnumberNoauto-discoveredCDP port
campaign-statistics

Get per-action statistics for a campaign.

ParameterTypeRequiredDefaultDescription
campaignIdnumberYes—Campaign ID
actionIdnumberNo—Filter to a specific action
maxErrorsnumberNo5Max top errors per action
cdpPortnumberNoauto-discoveredCDP port
campaign-retry

Reset specified people for re-run in a campaign.

ParameterTypeRequiredDefaultDescription
campaignIdnumberYes—Campaign ID
personIdsnumber[]Yes—Person IDs to retry
cdpPortnumberNoauto-discoveredCDP port
Campaign Actions
campaign-add-action

Add a new action to a campaign's action chain. Use describe-actions to explore available action types.

ParameterTypeRequiredDefaultDescription
campaignIdnumberYes—Campaign ID
namestringYes—Display name for the action
actionTypestringYes—Action type (e.g., VisitAndExtract, MessageToPerson)
descriptionstringNo—Action description
coolDownnumberNo—Milliseconds between executions
maxResultsnumberNo—Max results per iteration (-1 for unlimited)
actionSettingsobjectNo—Action-specific settings
cdpPortnumberNoauto-discoveredCDP port
campaign-remove-action

Remove an action from a campaign's action chain.

ParameterTypeRequiredDefaultDescription
campaignIdnumberYes—Campaign ID
actionIdnumberYes—Action ID to remove
cdpPortnumberNoauto-discoveredCDP port
campaign-update-action

Update an existing action's configuration in a campaign. Only provided fields are changed.

ParameterTypeRequiredDefaultDescription
campaignIdnumberYes—Campaign ID
actionIdnumberYes—Action ID to update
namestringNo—New display name
descriptionstring \nullNo—New description (null to clear)
coolDownnumberNo—Milliseconds between executions
maxActionResultsPerIterationnumberNo—Max results per iteration (-1 for unlimited)
actionSettingsstringNo—Action-specific settings as JSON (merged with existing)
cdpPortnumberNoauto-discoveredCDP port
campaign-reorder-actions

Reorder actions in a campaign's action chain.

ParameterTypeRequiredDefaultDescription
campaignIdnumberYes—Campaign ID
actionIdsnumber[]Yes—Action IDs in desired order
cdpPortnumberNoauto-discoveredCDP port
campaign-move-next

Move people from one action to the next in a campaign.

ParameterTypeRequiredDefaultDescription
campaignIdnumberYes—Campaign ID
actionIdnumberYes—Action ID to move people from
personIdsnumber[]Yes—Person IDs to move
cdpPortnumberNoauto-discoveredCDP port
Campaign Targeting
campaign-exclude-list

View the exclude list for a campaign or action.

ParameterTypeRequiredDefaultDescription
campaignIdnumberYes—Campaign ID
actionIdnumberNo—Action ID (for action-level list)
cdpPortnumberNoauto-discoveredCDP port
campaign-exclude-add

Add people to a campaign or action exclude list.

ParameterTypeRequiredDefaultDescription
campaignIdnumberYes—Campaign ID
personIdsnumber[]Yes—Person IDs to exclude
actionIdnumberNo—Action ID (for action-level list)
cdpPortnumberNoauto-discoveredCDP port
campaign-exclude-remove

Remove people from a campaign or action exclude list.

ParameterTypeRequiredDefaultDescription
campaignIdnumberYes—Campaign ID
personIdsnumber[]Yes—Person IDs to remove from exclude list
actionIdnumberNo—Action ID (for action-level list)
cdpPortnumberNoauto-discoveredCDP port
campaign-list-people

List people assigned to a campaign with their processing status.

ParameterTypeRequiredDefaultDescription
campaignIdnumberYes—Campaign ID
actionIdnumberNo—Filter to a specific action
statusstringNo—Filter by status (queued, processed, successful, failed)
limitnumberNo20Max results
offsetnumberNo0Pagination offset
cdpPortnumberNoauto-discoveredCDP port
campaign-remove-people

Remove people from a campaign's target list entirely. This is the inverse of import-people-from-urls.

ParameterTypeRequiredDefaultDescription
campaignIdnumberYes—Campaign ID
personIdsnumber[]Yes—Person IDs to remove
cdpPortnumberNoauto-discoveredCDP port
import-people-from-urls

Import LinkedIn profile URLs into a campaign action target list. Idempotent — previously imported URLs are skipped.

ParameterTypeRequiredDefaultDescription
campaignIdnumberYes—Campaign ID
urlsstring[]Yes—LinkedIn profile URLs
cdpPortnumberNoauto-discoveredCDP port
collect-people

Collect people from a LinkedIn page into a campaign. Detects the source type from the URL automatically.

ParameterTypeRequiredDefaultDescription
campaignIdnumberYes—Campaign ID to collect into
sourceUrlstringYes—LinkedIn page URL (search results, company people, group members)
limitnumberNo—Max profiles to collect
maxPagesnumberNo—Max pages to process
pageSizenumberNo—Results per page
sourceTypestringNo—Explicit source type (bypasses URL detection)
cdpPortnumberNoauto-discoveredCDP port
Collections
list-collections

List all LinkedHelper collections (Lists) with people counts.

ParameterTypeRequiredDefaultDescription
cdpPortnumberNoauto-discoveredCDP port
create-collection

Create a new named LinkedHelper collection (List).

ParameterTypeRequiredDefaultDescription
namestringYes—Name for the new collection
cdpPortnumberNoauto-discoveredCDP port
delete-collection

Delete a LinkedHelper collection (List) and all its people associations.

ParameterTypeRequiredDefaultDescription
collectionIdnumberYes—Collection ID to delete
cdpPortnumberNoauto-discoveredCDP port
add-people-to-collection

Add people to a LinkedHelper collection. Idempotent — adding an already-present person is a no-op.

ParameterTypeRequiredDefaultDescription
collectionIdnumberYes—Collection ID
personIdsnumber[]Yes—Person IDs to add
cdpPortnumberNoauto-discoveredCDP port
remove-people-from-collection

Remove people from a LinkedHelper collection.

ParameterTypeRequiredDefaultDescription
collectionIdnumberYes—Collection ID
personIdsnumber[]Yes—Person IDs to remove
cdpPortnumberNoauto-discoveredCDP port
import-people-from-collection

Import all people from a LinkedHelper collection into a campaign. Large sets are automatically chunked.

ParameterTypeRequiredDefaultDescription
collectionIdnumberYes—Collection ID to import from
campaignIdnumberYes—Campaign ID to import into
cdpPortnumberNoauto-discoveredCDP port
Profiles & Messaging
query-profile

Look up a cached LinkedIn profile from the local database by person ID or public ID.

ParameterTypeRequiredDefaultDescription
personIdnumberNo—Internal person ID
publicIdstringNo—LinkedIn public ID (URL slug)
includePositionsbooleanNofalseInclude full position history (career history)
query-profiles

Search for profiles in the local database with name, headline, or company filters.

ParameterTypeRequiredDefaultDescription
querystringNo—Search name or headline
companystringNo—Filter by company
includeHistorybooleanNofalseAlso search past positions (company history), not just current
limitnumberNo20Max results
offsetnumberNo0Pagination offset
query-profiles-bulk

Look up multiple cached LinkedIn profiles in a single call.

ParameterTypeRequiredDefaultDescription
personIdsnumber[]No—Look up by internal person IDs
publicIdsstring[]No—Look up by LinkedIn public IDs (URL slugs)
includePositionsbooleanNofalseInclude full position history
query-messages

Query messaging history from the local database.

ParameterTypeRequiredDefaultDescription
personIdnumberNo—Filter by person ID
chatIdnumberNo—Show specific conversation thread
searchstringNo—Search message text
limitnumberNo20Max results
offsetnumberNo0Pagination offset
check-replies

Check for new message replies from LinkedIn.

ParameterTypeRequiredDefaultDescription
sincestringNo—Only show replies after this ISO timestamp
cdpPortnumberNoauto-discoveredCDP port
scrape-messaging-history

Scrape messaging history from LinkedIn for specified people into the local database. This is a long-running operation that may take several minutes.

ParameterTypeRequiredDefaultDescription
personIdsnumber[]Yes—Person IDs whose messaging history should be scraped
cdpPortnumberNoauto-discoveredCDP port
LinkedIn Actions
visit-profile

Visit a LinkedIn profile via LinkedHelper's VisitAndExtract action and return the extracted profile data. Deducts from the daily action budget.

ParameterTypeRequiredDefaultDescription
personIdnumberNo—Internal person ID (provide this or url)
urlstringNo—LinkedIn profile URL (provide this or personId)
extractCurrentOrganizationsbooleanNofalseExtract current company info during visit
cdpPortnumberNoauto-discoveredCDP port
endorse-skills

Endorse skills on a LinkedIn profile via an ephemeral campaign. Deducts from the daily action budget.

ParameterTypeRequiredDefaultDescription
personIdnumberNo—Internal person ID (provide this or url)
urlstringNo—LinkedIn profile URL (provide this or personId)
skillNamesstring[]No—Specific skill names to endorse (mutually exclusive with limit)
limitnumberNo—Max number of skills to endorse (mutually exclusive with skillNames)
skipIfNotEndorsablebooleanNotrueSkip if person has no endorsable skills
keepCampaignbooleanNofalseArchive the ephemeral campaign instead of deleting it
cdpPortnumberNoauto-discoveredCDP port
enrich-profile

Enrich a LinkedIn profile by extracting additional data (emails, phones, socials, company info) via an ephemeral campaign. Deducts from the daily action budget.

ParameterTypeRequiredDefaultDescription
personIdnumberNo—Internal person ID (provide this or url)
urlstringNo—LinkedIn profile URL (provide this or personId)
profileInfoobjectNo—Enrich profile info (shouldEnrich required)
phonesobjectNo—Enrich phone numbers (shouldEnrich required)
emailsobjectNo—Enrich email addresses (shouldEnrich required)
socialsobjectNo—Enrich social profiles (shouldEnrich required)
companiesobjectNo—Enrich company data (shouldEnrich required)
keepCampaignbooleanNofalseArchive the ephemeral campaign instead of deleting it
cdpPortnumberNoauto-discoveredCDP port
follow-person

Follow or unfollow a LinkedIn profile via an ephemeral campaign. Deducts from the daily action budget.

ParameterTypeRequiredDefaultDescription
personIdnumberNo—Internal person ID (provide this or url)
urlstringNo—LinkedIn profile URL (provide this or personId)
modestringNofollowfollow or unfollow
skipIfUnfollowablebooleanNotrueSkip if person cannot be unfollowed
keepCampaignbooleanNofalseArchive the ephemeral campaign instead of deleting it
cdpPortnumberNoauto-discoveredCDP port
unfollow-profile

Unfollow a LinkedIn member profile or organization page by navigating to it and clicking the Following → Unfollow toggle. Accepts both profile URLs and company URLs — LinkedIn renders the same Follow/Following toggle on both surfaces. Prefer this over unfollow-from-feed for bulk feed-hygiene workflows: feed-based tools are limited to one action per feed fetch because the feed DOM refreshes after each hide/unfollow, invalidating other indexes; this tool works regardless of whether the author is currently in the home feed. For org-level feed-volume escalation it is also the substitute for a mute, since LinkedIn does not expose a Mute action on company pages. Returns the detected prior follow state and target kind (profile vs company), so bulk workflows can distinguish actual unfollows from no-op calls on already-unfollowed targets and from inaccessible targets (private/blocked profiles, restricted/unavailable companies). Unlike follow-person with mode: unfollow, which runs an ephemeral campaign and deducts from the daily action budget, this acts on the page directly and does not.

ParameterTypeRequiredDefaultDescription
profileUrlstringYes—LinkedIn profile URL (e.g. https://www.linkedin.com/in/{publicId}/) or company URL (e.g. https://www.linkedin.com/company/{slug}/)
dryRunbooleanNofalseWhen true, detects the follow state but does not click Unfollow (the dialog is opened and dismissed)
cdpPortnumberNoauto-discoveredCDP port
like-person-posts

Like and optionally comment on posts and articles by a LinkedIn profile via an ephemeral campaign. Deducts from the daily action budget.

ParameterTypeRequiredDefaultDescription
personIdnumberNo—Internal person ID (provide this or url)
urlstringNo—LinkedIn profile URL (provide this or personId)
numberOfArticlesnumberNo—Number of articles to like
numberOfPostsnumberNo—Number of posts to like
maxAgeOfArticlesnumberNo—Maximum age of articles in days
maxAgeOfPostsnumberNo—Maximum age of posts in days
shouldAddCommentbooleanNofalseAlso add a comment to liked posts/articles
messageTemplatestringNo—Comment text template as JSON (required when shouldAddComment is true)
skipIfNotLikedbooleanNotrueSkip if nothing was liked
keepCampaignbooleanNofalseArchive the ephemeral campaign instead of deleting it
cdpPortnumberNoauto-discoveredCDP port
message-person

Send a direct message to a 1st-degree LinkedIn connection via an ephemeral campaign. Deducts from the daily action budget.

ParameterTypeRequiredDefaultDescription
personIdnumberNo—Internal person ID (provide this or url)
urlstringNo—LinkedIn profile URL (provide this or personId)
messageTemplatestringYes—Message template as JSON
subjectTemplatestringNo—Subject line template as JSON
rejectIfRepliedbooleanNofalseSkip if person already replied
rejectIfMessagedbooleanNofalseSkip if already messaged
keepCampaignbooleanNofalseArchive the ephemeral campaign instead of deleting it
cdpPortnumberNoauto-discoveredCDP port
send-invite

Send a LinkedIn connection request via an ephemeral campaign. Deducts from the daily action budget.

ParameterTypeRequiredDefaultDescription
personIdnumberNo—Internal person ID (provide this or url)
urlstringNo—LinkedIn profile URL (provide this or personId)
messageTemplatestringNo—Invitation message template as JSON (empty for no message)
saveAsLeadSNbooleanNofalseSave as lead in Sales Navigator
keepCampaignbooleanNofalseArchive the ephemeral campaign instead of deleting it
cdpPortnumberNoauto-discoveredCDP port
send-inmail

Send an InMail message to a LinkedIn member (no connection required) via an ephemeral campaign. Deducts from the daily action budget.

ParameterTypeRequiredDefaultDescription
personIdnumberNo—Internal person ID (provide this or url)
urlstringNo—LinkedIn profile URL (provide this or personId)
messageTemplatestringYes—InMail body template as JSON
subjectTemplatestringNo—InMail subject line template as JSON
rejectIfRepliedbooleanNofalseSkip if person already replied
proceedOnOutOfCreditsbooleanNofalseContinue even when InMail credits are exhausted
keepCampaignbooleanNofalseArchive the ephemeral campaign instead of deleting it
cdpPortnumberNoauto-discoveredCDP port
remove-connection

Remove a person from 1st-degree LinkedIn connections (unfriend) via an ephemeral campaign. Deducts from the daily action budget.

ParameterTypeRequiredDefaultDescription
personIdnumberNo—Internal person ID (provide this or url)
urlstringNo—LinkedIn profile URL (provide this or personId)
keepCampaignbooleanNofalseArchive the ephemeral campaign instead of deleting it
cdpPortnumberNoauto-discoveredCDP port
Feed & Posts
get-feed

Read the LinkedIn home feed. Returns structured post data with cursor-based pagination.

ParameterTypeRequiredDefaultDescription
countnumberNo10Number of posts per page
cursorstringNo—Cursor token from a previous call for the next page
cdpPortnumberNoauto-discoveredCDP port
get-post

Get detailed data for a single LinkedIn post including its comment thread.

ParameterTypeRequiredDefaultDescription
postUrlstringYes—LinkedIn post URL or URN
commentCountnumberNo100Maximum number of comments to load (0 to skip)
cdpPortnumberNoauto-discoveredCDP port
get-post-stats

Get engagement statistics for a LinkedIn post: reaction count (broken down by type), comment count, and share count.

ParameterTypeRequiredDefaultDescription
postUrlstringYes—LinkedIn post URL or URN
cdpPortnumberNoauto-discoveredCDP port
get-post-engagers

List people who engaged with a LinkedIn post (reacted, etc.) with their profile info and engagement type. Supports pagination. Returns a shortfall field alongside the engagers: null when no reaction count the page rendered contradicted what was collected — the absence of a contradiction, not a guarantee of completeness — otherwise a record of how many rows were collected, how many were asked for, the reaction count the page itself rendered, and why collection stopped. paging.total carries the page's own reaction count where that was readable and falls back to the number of rows collected where it was not — the pre-pagination count, not the length of the returned slice, so with start > 0 it can exceed the rows you got back. Either way it is not an independent answer to whether more exist. MCP tool only — no CLI command.

ParameterTypeRequiredDefaultDescription
postUrlstringYes—LinkedIn post URL or URN
startnumberNo0Pagination offset
countnumberNo20Number of engagers per page
cdpPortnumberNoauto-discoveredCDP port
get-profile-activity

Get recent posts/activity from a LinkedIn profile with cursor-based pagination.

ParameterTypeRequiredDefaultDescription
profilestringYes—LinkedIn profile public ID or URL
countnumberNo10Number of posts per page
cursorstringNo—Cursor token from a previous call for the next page
cdpPortnumberNoauto-discoveredCDP port
search-posts

Search LinkedIn for posts by keyword or hashtag. Returns structured post data with cursor-based pagination.

ParameterTypeRequiredDefaultDescription
querystringYes—Search query — keywords or hashtag
countnumberNo10Number of results per page
cursornumberNo—Index-based cursor from a previous call for the next page
cdpPortnumberNoauto-discoveredCDP port
comment-on-post

Post a comment on a LinkedIn post. Checks action budget before attempting.

ParameterTypeRequiredDefaultDescription
postUrlstringYes—LinkedIn post URL
textstringYes—Comment text to post
cdpPortnumberNoauto-discoveredCDP port
react-to-post

React to a LinkedIn post with a specific reaction type. Checks action budget before attempting — refused when the PostLike limit is reached, including under dryRun.

ParameterTypeRequiredDefaultDescription
postUrlstringYes—LinkedIn post URL
reactionTypestringNolikelike, celebrate, support, love, insightful, or funny
dryRunbooleanNofalseWhen true, detects current reaction state without clicking
cdpPortnumberNoauto-discoveredCDP port
react-to-comment

React to a specific LinkedIn comment with a specific reaction type. Use this for the organic-engagement pattern of acknowledging a reply (e.g., the post author replied to your comment) without extending the thread. Mirrors react-to-post semantics scoped to one comment via its URN (as returned by get-post's commentUrn field).

ParameterTypeRequiredDefaultDescription
postUrlstringYes—LinkedIn post URL containing the target comment
commentUrnstringYes—Comment URN (e.g. urn:li:comment:(activity:1234567890,9876543210))
reactionTypestringNolikelike, celebrate, support, love, insightful, or funny
dryRunbooleanNofalseWhen true, detects current reaction state without clicking
cdpPortnumberNoauto-discoveredCDP port
dismiss-feed-post

Dismiss a post from the LinkedIn feed by clicking Not interested in its three-dot menu. Operates on the home feed by position index (pair with get-feed to identify posts).

ParameterTypeRequiredDefaultDescription
feedIndexnumberYes—Zero-based index of the post in the visible LinkedIn feed (pair with get-feed to identify posts)
dryRunbooleanNofalseWhen true, locates the menu item but does not click it
cdpPortnumberNoauto-discoveredCDP port
hide-feed-author

Click Hide posts by {Name} in a feed post's three-dot menu. Operates on the home feed by position index (pair with get-feed to identify posts). The hidden person may differ from the original author (e.g., a reposter). For bulk feed-hygiene workflows prefer hide-feed-author-profile, which is not limited to one action per feed fetch.

ParameterTypeRequiredDefaultDescription
feedIndexnumberYes—Zero-based index of the post in the visible LinkedIn feed (pair with get-feed to identify posts)
dryRunbooleanNofalseWhen true, locates the menu item but does not click it
cdpPortnumberNoauto-discoveredCDP port
hide-feed-author-profile

Mute a LinkedIn profile's posts in the home feed by navigating to the profile page and invoking Mute {Name} from the More menu. Prefer this over hide-feed-author for bulk feed-hygiene workflows: feed-based tools are limited to one action per feed fetch because the feed DOM refreshes after each hide/unfollow, invalidating other indexes. Works regardless of whether the author is currently in the feed. Returns structured results: success when muted; { success: false, reason: "mute_not_available" } for non-connections or profiles where Mute is not exposed; { success: false, reason: "already_muted" } when the profile is already muted. LinkedIn exposes no Mute action on company pages; use unfollow-profile for an organization.

ParameterTypeRequiredDefaultDescription
profileUrlstringYes—LinkedIn profile URL (e.g. https://www.linkedin.com/in/{publicId}/)
dryRunbooleanNofalseWhen true, opens the More menu and detects mute availability but does not click Mute
cdpPortnumberNoauto-discoveredCDP port
unfollow-from-feed

Unfollow the author of a LinkedIn feed post via its three-dot menu. Operates on the home feed by position index (pair with get-feed to identify posts). For bulk feed-hygiene workflows prefer unfollow-profile, which is not limited to one action per feed fetch.

ParameterTypeRequiredDefaultDescription
feedIndexnumberYes—Zero-based index of the post in the visible LinkedIn feed (pair with get-feed to identify posts)
dryRunbooleanNofalseWhen true, locates the menu item but does not click it
cdpPortnumberNoauto-discoveredCDP port
LinkedIn Search & Reference
build-linkedin-url

Build a LinkedIn URL for any supported source type. Supports SearchPage (basic search with faceted filters), SNSearchPage (Sales Navigator), and parameterised templates for company, school, group, and event pages. CLI command: build-url.

ParameterTypeRequiredDefaultDescription
sourceTypestringYes—LinkedIn source type (e.g., SearchPage, SNSearchPage, OrganizationPeople)
keywordsstringNo—Search keywords
currentCompanystring[]No—Current company IDs (SearchPage)
pastCompanystring[]No—Past company IDs (SearchPage)
geoUrnstring[]No—Geographic URN IDs (SearchPage)
industrystring[]No—Industry IDs (SearchPage)
schoolstring[]No—School IDs (SearchPage)
networkstring[]No—Connection degree codes: F, S, O (SearchPage)
profileLanguagestring[]No—Profile language codes (SearchPage)
serviceCategorystring[]No—Service category IDs (SearchPage)
filtersobject[]No—Sales Navigator filters (SNSearchPage) — each with type, values[]
slugstringNo—Company or school slug (OrganizationPeople, Alumni)
idstringNo—Entity ID (Group, Event, SNListPage, etc.)
resolve-linkedin-entity

Resolve human-readable names (company names, locations, schools) to LinkedIn entity IDs via LinkedIn's public typeahead endpoint. No authentication and no running LinkedHelper instance required. CLI command: resolve-entity.

ParameterTypeRequiredDefaultDescription
querystringYes—Search query (e.g., company name, city)
entityTypestringYes—COMPANY, GEO, or SCHOOL
list-linkedin-reference-data

List LinkedIn reference data for finite enumerations (industries, seniority levels, functions, company sizes, connection degrees, profile languages). Use this to discover valid IDs for search filters. CLI command: list-reference-data.

ParameterTypeRequiredDefaultDescription
dataTypestringYes—INDUSTRY, SENIORITY, FUNCTION, COMPANY_SIZE, CONNECTION_DEGREE, or PROFILE_LANGUAGE
Utilities
describe-actions

List available LinkedHelper action types with descriptions and configuration schemas.

ParameterTypeRequiredDefaultDescription
categorystringNo—Filter by category (people, messaging, engagement, crm, workflow)
actionTypestringNo—Get details for a specific action type
get-errors

Query current LinkedHelper UI errors, dialogs, and blocking popups.

ParameterTypeRequiredDefaultDescription
cdpPortnumberNoauto-discoveredCDP port
dismiss-errors

Dismiss closable error popups in the LinkedHelper instance UI by clicking their close/OK buttons. Recommended after UIBlockedError.

ParameterTypeRequiredDefaultDescription
cdpPortnumberNoauto-discoveredCDP port
get-action-budget

Get daily action budget showing limit types, thresholds, and today's usage from LH campaigns and CDP-direct actions.

ParameterTypeRequiredDefaultDescription
cdpPortnumberNoauto-discoveredCDP port
get-throttle-status

Check if LinkedIn is currently throttling the account.

ParameterTypeRequiredDefaultDescription
cdpPortnumberNoauto-discoveredCDP port

Known Limitations

  • Platform support: LinkedHelper runs on macOS, Windows, and Linux. Binary paths are detected automatically but can be overridden with the LINKEDHELPER_PATH environment variable.
  • Instance startup time: Starting an instance loads LinkedIn, which may take up to 45 seconds.
  • Profile data is cached: query-profile and query-profiles search the local LinkedHelper database. Profiles must have been visited or imported by LinkedHelper to appear in results.
  • Messaging scrape is slow: scrape-messaging-history navigates LinkedIn's messaging UI and can take several minutes depending on conversation volume.
  • Same-machine requirement: lhremote must run on the same machine as LinkedHelper. CDP connections are localhost-only by default (for security), and database access requires direct file system access to the LinkedHelper SQLite database.

Troubleshooting

LinkedHelper is not running

Error: LinkedHelper is not running (no processes found) — or LinkedHelper is not running (no CDP endpoint at port <port>) when you named a port explicitly.

Solution: Use launch-app to start LinkedHelper, or start it manually. lhremote communicates with LinkedHelper via the Chrome DevTools Protocol (CDP), which requires the application to be running. The second form means nothing answered on the port you named: drop --cdp-port and lhremote will discover the running instance's port itself.

LinkedHelper is unreachable

Error: LinkedHelper processes detected but CDP endpoint is unreachable

Solution: LinkedHelper is running but its CDP port is not responding. This typically means a stale or zombie process. Use launch-app --force to kill stale processes and relaunch, or manually restart LinkedHelper.

Application binary not found

Error: LinkedHelper application binary not found. Set LINKEDHELPER_PATH to override.

Solution: Install LinkedHelper from linkedhelper.com. If installed in a non-standard location, set the LINKEDHELPER_PATH environment variable to the binary path.

No accounts found

Error: No accounts found.

Solution: Open LinkedHelper and configure at least one LinkedIn account before using lhremote.

Multiple accounts found

Error: Multiple accounts found. Specify accountId. Use list-accounts to see available accounts.

Solution: Use list-accounts to see available accounts, then pass the desired accountId to start-instance, stop-instance, or other tools.

No instance running

Error: No LinkedHelper instance is running. Use start-instance first.

Solution: Run start-instance before using campaign or messaging tools. An instance must be running to interact with LinkedIn.

Instance initialization timeout

Error: Instance started but failed to initialize within timeout.

Solution: The instance was started but took too long to finish loading. This can happen on slow connections. Try again; the instance may still be starting in the background. Use check-status to verify.

Database not found

Error: No database found for account

Solution: The LinkedHelper database file is missing for the specified account. Ensure the account has been used at least once in LinkedHelper so that a local database has been created.

Disclaimer

lhremote is an independent project not affiliated with, endorsed by, or officially connected to:

  • LinkedIn or LinkedIn Corporation
  • LinkedHelper or its parent company

LinkedIn is a trademark of LinkedIn Corporation. LinkedHelper is a trademark of its respective owner.

Purpose

This project enables interoperability between automation tools and LinkedHelper, as permitted under DMCA § 1201(f). Implementation is based on publicly observable behavior (Chrome DevTools Protocol) without access to protected source code.

What This Project Does NOT Do

  • Circumvent copy protection or licensing
  • Bypass LinkedHelper authentication
  • Enable use without a valid LinkedHelper subscription
  • Provide access to LinkedIn without LinkedHelper

User Responsibility

Use of lhremote requires a valid LinkedHelper subscription and is subject to LinkedHelper's and LinkedIn's terms of service. Users accept all responsibility for compliance.

Ethical Use

This tool is for legitimate productivity. Do NOT use for spam, scraping at scale, or harassment.

License

AGPL-3.0-only — For commercial licensing, contact the maintainer.

Questions fréquentes

Qu'est-ce que Lhremote ?

LinkedHelper automation CLI: campaign management, messaging and profile queries via the Chrome DevTools Protocol.

Comment installer Lhremote ?

Suivez les étapes d'installation de sa documentation, affichée sur cette page et dans son dépôt.

Lhremote est-il open source ?

Oui. Son code est sur GitHub sous licence AGPL-3.0.

Qui maintient Lhremote ?

Il est publié par alexey-pelykh. AI.tn le référence sans le maintenir ni le recommander.

Issu des listes communautaires awesome-mcp-servers et du dépôt GitHub du projet. Être listé n'est pas une recommandation : vérifiez le code et les permissions d'un serveur avant de lui donner accès à vos données.