Model Context Protocol (MCP) server for the LinkedIn Marketing API. Read campaigns, pull analytics, create campaign groups + campaigns, flip statuses — all from any MCP client (Claude Desktop, Cursor, Continue, or your own agent).
There's no official LinkedIn MCP. This fills the gap with a thin, correctness-first wrapper that handles LinkedIn's quirky restli encoding rules so you don't have to.
Maintained by Nuraveda Lab as source-available tooling alongside the Mesh Pilot agent suite. Free to use under FSL-1.1-MIT — converts to MIT after two years.
LinkedIn's Marketing API gate is the real hassle: you apply for the Advertising API product, wait for approval (days, not always granted), run an OAuth dance, and manage refresh-token rotation yourself.
Don't want any of that? Mesh Pilot runs this MCP for you behind an already-approved LinkedIn Marketing app. Connect your LinkedIn account in one click and you're driving your ad accounts from your AI client immediately — no API application, no OAuth setup, no token management.
| Self-host (this repo) | Mesh Pilot (hosted) | |
|---|---|---|
| LinkedIn Marketing API approval | you apply + wait | already approved |
| OAuth + token rotation | you manage | handled for you |
| Setup time | hours–days | one click |
| Cost | free (FSL-1.1-MIT) | see meshpilot.app |
| Runs in your own infra | ✅ | hosted |
Prefer to run it yourself? Keep reading — the full self-host path is below.
If you've tried calling LinkedIn's /rest/adAnalytics endpoint by hand
you've probably hit walls like:
- Commas in
fields=get URL-encoded by default HTTP clients →400 not present in schema - URN colons inside
accounts=List(urn:li:sponsoredAccount:NNN)need to be%3Abut date-tuple colons must stay literal - Partial updates need
X-RestLi-Method: PARTIAL_UPDATEor they get silently ignored runSchedule.startmust be ≥ now-ish,totalBudget.amountmust be ≥ $100- New campaigns need
politicalIntent(LinkedIn's EU political-ad declaration)
This server has all those rules already encoded.
pip install linkedin-ads-mcp
# or:
uv add linkedin-ads-mcpFrom source:
git clone https://github.com/Nuraveda-Labs/linkedin-ads-mcp.git
cd linkedin-ads-mcp
uv pip install -e . # or: pip install -e .On PyPI as
linkedin-ads-mcp. The prior package nameglitch-grow-linkedin-ad-mcp(v0.1.1) is legacy and frozen — uselinkedin-ads-mcpgoing forward.
-
Create a LinkedIn app at https://www.linkedin.com/developers/apps.
-
On the Products tab, request Advertising API (auto-approved if you have an active Campaign Manager account).
-
Run any OAuth flow that grants the scopes
r_ads,rw_ads,r_ads_reporting— for example:https://www.linkedin.com/oauth/v2/authorization?response_type=code &client_id=$YOUR_CLIENT_ID &redirect_uri=$YOUR_REDIRECT_URI &scope=r_ads%20rw_ads%20r_ads_reporting -
Exchange the code for tokens; save the access + refresh tokens.
-
Copy
.env.exampleto.envand paste them.
# stdio (Claude Desktop, Cursor, Continue, etc.)
linkedin-ads-mcp
# SSE on :8000
linkedin-ads-mcp --transport sse --port 8000{
"mcpServers": {
"linkedin-ads": {
"command": "linkedin-ads-mcp",
"env": {
"LINKEDIN_CLIENT_ID": "...",
"LINKEDIN_CLIENT_SECRET": "...",
"LINKEDIN_REFRESH_TOKEN": "..."
}
}
}
}| Tool | What it does |
|---|---|
list_ad_accounts() |
Every ad account the OAuth user can access |
list_account_users(account_id) |
User → role assignments |
list_campaign_groups(account_id) |
Campaign groups + total budgets |
list_campaigns(account_id) |
All campaigns + structure (no metrics) |
list_creatives(account_id) |
Creative roster |
get_account_analytics(account_id, days=14) |
Account-level totals |
get_campaign_analytics(account_id, days=14) |
Per-campaign metrics, sorted by spend |
| Tool | What it does |
|---|---|
create_campaign_group(account_id, name, total_budget=100, days=30, status="DRAFT") |
Create a group |
create_campaign(account_id, name, campaign_group_urn, daily_budget=10, …) |
Create a campaign (defaults to safe DRAFT TEXT_AD) |
update_campaign_status(account_id, campaign_id, status) |
DRAFT / ACTIVE / PAUSED / ARCHIVED |
update_campaign_group_status(account_id, group_id, status) |
Same set + CANCELED |
All write tools default to DRAFT so nothing goes live by accident.
Promote a group → ACTIVE first, then promote campaigns → PAUSED → ACTIVE
in two explicit steps.
LinkedIn has no MCC, but Campaign Manager has equivalent "Manage Access" sharing. To run this MCP across multiple advertisers:
- Each client adds your OAuth user as
CAMPAIGN_MANAGERon their ad account (Campaign Manager → Account Settings → Manage Access). - After they accept,
list_ad_accounts()returns their account. - Pass that
account_idto any tool call. One OAuth dance, N advertiser accounts — same model as the Google Ads MCC pattern.
Read API + write API for groups + campaigns are battle-tested in
production. Sponsored-creative creation (image/video upload via
initializeUpload → bind to /rest/creatives → attach to a campaign)
reuses a proven /rest/documents + /rest/posts upload pattern; porting
it to the sponsored-ad surface is on the roadmap. PRs welcome.
FSL-1.1-MIT (Functional Source License). Free for any use except building a competing product or service — including a competing LinkedIn-ads MCP, agent platform, or hosted offering that substitutes for this software or for Mesh Pilot.
Permitted: internal use, modification, redistribution, client/professional services, and non-commercial research/education. Each released version automatically converts to the MIT License two years after its release, so the restriction is time-boxed, not permanent.
Earlier
0.2.0(and the legacyglitch-grow-linkedin-ad-mcp0.1.1) were published under MIT and remain MIT — the FSL terms apply to0.3.0onward.
Built and maintained by Nuraveda Lab as source-available tooling in the Mesh Pilot growth suite. Hardened against real LinkedIn Marketing API behavior in production. If you hit a restli encoding edge case we missed, open an issue with the offending URL and we'll codify the fix.