LinkedIn Ads MCP — MCP server for the LinkedIn Marketing API. Read + write campaigns, analytics, and creatives from any MCP client. Maintained by Nuraveda Lab.
Project description
LinkedIn Ads MCP
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.
⚡ Skip the setup — connect LinkedIn through Mesh Pilot
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.
Why this exists
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.
Install
pip install linkedin-ads-mcp
# or:
uv add linkedin-ads-mcp
From 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.
OAuth setup (self-host path)
-
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.
Run
# stdio (Claude Desktop, Cursor, Continue, etc.)
linkedin-ads-mcp
# SSE on :8000
linkedin-ads-mcp --transport sse --port 8000
Claude Desktop config
{
"mcpServers": {
"linkedin-ads": {
"command": "linkedin-ads-mcp",
"env": {
"LINKEDIN_CLIENT_ID": "...",
"LINKEDIN_CLIENT_SECRET": "...",
"LINKEDIN_REFRESH_TOKEN": "..."
}
}
}
}
Tools
Read
| 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 |
Write
| 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.
Multi-tenant pattern
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.
Status
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.
License
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.
About
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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file linkedin_ads_mcp-0.3.0.tar.gz.
File metadata
- Download URL: linkedin_ads_mcp-0.3.0.tar.gz
- Upload date:
- Size: 14.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6bc427620b2c4834701addce68c16826a2455659956b9d7f998a3d3948e2d1d8
|
|
| MD5 |
d70f62a6b3300144cafc385e4cab1c55
|
|
| BLAKE2b-256 |
19cbc80b90bb03bc59f9e988965ac51301a208cd2c9c0cd0d483ee4f2193ed47
|
Provenance
The following attestation bundles were made for linkedin_ads_mcp-0.3.0.tar.gz:
Publisher:
release.yml on Nuraveda-Labs/linkedin-ads-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
linkedin_ads_mcp-0.3.0.tar.gz -
Subject digest:
6bc427620b2c4834701addce68c16826a2455659956b9d7f998a3d3948e2d1d8 - Sigstore transparency entry: 1668472606
- Sigstore integration time:
-
Permalink:
Nuraveda-Labs/linkedin-ads-mcp@6d49415c1fe830856287772192af9a5e739db3fd -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/Nuraveda-Labs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6d49415c1fe830856287772192af9a5e739db3fd -
Trigger Event:
push
-
Statement type:
File details
Details for the file linkedin_ads_mcp-0.3.0-py3-none-any.whl.
File metadata
- Download URL: linkedin_ads_mcp-0.3.0-py3-none-any.whl
- Upload date:
- Size: 14.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e1a3478ed9912264c9a3c477f2496013612c016b8d36e33f9e02a8d1d9904510
|
|
| MD5 |
4f8a54ace32250de79a24ecb9f128e2f
|
|
| BLAKE2b-256 |
e8510266e918216dc0407b7e993698808c6412a3fb0ab23192ee196c056adfe8
|
Provenance
The following attestation bundles were made for linkedin_ads_mcp-0.3.0-py3-none-any.whl:
Publisher:
release.yml on Nuraveda-Labs/linkedin-ads-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
linkedin_ads_mcp-0.3.0-py3-none-any.whl -
Subject digest:
e1a3478ed9912264c9a3c477f2496013612c016b8d36e33f9e02a8d1d9904510 - Sigstore transparency entry: 1668472797
- Sigstore integration time:
-
Permalink:
Nuraveda-Labs/linkedin-ads-mcp@6d49415c1fe830856287772192af9a5e739db3fd -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/Nuraveda-Labs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6d49415c1fe830856287772192af9a5e739db3fd -
Trigger Event:
push
-
Statement type: