adloop 0.19.2__tar.gz → 0.19.3__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.
Files changed (64) hide show
  1. {adloop-0.19.2 → adloop-0.19.3}/PKG-INFO +1 -1
  2. {adloop-0.19.2 → adloop-0.19.3}/pyproject.toml +1 -1
  3. {adloop-0.19.2 → adloop-0.19.3}/pyproject.toml.orig +1 -1
  4. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/server.py +123 -109
  5. {adloop-0.19.2 → adloop-0.19.3}/README.md +0 -0
  6. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/__init__.py +0 -0
  7. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/__main__.py +0 -0
  8. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/_mcp_patches.py +0 -0
  9. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/ads/__init__.py +0 -0
  10. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/ads/ai_max.py +0 -0
  11. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/ads/brands.py +0 -0
  12. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/ads/client.py +0 -0
  13. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/ads/conversion_actions.py +0 -0
  14. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/ads/currency.py +0 -0
  15. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/ads/custom_conversion_goals.py +0 -0
  16. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/ads/enums.py +0 -0
  17. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/ads/forecast.py +0 -0
  18. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/ads/gaql.py +0 -0
  19. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/ads/image_fetch.py +0 -0
  20. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/ads/pmax.py +0 -0
  21. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/ads/read.py +0 -0
  22. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/ads/validate_only.py +0 -0
  23. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/ads/write.py +0 -0
  24. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/auth.py +0 -0
  25. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/cli.py +0 -0
  26. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/config.py +0 -0
  27. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/crossref.py +0 -0
  28. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/diagnostics.py +0 -0
  29. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/ga4/__init__.py +0 -0
  30. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/ga4/client.py +0 -0
  31. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/ga4/reports.py +0 -0
  32. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/ga4/tracking.py +0 -0
  33. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/ga4/write.py +0 -0
  34. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/gsc/__init__.py +0 -0
  35. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/gsc/client.py +0 -0
  36. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/gsc/reports.py +0 -0
  37. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/gtm/__init__.py +0 -0
  38. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/gtm/client.py +0 -0
  39. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/gtm/read.py +0 -0
  40. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/merchant/__init__.py +0 -0
  41. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/merchant/client.py +0 -0
  42. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/merchant/read.py +0 -0
  43. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/pagespeed.py +0 -0
  44. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/reddit/__init__.py +0 -0
  45. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/reddit/auth.py +0 -0
  46. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/reddit/client.py +0 -0
  47. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/reddit/read.py +0 -0
  48. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/reddit/schedule.py +0 -0
  49. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/reddit/write.py +0 -0
  50. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/rules/__init__.py +0 -0
  51. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/rules/adloop.md +0 -0
  52. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/rules/commands/analyze-performance.md +0 -0
  53. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/rules/commands/budget-plan.md +0 -0
  54. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/rules/commands/create-ad.md +0 -0
  55. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/rules/commands/create-campaign.md +0 -0
  56. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/rules/commands/diagnose-tracking.md +0 -0
  57. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/rules/commands/optimize-campaign.md +0 -0
  58. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/rules_install.py +0 -0
  59. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/runtime.py +0 -0
  60. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/safety/__init__.py +0 -0
  61. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/safety/audit.py +0 -0
  62. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/safety/guards.py +0 -0
  63. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/safety/preview.py +0 -0
  64. {adloop-0.19.2 → adloop-0.19.3}/src/adloop/tracking.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: adloop
3
- Version: 0.19.2
3
+ Version: 0.19.3
4
4
  Summary: The AI command center for Google Ads, Reddit Ads, GA4, and tracking code.
5
5
  Keywords: mcp,google-ads,reddit-ads,google-analytics,ga4,cursor,marketing
6
6
  Author: Daniel Klose
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "adloop"
3
- version = "0.19.2"
3
+ version = "0.19.3"
4
4
  description = "The AI command center for Google Ads, Reddit Ads, GA4, and tracking code."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "adloop"
3
- version = "0.19.2"
3
+ version = "0.19.3"
4
4
  description = "The AI command center for Google Ads, Reddit Ads, GA4, and tracking code."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -166,6 +166,20 @@ mcp = FastMCP(
166
166
  )
167
167
 
168
168
 
169
+ def _tool(*, title: str, annotations: ToolAnnotations, **kwargs):
170
+ """Register a tool with its title in both places MCP defines one.
171
+
172
+ The tool's own ``title`` is what clients display; ``annotations.title``
173
+ is what directory reviews (Claude's connector portal) check. One title,
174
+ copied into the shared annotation preset, keeps them from drifting.
175
+ """
176
+ return mcp.tool(
177
+ title=title,
178
+ annotations=annotations.model_copy(update={"title": title}),
179
+ **kwargs,
180
+ )
181
+
182
+
169
183
  def _reddit_structured_error(exc: Exception) -> dict | None:
170
184
  """Reddit-specific translations; keyed on exception type so Reddit's
171
185
  ``invalid_grant`` never gets the "Reconnect Google" hint below."""
@@ -400,7 +414,7 @@ def _safe(fn: Callable) -> Callable:
400
414
  # ---------------------------------------------------------------------------
401
415
 
402
416
 
403
- @mcp.tool(title="Connection health check", annotations=_READONLY, tags={"core"})
417
+ @_tool(title="Connection health check", annotations=_READONLY, tags={"core"})
404
418
  @_safe
405
419
  def health_check() -> dict:
406
420
  """Test AdLoop connectivity — checks OAuth token, GA4 API, Google Ads API,
@@ -531,7 +545,7 @@ def health_check() -> dict:
531
545
  # ---------------------------------------------------------------------------
532
546
 
533
547
 
534
- @mcp.tool(title="List Analytics properties", annotations=_READONLY, tags={"ga4"})
548
+ @_tool(title="List Analytics properties", annotations=_READONLY, tags={"ga4"})
535
549
  @_safe
536
550
  def get_account_summaries() -> dict:
537
551
  """List all GA4 accounts and properties accessible by the authenticated user.
@@ -544,7 +558,7 @@ def get_account_summaries() -> dict:
544
558
  return _impl(current_config())
545
559
 
546
560
 
547
- @mcp.tool(title="Analytics report", annotations=_READONLY, tags={"ga4"})
561
+ @_tool(title="Analytics report", annotations=_READONLY, tags={"ga4"})
548
562
  @_safe
549
563
  def run_ga4_report(
550
564
  dimensions: _StrListOpt = None,
@@ -577,7 +591,7 @@ def run_ga4_report(
577
591
  )
578
592
 
579
593
 
580
- @mcp.tool(title="Analytics realtime report", annotations=_READONLY, tags={"ga4"})
594
+ @_tool(title="Analytics realtime report", annotations=_READONLY, tags={"ga4"})
581
595
  @_safe
582
596
  def run_realtime_report(
583
597
  dimensions: _StrListOpt = None,
@@ -600,7 +614,7 @@ def run_realtime_report(
600
614
  )
601
615
 
602
616
 
603
- @mcp.tool(title="List Analytics events", annotations=_READONLY, tags={"ga4"})
617
+ @_tool(title="List Analytics events", annotations=_READONLY, tags={"ga4"})
604
618
  @_safe
605
619
  def get_tracking_events(
606
620
  date_range_start: str = "28daysAgo",
@@ -627,7 +641,7 @@ def get_tracking_events(
627
641
  # ---------------------------------------------------------------------------
628
642
 
629
643
 
630
- @mcp.tool(title="List Search Console properties", annotations=_READONLY, tags={"gsc"})
644
+ @_tool(title="List Search Console properties", annotations=_READONLY, tags={"gsc"})
631
645
  @_safe
632
646
  def list_gsc_sites() -> dict:
633
647
  """List all Google Search Console properties the authenticated user can access.
@@ -641,7 +655,7 @@ def list_gsc_sites() -> dict:
641
655
  return _impl(current_config())
642
656
 
643
657
 
644
- @mcp.tool(title="Search Console report", annotations=_READONLY, tags={"gsc"})
658
+ @_tool(title="Search Console report", annotations=_READONLY, tags={"gsc"})
645
659
  @_safe
646
660
  def run_gsc_report(
647
661
  site_url: str = "",
@@ -693,7 +707,7 @@ def run_gsc_report(
693
707
  # ---------------------------------------------------------------------------
694
708
 
695
709
 
696
- @mcp.tool(title="PageSpeed analysis", annotations=_READONLY, tags={"web"})
710
+ @_tool(title="PageSpeed analysis", annotations=_READONLY, tags={"web"})
697
711
  @_safe
698
712
  def analyze_page_speed(url: str, strategy: str = "mobile") -> dict:
699
713
  """Run PageSpeed Insights for a landing page — Lighthouse + real-user data.
@@ -712,7 +726,7 @@ def analyze_page_speed(url: str, strategy: str = "mobile") -> dict:
712
726
  return _impl(current_config(), url=url, strategy=strategy)
713
727
 
714
728
 
715
- @mcp.tool(
729
+ @_tool(
716
730
  title="List Merchant Center accounts",
717
731
  annotations=_READONLY,
718
732
  tags={"merchant"},
@@ -730,7 +744,7 @@ def list_merchant_accounts() -> dict:
730
744
  return _impl(current_config())
731
745
 
732
746
 
733
- @mcp.tool(title="Merchant Center feed health", annotations=_READONLY, tags={"merchant"})
747
+ @_tool(title="Merchant Center feed health", annotations=_READONLY, tags={"merchant"})
734
748
  @_safe
735
749
  def get_merchant_feed_health(account_id: str = "") -> dict:
736
750
  """Merchant Center feed health — disapproved products + account issues.
@@ -750,7 +764,7 @@ def get_merchant_feed_health(account_id: str = "") -> dict:
750
764
  return _impl(current_config(), account_id=account_id)
751
765
 
752
766
 
753
- @mcp.tool(title="List Google Ads accounts", annotations=_READONLY, tags={"ads"})
767
+ @_tool(title="List Google Ads accounts", annotations=_READONLY, tags={"ads"})
754
768
  @_safe
755
769
  def list_accounts(limit: int = 200) -> dict:
756
770
  """List accessible Google Ads accounts.
@@ -769,7 +783,7 @@ def list_accounts(limit: int = 200) -> dict:
769
783
  return _impl(current_config(), limit=limit)
770
784
 
771
785
 
772
- @mcp.tool(title="Campaign performance", annotations=_READONLY, tags={"ads"})
786
+ @_tool(title="Campaign performance", annotations=_READONLY, tags={"ads"})
773
787
  @_safe
774
788
  def get_campaign_performance(
775
789
  customer_id: str = "",
@@ -798,7 +812,7 @@ def get_campaign_performance(
798
812
  )
799
813
 
800
814
 
801
- @mcp.tool(title="Ad performance", annotations=_READONLY, tags={"ads"})
815
+ @_tool(title="Ad performance", annotations=_READONLY, tags={"ads"})
802
816
  @_safe
803
817
  def get_ad_performance(
804
818
  customer_id: str = "",
@@ -826,7 +840,7 @@ def get_ad_performance(
826
840
  )
827
841
 
828
842
 
829
- @mcp.tool(title="Keyword performance", annotations=_READONLY, tags={"ads"})
843
+ @_tool(title="Keyword performance", annotations=_READONLY, tags={"ads"})
830
844
  @_safe
831
845
  def get_keyword_performance(
832
846
  customer_id: str = "",
@@ -854,7 +868,7 @@ def get_keyword_performance(
854
868
  )
855
869
 
856
870
 
857
- @mcp.tool(title="Search terms report", annotations=_READONLY, tags={"ads"})
871
+ @_tool(title="Search terms report", annotations=_READONLY, tags={"ads"})
858
872
  @_safe
859
873
  def get_search_terms(
860
874
  customer_id: str = "",
@@ -882,7 +896,7 @@ def get_search_terms(
882
896
  )
883
897
 
884
898
 
885
- @mcp.tool(title="List negative keywords", annotations=_READONLY, tags={"ads"})
899
+ @_tool(title="List negative keywords", annotations=_READONLY, tags={"ads"})
886
900
  @_safe
887
901
  def get_negative_keywords(
888
902
  customer_id: str = "",
@@ -902,7 +916,7 @@ def get_negative_keywords(
902
916
  )
903
917
 
904
918
 
905
- @mcp.tool(
919
+ @_tool(
906
920
  title="List shared negative keyword lists",
907
921
  annotations=_READONLY,
908
922
  tags={"ads"},
@@ -922,7 +936,7 @@ def get_negative_keyword_lists(
922
936
  return _impl(current_config(), customer_id=customer_id or current_config().ads.customer_id)
923
937
 
924
938
 
925
- @mcp.tool(
939
+ @_tool(
926
940
  title="Keywords in a negative keyword list",
927
941
  annotations=_READONLY,
928
942
  tags={"ads"},
@@ -945,7 +959,7 @@ def get_negative_keyword_list_keywords(
945
959
  )
946
960
 
947
961
 
948
- @mcp.tool(
962
+ @_tool(
949
963
  title="Campaigns using a negative keyword list",
950
964
  annotations=_READONLY,
951
965
  tags={"ads"},
@@ -974,7 +988,7 @@ def get_negative_keyword_list_campaigns(
974
988
  # ---------------------------------------------------------------------------
975
989
 
976
990
 
977
- @mcp.tool(title="Google Ads recommendations", annotations=_READONLY, tags={"ads"})
991
+ @_tool(title="Google Ads recommendations", annotations=_READONLY, tags={"ads"})
978
992
  @_safe
979
993
  def get_recommendations(
980
994
  customer_id: str = "",
@@ -1003,7 +1017,7 @@ def get_recommendations(
1003
1017
  )
1004
1018
 
1005
1019
 
1006
- @mcp.tool(title="Performance Max performance", annotations=_READONLY, tags={"ads"})
1020
+ @_tool(title="Performance Max performance", annotations=_READONLY, tags={"ads"})
1007
1021
  @_safe
1008
1022
  def get_pmax_performance(
1009
1023
  customer_id: str = "",
@@ -1034,7 +1048,7 @@ def get_pmax_performance(
1034
1048
  )
1035
1049
 
1036
1050
 
1037
- @mcp.tool(
1051
+ @_tool(
1038
1052
  title="Performance Max asset performance",
1039
1053
  annotations=_READONLY,
1040
1054
  tags={"ads"},
@@ -1067,7 +1081,7 @@ def get_asset_performance(
1067
1081
  )
1068
1082
 
1069
1083
 
1070
- @mcp.tool(
1084
+ @_tool(
1071
1085
  title="Performance Max asset combinations",
1072
1086
  annotations=_READONLY,
1073
1087
  tags={"ads"},
@@ -1094,7 +1108,7 @@ def get_detailed_asset_performance(
1094
1108
  )
1095
1109
 
1096
1110
 
1097
- @mcp.tool(title="Audience performance", annotations=_READONLY, tags={"ads"})
1111
+ @_tool(title="Audience performance", annotations=_READONLY, tags={"ads"})
1098
1112
  @_safe
1099
1113
  def get_audience_performance(
1100
1114
  customer_id: str = "",
@@ -1125,7 +1139,7 @@ def get_audience_performance(
1125
1139
  )
1126
1140
 
1127
1141
 
1128
- @mcp.tool(title="Demographic targeting", annotations=_READONLY, tags={"ads"})
1142
+ @_tool(title="Demographic targeting", annotations=_READONLY, tags={"ads"})
1129
1143
  @_safe
1130
1144
  def get_demographic_targeting(
1131
1145
  ad_group_id: str = "",
@@ -1158,7 +1172,7 @@ def get_demographic_targeting(
1158
1172
  # ---------------------------------------------------------------------------
1159
1173
 
1160
1174
 
1161
- @mcp.tool(
1175
+ @_tool(
1162
1176
  title="Campaign conversions against Analytics",
1163
1177
  annotations=_READONLY,
1164
1178
  tags={"tracking"},
@@ -1192,7 +1206,7 @@ def analyze_campaign_conversions(
1192
1206
  )
1193
1207
 
1194
1208
 
1195
- @mcp.tool(title="Landing page analysis", annotations=_READONLY, tags={"tracking"})
1209
+ @_tool(title="Landing page analysis", annotations=_READONLY, tags={"tracking"})
1196
1210
  @_safe
1197
1211
  def landing_page_analysis(
1198
1212
  date_range_start: str = "",
@@ -1218,7 +1232,7 @@ def landing_page_analysis(
1218
1232
  )
1219
1233
 
1220
1234
 
1221
- @mcp.tool(title="Attribution check", annotations=_READONLY, tags={"tracking"})
1235
+ @_tool(title="Attribution check", annotations=_READONLY, tags={"tracking"})
1222
1236
  @_safe
1223
1237
  def attribution_check(
1224
1238
  date_range_start: str = "",
@@ -1249,7 +1263,7 @@ def attribution_check(
1249
1263
  )
1250
1264
 
1251
1265
 
1252
- @mcp.tool(title="Tracking coverage audit", annotations=_READONLY, tags={"gtm"})
1266
+ @_tool(title="Tracking coverage audit", annotations=_READONLY, tags={"gtm"})
1253
1267
  @_safe
1254
1268
  def audit_event_coverage(
1255
1269
  expected_events: list[str],
@@ -1302,7 +1316,7 @@ def audit_event_coverage(
1302
1316
  )
1303
1317
 
1304
1318
 
1305
- @mcp.tool(title="List Tag Manager accounts", annotations=_READONLY, tags={"gtm"})
1319
+ @_tool(title="List Tag Manager accounts", annotations=_READONLY, tags={"gtm"})
1306
1320
  @_safe
1307
1321
  def list_gtm_accounts() -> dict:
1308
1322
  """List all GTM accounts the AdLoop service account / OAuth user can read.
@@ -1317,7 +1331,7 @@ def list_gtm_accounts() -> dict:
1317
1331
  return _impl(current_config())
1318
1332
 
1319
1333
 
1320
- @mcp.tool(title="List Tag Manager containers", annotations=_READONLY, tags={"gtm"})
1334
+ @_tool(title="List Tag Manager containers", annotations=_READONLY, tags={"gtm"})
1321
1335
  @_safe
1322
1336
  def list_gtm_containers(gtm_account_id: str = "") -> dict:
1323
1337
  """List all containers under a GTM account.
@@ -1337,7 +1351,7 @@ def list_gtm_containers(gtm_account_id: str = "") -> dict:
1337
1351
  return _impl(current_config(), account_id=gtm_account_id)
1338
1352
 
1339
1353
 
1340
- @mcp.tool(title="List Tag Manager tags", annotations=_READONLY, tags={"gtm"})
1354
+ @_tool(title="List Tag Manager tags", annotations=_READONLY, tags={"gtm"})
1341
1355
  @_safe
1342
1356
  def list_gtm_tags(gtm_account_id: str = "", gtm_container_id: str = "") -> dict:
1343
1357
  """List every tag in the LIVE GTM container.
@@ -1357,7 +1371,7 @@ def list_gtm_tags(gtm_account_id: str = "", gtm_container_id: str = "") -> dict:
1357
1371
  )
1358
1372
 
1359
1373
 
1360
- @mcp.tool(title="Tag Manager tag details", annotations=_READONLY, tags={"gtm"})
1374
+ @_tool(title="Tag Manager tag details", annotations=_READONLY, tags={"gtm"})
1361
1375
  @_safe
1362
1376
  def get_gtm_tag(
1363
1377
  tag_id: str, gtm_account_id: str = "", gtm_container_id: str = ""
@@ -1382,7 +1396,7 @@ def get_gtm_tag(
1382
1396
  )
1383
1397
 
1384
1398
 
1385
- @mcp.tool(title="List Tag Manager triggers", annotations=_READONLY, tags={"gtm"})
1399
+ @_tool(title="List Tag Manager triggers", annotations=_READONLY, tags={"gtm"})
1386
1400
  @_safe
1387
1401
  def list_gtm_triggers(gtm_account_id: str = "", gtm_container_id: str = "") -> dict:
1388
1402
  """List every trigger in the LIVE GTM container.
@@ -1402,7 +1416,7 @@ def list_gtm_triggers(gtm_account_id: str = "", gtm_container_id: str = "") -> d
1402
1416
  )
1403
1417
 
1404
1418
 
1405
- @mcp.tool(title="Tag Manager trigger details", annotations=_READONLY, tags={"gtm"})
1419
+ @_tool(title="Tag Manager trigger details", annotations=_READONLY, tags={"gtm"})
1406
1420
  @_safe
1407
1421
  def get_gtm_trigger(
1408
1422
  trigger_id: str, gtm_account_id: str = "", gtm_container_id: str = ""
@@ -1427,7 +1441,7 @@ def get_gtm_trigger(
1427
1441
  )
1428
1442
 
1429
1443
 
1430
- @mcp.tool(title="List Tag Manager variables", annotations=_READONLY, tags={"gtm"})
1444
+ @_tool(title="List Tag Manager variables", annotations=_READONLY, tags={"gtm"})
1431
1445
  @_safe
1432
1446
  def list_gtm_variables(gtm_account_id: str = "", gtm_container_id: str = "") -> dict:
1433
1447
  """List GTM variables — both custom and enabled built-in.
@@ -1449,7 +1463,7 @@ def list_gtm_variables(gtm_account_id: str = "", gtm_container_id: str = "") ->
1449
1463
  )
1450
1464
 
1451
1465
 
1452
- @mcp.tool(title="List Tag Manager workspaces", annotations=_READONLY, tags={"gtm"})
1466
+ @_tool(title="List Tag Manager workspaces", annotations=_READONLY, tags={"gtm"})
1453
1467
  @_safe
1454
1468
  def list_gtm_workspaces(gtm_account_id: str = "", gtm_container_id: str = "") -> dict:
1455
1469
  """List workspaces (drafts) under a GTM container.
@@ -1469,7 +1483,7 @@ def list_gtm_workspaces(gtm_account_id: str = "", gtm_container_id: str = "") ->
1469
1483
  )
1470
1484
 
1471
1485
 
1472
- @mcp.tool(title="Tag Manager workspace changes", annotations=_READONLY, tags={"gtm"})
1486
+ @_tool(title="Tag Manager workspace changes", annotations=_READONLY, tags={"gtm"})
1473
1487
  @_safe
1474
1488
  def get_gtm_workspace_diff(
1475
1489
  workspace_id: str, gtm_account_id: str = "", gtm_container_id: str = ""
@@ -1496,7 +1510,7 @@ def get_gtm_workspace_diff(
1496
1510
  )
1497
1511
 
1498
1512
 
1499
- @mcp.tool(title="Tag Manager version history", annotations=_READONLY, tags={"gtm"})
1513
+ @_tool(title="Tag Manager version history", annotations=_READONLY, tags={"gtm"})
1500
1514
  @_safe
1501
1515
  def list_gtm_versions(
1502
1516
  gtm_account_id: str = "", gtm_container_id: str = "", page_size: int = 50
@@ -1522,7 +1536,7 @@ def list_gtm_versions(
1522
1536
  )
1523
1537
 
1524
1538
 
1525
- @mcp.tool(title="Tag Manager version details", annotations=_READONLY, tags={"gtm"})
1539
+ @_tool(title="Tag Manager version details", annotations=_READONLY, tags={"gtm"})
1526
1540
  @_safe
1527
1541
  def get_gtm_version(
1528
1542
  container_version_id: str, gtm_account_id: str = "", gtm_container_id: str = ""
@@ -1547,7 +1561,7 @@ def get_gtm_version(
1547
1561
  )
1548
1562
 
1549
1563
 
1550
- @mcp.tool(title="Custom Google Ads query", annotations=_READONLY, tags={"ads"})
1564
+ @_tool(title="Custom Google Ads query", annotations=_READONLY, tags={"ads"})
1551
1565
  @_safe
1552
1566
  def run_gaql(
1553
1567
  query: str,
@@ -1577,7 +1591,7 @@ def run_gaql(
1577
1591
  # ---------------------------------------------------------------------------
1578
1592
 
1579
1593
 
1580
- @mcp.tool(title="Draft a campaign", annotations=_WRITE, tags={"ads"})
1594
+ @_tool(title="Draft a campaign", annotations=_WRITE, tags={"ads"})
1581
1595
  @_safe
1582
1596
  def draft_campaign(
1583
1597
  campaign_name: str,
@@ -1644,7 +1658,7 @@ def draft_campaign(
1644
1658
  )
1645
1659
 
1646
1660
 
1647
- @mcp.tool(title="Draft an ad group", annotations=_WRITE, tags={"ads"})
1661
+ @_tool(title="Draft an ad group", annotations=_WRITE, tags={"ads"})
1648
1662
  @_safe
1649
1663
  def draft_ad_group(
1650
1664
  campaign_id: str,
@@ -1677,7 +1691,7 @@ def draft_ad_group(
1677
1691
  )
1678
1692
 
1679
1693
 
1680
- @mcp.tool(title="Draft campaign changes", annotations=_WRITE, tags={"ads"})
1694
+ @_tool(title="Draft campaign changes", annotations=_WRITE, tags={"ads"})
1681
1695
  @_safe
1682
1696
  def update_campaign(
1683
1697
  campaign_id: str,
@@ -1734,7 +1748,7 @@ def update_campaign(
1734
1748
  )
1735
1749
 
1736
1750
 
1737
- @mcp.tool(title="Draft a responsive search ad", annotations=_WRITE, tags={"ads"})
1751
+ @_tool(title="Draft a responsive search ad", annotations=_WRITE, tags={"ads"})
1738
1752
  @_safe
1739
1753
  def draft_responsive_search_ad(
1740
1754
  ad_group_id: str,
@@ -1777,7 +1791,7 @@ def draft_responsive_search_ad(
1777
1791
  )
1778
1792
 
1779
1793
 
1780
- @mcp.tool(title="Update a responsive search ad", annotations=_WRITE, tags={"ads"})
1794
+ @_tool(title="Update a responsive search ad", annotations=_WRITE, tags={"ads"})
1781
1795
  @_safe
1782
1796
  def update_responsive_search_ad(
1783
1797
  ad_id: str,
@@ -1836,7 +1850,7 @@ def update_responsive_search_ad(
1836
1850
  )
1837
1851
 
1838
1852
 
1839
- @mcp.tool(title="Draft keywords", annotations=_WRITE, tags={"ads"})
1853
+ @_tool(title="Draft keywords", annotations=_WRITE, tags={"ads"})
1840
1854
  @_safe
1841
1855
  def draft_keywords(
1842
1856
  ad_group_id: str,
@@ -1858,7 +1872,7 @@ def draft_keywords(
1858
1872
  )
1859
1873
 
1860
1874
 
1861
- @mcp.tool(title="Custom conversion goals", annotations=_READONLY, tags={"ads"})
1875
+ @_tool(title="Custom conversion goals", annotations=_READONLY, tags={"ads"})
1862
1876
  @_safe
1863
1877
  def get_custom_conversion_goals(
1864
1878
  campaign_id: str = "",
@@ -1887,7 +1901,7 @@ def get_custom_conversion_goals(
1887
1901
  )
1888
1902
 
1889
1903
 
1890
- @mcp.tool(
1904
+ @_tool(
1891
1905
  title="Draft a custom conversion goal",
1892
1906
  annotations=_WRITE,
1893
1907
  tags={"ads"},
@@ -1931,7 +1945,7 @@ def draft_custom_conversion_goal(
1931
1945
  )
1932
1946
 
1933
1947
 
1934
- @mcp.tool(
1948
+ @_tool(
1935
1949
  title="Draft changes to a custom conversion goal",
1936
1950
  annotations=_WRITE,
1937
1951
  tags={"ads"},
@@ -1969,7 +1983,7 @@ def draft_update_custom_conversion_goal(
1969
1983
  )
1970
1984
 
1971
1985
 
1972
- @mcp.tool(
1986
+ @_tool(
1973
1987
  title="Draft assigning a custom conversion goal",
1974
1988
  annotations=_WRITE,
1975
1989
  tags={"ads"},
@@ -2012,7 +2026,7 @@ def draft_assign_custom_conversion_goal(
2012
2026
  )
2013
2027
 
2014
2028
 
2015
- @mcp.tool(
2029
+ @_tool(
2016
2030
  title="Draft clearing a custom conversion goal",
2017
2031
  annotations=_WRITE,
2018
2032
  tags={"ads"},
@@ -2043,7 +2057,7 @@ def draft_clear_custom_conversion_goal(
2043
2057
  )
2044
2058
 
2045
2059
 
2046
- @mcp.tool(title="Draft negative keywords", annotations=_WRITE, tags={"ads"})
2060
+ @_tool(title="Draft negative keywords", annotations=_WRITE, tags={"ads"})
2047
2061
  @_safe
2048
2062
  def add_negative_keywords(
2049
2063
  campaign_id: str,
@@ -2068,7 +2082,7 @@ def add_negative_keywords(
2068
2082
  )
2069
2083
 
2070
2084
 
2071
- @mcp.tool(title="Draft negative locations", annotations=_WRITE, tags={"ads"})
2085
+ @_tool(title="Draft negative locations", annotations=_WRITE, tags={"ads"})
2072
2086
  @_safe
2073
2087
  def add_negative_locations(
2074
2088
  campaign_id: str,
@@ -2092,7 +2106,7 @@ def add_negative_locations(
2092
2106
  )
2093
2107
 
2094
2108
 
2095
- @mcp.tool(
2109
+ @_tool(
2096
2110
  title="Draft a shared negative keyword list",
2097
2111
  annotations=_WRITE,
2098
2112
  tags={"ads"},
@@ -2124,7 +2138,7 @@ def propose_negative_keyword_list(
2124
2138
  )
2125
2139
 
2126
2140
 
2127
- @mcp.tool(
2141
+ @_tool(
2128
2142
  title="Draft additions to a negative keyword list",
2129
2143
  annotations=_WRITE,
2130
2144
  tags={"ads"},
@@ -2161,7 +2175,7 @@ def add_to_negative_keyword_list(
2161
2175
  )
2162
2176
 
2163
2177
 
2164
- @mcp.tool(title="Draft attaching a shared set", annotations=_WRITE, tags={"ads"})
2178
+ @_tool(title="Draft attaching a shared set", annotations=_WRITE, tags={"ads"})
2165
2179
  @_safe
2166
2180
  def attach_shared_set_to_campaigns(
2167
2181
  shared_set_id: str,
@@ -2192,7 +2206,7 @@ def attach_shared_set_to_campaigns(
2192
2206
  )
2193
2207
 
2194
2208
 
2195
- @mcp.tool(title="Draft detaching a shared set", annotations=_WRITE, tags={"ads"})
2209
+ @_tool(title="Draft detaching a shared set", annotations=_WRITE, tags={"ads"})
2196
2210
  @_safe
2197
2211
  def detach_shared_set_from_campaigns(
2198
2212
  shared_set_id: str,
@@ -2223,7 +2237,7 @@ def detach_shared_set_from_campaigns(
2223
2237
  )
2224
2238
 
2225
2239
 
2226
- @mcp.tool(title="AI Max settings", annotations=_READONLY, tags={"ads"})
2240
+ @_tool(title="AI Max settings", annotations=_READONLY, tags={"ads"})
2227
2241
  @_safe
2228
2242
  def get_ai_max_settings(
2229
2243
  campaign_id: str = "",
@@ -2253,7 +2267,7 @@ def get_ai_max_settings(
2253
2267
  campaign_id=campaign_id,
2254
2268
  )
2255
2269
 
2256
- @mcp.tool(title="Draft AI Max settings", annotations=_WRITE, tags={"ads"})
2270
+ @_tool(title="Draft AI Max settings", annotations=_WRITE, tags={"ads"})
2257
2271
  @_safe
2258
2272
  def draft_ai_max_settings(
2259
2273
  campaign_id: str,
@@ -2318,7 +2332,7 @@ def draft_ai_max_settings(
2318
2332
  )
2319
2333
 
2320
2334
 
2321
- @mcp.tool(
2335
+ @_tool(
2322
2336
  title="Prepare a campaign for brand exclusions",
2323
2337
  annotations=_WRITE,
2324
2338
  tags={"ads"},
@@ -2361,7 +2375,7 @@ def draft_prepare_brand_exclusions(
2361
2375
  include_paused_ad_groups=include_paused_ad_groups,
2362
2376
  )
2363
2377
 
2364
- @mcp.tool(title="Draft demographic targeting", annotations=_WRITE, tags={"ads"})
2378
+ @_tool(title="Draft demographic targeting", annotations=_WRITE, tags={"ads"})
2365
2379
  @_safe
2366
2380
  def draft_demographic_targeting(
2367
2381
  customer_id: str = "",
@@ -2413,7 +2427,7 @@ def draft_demographic_targeting(
2413
2427
  )
2414
2428
 
2415
2429
 
2416
- @mcp.tool(title="Draft ad group changes", annotations=_WRITE, tags={"ads"})
2430
+ @_tool(title="Draft ad group changes", annotations=_WRITE, tags={"ads"})
2417
2431
  @_safe
2418
2432
  def update_ad_group(
2419
2433
  ad_group_id: str,
@@ -2433,7 +2447,7 @@ def update_ad_group(
2433
2447
  )
2434
2448
 
2435
2449
 
2436
- @mcp.tool(title="Draft callouts", annotations=_WRITE, tags={"ads"})
2450
+ @_tool(title="Draft callouts", annotations=_WRITE, tags={"ads"})
2437
2451
  @_safe
2438
2452
  def draft_callouts(
2439
2453
  campaign_id: str,
@@ -2451,7 +2465,7 @@ def draft_callouts(
2451
2465
  )
2452
2466
 
2453
2467
 
2454
- @mcp.tool(title="Draft structured snippets", annotations=_WRITE, tags={"ads"})
2468
+ @_tool(title="Draft structured snippets", annotations=_WRITE, tags={"ads"})
2455
2469
  @_safe
2456
2470
  def draft_structured_snippets(
2457
2471
  campaign_id: str,
@@ -2469,7 +2483,7 @@ def draft_structured_snippets(
2469
2483
  )
2470
2484
 
2471
2485
 
2472
- @mcp.tool(title="Draft image assets", annotations=_WRITE, tags={"ads"})
2486
+ @_tool(title="Draft image assets", annotations=_WRITE, tags={"ads"})
2473
2487
  @_safe
2474
2488
  def draft_image_assets(
2475
2489
  campaign_id: str,
@@ -2503,7 +2517,7 @@ def draft_image_assets(
2503
2517
  )
2504
2518
 
2505
2519
 
2506
- @mcp.tool(title="Draft pausing an entity", annotations=_WRITE, tags={"ads"})
2520
+ @_tool(title="Draft pausing an entity", annotations=_WRITE, tags={"ads"})
2507
2521
  @_safe
2508
2522
  def pause_entity(
2509
2523
  entity_type: str,
@@ -2531,7 +2545,7 @@ def pause_entity(
2531
2545
  )
2532
2546
 
2533
2547
 
2534
- @mcp.tool(title="Draft enabling an entity", annotations=_WRITE, tags={"ads"})
2548
+ @_tool(title="Draft enabling an entity", annotations=_WRITE, tags={"ads"})
2535
2549
  @_safe
2536
2550
  def enable_entity(
2537
2551
  entity_type: str,
@@ -2559,7 +2573,7 @@ def enable_entity(
2559
2573
  )
2560
2574
 
2561
2575
 
2562
- @mcp.tool(title="Draft removing an entity", annotations=_DESTRUCTIVE, tags={"ads"})
2576
+ @_tool(title="Draft removing an entity", annotations=_DESTRUCTIVE, tags={"ads"})
2563
2577
  @_safe
2564
2578
  def remove_entity(
2565
2579
  entity_type: str,
@@ -2595,7 +2609,7 @@ def remove_entity(
2595
2609
  )
2596
2610
 
2597
2611
 
2598
- @mcp.tool(title="Draft sitelinks", annotations=_WRITE, tags={"ads"})
2612
+ @_tool(title="Draft sitelinks", annotations=_WRITE, tags={"ads"})
2599
2613
  @_safe
2600
2614
  def draft_sitelinks(
2601
2615
  campaign_id: str,
@@ -2628,7 +2642,7 @@ def draft_sitelinks(
2628
2642
  )
2629
2643
 
2630
2644
 
2631
- @mcp.tool(title="Apply a previewed change", annotations=_DESTRUCTIVE, tags={"core"})
2645
+ @_tool(title="Apply a previewed change", annotations=_DESTRUCTIVE, tags={"core"})
2632
2646
  @_safe
2633
2647
  def confirm_and_apply(
2634
2648
  plan_id: str,
@@ -2681,7 +2695,7 @@ def _reddit_account(ad_account_id: str) -> str:
2681
2695
  return ad_account_id or current_config().reddit.ad_account_id
2682
2696
 
2683
2697
 
2684
- @mcp.tool(title="List Reddit ad accounts", annotations=_READONLY, tags={"reddit"})
2698
+ @_tool(title="List Reddit ad accounts", annotations=_READONLY, tags={"reddit"})
2685
2699
  @_safe
2686
2700
  def list_reddit_accounts() -> dict:
2687
2701
  """List Reddit businesses and ad accounts the connected Reddit user can access.
@@ -2695,7 +2709,7 @@ def list_reddit_accounts() -> dict:
2695
2709
  return _impl(current_config())
2696
2710
 
2697
2711
 
2698
- @mcp.tool(
2712
+ @_tool(
2699
2713
  title="Reddit billing + posting profiles", annotations=_READONLY, tags={"reddit"}
2700
2714
  )
2701
2715
  @_safe
@@ -2710,7 +2724,7 @@ def list_reddit_funding_instruments(ad_account_id: str = "") -> dict:
2710
2724
  return _impl(current_config(), ad_account_id=_reddit_account(ad_account_id))
2711
2725
 
2712
2726
 
2713
- @mcp.tool(title="Reddit campaigns", annotations=_READONLY, tags={"reddit"})
2727
+ @_tool(title="Reddit campaigns", annotations=_READONLY, tags={"reddit"})
2714
2728
  @_safe
2715
2729
  def get_reddit_campaigns(ad_account_id: str = "", include_archived: bool = False) -> dict:
2716
2730
  """List Reddit campaigns with status, objective, budget mode and bids.
@@ -2729,7 +2743,7 @@ def get_reddit_campaigns(ad_account_id: str = "", include_archived: bool = False
2729
2743
  )
2730
2744
 
2731
2745
 
2732
- @mcp.tool(title="Reddit ad groups", annotations=_READONLY, tags={"reddit"})
2746
+ @_tool(title="Reddit ad groups", annotations=_READONLY, tags={"reddit"})
2733
2747
  @_safe
2734
2748
  def get_reddit_ad_groups(ad_account_id: str = "", campaign_id: str = "") -> dict:
2735
2749
  """List Reddit ad groups (budget, bid, pixel, weekly schedule, targeting summary), optionally per campaign.
@@ -2746,7 +2760,7 @@ def get_reddit_ad_groups(ad_account_id: str = "", campaign_id: str = "") -> dict
2746
2760
  )
2747
2761
 
2748
2762
 
2749
- @mcp.tool(title="Reddit ads", annotations=_READONLY, tags={"reddit"})
2763
+ @_tool(title="Reddit ads", annotations=_READONLY, tags={"reddit"})
2750
2764
  @_safe
2751
2765
  def get_reddit_ads(
2752
2766
  ad_account_id: str = "",
@@ -2772,7 +2786,7 @@ def get_reddit_ads(
2772
2786
  )
2773
2787
 
2774
2788
 
2775
- @mcp.tool(title="Reddit performance", annotations=_READONLY, tags={"reddit"})
2789
+ @_tool(title="Reddit performance", annotations=_READONLY, tags={"reddit"})
2776
2790
  @_safe
2777
2791
  def get_reddit_performance(
2778
2792
  ad_account_id: str = "",
@@ -2808,7 +2822,7 @@ def get_reddit_performance(
2808
2822
  )
2809
2823
 
2810
2824
 
2811
- @mcp.tool(title="Raw Reddit report", annotations=_READONLY, tags={"reddit"})
2825
+ @_tool(title="Raw Reddit report", annotations=_READONLY, tags={"reddit"})
2812
2826
  @_safe
2813
2827
  def run_reddit_report(
2814
2828
  fields: _StrList,
@@ -2844,7 +2858,7 @@ def run_reddit_report(
2844
2858
  )
2845
2859
 
2846
2860
 
2847
- @mcp.tool(title="Reddit pixels", annotations=_READONLY, tags={"reddit"})
2861
+ @_tool(title="Reddit pixels", annotations=_READONLY, tags={"reddit"})
2848
2862
  @_safe
2849
2863
  def get_reddit_pixels(ad_account_id: str = "") -> dict:
2850
2864
  """Reddit pixels of the account and when each event (purchase, sign_up, lead, ...) last fired.
@@ -2858,7 +2872,7 @@ def get_reddit_pixels(ad_account_id: str = "") -> dict:
2858
2872
  return _impl(current_config(), ad_account_id=_reddit_account(ad_account_id))
2859
2873
 
2860
2874
 
2861
- @mcp.tool(title="Search Reddit targeting", annotations=_READONLY, tags={"reddit"})
2875
+ @_tool(title="Search Reddit targeting", annotations=_READONLY, tags={"reddit"})
2862
2876
  @_safe
2863
2877
  def search_reddit_targeting(
2864
2878
  kind: str, query: str = "", country: str = "", website_url: str = "", limit: int = 25
@@ -2880,7 +2894,7 @@ def search_reddit_targeting(
2880
2894
  )
2881
2895
 
2882
2896
 
2883
- @mcp.tool(title="Reddit account change history", annotations=_READONLY, tags={"reddit"})
2897
+ @_tool(title="Reddit account change history", annotations=_READONLY, tags={"reddit"})
2884
2898
  @_safe
2885
2899
  def get_reddit_account_history(
2886
2900
  ad_account_id: str = "",
@@ -2911,7 +2925,7 @@ def get_reddit_account_history(
2911
2925
  )
2912
2926
 
2913
2927
 
2914
- @mcp.tool(title="Estimate a Reddit ad group", annotations=_READONLY, tags={"reddit"})
2928
+ @_tool(title="Estimate a Reddit ad group", annotations=_READONLY, tags={"reddit"})
2915
2929
  @_safe
2916
2930
  def estimate_reddit_ad_group(
2917
2931
  daily_budget: float | None = None,
@@ -2971,7 +2985,7 @@ def estimate_reddit_ad_group(
2971
2985
  )
2972
2986
 
2973
2987
 
2974
- @mcp.tool(title="Draft pausing a Reddit entity", annotations=_WRITE, tags={"reddit"})
2988
+ @_tool(title="Draft pausing a Reddit entity", annotations=_WRITE, tags={"reddit"})
2975
2989
  @_safe
2976
2990
  def pause_reddit_entity(
2977
2991
  entity_type: str, entity_id: str, ad_account_id: str = ""
@@ -2991,7 +3005,7 @@ def pause_reddit_entity(
2991
3005
  )
2992
3006
 
2993
3007
 
2994
- @mcp.tool(title="Draft enabling a Reddit entity", annotations=_WRITE, tags={"reddit"})
3008
+ @_tool(title="Draft enabling a Reddit entity", annotations=_WRITE, tags={"reddit"})
2995
3009
  @_safe
2996
3010
  def enable_reddit_entity(
2997
3011
  entity_type: str, entity_id: str, ad_account_id: str = ""
@@ -3011,7 +3025,7 @@ def enable_reddit_entity(
3011
3025
  )
3012
3026
 
3013
3027
 
3014
- @mcp.tool(title="Draft archiving a Reddit entity", annotations=_DESTRUCTIVE, tags={"reddit"})
3028
+ @_tool(title="Draft archiving a Reddit entity", annotations=_DESTRUCTIVE, tags={"reddit"})
3015
3029
  @_safe
3016
3030
  def remove_reddit_entity(
3017
3031
  entity_type: str, entity_id: str, ad_account_id: str = ""
@@ -3032,7 +3046,7 @@ def remove_reddit_entity(
3032
3046
  )
3033
3047
 
3034
3048
 
3035
- @mcp.tool(title="Draft Reddit campaign changes", annotations=_WRITE, tags={"reddit"})
3049
+ @_tool(title="Draft Reddit campaign changes", annotations=_WRITE, tags={"reddit"})
3036
3050
  @_safe
3037
3051
  def update_reddit_campaign(
3038
3052
  campaign_id: str,
@@ -3080,7 +3094,7 @@ def update_reddit_campaign(
3080
3094
  )
3081
3095
 
3082
3096
 
3083
- @mcp.tool(title="Draft Reddit ad group changes", annotations=_WRITE, tags={"reddit"})
3097
+ @_tool(title="Draft Reddit ad group changes", annotations=_WRITE, tags={"reddit"})
3084
3098
  @_safe
3085
3099
  def update_reddit_ad_group(
3086
3100
  ad_group_id: str,
@@ -3151,7 +3165,7 @@ def update_reddit_ad_group(
3151
3165
  )
3152
3166
 
3153
3167
 
3154
- @mcp.tool(title="Draft Reddit ad changes", annotations=_WRITE, tags={"reddit"})
3168
+ @_tool(title="Draft Reddit ad changes", annotations=_WRITE, tags={"reddit"})
3155
3169
  @_safe
3156
3170
  def update_reddit_ad(
3157
3171
  ad_id: str,
@@ -3178,7 +3192,7 @@ def update_reddit_ad(
3178
3192
  )
3179
3193
 
3180
3194
 
3181
- @mcp.tool(title="Draft a Reddit campaign", annotations=_WRITE, tags={"reddit"})
3195
+ @_tool(title="Draft a Reddit campaign", annotations=_WRITE, tags={"reddit"})
3182
3196
  @_safe
3183
3197
  def draft_reddit_campaign(
3184
3198
  campaign_name: str,
@@ -3237,7 +3251,7 @@ def draft_reddit_campaign(
3237
3251
  )
3238
3252
 
3239
3253
 
3240
- @mcp.tool(title="Draft a Reddit ad group", annotations=_WRITE, tags={"reddit"})
3254
+ @_tool(title="Draft a Reddit ad group", annotations=_WRITE, tags={"reddit"})
3241
3255
  @_safe
3242
3256
  def draft_reddit_ad_group(
3243
3257
  campaign_id: str,
@@ -3318,7 +3332,7 @@ def draft_reddit_ad_group(
3318
3332
  )
3319
3333
 
3320
3334
 
3321
- @mcp.tool(title="Draft a Reddit ad", annotations=_WRITE, tags={"reddit"})
3335
+ @_tool(title="Draft a Reddit ad", annotations=_WRITE, tags={"reddit"})
3322
3336
  @_safe
3323
3337
  def draft_reddit_ad(
3324
3338
  ad_group_id: str,
@@ -3374,7 +3388,7 @@ def draft_reddit_ad(
3374
3388
  # ---------------------------------------------------------------------------
3375
3389
 
3376
3390
 
3377
- @mcp.tool(title="Draft an Analytics key event", annotations=_WRITE, tags={"ga4"})
3391
+ @_tool(title="Draft an Analytics key event", annotations=_WRITE, tags={"ga4"})
3378
3392
  @_safe
3379
3393
  def draft_key_event(
3380
3394
  event_name: str,
@@ -3399,7 +3413,7 @@ def draft_key_event(
3399
3413
  )
3400
3414
 
3401
3415
 
3402
- @mcp.tool(title="Draft a conversion action", annotations=_WRITE, tags={"ads"})
3416
+ @_tool(title="Draft a conversion action", annotations=_WRITE, tags={"ads"})
3403
3417
  @_safe
3404
3418
  def draft_create_conversion_action(
3405
3419
  name: str,
@@ -3452,7 +3466,7 @@ def draft_create_conversion_action(
3452
3466
  )
3453
3467
 
3454
3468
 
3455
- @mcp.tool(title="Draft conversion action changes", annotations=_WRITE, tags={"ads"})
3469
+ @_tool(title="Draft conversion action changes", annotations=_WRITE, tags={"ads"})
3456
3470
  @_safe
3457
3471
  def draft_update_conversion_action(
3458
3472
  conversion_action_id: str,
@@ -3499,7 +3513,7 @@ def draft_update_conversion_action(
3499
3513
  )
3500
3514
 
3501
3515
 
3502
- @mcp.tool(title="Draft removing a conversion action", annotations=_WRITE, tags={"ads"})
3516
+ @_tool(title="Draft removing a conversion action", annotations=_WRITE, tags={"ads"})
3503
3517
  @_safe
3504
3518
  def draft_remove_conversion_action(
3505
3519
  conversion_action_id: str,
@@ -3523,7 +3537,7 @@ def draft_remove_conversion_action(
3523
3537
  )
3524
3538
 
3525
3539
 
3526
- @mcp.tool(title="Validate tracking", annotations=_READONLY, tags={"tracking"})
3540
+ @_tool(title="Validate tracking", annotations=_READONLY, tags={"tracking"})
3527
3541
  @_safe
3528
3542
  def validate_tracking(
3529
3543
  expected_events: _StrList,
@@ -3551,7 +3565,7 @@ def validate_tracking(
3551
3565
  )
3552
3566
 
3553
3567
 
3554
- @mcp.tool(title="Generate tracking code", annotations=_READONLY, tags={"tracking"})
3568
+ @_tool(title="Generate tracking code", annotations=_READONLY, tags={"tracking"})
3555
3569
  @_safe
3556
3570
  def generate_tracking_code(
3557
3571
  event_name: str,
@@ -3586,7 +3600,7 @@ def generate_tracking_code(
3586
3600
  # ---------------------------------------------------------------------------
3587
3601
 
3588
3602
 
3589
- @mcp.tool(title="Budget forecast", annotations=_READONLY, tags={"ads"})
3603
+ @_tool(title="Budget forecast", annotations=_READONLY, tags={"ads"})
3590
3604
  @_safe
3591
3605
  def estimate_budget(
3592
3606
  keywords: _DictList,
@@ -3623,7 +3637,7 @@ def estimate_budget(
3623
3637
  )
3624
3638
 
3625
3639
 
3626
- @mcp.tool(title="Keyword ideas", annotations=_READONLY, tags={"ads"})
3640
+ @_tool(title="Keyword ideas", annotations=_READONLY, tags={"ads"})
3627
3641
  @_safe
3628
3642
  def discover_keywords(
3629
3643
  seed_keywords: _StrList = [], # noqa: B006 — mutable default required for MCP JSON schema
@@ -3671,7 +3685,7 @@ def discover_keywords(
3671
3685
  # ---------------------------------------------------------------------------
3672
3686
 
3673
3687
 
3674
- @mcp.tool(title="Suggest brands", annotations=_READONLY, tags={"ads"})
3688
+ @_tool(title="Suggest brands", annotations=_READONLY, tags={"ads"})
3675
3689
  @_safe
3676
3690
  def suggest_brands(
3677
3691
  brand_prefix: str,
@@ -3708,7 +3722,7 @@ def suggest_brands(
3708
3722
  )
3709
3723
 
3710
3724
 
3711
- @mcp.tool(title="Check brand names", annotations=_READONLY, tags={"ads"})
3725
+ @_tool(title="Check brand names", annotations=_READONLY, tags={"ads"})
3712
3726
  @_safe
3713
3727
  def check_brand_names(
3714
3728
  brand_names: _StrList,
@@ -3741,7 +3755,7 @@ def check_brand_names(
3741
3755
  customer_id=customer_id or current_config().ads.customer_id,
3742
3756
  )
3743
3757
 
3744
- @mcp.tool(title="List brand lists", annotations=_READONLY, tags={"ads"})
3758
+ @_tool(title="List brand lists", annotations=_READONLY, tags={"ads"})
3745
3759
  @_safe
3746
3760
  def get_brand_lists(
3747
3761
  customer_id: str = "",
@@ -3761,7 +3775,7 @@ def get_brand_lists(
3761
3775
  customer_id=customer_id or current_config().ads.customer_id,
3762
3776
  )
3763
3777
 
3764
- @mcp.tool(title="Brands in a brand list", annotations=_READONLY, tags={"ads"})
3778
+ @_tool(title="Brands in a brand list", annotations=_READONLY, tags={"ads"})
3765
3779
  @_safe
3766
3780
  def get_brand_list_brands(
3767
3781
  shared_set_id: str,
@@ -3785,7 +3799,7 @@ def get_brand_list_brands(
3785
3799
  shared_set_id=shared_set_id,
3786
3800
  )
3787
3801
 
3788
- @mcp.tool(title="Campaigns using a brand list", annotations=_READONLY, tags={"ads"})
3802
+ @_tool(title="Campaigns using a brand list", annotations=_READONLY, tags={"ads"})
3789
3803
  @_safe
3790
3804
  def get_brand_list_campaigns(
3791
3805
  shared_set_id: str = "",
@@ -3811,7 +3825,7 @@ def get_brand_list_campaigns(
3811
3825
  shared_set_id=shared_set_id,
3812
3826
  )
3813
3827
 
3814
- @mcp.tool(title="Draft a brand list", annotations=_WRITE, tags={"ads"})
3828
+ @_tool(title="Draft a brand list", annotations=_WRITE, tags={"ads"})
3815
3829
  @_safe
3816
3830
  def propose_brand_list(
3817
3831
  list_name: str,
@@ -3849,7 +3863,7 @@ def propose_brand_list(
3849
3863
  negative=negative,
3850
3864
  )
3851
3865
 
3852
- @mcp.tool(title="Draft additions to a brand list", annotations=_WRITE, tags={"ads"})
3866
+ @_tool(title="Draft additions to a brand list", annotations=_WRITE, tags={"ads"})
3853
3867
  @_safe
3854
3868
  def add_to_brand_list(
3855
3869
  shared_set_id: str,
@@ -3879,7 +3893,7 @@ def add_to_brand_list(
3879
3893
  brand_ids=brand_ids,
3880
3894
  )
3881
3895
 
3882
- @mcp.tool(title="Draft removing brands from a list", annotations=_DESTRUCTIVE, tags={"ads"})
3896
+ @_tool(title="Draft removing brands from a list", annotations=_DESTRUCTIVE, tags={"ads"})
3883
3897
  @_safe
3884
3898
  def remove_from_brand_list(
3885
3899
  shared_set_id: str,
@@ -3909,7 +3923,7 @@ def remove_from_brand_list(
3909
3923
  criterion_ids=criterion_ids,
3910
3924
  )
3911
3925
 
3912
- @mcp.tool(title="Draft attaching a brand list", annotations=_WRITE, tags={"ads"})
3926
+ @_tool(title="Draft attaching a brand list", annotations=_WRITE, tags={"ads"})
3913
3927
  @_safe
3914
3928
  def attach_brand_list_to_campaigns(
3915
3929
  shared_set_id: str,
@@ -3946,7 +3960,7 @@ def attach_brand_list_to_campaigns(
3946
3960
  negative=negative,
3947
3961
  )
3948
3962
 
3949
- @mcp.tool(title="Draft detaching a brand list", annotations=_DESTRUCTIVE, tags={"ads"})
3963
+ @_tool(title="Draft detaching a brand list", annotations=_DESTRUCTIVE, tags={"ads"})
3950
3964
  @_safe
3951
3965
  def detach_brand_list_from_campaigns(
3952
3966
  shared_set_id: str,
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