A Model Context Protocol (MCP) server for the LinkedIn Marketing API.
This server provides read and write access to LinkedIn Ads resources with guardrails.
- Go 1.25+
- LinkedIn Advertising API approval
- A LinkedIn OAuth access token with appropriate scopes
LinkedIn requires approval before you can use the Advertising API. This is a one-time process:
- Go to LinkedIn Developer Portal and create an app (or select an existing one)
- In the Settings tab, associate a LinkedIn Company Page
- Verify the company — send the verification URL to a Page Admin to approve
- In the Products tab, click Request access on Advertising API
- Fill out the request form with your business justification
- Wait for approval (typically 20 minuts after fill the form)
Without this approval, OAuth will fail with invalid_scope_error.
Once approved, the following scopes are available:
| Scope | Access | Required |
|---|---|---|
r_ads |
Read ad accounts and campaigns | Yes |
r_ads_reporting |
Read analytics and reports | Yes |
rw_ads |
Create and manage campaigns | For write operations |
r_organization_social |
Read organization posts | For creative content |
Read-only tools (default):
- Ad accounts list and details
- Campaigns and campaign groups list
- Creatives list
- Campaign, creative, and account analytics
- Demographic breakdowns
- Conversion tracking rules
Write tools (opt-in):
- Create, update, and delete campaign groups
- Create, update, and delete campaigns
- Create creatives
- Update creative status
go build -o mcp-linkedin-ads ./cmd/mcp-linkedin-ads
claude mcp add linkedin-ads \
--env LINKEDIN_ACCESS_TOKEN=your_token \
-- ./mcp-linkedin-ads{
"mcpServers": {
"linkedin-ads": {
"command": "/path/to/mcp-linkedin-ads",
"env": {
"LINKEDIN_ACCESS_TOKEN": "your_token"
}
}
}
}docker build -t mcp-linkedin-ads .
docker run -d -p 8001:8001 \
-e LINKEDIN_ACCESS_TOKEN=your_token \
mcp-linkedin-ads -t streamable-http --address 0.0.0.0:8001The server exposes .well-known endpoints so OAuth-capable clients can handle authentication automatically:
GET /.well-known/oauth-protected-resourceGET /.well-known/oauth-authorization-server
MCP clients like Ironclaw can use these to run the OAuth flow:
ironclaw mcp add linkedin-ads http://localhost:8001/mcp \
--client-id YOUR_CLIENT_ID \
--scopes "r_ads,r_ads_reporting"
ironclaw mcp auth linkedin-ads| Variable | Required | Description |
|---|---|---|
LINKEDIN_ACCESS_TOKEN |
Yes* | OAuth2 Bearer token |
LINKEDIN_API_BASE |
No | API base URL (default: https://api.linkedin.com) |
LINKEDIN_ALLOW_WRITE |
No | Enable write operations (default: false) |
LINKEDIN_ALLOWED_TOOLS |
No | Comma-separated tool allowlist |
LINKEDIN_TIMEOUT_MS |
No | Request timeout in ms (default: 30000) |
LINKEDIN_DEBUG |
No | Enable debug behavior (default: false) |
LINKEDIN_RATE_LIMIT_DISABLE |
No | Disable built-in rate limiting (default: false) |
LINKEDIN_MCP_URL |
No | Server URL for .well-known responses |
* Not required if the MCP client injects the token via the Authorization: Bearer header.
Optional per-request headers:
Authorization: Bearer <token>X-LinkedIn-URL
By default the client enforces conservative LinkedIn request limits:
- 10 requests/second burst
- 100 requests/minute
Disable with LINKEDIN_RATE_LIMIT_DISABLE=true.
| Tool | Description |
|---|---|
linkedin.accounts.list |
List all accessible ad accounts |
linkedin.accounts.get |
Get details for a specific ad account |
linkedin.campaigns.list |
List campaigns with pagination |
linkedin.campaign_groups.list |
List campaign groups |
linkedin.creatives.list |
List creatives with pagination |
linkedin.analytics.campaign_performance |
Campaign-level metrics (impressions, clicks, CTR, CPC, CPM, etc.) |
linkedin.analytics.creative_performance |
Creative-level metrics with engagement and video stats |
linkedin.analytics.account_performance |
Account-level aggregated metrics |
linkedin.demographics.by_function |
Breakdown by job function |
linkedin.demographics.by_seniority |
Breakdown by seniority level |
linkedin.demographics.by_industry |
Breakdown by industry |
linkedin.demographics.by_company_size |
Breakdown by company size |
linkedin.demographics.by_country |
Breakdown by country |
linkedin.conversions.list |
List conversion tracking rules |
| Tool | Description |
|---|---|
linkedin.campaign_groups.create |
Create a campaign group |
linkedin.campaign_groups.update |
Update group name or status |
linkedin.campaign_groups.delete |
Delete (DRAFT) or archive a group |
linkedin.campaigns.create |
Create a campaign |
linkedin.campaigns.update |
Update campaign name, status, or budget |
linkedin.campaigns.delete |
Delete (DRAFT) or archive a campaign |
linkedin.creatives.create |
Create a creative from a content reference |
linkedin.creatives.update_status |
Activate, pause, or archive a creative |
Analytics tools automatically calculate:
- CTR — Click-through rate (%)
- CPC — Cost per click
- CPM — Cost per 1,000 impressions
- Engagement Rate — Engagements / Impressions (%)
- Conversion Rate — Conversions / Clicks (%)
- Cost per Conversion — Spend / Conversions
| Flag | Transport | Use Case |
|---|---|---|
-t stdio |
Standard I/O (default) | Claude Code, Claude Desktop, Codex |
-t sse |
Server-Sent Events | Legacy HTTP clients |
-t streamable-http |
Streamable HTTP | Ironclaw, modern MCP clients |
# stdio (default)
./mcp-linkedin-ads
# HTTP
./mcp-linkedin-ads -t streamable-http --address 0.0.0.0:8001
# SSE
./mcp-linkedin-ads -t sse --address 0.0.0.0:8001 --base-path /Start the dev container:
task dev:upRun tests inside the container:
task dev:exec SV=mcp-linkedin-ads-dev CMD='go test ./...'Build and test locally:
go build ./...
go test ./...
go build -o mcp-linkedin-ads ./cmd/mcp-linkedin-ads- Write tools are disabled unless
LINKEDIN_ALLOW_WRITE=true. - List endpoints expose pagination via
startandcount, capped at 100. - Analytics and demographics require valid date ranges in
YYYY-MM-DDformat.
MIT