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
-
Create an app at LinkedIn Developers with access to the Advertising API product (requires LinkedIn's approval — see their docs, no published SLA).
-
Add
http://localhost:3000/callback(or your ownLINKEDIN_REDIRECT_URI) to the app's authorized redirect URLs. -
Set the following environment variables (e.g. in a
.envfile 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
-
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.jsonby default and refreshed automatically when a refresh token is available. -
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 callsget_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 legacyMEMBER_COUNTRYandMEMBER_REGIONnames 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, andOBJECTIVE_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, orconversionValueInLocalCurrency. IMPRESSION_DEVICE_TYPEdoes not support reach or conversion metrics.audiencePenetrationneedsACCOUNT,CAMPAIGN_GROUP, orCAMPAIGNamong the dimensions.CONVERSATION_NODE_OPTION_INDEXmust be requested together withCONVERSATION_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 serverauth/token_store.py— persistent token storage with automatic refreshapi/client.py—LinkedInClient, async wrapper around the LinkedIn Marketing REST APIapi/dimensions.py— supported dimensions, finder routing, and metric compatibility rulesapi/types.py— Pydantic models for API entities and responsescontext.py—ServerContextsingleton holding the active client and name→ID cachetools/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)
| 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)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| 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 logRelease 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 logSecurity 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.
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 logA 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.