linkedin-ads-mcp-py 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- linkedin_ads_mcp_py-0.1.0/PKG-INFO +148 -0
- linkedin_ads_mcp_py-0.1.0/README.md +127 -0
- linkedin_ads_mcp_py-0.1.0/pyproject.toml +72 -0
- linkedin_ads_mcp_py-0.1.0/src/linkedin_ads_mcp/__init__.py +1 -0
- linkedin_ads_mcp_py-0.1.0/src/linkedin_ads_mcp/api/__init__.py +1 -0
- linkedin_ads_mcp_py-0.1.0/src/linkedin_ads_mcp/api/client.py +787 -0
- linkedin_ads_mcp_py-0.1.0/src/linkedin_ads_mcp/api/types.py +252 -0
- linkedin_ads_mcp_py-0.1.0/src/linkedin_ads_mcp/auth/__init__.py +1 -0
- linkedin_ads_mcp_py-0.1.0/src/linkedin_ads_mcp/auth/oauth.py +219 -0
- linkedin_ads_mcp_py-0.1.0/src/linkedin_ads_mcp/auth/token_store.py +134 -0
- linkedin_ads_mcp_py-0.1.0/src/linkedin_ads_mcp/context.py +83 -0
- linkedin_ads_mcp_py-0.1.0/src/linkedin_ads_mcp/server.py +757 -0
- linkedin_ads_mcp_py-0.1.0/src/linkedin_ads_mcp/settings.py +42 -0
- linkedin_ads_mcp_py-0.1.0/src/linkedin_ads_mcp/tools/__init__.py +1 -0
- linkedin_ads_mcp_py-0.1.0/src/linkedin_ads_mcp/tools/accounts.py +176 -0
- linkedin_ads_mcp_py-0.1.0/src/linkedin_ads_mcp/tools/analytics.py +340 -0
- linkedin_ads_mcp_py-0.1.0/src/linkedin_ads_mcp/tools/campaigns.py +422 -0
- linkedin_ads_mcp_py-0.1.0/src/linkedin_ads_mcp/tools/conversions.py +169 -0
- linkedin_ads_mcp_py-0.1.0/src/linkedin_ads_mcp/tools/creatives.py +230 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: linkedin-ads-mcp-py
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: LinkedIn Ads MCP server with MCC client-switching support
|
|
5
|
+
Keywords: mcp,ai,tools,linkedin,ads
|
|
6
|
+
Author: LOCOMOTIVE Agency
|
|
7
|
+
Author-email: LOCOMOTIVE Agency <erick@locomotive.agency>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Requires-Dist: fastmcp>=0.1.0
|
|
15
|
+
Requires-Dist: httpx>=0.27.0
|
|
16
|
+
Requires-Dist: loguru>=0.7.3
|
|
17
|
+
Requires-Dist: pydantic>=2.11.7
|
|
18
|
+
Requires-Dist: pydantic-settings>=2.10.1
|
|
19
|
+
Requires-Python: >=3.12
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# linkedin-ads-mcp
|
|
23
|
+
|
|
24
|
+
MCP server for the LinkedIn Marketing API, with built-in MCC-style client switching across every
|
|
25
|
+
ad account your LinkedIn login can access.
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
Once published to PyPI:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
uv tool install linkedin-ads-mcp-py
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
This installs the `linkedin-ads-mcp` and `linkedin-ads-auth` commands. Or run it directly without
|
|
36
|
+
installing:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
uvx --from linkedin-ads-mcp-py linkedin-ads-mcp
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Point your MCP client (e.g. `.mcp.json` or Claude Desktop's config) at the `linkedin-ads-mcp`
|
|
43
|
+
command.
|
|
44
|
+
|
|
45
|
+
## Setup
|
|
46
|
+
|
|
47
|
+
1. Create an app at [LinkedIn Developers](https://www.linkedin.com/developers/apps) with access to
|
|
48
|
+
the **Advertising API** product (requires LinkedIn's approval — see their docs, no published
|
|
49
|
+
SLA).
|
|
50
|
+
2. Add `http://localhost:3000/callback` (or your own `LINKEDIN_REDIRECT_URI`) to the app's
|
|
51
|
+
authorized redirect URLs.
|
|
52
|
+
3. Set the following environment variables (e.g. in a `.env` file in your working directory, or
|
|
53
|
+
directly in your MCP client's server config):
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
LINKEDIN_CLIENT_ID=your_client_id
|
|
57
|
+
LINKEDIN_CLIENT_SECRET=your_client_secret
|
|
58
|
+
# Optional overrides:
|
|
59
|
+
# LINKEDIN_REDIRECT_URI=http://localhost:3000/callback
|
|
60
|
+
# LINKEDIN_TOKEN_PATH=/path/to/tokens.json
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
4. Authenticate (opens a browser for the LinkedIn OAuth consent screen):
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
uvx --from linkedin-ads-mcp-py linkedin-ads-auth
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Tokens are saved to `~/.linkedin-ads-mcp/tokens.json` by default and refreshed automatically
|
|
70
|
+
when a refresh token is available.
|
|
71
|
+
|
|
72
|
+
5. Run the server:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
uvx --from linkedin-ads-mcp-py linkedin-ads-mcp
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## MCC / multi-account support
|
|
79
|
+
|
|
80
|
+
Unlike the original TypeScript `linkedin-ads-mcp`, this server exposes every ad account your
|
|
81
|
+
LinkedIn login can access and lets you switch between them by name or ID:
|
|
82
|
+
|
|
83
|
+
- `list_clients` — list all accessible ad accounts (call this first)
|
|
84
|
+
- `set_active_client` — select a default account (by name or numeric ID) for subsequent calls
|
|
85
|
+
- `get_active_client` — show which account is currently active
|
|
86
|
+
|
|
87
|
+
Every account-specific tool also accepts an optional `account` argument to target a client
|
|
88
|
+
directly without changing the active selection.
|
|
89
|
+
|
|
90
|
+
## Tools
|
|
91
|
+
|
|
92
|
+
| Tool | Notes |
|
|
93
|
+
|---|---|
|
|
94
|
+
| `list_clients` | MCC view — call first |
|
|
95
|
+
| `set_active_client` | select account by name or ID |
|
|
96
|
+
| `get_active_client` | show current selection |
|
|
97
|
+
| `get_account_details` | account metadata |
|
|
98
|
+
| `list_campaign_groups` | |
|
|
99
|
+
| `list_campaigns` | |
|
|
100
|
+
| `get_campaign_performance` | report |
|
|
101
|
+
| `get_campaign_groups_performance` | report |
|
|
102
|
+
| `get_creative_performance` | report |
|
|
103
|
+
| `get_audience_demographics` | report |
|
|
104
|
+
| `get_audience_reach` | reach / frequency |
|
|
105
|
+
| `compare_performance` | compare two time windows or entity sets |
|
|
106
|
+
| `get_daily_trends` | daily time series |
|
|
107
|
+
| `list_creatives` | |
|
|
108
|
+
| `get_conversion_performance` | report |
|
|
109
|
+
| `list_conversions` | |
|
|
110
|
+
| `get_lead_gen_performance` | report |
|
|
111
|
+
| `list_lead_forms` | |
|
|
112
|
+
| `list_saved_audiences` | |
|
|
113
|
+
|
|
114
|
+
## Architecture
|
|
115
|
+
|
|
116
|
+
- `auth/oauth.py` — OAuth 2.0 authorization-code flow, browser + local callback server
|
|
117
|
+
- `auth/token_store.py` — persistent token storage with automatic refresh
|
|
118
|
+
- `api/client.py` — `LinkedInClient`, async wrapper around the LinkedIn Marketing REST API
|
|
119
|
+
- `api/types.py` — Pydantic models for API entities and responses
|
|
120
|
+
- `context.py` — `ServerContext` singleton holding the active client and name→ID cache
|
|
121
|
+
- `tools/accounts.py` — MCC tools (`list_clients`, `set_active_client`, `get_active_client`)
|
|
122
|
+
- `server.py` — MCP stdio server, tool registration
|
|
123
|
+
|
|
124
|
+
## Development
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
uv sync
|
|
128
|
+
uv run pre-commit install
|
|
129
|
+
|
|
130
|
+
uv run ruff format
|
|
131
|
+
uv run ruff check
|
|
132
|
+
uv run mypy src/
|
|
133
|
+
uv run pytest
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Building and publishing
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
uv build
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Publishing to PyPI is handled by `.github/workflows/pypi-publish.yml` via
|
|
143
|
+
[PyPI trusted publishing](https://docs.pypi.org/trusted-publishers/) whenever a GitHub release is
|
|
144
|
+
published — no API token needed once the trusted publisher is configured on the PyPI project.
|
|
145
|
+
|
|
146
|
+
## License
|
|
147
|
+
|
|
148
|
+
MIT License - see LICENSE file for details.
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# linkedin-ads-mcp
|
|
2
|
+
|
|
3
|
+
MCP server for the LinkedIn Marketing API, with built-in MCC-style client switching across every
|
|
4
|
+
ad account your LinkedIn login can access.
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
Once published to PyPI:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
uv tool install linkedin-ads-mcp-py
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
This installs the `linkedin-ads-mcp` and `linkedin-ads-auth` commands. Or run it directly without
|
|
15
|
+
installing:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
uvx --from linkedin-ads-mcp-py linkedin-ads-mcp
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Point your MCP client (e.g. `.mcp.json` or Claude Desktop's config) at the `linkedin-ads-mcp`
|
|
22
|
+
command.
|
|
23
|
+
|
|
24
|
+
## Setup
|
|
25
|
+
|
|
26
|
+
1. Create an app at [LinkedIn Developers](https://www.linkedin.com/developers/apps) with access to
|
|
27
|
+
the **Advertising API** product (requires LinkedIn's approval — see their docs, no published
|
|
28
|
+
SLA).
|
|
29
|
+
2. Add `http://localhost:3000/callback` (or your own `LINKEDIN_REDIRECT_URI`) to the app's
|
|
30
|
+
authorized redirect URLs.
|
|
31
|
+
3. Set the following environment variables (e.g. in a `.env` file in your working directory, or
|
|
32
|
+
directly in your MCP client's server config):
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
LINKEDIN_CLIENT_ID=your_client_id
|
|
36
|
+
LINKEDIN_CLIENT_SECRET=your_client_secret
|
|
37
|
+
# Optional overrides:
|
|
38
|
+
# LINKEDIN_REDIRECT_URI=http://localhost:3000/callback
|
|
39
|
+
# LINKEDIN_TOKEN_PATH=/path/to/tokens.json
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
4. Authenticate (opens a browser for the LinkedIn OAuth consent screen):
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
uvx --from linkedin-ads-mcp-py linkedin-ads-auth
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Tokens are saved to `~/.linkedin-ads-mcp/tokens.json` by default and refreshed automatically
|
|
49
|
+
when a refresh token is available.
|
|
50
|
+
|
|
51
|
+
5. Run the server:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
uvx --from linkedin-ads-mcp-py linkedin-ads-mcp
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## MCC / multi-account support
|
|
58
|
+
|
|
59
|
+
Unlike the original TypeScript `linkedin-ads-mcp`, this server exposes every ad account your
|
|
60
|
+
LinkedIn login can access and lets you switch between them by name or ID:
|
|
61
|
+
|
|
62
|
+
- `list_clients` — list all accessible ad accounts (call this first)
|
|
63
|
+
- `set_active_client` — select a default account (by name or numeric ID) for subsequent calls
|
|
64
|
+
- `get_active_client` — show which account is currently active
|
|
65
|
+
|
|
66
|
+
Every account-specific tool also accepts an optional `account` argument to target a client
|
|
67
|
+
directly without changing the active selection.
|
|
68
|
+
|
|
69
|
+
## Tools
|
|
70
|
+
|
|
71
|
+
| Tool | Notes |
|
|
72
|
+
|---|---|
|
|
73
|
+
| `list_clients` | MCC view — call first |
|
|
74
|
+
| `set_active_client` | select account by name or ID |
|
|
75
|
+
| `get_active_client` | show current selection |
|
|
76
|
+
| `get_account_details` | account metadata |
|
|
77
|
+
| `list_campaign_groups` | |
|
|
78
|
+
| `list_campaigns` | |
|
|
79
|
+
| `get_campaign_performance` | report |
|
|
80
|
+
| `get_campaign_groups_performance` | report |
|
|
81
|
+
| `get_creative_performance` | report |
|
|
82
|
+
| `get_audience_demographics` | report |
|
|
83
|
+
| `get_audience_reach` | reach / frequency |
|
|
84
|
+
| `compare_performance` | compare two time windows or entity sets |
|
|
85
|
+
| `get_daily_trends` | daily time series |
|
|
86
|
+
| `list_creatives` | |
|
|
87
|
+
| `get_conversion_performance` | report |
|
|
88
|
+
| `list_conversions` | |
|
|
89
|
+
| `get_lead_gen_performance` | report |
|
|
90
|
+
| `list_lead_forms` | |
|
|
91
|
+
| `list_saved_audiences` | |
|
|
92
|
+
|
|
93
|
+
## Architecture
|
|
94
|
+
|
|
95
|
+
- `auth/oauth.py` — OAuth 2.0 authorization-code flow, browser + local callback server
|
|
96
|
+
- `auth/token_store.py` — persistent token storage with automatic refresh
|
|
97
|
+
- `api/client.py` — `LinkedInClient`, async wrapper around the LinkedIn Marketing REST API
|
|
98
|
+
- `api/types.py` — Pydantic models for API entities and responses
|
|
99
|
+
- `context.py` — `ServerContext` singleton holding the active client and name→ID cache
|
|
100
|
+
- `tools/accounts.py` — MCC tools (`list_clients`, `set_active_client`, `get_active_client`)
|
|
101
|
+
- `server.py` — MCP stdio server, tool registration
|
|
102
|
+
|
|
103
|
+
## Development
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
uv sync
|
|
107
|
+
uv run pre-commit install
|
|
108
|
+
|
|
109
|
+
uv run ruff format
|
|
110
|
+
uv run ruff check
|
|
111
|
+
uv run mypy src/
|
|
112
|
+
uv run pytest
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Building and publishing
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
uv build
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Publishing to PyPI is handled by `.github/workflows/pypi-publish.yml` via
|
|
122
|
+
[PyPI trusted publishing](https://docs.pypi.org/trusted-publishers/) whenever a GitHub release is
|
|
123
|
+
published — no API token needed once the trusted publisher is configured on the PyPI project.
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
MIT License - see LICENSE file for details.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "linkedin-ads-mcp-py"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "LinkedIn Ads MCP server with MCC client-switching support"
|
|
5
|
+
authors = [{ name = "LOCOMOTIVE Agency", email = "erick@locomotive.agency" }]
|
|
6
|
+
license = "MIT"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
keywords = ["mcp", "ai", "tools", "linkedin", "ads"]
|
|
9
|
+
classifiers = [
|
|
10
|
+
"Development Status :: 4 - Beta",
|
|
11
|
+
"Intended Audience :: Developers",
|
|
12
|
+
"License :: OSI Approved :: MIT License",
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"Programming Language :: Python :: 3.12",
|
|
15
|
+
]
|
|
16
|
+
requires-python = ">= 3.12"
|
|
17
|
+
dependencies = [
|
|
18
|
+
"fastmcp>=0.1.0",
|
|
19
|
+
"httpx>=0.27.0",
|
|
20
|
+
"loguru>=0.7.3",
|
|
21
|
+
"pydantic>=2.11.7",
|
|
22
|
+
"pydantic-settings>=2.10.1",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[dependency-groups]
|
|
26
|
+
dev = [
|
|
27
|
+
"mypy>=1.15.0",
|
|
28
|
+
"pre-commit>=4.1.0",
|
|
29
|
+
"pytest>=8.3.4",
|
|
30
|
+
"pytest-asyncio>=0.25.3",
|
|
31
|
+
"ruff>=0.9.7",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.scripts]
|
|
35
|
+
linkedin-ads-mcp = "linkedin_ads_mcp.server:main"
|
|
36
|
+
linkedin-ads-auth = "linkedin_ads_mcp.auth.oauth:main"
|
|
37
|
+
|
|
38
|
+
[tool.uv.build-backend]
|
|
39
|
+
module-name = "linkedin_ads_mcp"
|
|
40
|
+
|
|
41
|
+
[build-system]
|
|
42
|
+
requires = ["uv_build>=0.8.11,<0.9.0"]
|
|
43
|
+
build-backend = "uv_build"
|
|
44
|
+
|
|
45
|
+
[tool.pytest.ini_options]
|
|
46
|
+
pythonpath = ["src"]
|
|
47
|
+
asyncio_mode = "auto"
|
|
48
|
+
|
|
49
|
+
[tool.ruff]
|
|
50
|
+
line-length = 88
|
|
51
|
+
src = ["src"]
|
|
52
|
+
output-format = "concise"
|
|
53
|
+
|
|
54
|
+
[tool.ruff.lint]
|
|
55
|
+
select = ["E", "F", "I", "B", "N", "ANN", "TRY", "PD", "RUF200", "UP", "G"]
|
|
56
|
+
ignore = ["E501", "ANN003", "ANN401", "TRY003", "TRY301", "TRY300"]
|
|
57
|
+
|
|
58
|
+
[tool.ruff.lint.extend-per-file-ignores]
|
|
59
|
+
"__init__.py" = ["F401", "F403", "E402"]
|
|
60
|
+
"tests/**/*.py" = ["D", "ANN"]
|
|
61
|
+
|
|
62
|
+
[tool.ruff.lint.pydocstyle]
|
|
63
|
+
convention = "google"
|
|
64
|
+
|
|
65
|
+
[tool.ruff.lint.mccabe]
|
|
66
|
+
max-complexity = 6
|
|
67
|
+
|
|
68
|
+
[tool.mypy]
|
|
69
|
+
python_version = "3.12"
|
|
70
|
+
warn_return_any = true
|
|
71
|
+
warn_unused_configs = true
|
|
72
|
+
disallow_untyped_defs = true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""LinkedIn Ads MCP server with MCC client-switching support."""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""LinkedIn Marketing API client and type definitions."""
|