loading…
Search for a command to run...
loading…
Enables users to read and analyze their own LinkedIn posts and shares using OAuth login and LinkedIn's Member Data Portability API. Supports post listing, analy
Enables users to read and analyze their own LinkedIn posts and shares using OAuth login and LinkedIn's Member Data Portability API. Supports post listing, analysis, draft matching, and engagement enrichment.
flin-linkedin-posts-mcp is a local MCP server for reading and analyzing the authenticated member's own LinkedIn post/share data.
It uses LinkedIn member OAuth login and the Member Data Portability API. Each user runs the MCP locally, signs into their own LinkedIn account in the system browser, and stores their token on their own machine.
The MCP supports two login flows:
This MCP does not bypass LinkedIn API approval. Each user should create their own LinkedIn Developer app with access to Member Data Portability API (Member) and the r_dma_portability_self_serve permission. Without that product/scope, LinkedIn returns 403 ACCESS_DENIED for memberSnapshotData.
Native PKCE is a separate LinkedIn app capability. If LinkedIn shows Not enough permissions to access Native PKCE protocol, use the regular 3-legged OAuth setup below or ask LinkedIn to enable Native PKCE for the app.
Relevant LinkedIn docs:
MEMBER_SHARE_INFOsocialMetadata and memberCreatorPostAnalyticsauth_statusloginlogoutlist_snapshot_domainslist_member_postsanalyze_member_postsmatch_drafts_to_member_postsget_post_social_metadataget_member_post_analyticsenrich_member_posts_with_engagementRequired for login:
LINKEDIN_CLIENT_ID: LinkedIn Developer app client IDRecommended for regular 3-legged OAuth:
LINKEDIN_CLIENT_SECRET: LinkedIn Developer app client secret. If set, LINKEDIN_OAUTH_FLOW defaults to authorization_code.LINKEDIN_REDIRECT_URI: exact local callback URI registered in the LinkedIn app, for example http://127.0.0.1:63141/callback.Optional:
LINKEDIN_OAUTH_FLOW: authorization_code or native_pkce. Defaults to authorization_code when LINKEDIN_CLIENT_SECRET is set, otherwise native_pkce.LINKEDIN_SCOPES: defaults to r_dma_portability_self_serveLINKEDIN_API_VERSION: defaults to 202312LINKEDIN_RESTLI_PROTOCOL_VERSION: defaults to 2.0.0LINKEDIN_TIMEOUT_SECONDS: defaults to 30LINKEDIN_MAX_RETRIES: defaults to 3LINKEDIN_OAUTH_TIMEOUT_SECONDS: defaults to 300LINKEDIN_TOKEN_FILE: defaults to ~/.flin-linkedin-posts-mcp/tokens.jsonAdditional LinkedIn access may be required for engagement enrichment:
socialMetadata access for comment and reaction summariesr_member_postAnalytics for member post impressions, reach, reactions, comments, and resharesThe MCP intentionally does not require LINKEDIN_ACCESS_TOKEN anymore. Tokens are created through the login tool.
Member Data Portability API (Member).r_dma_portability_self_serve.http://127.0.0.1:63141/callback.LINKEDIN_CLIENT_ID, LINKEDIN_CLIENT_SECRET, and LINKEDIN_REDIRECT_URI to the same redirect URL.LINKEDIN_CLIENT_SECRET, and configure loopback redirect URIs as LinkedIn requires.For a published package:
{
"mcpServers": {
"flin-linkedin-posts-mcp": {
"command": "uvx",
"args": ["--refresh", "flin-linkedin-posts-mcp@latest"],
"env": {
"LINKEDIN_CLIENT_ID": "<YOUR_LINKEDIN_CLIENT_ID>",
"LINKEDIN_CLIENT_SECRET": "<YOUR_LINKEDIN_CLIENT_SECRET>",
"LINKEDIN_REDIRECT_URI": "http://127.0.0.1:63141/callback",
"LINKEDIN_SCOPES": "r_dma_portability_self_serve",
"LINKEDIN_API_VERSION": "202312"
}
}
}
}
For local development from this repository:
{
"mcpServers": {
"flin-linkedin-posts-mcp": {
"command": "uv",
"args": ["run", "flin-linkedin-posts-mcp"],
"cwd": "/path/to/flin-linkedin-posts-mcp",
"env": {
"LINKEDIN_CLIENT_ID": "<YOUR_LINKEDIN_CLIENT_ID>",
"LINKEDIN_CLIENT_SECRET": "<YOUR_LINKEDIN_CLIENT_SECRET>",
"LINKEDIN_REDIRECT_URI": "http://127.0.0.1:63141/callback",
"LINKEDIN_SCOPES": "r_dma_portability_self_serve",
"LINKEDIN_API_VERSION": "202312"
}
}
}
}
After adding the config, restart the MCP host and call:
auth_statusloginlist_snapshot_domainslist_member_posts or analyze_member_postsmatch_drafts_to_member_posts if you want to compare draft text to published postsget_post_social_metadata, get_member_post_analytics, or enrich_member_posts_with_engagement if your LinkedIn app has the required engagement scopespython3 -m pip install -e '.[dev]'
pytest -q
ruff check .
python3 -m build
The package entry point is:
flin-linkedin-posts-mcp
LINKEDIN_CLIENT_ID is required before running login: set LINKEDIN_CLIENT_ID in the MCP config.Not enough permissions to access Native PKCE protocol: the LinkedIn app does not have Native PKCE enabled. Set LINKEDIN_CLIENT_SECRET and LINKEDIN_REDIRECT_URI to use regular 3-legged OAuth, or ask LinkedIn to enable Native PKCE for the app.LINKEDIN_REDIRECT_URI is required when LINKEDIN_OAUTH_FLOW=authorization_code: add the same exact local callback URL to the LinkedIn app's Auth tab and to the MCP config.403 ACCESS_DENIED for partnerApiMemberSnapshotData: the LinkedIn Developer app/token likely does not have Member Data Portability API access or r_dma_portability_self_serve.LinkedIn token has expired: run login again. If LinkedIn issued a refresh token, the MCP attempts a refresh automatically before requiring login.Timed out waiting for LinkedIn OAuth callback: rerun login and complete the browser flow within LINKEDIN_OAUTH_TIMEOUT_SECONDS.403 ACCESS_DENIED on socialMetadata or memberCreatorPostAnalytics: the LinkedIn app/token does not have the extra engagement permissions needed for those tools.MEMBER_SHARE_INFO is snapshot/export-style data, so field names can vary. The normalizer is intentionally tolerant and keeps include_raw=true available for debugging.Shares is documented around fields like date, link, commentary, media URL, and visibility. Likes, comments, and impressions are exposed only if they appear in the snapshot payload returned for that member.match_drafts_to_member_posts compares draft texts you already have against published posts; it does not fetch drafts from LinkedIn.analyze_member_posts can return very large payloads when include_posts=true. Use post_limit to cap the embedded posts list, or set include_posts=false when you only need aggregate metrics.enrich_member_posts_with_engagement keeps export discovery and engagement lookup separate. It fetches exported posts first, derives post URNs, then merges in socialMetadata and analytics where available.limit to keep the number of analytics calls under control and expect per-post engagement_errors when a URN cannot be derived or LinkedIn denies a lookup.Run in your terminal:
claude mcp add flin-linkedin-posts-mcp -- npx Security
Low riskAutomated heuristic from public metadata — not a security guarantee.