adloop 0.19.3__tar.gz → 0.19.4__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.
- {adloop-0.19.3 → adloop-0.19.4}/PKG-INFO +1 -1
- {adloop-0.19.3 → adloop-0.19.4}/pyproject.toml +1 -1
- {adloop-0.19.3 → adloop-0.19.4}/pyproject.toml.orig +1 -1
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/server.py +55 -8
- {adloop-0.19.3 → adloop-0.19.4}/README.md +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/__init__.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/__main__.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/_mcp_patches.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/ads/__init__.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/ads/ai_max.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/ads/brands.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/ads/client.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/ads/conversion_actions.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/ads/currency.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/ads/custom_conversion_goals.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/ads/enums.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/ads/forecast.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/ads/gaql.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/ads/image_fetch.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/ads/pmax.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/ads/read.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/ads/validate_only.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/ads/write.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/auth.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/cli.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/config.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/crossref.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/diagnostics.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/ga4/__init__.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/ga4/client.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/ga4/reports.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/ga4/tracking.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/ga4/write.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/gsc/__init__.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/gsc/client.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/gsc/reports.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/gtm/__init__.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/gtm/client.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/gtm/read.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/merchant/__init__.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/merchant/client.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/merchant/read.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/pagespeed.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/reddit/__init__.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/reddit/auth.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/reddit/client.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/reddit/read.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/reddit/schedule.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/reddit/write.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/rules/__init__.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/rules/adloop.md +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/rules/commands/analyze-performance.md +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/rules/commands/budget-plan.md +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/rules/commands/create-ad.md +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/rules/commands/create-campaign.md +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/rules/commands/diagnose-tracking.md +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/rules/commands/optimize-campaign.md +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/rules_install.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/runtime.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/safety/__init__.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/safety/audit.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/safety/guards.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/safety/preview.py +0 -0
- {adloop-0.19.3 → adloop-0.19.4}/src/adloop/tracking.py +0 -0
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import functools
|
|
6
|
+
import inspect
|
|
6
7
|
import json
|
|
7
8
|
from typing import Annotated, Callable
|
|
8
9
|
|
|
9
10
|
from fastmcp import FastMCP
|
|
11
|
+
from fastmcp.utilities.docstring_parsing import parse_docstring
|
|
10
12
|
from mcp.types import ToolAnnotations
|
|
11
13
|
from pydantic import BeforeValidator
|
|
12
14
|
|
|
@@ -166,18 +168,63 @@ mcp = FastMCP(
|
|
|
166
168
|
)
|
|
167
169
|
|
|
168
170
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
+
# The API each toolset calls, linked at the end of every tool description:
|
|
172
|
+
# directory reviews ask descriptions to reference the API they target.
|
|
173
|
+
_API_DOCS: dict[str, str] = {
|
|
174
|
+
"ads": "Google Ads API: https://developers.google.com/google-ads/api/docs/start",
|
|
175
|
+
"ga4": (
|
|
176
|
+
"Google Analytics Data API: "
|
|
177
|
+
"https://developers.google.com/analytics/devguides/reporting/data/v1 "
|
|
178
|
+
"and Admin API: https://developers.google.com/analytics/devguides/config/admin/v1"
|
|
179
|
+
),
|
|
180
|
+
"tracking": (
|
|
181
|
+
"Google Analytics Data API: "
|
|
182
|
+
"https://developers.google.com/analytics/devguides/reporting/data/v1 "
|
|
183
|
+
"and Google Ads API: https://developers.google.com/google-ads/api/docs/start"
|
|
184
|
+
),
|
|
185
|
+
"gtm": "Tag Manager API: https://developers.google.com/tag-platform/tag-manager/api/v2",
|
|
186
|
+
"gsc": "Search Console API: https://developers.google.com/webmaster-tools",
|
|
187
|
+
"web": "PageSpeed Insights API: https://developers.google.com/speed/docs/insights/v5/about",
|
|
188
|
+
"merchant": "Merchant API: https://developers.google.com/merchant/api/overview",
|
|
189
|
+
"reddit": "Reddit Ads API: https://ads-api.reddit.com/docs/v3/",
|
|
190
|
+
# health_check and confirm_and_apply act on whichever platform is set up.
|
|
191
|
+
"core": (
|
|
192
|
+
"Google Ads API: https://developers.google.com/google-ads/api/docs/start, "
|
|
193
|
+
"Google Analytics Data API: "
|
|
194
|
+
"https://developers.google.com/analytics/devguides/reporting/data/v1 "
|
|
195
|
+
"and Reddit Ads API: https://ads-api.reddit.com/docs/v3/"
|
|
196
|
+
),
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _tool(*, title: str, annotations: ToolAnnotations, tags: set[str], **kwargs):
|
|
201
|
+
"""Register a tool with its title in both places MCP defines one, and
|
|
202
|
+
with a link to the API it calls.
|
|
171
203
|
|
|
172
204
|
The tool's own ``title`` is what clients display; ``annotations.title``
|
|
173
205
|
is what directory reviews (Claude's connector portal) check. One title,
|
|
174
206
|
copied into the shared annotation preset, keeps them from drifting.
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
207
|
+
Descriptions that already link their API (the free-form query tools,
|
|
208
|
+
which point at the query reference) keep their own link.
|
|
209
|
+
"""
|
|
210
|
+
annotations = annotations.model_copy(update={"title": title})
|
|
211
|
+
docs = " ".join(_API_DOCS[tag] for tag in sorted(tags) if tag in _API_DOCS)
|
|
212
|
+
|
|
213
|
+
def register(fn):
|
|
214
|
+
# FastMCP's own parser: it moves an Args: section into the
|
|
215
|
+
# parameter schema, so the description must not repeat it.
|
|
216
|
+
description = parse_docstring(fn).description or inspect.cleandoc(fn.__doc__ or "")
|
|
217
|
+
if docs and "https://" not in description:
|
|
218
|
+
description = f"{description}\n\n{docs}"
|
|
219
|
+
return mcp.tool(
|
|
220
|
+
title=title,
|
|
221
|
+
annotations=annotations,
|
|
222
|
+
tags=tags,
|
|
223
|
+
description=description,
|
|
224
|
+
**kwargs,
|
|
225
|
+
)(fn)
|
|
226
|
+
|
|
227
|
+
return register
|
|
181
228
|
|
|
182
229
|
|
|
183
230
|
def _reddit_structured_error(exc: Exception) -> dict | None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|