npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

lhremote

v0.20.1

Published

LinkedHelper automation toolkit — CLI & MCP server

Readme

lhremote: LinkedHelper Automation Toolkit

CI codecov npm version npm downloads GitHub Repo stars License

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 all 68 tools 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.

App Management

lhremote find-app [--json]
lhremote launch-app
lhremote quit-app

Account & Instance

lhremote list-accounts [--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]
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]
lhremote campaign-list-people <campaignId> [--action-id <id>] [--status <status>] [--limit <n>] [--offset <n>] [--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-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 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]

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, all CDP-connected tools accept:

| Parameter | CLI Flag | Type | Default | Description | |-----------|----------|------|---------|-------------| | cdpPort | --cdp-port | number | 9222 | CDP debugging port | | cdpHost | --cdp-host | string | 127.0.0.1 | CDP host address | | allowRemote | --allow-remote | boolean | false | Allow connections to non-loopback addresses |

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.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | cdpPort | number | No | auto-select | CDP port to use | | force | boolean | No | false | Kill existing LinkedHelper processes before launching |

quit-app

Quit the LinkedHelper application.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | cdpPort | number | No | 9222 | CDP port |

Account & Instance

list-accounts

List available LinkedHelper accounts. Returns account ID, LinkedIn ID, name, and email for each account.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | cdpPort | number | No | 9222 | CDP port |

start-instance

Start a LinkedHelper instance for a LinkedIn account.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | accountId | number | No | auto-select if single account | Account ID | | cdpPort | number | No | 9222 | CDP port |

stop-instance

Stop a running LinkedHelper instance.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | accountId | number | No | auto-select if single account | Account ID | | cdpPort | number | No | 9222 | CDP port |

check-status

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | cdpPort | number | No | 9222 | CDP port |

Campaigns

campaign-list

List existing campaigns with summary statistics.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | includeArchived | boolean | No | false | Include archived campaigns | | cdpPort | number | No | 9222 | CDP port |

campaign-create

Create a new campaign from YAML or JSON configuration.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | config | string | Yes | — | Campaign configuration in YAML or JSON format | | format | string | No | yaml | Configuration format (yaml or json) | | cdpPort | number | No | 9222 | CDP port |

campaign-get

Get detailed campaign information including action chain.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | campaignId | number | Yes | — | Campaign ID | | cdpPort | number | No | 9222 | CDP port |

campaign-export

Export campaign configuration as YAML or JSON.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | campaignId | number | Yes | — | Campaign ID | | format | string | No | yaml | Export format (yaml or json) | | cdpPort | number | No | 9222 | CDP port |

campaign-update

Update a campaign's name and/or description.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | campaignId | number | Yes | — | Campaign ID | | name | string | No | — | New campaign name | | description | string | No | — | New description (empty string to clear) | | cdpPort | number | No | 9222 | CDP port |

campaign-delete

Delete (archive) a campaign.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | campaignId | number | Yes | — | Campaign ID | | cdpPort | number | No | 9222 | CDP 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.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | campaignId | number | Yes | — | Campaign ID | | cdpPort | number | No | 9222 | CDP port |

campaign-start

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | campaignId | number | Yes | — | Campaign ID | | personIds | number[] | Yes | — | Person IDs to target | | cdpPort | number | No | 9222 | CDP port |

campaign-stop

Stop a running campaign.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | campaignId | number | Yes | — | Campaign ID | | cdpPort | number | No | 9222 | CDP port |

campaign-status

Check campaign execution status and results.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | campaignId | number | Yes | — | Campaign ID | | includeResults | boolean | No | false | Include execution results | | limit | number | No | 20 | Max results to return | | cdpPort | number | No | 9222 | CDP port |

campaign-statistics

Get per-action statistics for a campaign.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | campaignId | number | Yes | — | Campaign ID | | actionId | number | No | — | Filter to a specific action | | maxErrors | number | No | 5 | Max top errors per action | | cdpPort | number | No | 9222 | CDP port |

campaign-retry

Reset specified people for re-run in a campaign.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | campaignId | number | Yes | — | Campaign ID | | personIds | number[] | Yes | — | Person IDs to retry | | cdpPort | number | No | 9222 | CDP port |

Campaign Actions

campaign-add-action

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | campaignId | number | Yes | — | Campaign ID | | name | string | Yes | — | Display name for the action | | actionType | string | Yes | — | Action type (e.g., VisitAndExtract, MessageToPerson) | | description | string | No | — | Action description | | coolDown | number | No | — | Milliseconds between executions | | maxResults | number | No | — | Max results per iteration (-1 for unlimited) | | actionSettings | object | No | — | Action-specific settings | | cdpPort | number | No | 9222 | CDP port |

campaign-remove-action

Remove an action from a campaign's action chain.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | campaignId | number | Yes | — | Campaign ID | | actionId | number | Yes | — | Action ID to remove | | cdpPort | number | No | 9222 | CDP port |

campaign-update-action

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | campaignId | number | Yes | — | Campaign ID | | actionId | number | Yes | — | Action ID to update | | name | string | No | — | New display name | | description | string | null | No | — | New description (null to clear) | | coolDown | number | No | — | Milliseconds between executions | | maxActionResultsPerIteration | number | No | — | Max results per iteration (-1 for unlimited) | | actionSettings | string | No | — | Action-specific settings as JSON (merged with existing) | | cdpPort | number | No | 9222 | CDP port |

campaign-reorder-actions

Reorder actions in a campaign's action chain.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | campaignId | number | Yes | — | Campaign ID | | actionIds | number[] | Yes | — | Action IDs in desired order | | cdpPort | number | No | 9222 | CDP port |

campaign-move-next

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | campaignId | number | Yes | — | Campaign ID | | actionId | number | Yes | — | Action ID to move people from | | personIds | number[] | Yes | — | Person IDs to move | | cdpPort | number | No | 9222 | CDP port |

Campaign Targeting

campaign-exclude-list

View the exclude list for a campaign or action.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | campaignId | number | Yes | — | Campaign ID | | actionId | number | No | — | Action ID (for action-level list) | | cdpPort | number | No | 9222 | CDP port |

campaign-exclude-add

Add people to a campaign or action exclude list.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | campaignId | number | Yes | — | Campaign ID | | personIds | number[] | Yes | — | Person IDs to exclude | | actionId | number | No | — | Action ID (for action-level list) | | cdpPort | number | No | 9222 | CDP port |

campaign-exclude-remove

Remove people from a campaign or action exclude list.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | campaignId | number | Yes | — | Campaign ID | | personIds | number[] | Yes | — | Person IDs to remove from exclude list | | actionId | number | No | — | Action ID (for action-level list) | | cdpPort | number | No | 9222 | CDP port |

campaign-list-people

List people assigned to a campaign with their processing status.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | campaignId | number | Yes | — | Campaign ID | | actionId | number | No | — | Filter to a specific action | | status | string | No | — | Filter by status (queued, processed, successful, failed) | | limit | number | No | 20 | Max results | | offset | number | No | 0 | Pagination offset | | cdpPort | number | No | 9222 | CDP port |

campaign-remove-people

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | campaignId | number | Yes | — | Campaign ID | | personIds | number[] | Yes | — | Person IDs to remove | | cdpPort | number | No | 9222 | CDP port |

import-people-from-urls

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | campaignId | number | Yes | — | Campaign ID | | urls | string[] | Yes | — | LinkedIn profile URLs | | cdpPort | number | No | 9222 | CDP port |

collect-people

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | campaignId | number | Yes | — | Campaign ID to collect into | | sourceUrl | string | Yes | — | LinkedIn page URL (search results, company people, group members) | | limit | number | No | — | Max profiles to collect | | maxPages | number | No | — | Max pages to process | | pageSize | number | No | — | Results per page | | sourceType | string | No | — | Explicit source type (bypasses URL detection) | | cdpPort | number | No | 9222 | CDP port |

Collections

list-collections

List all LinkedHelper collections (Lists) with people counts.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | cdpPort | number | No | 9222 | CDP port |

create-collection

Create a new named LinkedHelper collection (List).

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | name | string | Yes | — | Name for the new collection | | cdpPort | number | No | 9222 | CDP port |

delete-collection

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | collectionId | number | Yes | — | Collection ID to delete | | cdpPort | number | No | 9222 | CDP port |

add-people-to-collection

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | collectionId | number | Yes | — | Collection ID | | personIds | number[] | Yes | — | Person IDs to add | | cdpPort | number | No | 9222 | CDP port |

remove-people-from-collection

Remove people from a LinkedHelper collection.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | collectionId | number | Yes | — | Collection ID | | personIds | number[] | Yes | — | Person IDs to remove | | cdpPort | number | No | 9222 | CDP port |

import-people-from-collection

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | collectionId | number | Yes | — | Collection ID to import from | | campaignId | number | Yes | — | Campaign ID to import into | | cdpPort | number | No | 9222 | CDP port |

Profiles & Messaging

query-profile

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | personId | number | No | — | Internal person ID | | publicId | string | No | — | LinkedIn public ID (URL slug) | | includePositions | boolean | No | false | Include full position history (career history) |

query-profiles

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | query | string | No | — | Search name or headline | | company | string | No | — | Filter by company | | includeHistory | boolean | No | false | Also search past positions (company history), not just current | | limit | number | No | 20 | Max results | | offset | number | No | 0 | Pagination offset |

query-profiles-bulk

Look up multiple cached LinkedIn profiles in a single call.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | personIds | number[] | No | — | Look up by internal person IDs | | publicIds | string[] | No | — | Look up by LinkedIn public IDs (URL slugs) | | includePositions | boolean | No | false | Include full position history |

query-messages

Query messaging history from the local database.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | personId | number | No | — | Filter by person ID | | chatId | number | No | — | Show specific conversation thread | | search | string | No | — | Search message text | | limit | number | No | 20 | Max results | | offset | number | No | 0 | Pagination offset |

check-replies

Check for new message replies from LinkedIn.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | since | string | No | — | Only show replies after this ISO timestamp | | cdpPort | number | No | 9222 | CDP 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.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | personIds | number[] | Yes | — | Person IDs whose messaging history should be scraped | | cdpPort | number | No | 9222 | CDP 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.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | personId | number | No | — | Internal person ID (provide this or url) | | url | string | No | — | LinkedIn profile URL (provide this or personId) | | extractCurrentOrganizations | boolean | No | false | Extract current company info during visit | | cdpPort | number | No | 9222 | CDP port |

endorse-skills

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | personId | number | No | — | Internal person ID (provide this or url) | | url | string | No | — | LinkedIn profile URL (provide this or personId) | | skillNames | string[] | No | — | Specific skill names to endorse (mutually exclusive with limit) | | limit | number | No | — | Max number of skills to endorse (mutually exclusive with skillNames) | | skipIfNotEndorsable | boolean | No | true | Skip if person has no endorsable skills | | keepCampaign | boolean | No | false | Archive the ephemeral campaign instead of deleting it | | cdpPort | number | No | 9222 | CDP 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.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | personId | number | No | — | Internal person ID (provide this or url) | | url | string | No | — | LinkedIn profile URL (provide this or personId) | | profileInfo | object | No | — | Enrich profile info (shouldEnrich required) | | phones | object | No | — | Enrich phone numbers (shouldEnrich required) | | emails | object | No | — | Enrich email addresses (shouldEnrich required) | | socials | object | No | — | Enrich social profiles (shouldEnrich required) | | companies | object | No | — | Enrich company data (shouldEnrich required) | | keepCampaign | boolean | No | false | Archive the ephemeral campaign instead of deleting it | | cdpPort | number | No | 9222 | CDP port |

follow-person

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | personId | number | No | — | Internal person ID (provide this or url) | | url | string | No | — | LinkedIn profile URL (provide this or personId) | | mode | string | No | follow | follow or unfollow | | skipIfUnfollowable | boolean | No | true | Skip if person cannot be unfollowed | | keepCampaign | boolean | No | false | Archive the ephemeral campaign instead of deleting it | | cdpPort | number | No | 9222 | CDP 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.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | personId | number | No | — | Internal person ID (provide this or url) | | url | string | No | — | LinkedIn profile URL (provide this or personId) | | numberOfArticles | number | No | — | Number of articles to like | | numberOfPosts | number | No | — | Number of posts to like | | maxAgeOfArticles | number | No | — | Maximum age of articles in days | | maxAgeOfPosts | number | No | — | Maximum age of posts in days | | shouldAddComment | boolean | No | false | Also add a comment to liked posts/articles | | messageTemplate | string | No | — | Comment text template as JSON (required when shouldAddComment is true) | | skipIfNotLiked | boolean | No | true | Skip if nothing was liked | | keepCampaign | boolean | No | false | Archive the ephemeral campaign instead of deleting it | | cdpPort | number | No | 9222 | CDP port |

message-person

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | personId | number | No | — | Internal person ID (provide this or url) | | url | string | No | — | LinkedIn profile URL (provide this or personId) | | messageTemplate | string | Yes | — | Message template as JSON | | subjectTemplate | string | No | — | Subject line template as JSON | | rejectIfReplied | boolean | No | false | Skip if person already replied | | rejectIfMessaged | boolean | No | false | Skip if already messaged | | keepCampaign | boolean | No | false | Archive the ephemeral campaign instead of deleting it | | cdpPort | number | No | 9222 | CDP port |

send-invite

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | personId | number | No | — | Internal person ID (provide this or url) | | url | string | No | — | LinkedIn profile URL (provide this or personId) | | messageTemplate | string | No | — | Invitation message template as JSON (empty for no message) | | saveAsLeadSN | boolean | No | false | Save as lead in Sales Navigator | | keepCampaign | boolean | No | false | Archive the ephemeral campaign instead of deleting it | | cdpPort | number | No | 9222 | CDP port |

send-inmail

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | personId | number | No | — | Internal person ID (provide this or url) | | url | string | No | — | LinkedIn profile URL (provide this or personId) | | messageTemplate | string | Yes | — | InMail body template as JSON | | subjectTemplate | string | No | — | InMail subject line template as JSON | | rejectIfReplied | boolean | No | false | Skip if person already replied | | proceedOnOutOfCredits | boolean | No | false | Continue even when InMail credits are exhausted | | keepCampaign | boolean | No | false | Archive the ephemeral campaign instead of deleting it | | cdpPort | number | No | 9222 | CDP port |

remove-connection

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | personId | number | No | — | Internal person ID (provide this or url) | | url | string | No | — | LinkedIn profile URL (provide this or personId) | | keepCampaign | boolean | No | false | Archive the ephemeral campaign instead of deleting it | | cdpPort | number | No | 9222 | CDP port |

Feed & Posts

get-feed

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | count | number | No | 10 | Number of posts per page | | cursor | string | No | — | Cursor token from a previous call for the next page | | cdpPort | number | No | 9222 | CDP port |

get-post

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | postUrl | string | Yes | — | LinkedIn post URL or URN | | commentCount | number | No | 100 | Maximum number of comments to load (0 to skip) | | cdpPort | number | No | 9222 | CDP port |

get-post-stats

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | postUrl | string | Yes | — | LinkedIn post URL or URN | | cdpPort | number | No | 9222 | CDP port |

get-post-engagers

List people who engaged with a LinkedIn post (reacted, etc.) with their profile info and engagement type. Supports pagination. MCP tool only — no CLI command.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | postUrl | string | Yes | — | LinkedIn post URL or URN | | start | number | No | 0 | Pagination offset | | count | number | No | 20 | Number of engagers per page | | cdpPort | number | No | 9222 | CDP port |

get-profile-activity

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | profile | string | Yes | — | LinkedIn profile public ID or URL | | count | number | No | 10 | Number of posts per page | | cursor | string | No | — | Cursor token from a previous call for the next page | | cdpPort | number | No | 9222 | CDP port |

search-posts

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | query | string | Yes | — | Search query — keywords or hashtag | | count | number | No | 10 | Number of results per page | | cursor | number | No | — | Index-based cursor from a previous call for the next page | | cdpPort | number | No | 9222 | CDP port |

comment-on-post

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | postUrl | string | Yes | — | LinkedIn post URL | | text | string | Yes | — | Comment text to post | | cdpPort | number | No | 9222 | CDP port |

react-to-post

React to a LinkedIn post with a specific reaction type.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | postUrl | string | Yes | — | LinkedIn post URL | | reactionType | string | No | like | like, celebrate, support, love, insightful, or funny | | cdpPort | number | No | 9222 | CDP 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).

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | postUrl | string | Yes | — | LinkedIn post URL containing the target comment | | commentUrn | string | Yes | — | Comment URN (e.g. urn:li:comment:(activity:1234567890,9876543210)) | | reactionType | string | No | like | like, celebrate, support, love, insightful, or funny | | dryRun | boolean | No | false | When true, detects current reaction state without clicking | | cdpPort | number | No | 9222 | CDP 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.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | sourceType | string | Yes | — | LinkedIn source type (e.g., SearchPage, SNSearchPage, OrganizationPeople) | | keywords | string | No | — | Search keywords | | currentCompany | string[] | No | — | Current company IDs (SearchPage) | | pastCompany | string[] | No | — | Past company IDs (SearchPage) | | geoUrn | string[] | No | — | Geographic URN IDs (SearchPage) | | industry | string[] | No | — | Industry IDs (SearchPage) | | school | string[] | No | — | School IDs (SearchPage) | | network | string[] | No | — | Connection degree codes: F, S, O (SearchPage) | | profileLanguage | string[] | No | — | Profile language codes (SearchPage) | | serviceCategory | string[] | No | — | Service category IDs (SearchPage) | | filters | object[] | No | — | Sales Navigator filters (SNSearchPage) — each with type, values[] | | slug | string | No | — | Company or school slug (OrganizationPeople, Alumni) | | id | string | No | — | 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.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | query | string | Yes | — | Search query (e.g., company name, city) | | entityType | string | Yes | — | 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.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | dataType | string | Yes | — | INDUSTRY, SENIORITY, FUNCTION, COMPANY_SIZE, CONNECTION_DEGREE, or PROFILE_LANGUAGE |

Utilities

describe-actions

List available LinkedHelper action types with descriptions and configuration schemas.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | category | string | No | — | Filter by category (people, messaging, engagement, crm, workflow) | | actionType | string | No | — | Get details for a specific action type |

get-errors

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | cdpPort | number | No | 9222 | CDP port |

dismiss-errors

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | cdpPort | number | No | 9222 | CDP port |

get-action-budget

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

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | cdpPort | number | No | 9222 | CDP port |

get-throttle-status

Check if LinkedIn is currently throttling the account.

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | cdpPort | number | No | 9222 | CDP 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 CDP endpoint at port 9222)

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.

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.