Skip to main content
Glama
linkedinmcp

linkedinmcp

by vielhuber
README.md
# 💼linkedinmcp💼

one small php helper and mcp server for linkedin posting and your own member data. two apps, separate tokens, one tool set.

## php

```sh
composer require vielhuber/linkedinmcp
```

```php
use vielhuber\linkedinmcp\linkedinmcp;

$linkedin = new linkedinmcp();
$linkedin->profile();
$linkedin->snapshot(domain: 'PROFILE', start: 0);
$linkedin->posts(start: 0);
$linkedin->activity(startTime: $processedAt, count: 10);
$linkedin->memberAuthorization();
$linkedin->createPost(text: 'your approved text');
$linkedin->updatePost(postUrn: $postUrn, text: 'your approved replacement');
$linkedin->deletePost(postUrn: $postUrn);
$linkedin->uploadImage(filePath: '/absolute/path/to/image.png');
$linkedin->uploadVideo(filePath: '/absolute/path/to/video.mp4');
$linkedin->videoStatus(videoUrn: $mediaUrn);
```

two separate apps and tokens:

| app                                         | token environment variable      | scopes                           |
| ------------------------------------------- | ------------------------------- | -------------------------------- |
| posting: openid connect + share on linkedin | `LINKEDIN_POSTING_ACCESS_TOKEN` | `openid profile w_member_social` |
| member data portability api (member)        | `LINKEDIN_MEMBER_ACCESS_TOKEN`  | `r_dma_portability_self_serve`   |

member access is region-dependent and limited to the app administrator's own data. keep both apps' identities separate. api versions: posting `202610`, member `202312`; override with `LINKEDIN_POSTING_VERSION` / `LINKEDIN_MEMBER_VERSION`.

php oauth: one `Authorization(clientId: ..., clientSecret: ..., redirectUri: ...)` per app; `url($state, $scopes)`, verify state, `exchangeCode($code)`. refresh only with an issued refresh token. oauth helpers are not mcp tools.

- snapshots: page index `start`; no-data response is http 404. no home feed.
- activity: 28 days after consent; cursor `processedAt`, deduplicate `activityId`, poll at most hourly.
- uploads return `id`, not a post; videos must be `AVAILABLE`. image processing checks may require extra permissions.
- approve writes first. edits change commentary only; use exact share/ugcpost urns. never blindly retry uncertain writes.

## mcp

```json
{
    "mcpServers": {
        "linkedin": {
            "command": "php",
            "args": ["/absolute/path/to/vendor/bin/mcp-server.php"],
            "env": {
                "LINKEDIN_POSTING_ACCESS_TOKEN": "your-posting-access-token",
                "LINKEDIN_MEMBER_ACCESS_TOKEN": "your-member-access-token"
            }
        }
    }
}
```

11 tools; php methods become snake_case tool names. stdio is private. http requires https and `MCP_TOKEN`.

[posting api](https://learn.microsoft.com/en-us/linkedin/marketing/community-management/shares/posts-api) · [member portability api](https://learn.microsoft.com/en-us/linkedin/dma/member-data-portability/member-data-portability-member/) · [oauth](https://learn.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow)