Are you the author? Sign in to claim
flin-linkedin-ads-mcp
flin-linkedin-ads-mcp ist ein strikt read-only MCP Server für LinkedIn Ads.
Er ist dafür gebaut, direkt in Claude über MCP eingebunden und getestet zu werden.
get_share_content, best effort für Bild-URLs)/accountIntelligence, private API Access nötig)v0.1.xv0.1.x (strict read-only)In Claude:
Settings öffnenDeveloper öffnen{
"mcpServers": {
"flin-linkedin-ads-mcp-local": {
"command": "uv",
"args": [
"run",
"--directory",
"/Users/nicolasg/Antigravity/flin-linkedin-ads-mcp",
"flin-linkedin-ads-mcp"
],
"env": {
"LINKEDIN_ACCESS_TOKEN": "AQX...",
"LINKEDIN_API_VERSION": "202603",
"LINKEDIN_RESTLI_PROTOCOL_VERSION": "2.0.0"
}
}
}
}
Danach Claude neu starten.
uvx (wenn Paket publiziert ist){
"mcpServers": {
"flin-linkedin-ads-mcp": {
"command": "uvx",
"args": ["--refresh", "flin-linkedin-ads-mcp"],
"env": {
"LINKEDIN_ACCESS_TOKEN": "AQX...",
"LINKEDIN_API_VERSION": "202603",
"LINKEDIN_RESTLI_PROTOCOL_VERSION": "2.0.0"
}
}
}
}
Nach dem Restart in Claude diese Calls testen:
list_ad_accountslist_campaignsget_insights mit pivot=campaignWenn list_campaigns eine Auswahl zurückgibt, den Call mit einem vorgeschlagenen ad_account_id wiederholen.
Hinweis zu get_insights:
analytics Finder mit Single-Pivot.view=li-lms-2026-03 ausgerichtet.date_from ist Pflicht (LinkedIn adAnalytics erwartet dateRange).fields folgen der Metrics-Tabelle aus der offiziellen Reporting-Schema-Doku (max. 20 Felder pro Request).pivot.value / timeGranularity.value und hat zusätzlich einen Fallback auf Legacy-Parameternamen für bessere API-Kompatibilität.videoWatchTime, averageVideoWatchTime, eventViews, eventWatchTime, averageEventWatchTime.Beispiel für einen stabilen Test-Call:
{
"ad_account_id": "508834004",
"date_from": "2025-08-01",
"date_to": "2025-12-31",
"fields": [
"impressions",
"clicks",
"costInLocalCurrency",
"dateRange"
],
"pivot": "account",
"time_granularity": "MONTHLY"
}
get_insights Parameter (2026-03)Wichtigste Parameter:
pivot: z. B. account, campaign_group, campaign, creative, member_company_size, member_industry, member_seniority, member_job_title, member_job_function, member_country_v2, member_region_v2, member_company, member_county, share, company, conversiontime_granularity: DAILY, MONTHLY, ALL, YEARLYfields: Liste aus der offiziellen Metrics-Tabelle, maximal 20 Einträge, case-sensitive
pivotValue → pivotValues, clickThroughRate (berechnet als clicks / impressions), costPerClick (berechnet als costInLocalCurrency / clicks)date_from: YYYY-MM-DD (Pflicht)date_to: optional, YYYY-MM-DDFacets/Filter:
ad_account_id (ein Konto) oder account_ids (mehrere Konten)campaign_ids, campaign_group_ids, creative_ids, share_ids, company_idscampaign_type, objective_typesort_by_field + sort_order (müssen zusammen angegeben werden)Kompatibilität:
entity_ids bleibt für Backward-Kompatibilität erhalten (z. B. bei pivot=campaign).list_creatives / get_creative Bild-URL (optional)Für Creative-Calls kann optional das Derived-Field imageUrl in fields angefordert werden.
imageUrl wird bevorzugt direkt aus dem Creative-content extrahiert.content.reference auf urn:li:adDirectSponsoredContent:* zeigt, versucht der MCP zusätzlich eine Auflösung über adDirectSponsoredContents/{urn}.imageUrl null.Beispiel:
{
"ad_account_id": "508834004",
"id": "urn:li:sponsoredCreative:935973186",
"fields": ["id", "name", "imageUrl"]
}
list_account_intelligence (202603)Neues Tool:
list_account_intelligenceDieser Endpunkt nutzt GET /rest/accountIntelligence?q=account und ist laut LinkedIn private API (zusätzliche Freischaltung erforderlich).
Wichtige Parameter:
ad_account_id (oder Auto-Resolve bei genau einem Account)lookback_window: LAST_7_DAYS, LAST_30_DAYS, LAST_60_DAYS, LAST_90_DAYSad_segment_ids, campaign_idskip_company_decorationpage_start, page_size (max. 1000)Wichtige Response-Felder:
companyName, engagementLevelpaidImpressions, paidClicks, paidEngagements, paidLeadspaidQualifiedLeads, conversions (ab API-Version 202603)organicImpressions, organicEngagementsBeispiel:
{
"ad_account_id": "508834004",
"lookback_window": "LAST_30_DAYS",
"page_size": 100
}
get_share_content (best effort)Neues Tool:
get_share_contentWichtige Parameter:
share_urn (Pflicht, Format urn:li:share:<id>)include_raw (optional, true gibt zusätzlich das rohe API-Payload zurück)Response enthält u. a.:
share_urn, source_endpoint (shares oder posts)post_url (LinkedIn Feed URL)textimage_url (erstes gefundenes Bild oder null)image_urls, thumbnail_urlsBeispiel:
{
"share_urn": "urn:li:share:7379073146093568000",
"include_raw": false
}
get_insightsBei ILLEGAL_ARGUMENT oder RESOURCE_NOT_FOUND bitte prüfen:
clicks statt click, impressions statt impression).fields.ad_account_id/account_ids oder andere Facets).share_ids und company_ids als URN).YYYY-MM-DD.date_from ist gesetzt (ohne dateRange antwortet LinkedIn oft mit ILLEGAL_ARGUMENT).pivot ist einer der dokumentierten Werte (siehe oben).Schneller API-Gegencheck (ohne MCP) für das Token:
curl -i 'https://api.linkedin.com/rest/adAccounts?q=search&pageSize=1' \
-H 'Authorization: Bearer DEIN_ACCESS_TOKEN' \
-H 'Linkedin-Version: 202603' \
-H 'X-Restli-Protocol-Version: 2.0.0'
Voraussetzung:
r_ads für Entities (Accounts/Campaigns/Creatives)r_ads_reporting für get_insightsProducts -> View Ad Accounts gemapptVIEWER)Auth:
Client ID und Client Secret notierenhttp://localhost:9876/callbackr_ads + r_ads_reporting)Im Browser öffnen (Werte ersetzen, redirect_uri URL-encoden).
Für diesen MCP müssen im Scope mindestens enthalten sein:
r_ads (Accounts/Campaigns/Creatives)r_ads_reporting (get_insights)Optional (nur wenn deine App dafür freigeschaltet ist):
offline_access (für Refresh-Token-Flow)Empfohlene URL für den MCP (ohne Refresh-Token):
https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=DEIN_CLIENT_ID&redirect_uri=http%3A%2F%2Flocalhost%3A9876%2Fcallback&scope=r_ads%20r_ads_reporting&state=dein_csrf_state
Variante mit optionalem offline_access:
https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=DEIN_CLIENT_ID&redirect_uri=http%3A%2F%2Flocalhost%3A9876%2Fcallback&scope=r_ads%20r_ads_reporting%20offline_access&state=dein_csrf_state
Nach Login/Consent leitet LinkedIn auf deine Redirect-URL zurück:
http://localhost:9876/callback?code=AUTH_CODE&state=dein_csrf_state
Den code aus der URL kopieren.
curl -X POST 'https://www.linkedin.com/oauth/v2/accessToken' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'code=AUTH_CODE' \
--data-urlencode 'redirect_uri=http://localhost:9876/callback' \
--data-urlencode 'client_id=DEIN_CLIENT_ID' \
--data-urlencode 'client_secret=DEIN_CLIENT_SECRET'
Beispiel-Response:
{
"access_token": "AQX...",
"expires_in": 5183999
}
access_token als LINKEDIN_ACCESS_TOKEN in die Claude MCP Config übernehmen.
Wichtig: Bei diesem MCP erfolgt Authentifizierung über env in der MCP Config.
Es gibt hier keinen eingebauten OAuth-Refresh-Flow im Server selbst.
Wenn der Token abläuft, musst du einen neuen Token erzeugen und in der Config ersetzen.
Token gegen Ads-Account-Endpunkt testen:
curl -i 'https://api.linkedin.com/rest/adAccounts?q=search&pageSize=1' \
-H 'Authorization: Bearer DEIN_ACCESS_TOKEN' \
-H 'Linkedin-Version: 202603' \
-H 'X-Restli-Protocol-Version: 2.0.0'
Interpretation:
401 Unauthorized: Token abgelaufen, widerrufen oder falscher Scope-Set wurde neu konsentiert403 Forbidden: Token ist gültig, aber Scope/Rolle/App-Mapping fehlt200 OK: Auth grundsätzlich korrektPflicht:
LINKEDIN_ACCESS_TOKENOptional:
LINKEDIN_API_VERSION (Default: 202603)LINKEDIN_RESTLI_PROTOCOL_VERSION (Default: 2.0.0)LINKEDIN_TIMEOUT_SECONDS (Default: 30)LINKEDIN_MAX_RETRIES (Default: 3)cd /Users/nicolasg/Antigravity/flin-linkedin-ads-mcp
python -m pip install -e ".[dev]"
pytest -q
ruff check .
mypy src
LINKEDIN_ACCESS_TOKEN oder OAuth Secrets ins Repo committen.env ist bereits in .gitignoreenv in Claude oder über lokale Shell-Umgebung setzengit status
git diff -- .env
Wenn eine Datei mit Secrets auftaucht: Commit abbrechen und Secrets rotieren.
Dieses Repo enthält:
.github/workflows/ci.yml.github/workflows/release.ymlDer Release-Workflow macht bei v* Tags:
twine checkdist/* ArtefaktenNode-20-Deprecation-Warnungen sind abgefangen durch:
checkout@v6, setup-python@v6)FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true in beiden Workflowsflin-linkedin-ads-mcpflin-agencyflin-linkedin-ads-mcprelease.ymlpypigit add -A
git commit -m "release: v0.1.0"
git tag v0.1.0
git push origin main --tags
Run Claude Code as an MCP server so any agent can delegate coding tasks to it
Browser automation using accessibility snapshots instead of screenshots
Google's universal MCP server supporting PostgreSQL, MySQL, MongoDB, Redis, and 10+ databases
Official GitHub integration for repos, issues, PRs, and CI/CD workflows