{
  "markdown": "![LinkedCtl: The Complete CLI & MCP for LinkedIn](https://raw.githubusercontent.com/linkedctl/.github/main/profile/assets/social-preview.png)\n\n[![CI](https://img.shields.io/github/check-runs/alexey-pelykh/linkedctl/main)](https://github.com/alexey-pelykh/linkedctl/actions/workflows/ci.yml)\n[![CodeQL](https://github.com/alexey-pelykh/linkedctl/actions/workflows/codeql.yml/badge.svg)](https://github.com/alexey-pelykh/linkedctl/actions/workflows/codeql.yml)\n[![License](https://img.shields.io/github/license/alexey-pelykh/linkedctl)](https://www.gnu.org/licenses/agpl-3.0.txt)\n\nOAuth2 CLI and MCP server for the [LinkedIn](https://www.linkedin.com) API.\n\n## What It Does\n\n- **Post content** — text, images, video, documents, articles, multi-image carousels, and polls\n- **Comments & reactions** — create, list, and delete comments and reactions on posts\n- **Organization support** — post, comment, react, and view analytics as an organization\n- **Analytics** — per-post, per-member, and per-organization statistics\n- **Media uploads** — upload images, video, and documents to LinkedIn\n- **Draft posts** — save posts as drafts before publishing\n- **OAuth 2.0 authentication** with your own LinkedIn app\n- **Direct token passing** for tokens obtained from other applications\n- **MCP server** for AI assistant integration (Claude, Cursor, etc.)\n- **CLI** for scriptable LinkedIn operations\n\n## Prerequisites\n\n- [Node.js](https://nodejs.org) >= 24\n- A [LinkedIn Developer App](https://www.linkedin.com/developers/apps) with appropriate permissions\n\n## Installation\n\n```sh\nnpm install -g linkedctl\n```\n\nOr run directly:\n\n```sh\nnpx linkedctl --help\n```\n\n## Quick Start\n\n1. Create a LinkedIn app at [linkedin.com/developers](https://www.linkedin.com/developers/apps)\n2. Configure OAuth 2.0 credentials\n3. Authenticate:\n    ```sh\n    linkedctl auth login --client-id YOUR_CLIENT_ID --client-secret YOUR_CLIENT_SECRET\n    ```\n4. Start using:\n    ```sh\n    linkedctl post \"Hello from LinkedCtl!\"\n    ```\n\nSee the [OAuth Setup Guide](https://github.com/alexey-pelykh/linkedctl/blob/main/docs/oauth-setup.md) for detailed step-by-step instructions.\n\n## MCP Integration\n\n### MCP Client Configuration\n\n<details>\n<summary><b>Claude Desktop</b></summary>\n\nAdd to your Claude Desktop configuration (`claude_desktop_config.json`):\n\n```json\n{\n    \"mcpServers\": {\n        \"linkedctl\": {\n            \"command\": \"npx\",\n            \"args\": [\"linkedctl\", \"mcp\"]\n        }\n    }\n}\n```\n\n</details>\n\n<details>\n<summary><b>Claude Code</b></summary>\n\n```sh\nclaude mcp add linkedctl -- npx linkedctl mcp\n```\n\n</details>\n\n<details>\n<summary><b>Cursor</b></summary>\n\nAdd to `.cursor/mcp.json` in your project root:\n\n```json\n{\n    \"mcpServers\": {\n        \"linkedctl\": {\n            \"command\": \"npx\",\n            \"args\": [\"linkedctl\", \"mcp\"]\n        }\n    }\n}\n```\n\n</details>\n\n<details>\n<summary><b>Windsurf</b></summary>\n\nAdd to `~/.codeium/windsurf/mcp_config.json`:\n\n```json\n{\n    \"mcpServers\": {\n        \"linkedctl\": {\n            \"command\": \"npx\",\n            \"args\": [\"linkedctl\", \"mcp\"]\n        }\n    }\n}\n```\n\n</details>\n\n### Available Tools\n\nAll tools accept an optional `profile` parameter to select a configuration profile.\n\n#### Authentication\n\n| Tool          | Description                                                     |\n| ------------- | --------------------------------------------------------------- |\n| `whoami`      | Show the current user's name, email, and profile picture URL    |\n| `auth_status` | Show authentication status for a profile                        |\n| `auth_revoke` | Revoke the access token server-side and clear local credentials |\n\n#### Posts\n\n| Tool          | Description                                                                |\n| ------------- | -------------------------------------------------------------------------- |\n| `post_create` | Create a post on LinkedIn with optional media, poll, or article attachment |\n| `post_get`    | Fetch a single post by URN                                                 |\n| `post_list`   | List posts with pagination (supports `as_org` for organization posts)      |\n| `post_update` | Update the commentary text of an existing post                             |\n| `post_delete` | Delete a post by URN                                                       |\n\n`post_create` supports rich content types:\n\n| Parameter                    | Description                                           |\n| ---------------------------- | ----------------------------------------------------- |\n| `text`                       | Post text content (required)                          |\n| `visibility`                 | `PUBLIC` or `CONNECTIONS` (default `PUBLIC`)          |\n| `draft`                      | Save as draft instead of publishing                   |\n| `image` / `image_file`       | Attach a single image (URN or local file path)        |\n| `video` / `video_file`       | Attach a video (URN or local file path)               |\n| `document` / `document_file` | Attach a document (URN or local file path)            |\n| `images` / `image_files`     | Attach multiple images (minimum 2)                    |\n| `article_url`                | Attach an article link                                |\n| `poll`                       | Poll question text                                    |\n| `poll_options`               | Poll answer options (2–4 required when `poll` is set) |\n| `poll_duration`              | `ONE_DAY`, `THREE_DAYS`, `ONE_WEEK`, or `TWO_WEEKS`   |\n| `as_org`                     | Post as an organization (numeric ID)                  |\n\n#### Comments\n\n| Tool             | Description                                    |\n| ---------------- | ---------------------------------------------- |\n| `comment_create` | Create a comment on a post (supports `as_org`) |\n| `comment_list`   | List comments on a post                        |\n| `comment_get`    | Get a specific comment by URN                  |\n| `comment_delete` | Delete a comment by URN                        |\n\n#### Reactions\n\n| Tool              | Description                                                                                                                   |\n| ----------------- | ----------------------------------------------------------------------------------------------------------------------------- |\n| `reaction_create` | Add a reaction to a post (supports `as_org`). Types: `LIKE`, `PRAISE`, `EMPATHY`, `INTEREST`, `APPRECIATION`, `ENTERTAINMENT` |\n| `reaction_list`   | List reactions on a post                                                                                                      |\n| `reaction_delete` | Remove a reaction from a post (supports `as_org`)                                                                             |\n\n#### Organizations\n\n| Tool            | Description                                           |\n| --------------- | ----------------------------------------------------- |\n| `org_list`      | List organizations the authenticated user administers |\n| `org_get`       | Fetch a single organization by ID                     |\n| `org_followers` | Get the follower count for an organization            |\n\n#### Analytics\n\n| Tool         | Description                                                                         |\n| ------------ | ----------------------------------------------------------------------------------- |\n| `stats_post` | Get analytics for a single post (impressions, reach, reactions, comments, reshares) |\n| `stats_me`   | Get aggregated analytics across all your posts                                      |\n| `stats_org`  | Get share statistics for an organization (lifetime or time-bucketed)                |\n\n#### Media\n\n| Tool              | Description                                                           |\n| ----------------- | --------------------------------------------------------------------- |\n| `document_upload` | Upload a document to LinkedIn (PDF, DOCX, PPTX, DOC, PPT; max 100 MB) |\n\n## Configuration\n\n### Config File Format\n\nLinkedCtl stores configuration in YAML files. Each file represents a single profile:\n\n```yaml\napi-version: \"202601\"\noauth:\n    client-id: \"YOUR_CLIENT_ID\"\n    client-secret: \"YOUR_CLIENT_SECRET\"\n    access-token: \"YOUR_ACCESS_TOKEN\"\n    refresh-token: \"YOUR_REFRESH_TOKEN\"\n    token-expires-at: \"2026-05-03T12:00:00.000Z\"\n```\n\n**Available keys:**\n\n| Key                      | Description                           |\n| ------------------------ | ------------------------------------- |\n| `api-version`            | LinkedIn API version (e.g. `202601`)  |\n| `oauth.client-id`        | OAuth 2.0 client ID                   |\n| `oauth.client-secret`    | OAuth 2.0 client secret               |\n| `oauth.access-token`     | OAuth 2.0 access token                |\n| `oauth.refresh-token`    | OAuth 2.0 refresh token               |\n| `oauth.token-expires-at` | Token expiration timestamp (ISO 8601) |\n\nConfig files are written with `0600` permissions (owner read/write only).\n\n### File Location and Precedence\n\nWithout a profile, LinkedCtl searches for config files in this order:\n\n1. `.linkedctl.yaml` in the current working directory\n2. `~/.linkedctl.yaml` in the home directory\n\nThe first file found is used. When writing (e.g. after `auth login`), LinkedCtl writes to the CWD file if it exists, otherwise to the home directory file.\n\n### Profiles\n\nProfiles let you manage multiple LinkedIn accounts or configurations. Each profile is stored as a separate YAML file under `~/.linkedctl/`:\n\n| Profile    | Config file path             |\n| ---------- | ---------------------------- |\n| (default)  | `~/.linkedctl.yaml`          |\n| `work`     | `~/.linkedctl/work.yaml`     |\n| `personal` | `~/.linkedctl/personal.yaml` |\n\nUse the `--profile` flag with any command:\n\n```sh\nlinkedctl --profile work auth login --client-id ID --client-secret SECRET\nlinkedctl --profile work post \"Hello from my work account!\"\n```\n\nManage profiles with the `profile` command:\n\n```sh\nlinkedctl profile create work --access-token YOUR_TOKEN --api-version 202601\nlinkedctl profile list\nlinkedctl profile show work\nlinkedctl profile delete work\n```\n\n### Authentication Methods\n\n#### OAuth 2.0 (Recommended)\n\nLinkedCtl supports OAuth 2.0 with your own LinkedIn Developer App:\n\n```sh\nlinkedctl auth login --client-id YOUR_CLIENT_ID --client-secret YOUR_CLIENT_SECRET\n```\n\n#### Direct Token\n\nIf you already have an access token from another application:\n\n```sh\nlinkedctl auth token --access-token YOUR_TOKEN\n```\n\n### Environment Variables\n\n| Variable                  | Description                                              |\n| ------------------------- | -------------------------------------------------------- |\n| `LINKEDCTL_CLIENT_ID`     | LinkedIn OAuth 2.0 client ID                             |\n| `LINKEDCTL_CLIENT_SECRET` | LinkedIn OAuth 2.0 client secret                         |\n| `LINKEDCTL_ACCESS_TOKEN`  | Direct access token (bypasses OAuth flow)                |\n| `LINKEDCTL_API_VERSION`   | LinkedIn API version string (e.g. `202601`) **required** |\n\nEnvironment variables take precedence over config file values.\n\n#### Profile-Prefixed Environment Variables\n\nWhen using a named profile, LinkedCtl also reads profile-prefixed environment variables. The profile name is uppercased with hyphens converted to underscores:\n\n| Profile    | Variable                          |\n| ---------- | --------------------------------- |\n| (default)  | `LINKEDCTL_ACCESS_TOKEN`          |\n| `work`     | `LINKEDCTL_WORK_ACCESS_TOKEN`     |\n| `my-brand` | `LINKEDCTL_MY_BRAND_ACCESS_TOKEN` |\n\nThe same pattern applies to `CLIENT_ID`, `CLIENT_SECRET`, and `API_VERSION`.\n\n### Precedence Order\n\nConfiguration values are resolved in this order (highest priority first):\n\n1. **Environment variables** (profile-prefixed if a profile is active)\n2. **Config file** (profile-specific file, or CWD/home fallback)\n\n## CLI Reference\n\n### Global Options\n\n| Option             | Description                          |\n| ------------------ | ------------------------------------ |\n| `--profile <name>` | Use a specific configuration profile |\n| `--json`           | Force JSON output                    |\n| `-q, --quiet`      | Suppress informational output        |\n| `--no-color`       | Disable color output                 |\n\n### `auth` — Manage Authentication\n\n| Command        | Description                                                |\n| -------------- | ---------------------------------------------------------- |\n| `auth setup`   | Configure OAuth client credentials interactively           |\n| `auth login`   | Authenticate via OAuth 2.0 (opens browser)                 |\n| `auth token`   | Store a direct access token                                |\n| `auth status`  | Show authentication status and token expiry                |\n| `auth logout`  | Clear stored credentials from the active profile           |\n| `auth refresh` | Refresh the access token using a stored refresh token      |\n| `auth revoke`  | Revoke the access token server-side and clear local tokens |\n\n**`auth login` options:**\n\n| Option                     | Description                        | Default                          |\n| -------------------------- | ---------------------------------- | -------------------------------- |\n| `--client-id <id>`         | OAuth 2.0 client ID                | from config                      |\n| `--client-secret <secret>` | OAuth 2.0 client secret            | from config                      |\n| `--scope <scopes>`         | OAuth 2.0 scopes (space-separated) | `openid profile w_member_social` |\n\n**`auth token` options:**\n\n| Option                   | Description                      |\n| ------------------------ | -------------------------------- |\n| `--access-token <token>` | Access token to store (required) |\n\n### `post` — Manage LinkedIn Posts\n\n```sh\n# Shorthand: pass text as an argument\nlinkedctl post \"Hello from LinkedCtl!\"\n\n# Explicit subcommand\nlinkedctl post create --text \"Hello from LinkedCtl!\"\n\n# Pipe content from stdin\necho \"Hello from LinkedCtl!\" | linkedctl post create\n```\n\n| Command             | Description                       |\n| ------------------- | --------------------------------- |\n| `post create`       | Create a post on LinkedIn         |\n| `post get <urn>`    | Fetch a post by URN               |\n| `post list`         | List posts with pagination        |\n| `post update <urn>` | Update a post's commentary text   |\n| `post delete <urn>` | Delete a post (with confirmation) |\n\n**`post create` options:**\n\n| Option                       | Description                                                     | Default      |\n| ---------------------------- | --------------------------------------------------------------- | ------------ |\n| `--text <text>`              | Text content (also accepts `--text-file`, positional, or stdin) |              |\n| `--visibility <visibility>`  | `PUBLIC` or `CONNECTIONS`                                       | `PUBLIC`     |\n| `--draft`                    | Save as draft instead of publishing                             |              |\n| `--image <urn>`              | Image URN to attach                                             |              |\n| `--image-file <path>`        | Local image file to upload and attach                           |              |\n| `--video <urn>`              | Video URN to attach                                             |              |\n| `--video-file <path>`        | Local video file to upload and attach                           |              |\n| `--document <urn>`           | Document URN to attach                                          |              |\n| `--document-file <path>`     | Local document file to upload and attach                        |              |\n| `--images <urns>`            | Multiple image URNs (comma-separated, minimum 2)                |              |\n| `--image-files <paths>`      | Multiple local image files (comma-separated, minimum 2)         |              |\n| `--article-url <url>`        | Article URL to attach                                           |              |\n| `--poll <question>`          | Poll question text                                              |              |\n| `--option <text>`            | Poll option (repeat 2–4 times)                                  |              |\n| `--poll-duration <duration>` | `ONE_DAY`, `THREE_DAYS`, `ONE_WEEK`, or `TWO_WEEKS`             | `THREE_DAYS` |\n| `--as-org <id>`              | Post as an organization (numeric ID)                            |              |\n| `--format <format>`          | Output format (`json`, `table`)                                 | auto         |\n\n**`post list` options:**\n\n| Option          | Description                                | Default |\n| --------------- | ------------------------------------------ | ------- |\n| `--count <n>`   | Number of posts to return (max 100)        | `10`    |\n| `--start <n>`   | Starting index for pagination              | `0`     |\n| `--as-org <id>` | List posts of an organization (numeric ID) |         |\n\n### `comment` — Manage Comments\n\n| Command                        | Description                          |\n| ------------------------------ | ------------------------------------ |\n| `comment create <post-urn>`    | Create a comment on a post           |\n| `comment list <post-urn>`      | List comments on a post              |\n| `comment get <comment-urn>`    | Get a specific comment by URN        |\n| `comment delete <comment-urn>` | Delete a comment (with confirmation) |\n\n**`comment create` options:**\n\n| Option          | Description                             |\n| --------------- | --------------------------------------- |\n| `--text <text>` | Comment text (required)                 |\n| `--as-org <id>` | Comment as an organization (numeric ID) |\n\n### `reaction` — Manage Reactions\n\n| Command                 | Description                   |\n| ----------------------- | ----------------------------- |\n| `reaction create <urn>` | Add a reaction to a post      |\n| `reaction list <urn>`   | List reactions on a post      |\n| `reaction delete <urn>` | Remove a reaction from a post |\n\n**`reaction create` options:**\n\n| Option          | Description                                                                                  |\n| --------------- | -------------------------------------------------------------------------------------------- |\n| `--type <type>` | `LIKE`, `PRAISE`, `EMPATHY`, `INTEREST`, `APPRECIATION`, or `ENTERTAINMENT` (default `LIKE`) |\n| `--as-org <id>` | React as an organization (numeric ID)                                                        |\n\n### `org` — Manage Organizations\n\n| Command              | Description                                           |\n| -------------------- | ----------------------------------------------------- |\n| `org list`           | List organizations the authenticated user administers |\n| `org get <id>`       | Get organization details                              |\n| `org followers <id>` | Get organization follower count                       |\n\n### `media` — Upload Media\n\n| Command                        | Description                                               |\n| ------------------------------ | --------------------------------------------------------- |\n| `media upload-image <file>`    | Upload an image (JPG, PNG, GIF)                           |\n| `media upload-video <file>`    | Upload a video                                            |\n| `media upload-document <file>` | Upload a document (PDF, DOCX, PPTX, DOC, PPT; max 100 MB) |\n\nAll media commands accept `--as-org <id>` and `--format json|table`.\n\n### `stats` — View Analytics\n\n| Command            | Description                                         |\n| ------------------ | --------------------------------------------------- |\n| `stats post <urn>` | Get post analytics (impressions, reach, engagement) |\n| `stats me`         | Get aggregated analytics across all your posts      |\n| `stats org <id>`   | Get organization share statistics                   |\n\n**Common options:**\n\n| Option                             | Description                       |\n| ---------------------------------- | --------------------------------- |\n| `--from <date>` / `--start <date>` | Start of date range (YYYY-MM-DD)  |\n| `--to <date>` / `--end <date>`     | End of date range (YYYY-MM-DD)    |\n| `--time-granularity <granularity>` | `DAY` or `MONTH` (org stats only) |\n\n### `profile` — Manage Configuration Profiles\n\n| Command                 | Description                          |\n| ----------------------- | ------------------------------------ |\n| `profile create <name>` | Create a new profile                 |\n| `profile list`          | List all profiles                    |\n| `profile show [<name>]` | Show profile details (redacted)      |\n| `profile delete <name>` | Delete a profile (with confirmation) |\n\n**`profile create` options:**\n\n| Option                    | Description                                    |\n| ------------------------- | ---------------------------------------------- |\n| `--access-token <token>`  | OAuth 2.0 access token (required)              |\n| `--api-version <version>` | LinkedIn API version, e.g. `202601` (required) |\n\n### `whoami` — Show Current User\n\n```sh\nlinkedctl whoami\nlinkedctl whoami --format json\n```\n\n| Option              | Description                     | Default |\n| ------------------- | ------------------------------- | ------- |\n| `--format <format>` | Output format (`json`, `table`) | auto    |\n\n### `completion` — Shell Completions\n\n```sh\nlinkedctl completion bash\nlinkedctl completion zsh\n```\n\n## Security\n\n- **Dependabot** keeps dependencies up to date with automated pull requests\n- **CodeQL** analysis runs on every push and pull request for automated vulnerability detection\n- **Path traversal validation** on profile names prevents directory escape attacks\n- **Unified auth error handling** across all MCP tools provides consistent authentication failure messages\n- **Destructive operations** (delete post, delete comment, delete profile) require interactive confirmation unless `--force` is passed\n\n## Disclaimer\n\nThis is an independent project and is not affiliated with, endorsed by, or associated with LinkedIn Corporation. LinkedIn is a trademark of LinkedIn Corporation.\n\n## License\n\n[AGPL-3.0-only](https://www.gnu.org/licenses/agpl-3.0.txt)\n\n**Using LinkedCtl as a CLI tool or MCP server**: No license obligations — use freely.\n\n**Using `@linkedctl/core` as a library in your project**: Your combined work must be licensed under AGPL-3.0 (or a compatible license).\n",
  "bytes": 22794,
  "sha": "7bfd240286a8ab443ba5bdeb0c2125dd2c1a80642d014064b1c43d18f66fc1fd",
  "repo_slug": "alexey-pelykh/linkedctl",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_alexey_pelykh_linkedctl_064469e5/readme"
}