Skip to main content

linkedin-ads-mcp

MCP server for the LinkedIn Marketing API, with built-in MCC-style client switching across every ad account your LinkedIn login can access.

Installation

Once published to PyPI:

uv tool install linkedin-ads-mcp-py

This installs the linkedin-ads-mcp and linkedin-ads-auth commands. Or run it directly without installing:

uvx --from linkedin-ads-mcp-py linkedin-ads-mcp

Point your MCP client (e.g. .mcp.json or Claude Desktop's config) at the linkedin-ads-mcp command.

Setup

  1. Create an app at LinkedIn Developers with access to the Advertising API product (requires LinkedIn's approval — see their docs, no published SLA).

  2. Add http://localhost:3000/callback (or your own LINKEDIN_REDIRECT_URI) to the app's authorized redirect URLs.

  3. Set the following environment variables (e.g. in a .env file in your working directory, or directly in your MCP client's server config):

    LINKEDIN_CLIENT_ID=your_client_id
    LINKEDIN_CLIENT_SECRET=your_client_secret
    # Optional overrides:
    # LINKEDIN_REDIRECT_URI=http://localhost:3000/callback
    # LINKEDIN_TOKEN_PATH=/path/to/tokens.json
    
  4. Authenticate (opens a browser for the LinkedIn OAuth consent screen):

    uvx --from linkedin-ads-mcp-py linkedin-ads-auth
    

    Tokens are saved to ~/.linkedin-ads-mcp/tokens.json by default and refreshed automatically when a refresh token is available.

  5. Run the server:

    uvx --from linkedin-ads-mcp-py linkedin-ads-mcp
    

Headless / remote deployments

Hosts that can't open a browser or receive the local OAuth callback (e.g. MCP Anywhere) can't run linkedin-ads-auth themselves. Instead, run it once locally as above, then read the refresh_token value out of the saved token file (~/.linkedin-ads-mcp/tokens.json by default) and set it as LINKEDIN_REFRESH_TOKEN alongside LINKEDIN_CLIENT_ID/LINKEDIN_CLIENT_SECRET in that host's environment variables. On first tool call, the server exchanges it for a live access token automatically, no browser needed at runtime.

If the host requires a uvx-prefixed start command, use uvx --from linkedin-ads-mcp-py linkedin-ads-mcp — plain uvx linkedin-ads-mcp fetches an unrelated package.

If auth fails despite a valid token, double-check it pasted in full.

MCC / multi-account support

Unlike the original TypeScript linkedin-ads-mcp, this server exposes every ad account your LinkedIn login can access and lets you switch between them by name or ID:

  • list_clients — list all accessible ad accounts (call this first)
  • set_active_client — select a default account (by name or numeric ID) for subsequent calls
  • get_active_client — show which account is currently active

Every account-specific tool also accepts an optional account argument to target a client directly without changing the active selection.

Tools

Tool Notes
list_clients MCC view — call first
set_active_client select account by name or ID
get_active_client show current selection
get_account_details account metadata
list_campaign_groups
list_campaigns
get_campaign_performance report
get_campaign_groups_performance report
get_creative_performance report
get_audience_demographics demographic breakdown, top segments by impressions
list_report_dimensions catalogue of every dimension reports can use
get_dimension_report metrics by any dimension, or up to three crossed
get_audience_reach reach / frequency
compare_performance compare two time windows or entity sets
get_daily_trends daily time series
list_creatives
get_conversion_performance report
list_conversions
get_lead_gen_performance report
list_lead_forms
list_saved_audiences

Dimension reports

get_dimension_report reaches every dimension LinkedIn's adAnalytics endpoint offers. Call list_report_dimensions for the catalogue.

  • Demographic (one per report): MEMBER_COMPANY, MEMBER_COMPANY_SIZE, MEMBER_COUNTRY_V2, MEMBER_COUNTY, MEMBER_INDUSTRY, MEMBER_JOB_FUNCTION, MEMBER_JOB_TITLE, MEMBER_REGION_V2, MEMBER_SENIORITY. The legacy MEMBER_COUNTRY and MEMBER_REGION names map to their V2 equivalents.
  • Performance (up to three crossed): ACCOUNT, CAMPAIGN_GROUP, CAMPAIGN, CREATIVE, SHARE, COMPANY, CONVERSION, CONVERSATION_NODE, CONVERSATION_NODE_OPTION_INDEX, SERVING_LOCATION, CARD_INDEX, PLACEMENT_NAME, IMPRESSION_DEVICE_TYPE, EVENT_STAGE, and OBJECTIVE_TYPE (statistics finder only).

Rules the server enforces before calling LinkedIn, so a bad request fails fast with a clear message:

  • Demographic dimensions cannot be combined with any other dimension.
  • Demographic dimensions do not support approximateMemberReach, audiencePenetration, carousel card metrics, or conversionValueInLocalCurrency.
  • IMPRESSION_DEVICE_TYPE does not support reach or conversion metrics.
  • audiencePenetration needs ACCOUNT, CAMPAIGN_GROUP, or CAMPAIGN among the dimensions.
  • CONVERSATION_NODE_OPTION_INDEX must be requested together with CONVERSATION_NODE.
  • At most 20 metrics per request.

LinkedIn drops demographic values with fewer than 3 events, returns only the top 100 values per creative per day, and can lag 12 to 24 hours on demographic data. Responses repeat these caveats in a notes field. Rows carry URNs (for example urn:li:seniority:5) in pivotValues; MEMBER_COMPANY_SIZE returns values like SIZE_51_TO_200. Some breakdowns overlap: in a live check MEMBER_INDUSTRY rows summed to 2.6x the account total, so compare rows to each other rather than adding them up.

MEMBER_DESIGNATED_MARKET_AREA (Nielsen DMA) needs API version 202609 or later, so it is not exposed while the client targets 202601.

Architecture

  • auth/oauth.py — OAuth 2.0 authorization-code flow, browser + local callback server
  • auth/token_store.py — persistent token storage with automatic refresh
  • api/client.py — LinkedInClient, async wrapper around the LinkedIn Marketing REST API
  • api/dimensions.py — supported dimensions, finder routing, and metric compatibility rules
  • api/types.py — Pydantic models for API entities and responses
  • context.py — ServerContext singleton holding the active client and name→ID cache
  • tools/accounts.py — MCC tools (list_clients, set_active_client, get_active_client)
  • server.py — MCP stdio server, tool registration

Development

uv sync
uv run pre-commit install

uv run ruff format
uv run ruff check
uv run mypy src/
uv run pytest

pytest runs offline against a fake HTTP layer. The live suite pulls every dimension from the real LinkedIn API, read only, and skips itself when no valid login exists:

uv run linkedin-ads-auth                     # fresh login if your token was revoked
uv run pytest -m live -v
# LINKEDIN_TEST_ACCOUNT_ID=123456 to pin the ad account under test

Building and publishing

uv build

Publishing to PyPI is handled by .github/workflows/pypi-publish.yml via PyPI trusted publishing whenever a GitHub release is published — no API token needed once the trusted publisher is configured on the PyPI project.

License

MIT License - see LICENSE file for details.

Metadata

Release files for linkedin-ads-mcp-py 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for linkedin-ads-mcp-py 0.1.2
File Size Uploaded Provenance Actions
linkedin_ads_mcp_py-0.1.2.tar.gz 31.0 kB With provenance Details for linkedin_ads_mcp_py-0.1.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for linkedin-ads-mcp-py 0.1.2
File Interpreter ABI Platform Actions
linkedin_ads_mcp_py-0.1.2-py3-none-any.whl Python 3 none any Details for linkedin_ads_mcp_py-0.1.2-py3-none-any.whl

Total release size: 70.9 kB

Release files / linkedin_ads_mcp_py-0.1.2.tar.gz

Download URL linkedin_ads_mcp_py-0.1.2.tar.gz
Size 31.0 kB
Tags Source
SHA-256 checksum
How to use checksums
58cb58dd1d7bb487468b023db131140a03ad3d05fd451a1a9a4757ba6fa79663
BLAKE2b-256 checksum
How to use checksums
dc7fd0fab1ebd00f11671b49232bc2672cb10c9b80340aa5b34fae4681ceb490
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 5, 2026.

Transparency log

Release files / linkedin_ads_mcp_py-0.1.2-py3-none-any.whl

Download URL linkedin_ads_mcp_py-0.1.2-py3-none-any.whl
Size 39.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9bf8bfc27aab7928cef13199e91de1e39a8012a5bc7722998001fba90bae4e86
BLAKE2b-256 checksum
How to use checksums
33446263a706fadb638677637cd9422105d3a8de7daf5e7790eb182da3d3c9d3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 5, 2026.

Transparency log

Security for linkedin-ads-mcp-py 0.1.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for linkedin_ads_mcp_py-0.1.2.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 5, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page