@yoryoboy/bi-mcp 1.5.2 → 1.6.0

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 (182) hide show
  1. package/README.md +87 -87
  2. package/bin/bi-mcp.js +9 -9
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/index.js +1 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/mcp-use.json +2 -2
  7. package/dist/public/icon.svg +6 -6
  8. package/dist/scripts/_helpers.js.map +1 -1
  9. package/dist/scripts/admin-profile-delete.js.map +1 -1
  10. package/dist/scripts/admin-profile-list.js.map +1 -1
  11. package/dist/scripts/admin-profile-upsert.js.map +1 -1
  12. package/dist/scripts/admin-vtex-list.js.map +1 -1
  13. package/dist/scripts/admin-vtex-upsert.js.map +1 -1
  14. package/dist/scripts/admin-vtex-validate.js.map +1 -1
  15. package/dist/scripts/decrypt-secret.js +36 -0
  16. package/dist/scripts/decrypt-secret.js.map +7 -0
  17. package/dist/scripts/run-migrations.js.map +1 -1
  18. package/dist/scripts/test-db-connection.js.map +1 -1
  19. package/dist/src/analytics/ga4-channel-groups.js.map +1 -1
  20. package/dist/src/analytics/ga4-report-utils.js.map +1 -1
  21. package/dist/src/config/benchmarks.js.map +1 -1
  22. package/dist/src/config/google-store.js.map +1 -1
  23. package/dist/src/config/google.js.map +1 -1
  24. package/dist/src/config/mercadolibre-profile-store.js.map +1 -1
  25. package/dist/src/config/mercadolibre.js.map +1 -1
  26. package/dist/src/config/meta.js.map +1 -1
  27. package/dist/src/config/profile-store.js.map +1 -1
  28. package/dist/src/config/vtex-crypto.js.map +1 -1
  29. package/dist/src/config/vtex-profile-store.js.map +1 -1
  30. package/dist/src/config/vtex.js.map +1 -1
  31. package/dist/src/db/client.js.map +1 -1
  32. package/dist/src/meta/meta-utils.js.map +1 -1
  33. package/dist/src/prompts/reporte-ventas.js.map +1 -1
  34. package/dist/src/services/analytics/ga4-client.js.map +1 -1
  35. package/dist/src/services/analytics/oauth.js.map +1 -1
  36. package/dist/src/services/google-ads/google-ads-client.js.map +1 -1
  37. package/dist/src/services/mercadolibre/mercadolibre-api.js.map +1 -1
  38. package/dist/src/services/mercadolibre/mercadolibre-items.js.map +1 -1
  39. package/dist/src/services/mercadolibre/mercadolibre-orders.js +19 -5
  40. package/dist/src/services/mercadolibre/mercadolibre-orders.js.map +2 -2
  41. package/dist/src/services/mercadolibre/mercadolibre-questions.js.map +1 -1
  42. package/dist/src/services/meta/meta-ads.js.map +1 -1
  43. package/dist/src/services/meta/meta-api.js.map +1 -1
  44. package/dist/src/services/search-console/search-console-client.js.map +1 -1
  45. package/dist/src/services/search-console/search-console-utils.js.map +1 -1
  46. package/dist/src/services/vtex/vtex-api.js.map +1 -1
  47. package/dist/src/services/vtex/vtex-catalog-write.js.map +1 -1
  48. package/dist/src/services/vtex/vtex-catalog.js.map +1 -1
  49. package/dist/src/services/vtex/vtex-logistics.js.map +1 -1
  50. package/dist/src/services/vtex/vtex-orders-write.js.map +1 -1
  51. package/dist/src/services/vtex/vtex-orders.js.map +1 -1
  52. package/dist/src/services/vtex/vtex-pricing-write.js.map +1 -1
  53. package/dist/src/services/vtex/vtex-pricing.js.map +1 -1
  54. package/dist/src/services/vtex/vtex-write.js.map +1 -1
  55. package/dist/src/tools/analytics/attribution-gaps.js.map +1 -1
  56. package/dist/src/tools/analytics/channel-mix.js.map +1 -1
  57. package/dist/src/tools/analytics/ecommerce-tracking-health.js.map +1 -1
  58. package/dist/src/tools/analytics/engagement-overview.js.map +1 -1
  59. package/dist/src/tools/analytics/index.js.map +1 -1
  60. package/dist/src/tools/analytics/list-accessible-properties.js.map +1 -1
  61. package/dist/src/tools/analytics/property-info.js.map +1 -1
  62. package/dist/src/tools/analytics/revenue-by-channel.js.map +1 -1
  63. package/dist/src/tools/analytics/revenue-overview.js.map +1 -1
  64. package/dist/src/tools/analytics/revenue-trend.js.map +1 -1
  65. package/dist/src/tools/analytics/source-medium-breakdown.js.map +1 -1
  66. package/dist/src/tools/analytics/top-landing-pages.js.map +1 -1
  67. package/dist/src/tools/config/check-database-connection.js.map +1 -1
  68. package/dist/src/tools/config/index.js.map +1 -1
  69. package/dist/src/tools/config/list-profiles.js.map +1 -1
  70. package/dist/src/tools/google-ads/account-overview.js.map +1 -1
  71. package/dist/src/tools/google-ads/account-risks.js.map +1 -1
  72. package/dist/src/tools/google-ads/break-even-analysis.js.map +1 -1
  73. package/dist/src/tools/google-ads/campaign-performance.js.map +1 -1
  74. package/dist/src/tools/google-ads/channel-mix.js.map +1 -1
  75. package/dist/src/tools/google-ads/compare-accounts.js.map +1 -1
  76. package/dist/src/tools/google-ads/customer-clients.js.map +1 -1
  77. package/dist/src/tools/google-ads/customer-info.js.map +1 -1
  78. package/dist/src/tools/google-ads/index.js.map +1 -1
  79. package/dist/src/tools/google-ads/scaling-health.js.map +1 -1
  80. package/dist/src/tools/google-ads/search-terms-summary.js.map +1 -1
  81. package/dist/src/tools/google-ads/time-series.js.map +1 -1
  82. package/dist/src/tools/index.js.map +1 -1
  83. package/dist/src/tools/mercadolibre/answer-question.js.map +1 -1
  84. package/dist/src/tools/mercadolibre/create-item.js.map +1 -1
  85. package/dist/src/tools/mercadolibre/estimate-listing-fee.js.map +1 -1
  86. package/dist/src/tools/mercadolibre/get-account-context.js.map +1 -1
  87. package/dist/src/tools/mercadolibre/get-category-requirements.js.map +1 -1
  88. package/dist/src/tools/mercadolibre/get-item-details.js.map +1 -1
  89. package/dist/src/tools/mercadolibre/get-item-visits.js.map +1 -1
  90. package/dist/src/tools/mercadolibre/get-listing-quality.js.map +1 -1
  91. package/dist/src/tools/mercadolibre/get-order-details.js.map +1 -1
  92. package/dist/src/tools/mercadolibre/get-orders-summary.js +670 -38
  93. package/dist/src/tools/mercadolibre/get-orders-summary.js.map +2 -2
  94. package/dist/src/tools/mercadolibre/get-sales-by-item.js.map +1 -1
  95. package/dist/src/tools/mercadolibre/get-sales-trend.js.map +1 -1
  96. package/dist/src/tools/mercadolibre/get-shipping-summary.js.map +1 -1
  97. package/dist/src/tools/mercadolibre/get-store-performance.js.map +1 -1
  98. package/dist/src/tools/mercadolibre/helpers.js +13 -0
  99. package/dist/src/tools/mercadolibre/helpers.js.map +2 -2
  100. package/dist/src/tools/mercadolibre/index.js.map +1 -1
  101. package/dist/src/tools/mercadolibre/pause-or-reactivate-item.js.map +1 -1
  102. package/dist/src/tools/mercadolibre/predict-category.js.map +1 -1
  103. package/dist/src/tools/mercadolibre/profile-resolution.js.map +1 -1
  104. package/dist/src/tools/mercadolibre/search-items.js.map +1 -1
  105. package/dist/src/tools/mercadolibre/search-questions.js.map +1 -1
  106. package/dist/src/tools/mercadolibre/update-item-basic-fields.js.map +1 -1
  107. package/dist/src/tools/mercadolibre/update-item-description.js.map +1 -1
  108. package/dist/src/tools/mercadolibre/update-item-pictures.js.map +1 -1
  109. package/dist/src/tools/mercadolibre/validate-connection.js.map +1 -1
  110. package/dist/src/tools/mercadolibre/write-helpers.js.map +1 -1
  111. package/dist/src/tools/meta/account-overview.js.map +1 -1
  112. package/dist/src/tools/meta/ad-account-info.js.map +1 -1
  113. package/dist/src/tools/meta/ads-performance.js.map +1 -1
  114. package/dist/src/tools/meta/campaign-performance.js.map +1 -1
  115. package/dist/src/tools/meta/index.js.map +1 -1
  116. package/dist/src/tools/meta/list-accessible-ad-accounts.js.map +1 -1
  117. package/dist/src/tools/meta/list-accessible-businesses.js.map +1 -1
  118. package/dist/src/tools/meta/placement-mix.js.map +1 -1
  119. package/dist/src/tools/meta/time-series.js.map +1 -1
  120. package/dist/src/tools/search-console/country-breakdown.js.map +1 -1
  121. package/dist/src/tools/search-console/device-breakdown.js.map +1 -1
  122. package/dist/src/tools/search-console/high-impression-low-click-queries.js.map +1 -1
  123. package/dist/src/tools/search-console/index.js.map +1 -1
  124. package/dist/src/tools/search-console/list-accessible-sites.js.map +1 -1
  125. package/dist/src/tools/search-console/low-ctr-opportunities.js.map +1 -1
  126. package/dist/src/tools/search-console/page-performance.js.map +1 -1
  127. package/dist/src/tools/search-console/product-demand-low-capture-queries.js.map +1 -1
  128. package/dist/src/tools/search-console/query-page-matrix.js.map +1 -1
  129. package/dist/src/tools/search-console/query-performance.js.map +1 -1
  130. package/dist/src/tools/search-console/quick-win-opportunities.js.map +1 -1
  131. package/dist/src/tools/search-console/rising-non-brand-queries.js.map +1 -1
  132. package/dist/src/tools/search-console/search-performance.js.map +1 -1
  133. package/dist/src/tools/search-console/site-context.js.map +1 -1
  134. package/dist/src/tools/search-console/visibility-declines.js.map +1 -1
  135. package/dist/src/tools/vtex/activate-sku.js.map +1 -1
  136. package/dist/src/tools/vtex/add-order-tracking.js.map +1 -1
  137. package/dist/src/tools/vtex/associate-specification.js.map +1 -1
  138. package/dist/src/tools/vtex/attach-catalog-image.js.map +1 -1
  139. package/dist/src/tools/vtex/cancel-order.js.map +1 -1
  140. package/dist/src/tools/vtex/computed-price.js.map +1 -1
  141. package/dist/src/tools/vtex/create-brand.js.map +1 -1
  142. package/dist/src/tools/vtex/create-category.js.map +1 -1
  143. package/dist/src/tools/vtex/create-product-with-sku.js.map +1 -1
  144. package/dist/src/tools/vtex/create-product.js.map +1 -1
  145. package/dist/src/tools/vtex/create-sku.js.map +1 -1
  146. package/dist/src/tools/vtex/create-specification-value.js.map +1 -1
  147. package/dist/src/tools/vtex/create-specification.js.map +1 -1
  148. package/dist/src/tools/vtex/deactivate-sku.js.map +1 -1
  149. package/dist/src/tools/vtex/delete-fixed-price.js.map +1 -1
  150. package/dist/src/tools/vtex/index.js.map +1 -1
  151. package/dist/src/tools/vtex/inventory-check.js.map +1 -1
  152. package/dist/src/tools/vtex/invoice-order.js.map +1 -1
  153. package/dist/src/tools/vtex/order-details.js.map +1 -1
  154. package/dist/src/tools/vtex/orders-summary.js.map +1 -1
  155. package/dist/src/tools/vtex/product-offers.js.map +1 -1
  156. package/dist/src/tools/vtex/profile-resolution.js.map +1 -1
  157. package/dist/src/tools/vtex/sku-offers.js.map +1 -1
  158. package/dist/src/tools/vtex/sku-price.js.map +1 -1
  159. package/dist/src/tools/vtex/toggle-unlimited-quantity.js.map +1 -1
  160. package/dist/src/tools/vtex/update-inventory.js.map +1 -1
  161. package/dist/src/tools/vtex/update-lead-time.js.map +1 -1
  162. package/dist/src/tools/vtex/update-product-basic-fields.js.map +1 -1
  163. package/dist/src/tools/vtex/update-sku-basic-fields.js.map +1 -1
  164. package/dist/src/tools/vtex/update-sku-price.js.map +1 -1
  165. package/dist/src/tools/vtex/upsert-fixed-price.js.map +1 -1
  166. package/dist/src/tools/vtex/warehouse-inventory.js.map +1 -1
  167. package/dist/src/tools/vtex/write-helpers.js.map +1 -1
  168. package/dist/src/utils/case-conversion.js.map +1 -1
  169. package/dist/src/utils/currency.js.map +1 -1
  170. package/dist/src/utils/format-order-details.js.map +1 -1
  171. package/dist/src/utils/google-ads.js.map +1 -1
  172. package/dist/src/utils/money.js.map +1 -1
  173. package/dist/src/utils/order-status.js.map +1 -1
  174. package/dist/src/utils/pagination.js.map +1 -1
  175. package/dist/src/utils/strip-payload.js.map +1 -1
  176. package/dist/src/utils/type-guards.js.map +1 -1
  177. package/package.json +4 -3
  178. package/public/icon.svg +6 -6
  179. package/dist/src/google-ads/report-utils.js +0 -78
  180. package/dist/src/google-ads/report-utils.js.map +0 -7
  181. package/dist/src/search-console/search-console-utils.js +0 -275
  182. package/dist/src/search-console/search-console-utils.js.map +0 -7
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/tools/analytics/top-landing-pages.ts"],
4
- "sourcesContent": ["import { error, object } from \"mcp-use/server\";\nimport { z } from \"zod\";\n\nimport {\n extractQuotaSnapshot,\n getDimensionValue,\n getMetricValueByName,\n resolveGa4PropertyId,\n round,\n toPercent,\n} from \"../../analytics/ga4-report-utils.js\";\nimport { runReport } from \"../../services/analytics/ga4-client.js\";\nimport { stripNulls } from \"../../utils/strip-payload.js\";\n\nconst dateRegex = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nexport const topLandingPagesSchema = z.object({\n startDate: z.string().regex(dateRegex).describe(\"Start date in YYYY-MM-DD format\"),\n endDate: z.string().regex(dateRegex).describe(\"End date in YYYY-MM-DD format\"),\n propertyId: z.string().optional().describe(\"GA4 property ID. If omitted, resolves profileId mapping first and then GA4_PROPERTY_ID.\"),\n profileId: z.string().optional().describe(\"Optional business profile ID used to resolve the mapped GA4 property.\"),\n limit: z.number().int().min(1).max(100).optional().describe(\"Maximum rows to return\"),\n});\n\nexport async function topLandingPagesHandler(params: z.infer<typeof topLandingPagesSchema>) {\n try {\n const propertyId = await resolveGa4PropertyId(params.propertyId, params.profileId);\n const limit = params.limit ?? 20;\n\n const [landingReport, totalReport] = await Promise.all([\n runReport(propertyId, {\n dateRanges: [{ startDate: params.startDate, endDate: params.endDate }],\n dimensions: [{ name: \"landingPage\" }],\n metrics: [{ name: \"sessions\" }, { name: \"totalUsers\" }],\n orderBys: [{ metric: { metricName: \"sessions\" }, desc: true }],\n limit,\n returnPropertyQuota: true,\n }),\n runReport(propertyId, {\n dateRanges: [{ startDate: params.startDate, endDate: params.endDate }],\n metrics: [{ name: \"sessions\" }, { name: \"totalUsers\" }],\n }),\n ]);\n\n const totalSessions = getMetricValueByName(\n totalReport.rows?.[0] ?? {},\n totalReport.metricHeaders,\n \"sessions\"\n );\n\n const landingPages = (landingReport.rows ?? []).map((row) => {\n const sessions = getMetricValueByName(row, landingReport.metricHeaders, \"sessions\");\n const users = getMetricValueByName(row, landingReport.metricHeaders, \"totalUsers\");\n\n return {\n landing_page: getDimensionValue(row),\n sessions,\n users,\n session_share_percent: round(toPercent(sessions, totalSessions)),\n is_not_set: getDimensionValue(row) === \"(not set)\",\n };\n });\n\n return object(\n stripNulls({\n property_id: propertyId,\n date_range: {\n start_date: params.startDate,\n end_date: params.endDate,\n },\n overview: {\n total_sessions: totalSessions,\n returned_rows: landingPages.length,\n available_rows: landingReport.rowCount,\n top_landing_page: landingPages[0]?.landing_page,\n },\n landing_pages: landingPages,\n quota: extractQuotaSnapshot(landingReport),\n })\n );\n } catch (err) {\n return error(err instanceof Error ? err.message : \"Failed to fetch GA4 top landing pages\");\n }\n}\n"],
4
+ "sourcesContent": ["import { error, object } from \"mcp-use/server\";\r\nimport { z } from \"zod\";\r\n\r\nimport {\r\n extractQuotaSnapshot,\r\n getDimensionValue,\r\n getMetricValueByName,\r\n resolveGa4PropertyId,\r\n round,\r\n toPercent,\r\n} from \"../../analytics/ga4-report-utils.js\";\r\nimport { runReport } from \"../../services/analytics/ga4-client.js\";\r\nimport { stripNulls } from \"../../utils/strip-payload.js\";\r\n\r\nconst dateRegex = /^\\d{4}-\\d{2}-\\d{2}$/;\r\n\r\nexport const topLandingPagesSchema = z.object({\r\n startDate: z.string().regex(dateRegex).describe(\"Start date in YYYY-MM-DD format\"),\r\n endDate: z.string().regex(dateRegex).describe(\"End date in YYYY-MM-DD format\"),\r\n propertyId: z.string().optional().describe(\"GA4 property ID. If omitted, resolves profileId mapping first and then GA4_PROPERTY_ID.\"),\r\n profileId: z.string().optional().describe(\"Optional business profile ID used to resolve the mapped GA4 property.\"),\r\n limit: z.number().int().min(1).max(100).optional().describe(\"Maximum rows to return\"),\r\n});\r\n\r\nexport async function topLandingPagesHandler(params: z.infer<typeof topLandingPagesSchema>) {\r\n try {\r\n const propertyId = await resolveGa4PropertyId(params.propertyId, params.profileId);\r\n const limit = params.limit ?? 20;\r\n\r\n const [landingReport, totalReport] = await Promise.all([\r\n runReport(propertyId, {\r\n dateRanges: [{ startDate: params.startDate, endDate: params.endDate }],\r\n dimensions: [{ name: \"landingPage\" }],\r\n metrics: [{ name: \"sessions\" }, { name: \"totalUsers\" }],\r\n orderBys: [{ metric: { metricName: \"sessions\" }, desc: true }],\r\n limit,\r\n returnPropertyQuota: true,\r\n }),\r\n runReport(propertyId, {\r\n dateRanges: [{ startDate: params.startDate, endDate: params.endDate }],\r\n metrics: [{ name: \"sessions\" }, { name: \"totalUsers\" }],\r\n }),\r\n ]);\r\n\r\n const totalSessions = getMetricValueByName(\r\n totalReport.rows?.[0] ?? {},\r\n totalReport.metricHeaders,\r\n \"sessions\"\r\n );\r\n\r\n const landingPages = (landingReport.rows ?? []).map((row) => {\r\n const sessions = getMetricValueByName(row, landingReport.metricHeaders, \"sessions\");\r\n const users = getMetricValueByName(row, landingReport.metricHeaders, \"totalUsers\");\r\n\r\n return {\r\n landing_page: getDimensionValue(row),\r\n sessions,\r\n users,\r\n session_share_percent: round(toPercent(sessions, totalSessions)),\r\n is_not_set: getDimensionValue(row) === \"(not set)\",\r\n };\r\n });\r\n\r\n return object(\r\n stripNulls({\r\n property_id: propertyId,\r\n date_range: {\r\n start_date: params.startDate,\r\n end_date: params.endDate,\r\n },\r\n overview: {\r\n total_sessions: totalSessions,\r\n returned_rows: landingPages.length,\r\n available_rows: landingReport.rowCount,\r\n top_landing_page: landingPages[0]?.landing_page,\r\n },\r\n landing_pages: landingPages,\r\n quota: extractQuotaSnapshot(landingReport),\r\n })\r\n );\r\n } catch (err) {\r\n return error(err instanceof Error ? err.message : \"Failed to fetch GA4 top landing pages\");\r\n }\r\n}\r\n"],
5
5
  "mappings": "AAAA,SAAS,OAAO,cAAc;AAC9B,SAAS,SAAS;AAElB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAE3B,MAAM,YAAY;AAEX,MAAM,wBAAwB,EAAE,OAAO;AAAA,EAC5C,WAAW,EAAE,OAAO,EAAE,MAAM,SAAS,EAAE,SAAS,iCAAiC;AAAA,EACjF,SAAS,EAAE,OAAO,EAAE,MAAM,SAAS,EAAE,SAAS,+BAA+B;AAAA,EAC7E,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yFAAyF;AAAA,EACpI,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,uEAAuE;AAAA,EACjH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,SAAS,EAAE,SAAS,wBAAwB;AACtF,CAAC;AAED,eAAsB,uBAAuB,QAA+C;AAC1F,MAAI;AACF,UAAM,aAAa,MAAM,qBAAqB,OAAO,YAAY,OAAO,SAAS;AACjF,UAAM,QAAQ,OAAO,SAAS;AAE9B,UAAM,CAAC,eAAe,WAAW,IAAI,MAAM,QAAQ,IAAI;AAAA,MACrD,UAAU,YAAY;AAAA,QACpB,YAAY,CAAC,EAAE,WAAW,OAAO,WAAW,SAAS,OAAO,QAAQ,CAAC;AAAA,QACrE,YAAY,CAAC,EAAE,MAAM,cAAc,CAAC;AAAA,QACpC,SAAS,CAAC,EAAE,MAAM,WAAW,GAAG,EAAE,MAAM,aAAa,CAAC;AAAA,QACtD,UAAU,CAAC,EAAE,QAAQ,EAAE,YAAY,WAAW,GAAG,MAAM,KAAK,CAAC;AAAA,QAC7D;AAAA,QACA,qBAAqB;AAAA,MACvB,CAAC;AAAA,MACD,UAAU,YAAY;AAAA,QACpB,YAAY,CAAC,EAAE,WAAW,OAAO,WAAW,SAAS,OAAO,QAAQ,CAAC;AAAA,QACrE,SAAS,CAAC,EAAE,MAAM,WAAW,GAAG,EAAE,MAAM,aAAa,CAAC;AAAA,MACxD,CAAC;AAAA,IACH,CAAC;AAED,UAAM,gBAAgB;AAAA,MACpB,YAAY,OAAO,CAAC,KAAK,CAAC;AAAA,MAC1B,YAAY;AAAA,MACZ;AAAA,IACF;AAEA,UAAM,gBAAgB,cAAc,QAAQ,CAAC,GAAG,IAAI,CAAC,QAAQ;AAC3D,YAAM,WAAW,qBAAqB,KAAK,cAAc,eAAe,UAAU;AAClF,YAAM,QAAQ,qBAAqB,KAAK,cAAc,eAAe,YAAY;AAEjF,aAAO;AAAA,QACL,cAAc,kBAAkB,GAAG;AAAA,QACnC;AAAA,QACA;AAAA,QACA,uBAAuB,MAAM,UAAU,UAAU,aAAa,CAAC;AAAA,QAC/D,YAAY,kBAAkB,GAAG,MAAM;AAAA,MACzC;AAAA,IACF,CAAC;AAED,WAAO;AAAA,MACL,WAAW;AAAA,QACT,aAAa;AAAA,QACb,YAAY;AAAA,UACV,YAAY,OAAO;AAAA,UACnB,UAAU,OAAO;AAAA,QACnB;AAAA,QACA,UAAU;AAAA,UACR,gBAAgB;AAAA,UAChB,eAAe,aAAa;AAAA,UAC5B,gBAAgB,cAAc;AAAA,UAC9B,kBAAkB,aAAa,CAAC,GAAG;AAAA,QACrC;AAAA,QACA,eAAe;AAAA,QACf,OAAO,qBAAqB,aAAa;AAAA,MAC3C,CAAC;AAAA,IACH;AAAA,EACF,SAAS,KAAK;AACZ,WAAO,MAAM,eAAe,QAAQ,IAAI,UAAU,uCAAuC;AAAA,EAC3F;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/tools/config/check-database-connection.ts"],
4
- "sourcesContent": ["import { error, object } from \"mcp-use/server\";\nimport { z } from \"zod\";\n\nimport { checkTableExists, getDatabaseUrl, testDatabaseConnection } from \"../../db/client.js\";\nimport { stripNulls } from \"../../utils/strip-payload.js\";\n\nexport const checkDatabaseConnectionSchema = z.object({});\n\nfunction getConnectionHints() {\n const databaseUrl = getDatabaseUrl();\n if (!databaseUrl) {\n return {\n database_url_present: false,\n host_hint: undefined,\n port_hint: undefined,\n };\n }\n\n try {\n const parsedUrl = new URL(databaseUrl);\n\n return {\n database_url_present: true,\n host_hint: parsedUrl.hostname || undefined,\n port_hint: parsedUrl.port || undefined,\n };\n } catch {\n return {\n database_url_present: true,\n host_hint: undefined,\n port_hint: undefined,\n };\n }\n}\n\nexport async function checkDatabaseConnectionHandler() {\n try {\n const result = await testDatabaseConnection();\n const schemaMigrationsTablePresent = await checkTableExists(\"schema_migrations\");\n const profilesTablePresent = await checkTableExists(\"profiles\");\n const profileVtexConnectionsTablePresent = await checkTableExists(\"profile_vtex_connections\");\n const hints = getConnectionHints();\n\n return object(\n stripNulls({\n status: \"ok\",\n database: result.current_database,\n user: result.current_user,\n server_time: result.now.toISOString(),\n database_url_present: hints.database_url_present,\n host_hint: hints.host_hint,\n port_hint: hints.port_hint,\n schema_migrations_table_present: schemaMigrationsTablePresent,\n profiles_table_present: profilesTablePresent,\n profile_vtex_connections_table_present: profileVtexConnectionsTablePresent,\n })\n );\n } catch (err) {\n return error(err instanceof Error ? err.message : \"Database connection check failed\");\n }\n}\n"],
4
+ "sourcesContent": ["import { error, object } from \"mcp-use/server\";\r\nimport { z } from \"zod\";\r\n\r\nimport { checkTableExists, getDatabaseUrl, testDatabaseConnection } from \"../../db/client.js\";\r\nimport { stripNulls } from \"../../utils/strip-payload.js\";\r\n\r\nexport const checkDatabaseConnectionSchema = z.object({});\r\n\r\nfunction getConnectionHints() {\r\n const databaseUrl = getDatabaseUrl();\r\n if (!databaseUrl) {\r\n return {\r\n database_url_present: false,\r\n host_hint: undefined,\r\n port_hint: undefined,\r\n };\r\n }\r\n\r\n try {\r\n const parsedUrl = new URL(databaseUrl);\r\n\r\n return {\r\n database_url_present: true,\r\n host_hint: parsedUrl.hostname || undefined,\r\n port_hint: parsedUrl.port || undefined,\r\n };\r\n } catch {\r\n return {\r\n database_url_present: true,\r\n host_hint: undefined,\r\n port_hint: undefined,\r\n };\r\n }\r\n}\r\n\r\nexport async function checkDatabaseConnectionHandler() {\r\n try {\r\n const result = await testDatabaseConnection();\r\n const schemaMigrationsTablePresent = await checkTableExists(\"schema_migrations\");\r\n const profilesTablePresent = await checkTableExists(\"profiles\");\r\n const profileVtexConnectionsTablePresent = await checkTableExists(\"profile_vtex_connections\");\r\n const hints = getConnectionHints();\r\n\r\n return object(\r\n stripNulls({\r\n status: \"ok\",\r\n database: result.current_database,\r\n user: result.current_user,\r\n server_time: result.now.toISOString(),\r\n database_url_present: hints.database_url_present,\r\n host_hint: hints.host_hint,\r\n port_hint: hints.port_hint,\r\n schema_migrations_table_present: schemaMigrationsTablePresent,\r\n profiles_table_present: profilesTablePresent,\r\n profile_vtex_connections_table_present: profileVtexConnectionsTablePresent,\r\n })\r\n );\r\n } catch (err) {\r\n return error(err instanceof Error ? err.message : \"Database connection check failed\");\r\n }\r\n}\r\n"],
5
5
  "mappings": "AAAA,SAAS,OAAO,cAAc;AAC9B,SAAS,SAAS;AAElB,SAAS,kBAAkB,gBAAgB,8BAA8B;AACzE,SAAS,kBAAkB;AAEpB,MAAM,gCAAgC,EAAE,OAAO,CAAC,CAAC;AAExD,SAAS,qBAAqB;AAC5B,QAAM,cAAc,eAAe;AACnC,MAAI,CAAC,aAAa;AAChB,WAAO;AAAA,MACL,sBAAsB;AAAA,MACtB,WAAW;AAAA,MACX,WAAW;AAAA,IACb;AAAA,EACF;AAEA,MAAI;AACF,UAAM,YAAY,IAAI,IAAI,WAAW;AAErC,WAAO;AAAA,MACL,sBAAsB;AAAA,MACtB,WAAW,UAAU,YAAY;AAAA,MACjC,WAAW,UAAU,QAAQ;AAAA,IAC/B;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,MACL,sBAAsB;AAAA,MACtB,WAAW;AAAA,MACX,WAAW;AAAA,IACb;AAAA,EACF;AACF;AAEA,eAAsB,iCAAiC;AACrD,MAAI;AACF,UAAM,SAAS,MAAM,uBAAuB;AAC5C,UAAM,+BAA+B,MAAM,iBAAiB,mBAAmB;AAC/E,UAAM,uBAAuB,MAAM,iBAAiB,UAAU;AAC9D,UAAM,qCAAqC,MAAM,iBAAiB,0BAA0B;AAC5F,UAAM,QAAQ,mBAAmB;AAEjC,WAAO;AAAA,MACL,WAAW;AAAA,QACT,QAAQ;AAAA,QACR,UAAU,OAAO;AAAA,QACjB,MAAM,OAAO;AAAA,QACb,aAAa,OAAO,IAAI,YAAY;AAAA,QACpC,sBAAsB,MAAM;AAAA,QAC5B,WAAW,MAAM;AAAA,QACjB,WAAW,MAAM;AAAA,QACjB,iCAAiC;AAAA,QACjC,wBAAwB;AAAA,QACxB,wCAAwC;AAAA,MAC1C,CAAC;AAAA,IACH;AAAA,EACF,SAAS,KAAK;AACZ,WAAO,MAAM,eAAe,QAAQ,IAAI,UAAU,kCAAkC;AAAA,EACtF;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/tools/config/index.ts"],
4
- "sourcesContent": ["export * from \"./check-database-connection.js\";\nexport * from \"./list-profiles.js\";\n"],
4
+ "sourcesContent": ["export * from \"./check-database-connection.js\";\r\nexport * from \"./list-profiles.js\";\r\n"],
5
5
  "mappings": "AAAA,cAAc;AACd,cAAc;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/tools/config/list-profiles.ts"],
4
- "sourcesContent": ["import { error, object } from 'mcp-use/server';\nimport { z } from 'zod';\nimport { listProfileGoogleServiceMappings } from '../../config/google-store.js';\nimport { listProfiles } from '../../config/profile-store.js';\n\nexport const listProfilesSchema = z.object({});\n\nexport async function listProfilesHandler() {\n try {\n const [profiles, mappings] = await Promise.all([listProfiles({ activeOnly: true }), listProfileGoogleServiceMappings()]);\n const mappingsByProfileId = new Map(mappings.map((mapping) => [mapping.profileId, mapping]));\n return object({\n total_profiles: profiles.length,\n profiles: profiles.map((profile) => {\n const mapping = mappingsByProfileId.get(profile.id);\n return {\n profile_id: profile.id,\n name: profile.name,\n is_active: profile.isActive,\n created_at: profile.createdAt.toISOString(),\n updated_at: profile.updatedAt.toISOString(),\n google_services: {\n has_google_ads: Boolean(mapping?.googleAdsCustomerId),\n has_ga4: Boolean(mapping?.ga4PropertyId),\n has_search_console: Boolean(mapping?.searchConsoleSiteUrl),\n statuses: {\n google_ads: mapping?.googleAdsCustomerId ? mapping.googleAdsStatus : 'not_configured',\n ga4: mapping?.ga4PropertyId ? mapping.ga4Status : 'not_configured',\n search_console: mapping?.searchConsoleSiteUrl ? mapping.searchConsoleStatus : 'not_configured',\n },\n },\n };\n }),\n });\n } catch (err) {\n return error(err instanceof Error ? err.message : 'Failed to list profiles');\n }\n}\n"],
4
+ "sourcesContent": ["import { error, object } from 'mcp-use/server';\r\nimport { z } from 'zod';\r\nimport { listProfileGoogleServiceMappings } from '../../config/google-store.js';\r\nimport { listProfiles } from '../../config/profile-store.js';\r\n\r\nexport const listProfilesSchema = z.object({});\r\n\r\nexport async function listProfilesHandler() {\r\n try {\r\n const [profiles, mappings] = await Promise.all([listProfiles({ activeOnly: true }), listProfileGoogleServiceMappings()]);\r\n const mappingsByProfileId = new Map(mappings.map((mapping) => [mapping.profileId, mapping]));\r\n return object({\r\n total_profiles: profiles.length,\r\n profiles: profiles.map((profile) => {\r\n const mapping = mappingsByProfileId.get(profile.id);\r\n return {\r\n profile_id: profile.id,\r\n name: profile.name,\r\n is_active: profile.isActive,\r\n created_at: profile.createdAt.toISOString(),\r\n updated_at: profile.updatedAt.toISOString(),\r\n google_services: {\r\n has_google_ads: Boolean(mapping?.googleAdsCustomerId),\r\n has_ga4: Boolean(mapping?.ga4PropertyId),\r\n has_search_console: Boolean(mapping?.searchConsoleSiteUrl),\r\n statuses: {\r\n google_ads: mapping?.googleAdsCustomerId ? mapping.googleAdsStatus : 'not_configured',\r\n ga4: mapping?.ga4PropertyId ? mapping.ga4Status : 'not_configured',\r\n search_console: mapping?.searchConsoleSiteUrl ? mapping.searchConsoleStatus : 'not_configured',\r\n },\r\n },\r\n };\r\n }),\r\n });\r\n } catch (err) {\r\n return error(err instanceof Error ? err.message : 'Failed to list profiles');\r\n }\r\n}\r\n"],
5
5
  "mappings": "AAAA,SAAS,OAAO,cAAc;AAC9B,SAAS,SAAS;AAClB,SAAS,wCAAwC;AACjD,SAAS,oBAAoB;AAEtB,MAAM,qBAAqB,EAAE,OAAO,CAAC,CAAC;AAE7C,eAAsB,sBAAsB;AAC1C,MAAI;AACF,UAAM,CAAC,UAAU,QAAQ,IAAI,MAAM,QAAQ,IAAI,CAAC,aAAa,EAAE,YAAY,KAAK,CAAC,GAAG,iCAAiC,CAAC,CAAC;AACvH,UAAM,sBAAsB,IAAI,IAAI,SAAS,IAAI,CAAC,YAAY,CAAC,QAAQ,WAAW,OAAO,CAAC,CAAC;AAC3F,WAAO,OAAO;AAAA,MACZ,gBAAgB,SAAS;AAAA,MACzB,UAAU,SAAS,IAAI,CAAC,YAAY;AAClC,cAAM,UAAU,oBAAoB,IAAI,QAAQ,EAAE;AAClD,eAAO;AAAA,UACL,YAAY,QAAQ;AAAA,UACpB,MAAM,QAAQ;AAAA,UACd,WAAW,QAAQ;AAAA,UACnB,YAAY,QAAQ,UAAU,YAAY;AAAA,UAC1C,YAAY,QAAQ,UAAU,YAAY;AAAA,UAC1C,iBAAiB;AAAA,YACf,gBAAgB,QAAQ,SAAS,mBAAmB;AAAA,YACpD,SAAS,QAAQ,SAAS,aAAa;AAAA,YACvC,oBAAoB,QAAQ,SAAS,oBAAoB;AAAA,YACzD,UAAU;AAAA,cACR,YAAY,SAAS,sBAAsB,QAAQ,kBAAkB;AAAA,cACrE,KAAK,SAAS,gBAAgB,QAAQ,YAAY;AAAA,cAClD,gBAAgB,SAAS,uBAAuB,QAAQ,sBAAsB;AAAA,YAChF;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,WAAO,MAAM,eAAe,QAAQ,IAAI,UAAU,yBAAyB;AAAA,EAC7E;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/tools/google-ads/account-overview.ts"],
4
- "sourcesContent": ["import { error, object } from \"mcp-use/server\";\nimport { z } from \"zod\";\n\nimport {\n dateRegex,\n getFieldValue,\n microsToCurrency,\n parseGoogleAdsMetricValue,\n resolveGoogleAdsCustomerId,\n resolveGoogleAdsDeveloperToken,\n resolveGoogleAdsLoginCustomerId,\n round,\n toPercent,\n} from \"../../utils/google-ads.js\";\nimport { searchGoogleAds } from \"../../services/google-ads/google-ads-client.js\";\nimport { stripNulls } from \"../../utils/strip-payload.js\";\n\nexport const googleAdsAccountOverviewSchema = z.object({\n startDate: z.string().regex(dateRegex).describe(\"Start date in YYYY-MM-DD format.\"),\n endDate: z.string().regex(dateRegex).describe(\"End date in YYYY-MM-DD format.\"),\n customerId: z.string().optional().describe(\"Google Ads customer ID to query. If omitted, resolves the profileId mapping. Accepts digits with or without hyphens.\"),\n profileId: z.string().optional().describe(\"Optional business profile ID used to resolve the mapped Google Ads customer ID.\"),\n loginCustomerId: z\n .string()\n .optional()\n .describe(\n \"Optional manager account ID used as login customer. If omitted, uses GOOGLE_ADS_LOGIN_CUSTOMER_ID when configured.\"\n ),\n});\n\nexport async function googleAdsAccountOverviewHandler(\n params: z.infer<typeof googleAdsAccountOverviewSchema>\n) {\n try {\n const customerId = await resolveGoogleAdsCustomerId(params.customerId, params.profileId);\n const loginCustomerId = resolveGoogleAdsLoginCustomerId(params.loginCustomerId);\n const developerToken = resolveGoogleAdsDeveloperToken();\n\n const result = await searchGoogleAds(\n customerId,\n [\n \"SELECT\",\n \"customer.id,\",\n \"customer.descriptive_name,\",\n \"customer.currency_code,\",\n \"customer.time_zone,\",\n \"metrics.impressions,\",\n \"metrics.clicks,\",\n \"metrics.cost_micros,\",\n \"metrics.conversions,\",\n \"metrics.conversions_value,\",\n \"metrics.ctr,\",\n \"metrics.average_cpc\",\n \"FROM customer\",\n `WHERE segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`,\n ].join(\" \"),\n developerToken,\n loginCustomerId\n );\n\n const row = result.rows[0] ?? {};\n const impressions = parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.impressions\"));\n const clicks = parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.clicks\"));\n const cost = microsToCurrency(getFieldValue(row, \"metrics.costMicros\"));\n const conversions = round(\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversions\"))\n );\n const conversionValue = round(\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversionsValue\"))\n );\n const ctrPercent = round(\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.ctr\")) * 100\n );\n const averageCpc = microsToCurrency(getFieldValue(row, \"metrics.averageCpc\"));\n\n return object(\n stripNulls({\n customer_id: customerId,\n login_customer_id: loginCustomerId,\n date_range: {\n start_date: params.startDate,\n end_date: params.endDate,\n },\n currency_code: getFieldValue(row, \"customer.currencyCode\"),\n customer_name: getFieldValue(row, \"customer.descriptiveName\"),\n time_zone: getFieldValue(row, \"customer.timeZone\"),\n overview: {\n impressions,\n clicks,\n cost,\n conversions,\n conversion_value: conversionValue,\n ctr_percent: ctrPercent,\n average_cpc: averageCpc,\n cpa: round(cost / conversions),\n roas: round(conversionValue / cost),\n conversion_rate_percent: round(toPercent(conversions, clicks)),\n },\n metadata: {\n row_count: result.rows.length,\n request_id: result.requestId,\n },\n })\n );\n } catch (err) {\n return error(\n err instanceof Error ? err.message : \"Failed to fetch Google Ads account overview\"\n );\n }\n}\n"],
4
+ "sourcesContent": ["import { error, object } from \"mcp-use/server\";\r\nimport { z } from \"zod\";\r\n\r\nimport {\r\n dateRegex,\r\n getFieldValue,\r\n microsToCurrency,\r\n parseGoogleAdsMetricValue,\r\n resolveGoogleAdsCustomerId,\r\n resolveGoogleAdsDeveloperToken,\r\n resolveGoogleAdsLoginCustomerId,\r\n round,\r\n toPercent,\r\n} from \"../../utils/google-ads.js\";\r\nimport { searchGoogleAds } from \"../../services/google-ads/google-ads-client.js\";\r\nimport { stripNulls } from \"../../utils/strip-payload.js\";\r\n\r\nexport const googleAdsAccountOverviewSchema = z.object({\r\n startDate: z.string().regex(dateRegex).describe(\"Start date in YYYY-MM-DD format.\"),\r\n endDate: z.string().regex(dateRegex).describe(\"End date in YYYY-MM-DD format.\"),\r\n customerId: z.string().optional().describe(\"Google Ads customer ID to query. If omitted, resolves the profileId mapping. Accepts digits with or without hyphens.\"),\r\n profileId: z.string().optional().describe(\"Optional business profile ID used to resolve the mapped Google Ads customer ID.\"),\r\n loginCustomerId: z\r\n .string()\r\n .optional()\r\n .describe(\r\n \"Optional manager account ID used as login customer. If omitted, uses GOOGLE_ADS_LOGIN_CUSTOMER_ID when configured.\"\r\n ),\r\n});\r\n\r\nexport async function googleAdsAccountOverviewHandler(\r\n params: z.infer<typeof googleAdsAccountOverviewSchema>\r\n) {\r\n try {\r\n const customerId = await resolveGoogleAdsCustomerId(params.customerId, params.profileId);\r\n const loginCustomerId = resolveGoogleAdsLoginCustomerId(params.loginCustomerId);\r\n const developerToken = resolveGoogleAdsDeveloperToken();\r\n\r\n const result = await searchGoogleAds(\r\n customerId,\r\n [\r\n \"SELECT\",\r\n \"customer.id,\",\r\n \"customer.descriptive_name,\",\r\n \"customer.currency_code,\",\r\n \"customer.time_zone,\",\r\n \"metrics.impressions,\",\r\n \"metrics.clicks,\",\r\n \"metrics.cost_micros,\",\r\n \"metrics.conversions,\",\r\n \"metrics.conversions_value,\",\r\n \"metrics.ctr,\",\r\n \"metrics.average_cpc\",\r\n \"FROM customer\",\r\n `WHERE segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`,\r\n ].join(\" \"),\r\n developerToken,\r\n loginCustomerId\r\n );\r\n\r\n const row = result.rows[0] ?? {};\r\n const impressions = parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.impressions\"));\r\n const clicks = parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.clicks\"));\r\n const cost = microsToCurrency(getFieldValue(row, \"metrics.costMicros\"));\r\n const conversions = round(\r\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversions\"))\r\n );\r\n const conversionValue = round(\r\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversionsValue\"))\r\n );\r\n const ctrPercent = round(\r\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.ctr\")) * 100\r\n );\r\n const averageCpc = microsToCurrency(getFieldValue(row, \"metrics.averageCpc\"));\r\n\r\n return object(\r\n stripNulls({\r\n customer_id: customerId,\r\n login_customer_id: loginCustomerId,\r\n date_range: {\r\n start_date: params.startDate,\r\n end_date: params.endDate,\r\n },\r\n currency_code: getFieldValue(row, \"customer.currencyCode\"),\r\n customer_name: getFieldValue(row, \"customer.descriptiveName\"),\r\n time_zone: getFieldValue(row, \"customer.timeZone\"),\r\n overview: {\r\n impressions,\r\n clicks,\r\n cost,\r\n conversions,\r\n conversion_value: conversionValue,\r\n ctr_percent: ctrPercent,\r\n average_cpc: averageCpc,\r\n cpa: round(cost / conversions),\r\n roas: round(conversionValue / cost),\r\n conversion_rate_percent: round(toPercent(conversions, clicks)),\r\n },\r\n metadata: {\r\n row_count: result.rows.length,\r\n request_id: result.requestId,\r\n },\r\n })\r\n );\r\n } catch (err) {\r\n return error(\r\n err instanceof Error ? err.message : \"Failed to fetch Google Ads account overview\"\r\n );\r\n }\r\n}\r\n"],
5
5
  "mappings": "AAAA,SAAS,OAAO,cAAc;AAC9B,SAAS,SAAS;AAElB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,uBAAuB;AAChC,SAAS,kBAAkB;AAEpB,MAAM,iCAAiC,EAAE,OAAO;AAAA,EACrD,WAAW,EAAE,OAAO,EAAE,MAAM,SAAS,EAAE,SAAS,kCAAkC;AAAA,EAClF,SAAS,EAAE,OAAO,EAAE,MAAM,SAAS,EAAE,SAAS,gCAAgC;AAAA,EAC9E,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,sHAAsH;AAAA,EACjK,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,iFAAiF;AAAA,EAC3H,iBAAiB,EACd,OAAO,EACP,SAAS,EACT;AAAA,IACC;AAAA,EACF;AACJ,CAAC;AAED,eAAsB,gCACpB,QACA;AACA,MAAI;AACF,UAAM,aAAa,MAAM,2BAA2B,OAAO,YAAY,OAAO,SAAS;AACvF,UAAM,kBAAkB,gCAAgC,OAAO,eAAe;AAC9E,UAAM,iBAAiB,+BAA+B;AAEtD,UAAM,SAAS,MAAM;AAAA,MACnB;AAAA,MACA;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,gCAAgC,OAAO,SAAS,UAAU,OAAO,OAAO;AAAA,MAC1E,EAAE,KAAK,GAAG;AAAA,MACV;AAAA,MACA;AAAA,IACF;AAEA,UAAM,MAAM,OAAO,KAAK,CAAC,KAAK,CAAC;AAC/B,UAAM,cAAc,0BAA0B,cAAc,KAAK,qBAAqB,CAAC;AACvF,UAAM,SAAS,0BAA0B,cAAc,KAAK,gBAAgB,CAAC;AAC7E,UAAM,OAAO,iBAAiB,cAAc,KAAK,oBAAoB,CAAC;AACtE,UAAM,cAAc;AAAA,MAClB,0BAA0B,cAAc,KAAK,qBAAqB,CAAC;AAAA,IACrE;AACA,UAAM,kBAAkB;AAAA,MACtB,0BAA0B,cAAc,KAAK,0BAA0B,CAAC;AAAA,IAC1E;AACA,UAAM,aAAa;AAAA,MACjB,0BAA0B,cAAc,KAAK,aAAa,CAAC,IAAI;AAAA,IACjE;AACA,UAAM,aAAa,iBAAiB,cAAc,KAAK,oBAAoB,CAAC;AAE5E,WAAO;AAAA,MACL,WAAW;AAAA,QACT,aAAa;AAAA,QACb,mBAAmB;AAAA,QACnB,YAAY;AAAA,UACV,YAAY,OAAO;AAAA,UACnB,UAAU,OAAO;AAAA,QACnB;AAAA,QACA,eAAe,cAAc,KAAK,uBAAuB;AAAA,QACzD,eAAe,cAAc,KAAK,0BAA0B;AAAA,QAC5D,WAAW,cAAc,KAAK,mBAAmB;AAAA,QACjD,UAAU;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,kBAAkB;AAAA,UAClB,aAAa;AAAA,UACb,aAAa;AAAA,UACb,KAAK,MAAM,OAAO,WAAW;AAAA,UAC7B,MAAM,MAAM,kBAAkB,IAAI;AAAA,UAClC,yBAAyB,MAAM,UAAU,aAAa,MAAM,CAAC;AAAA,QAC/D;AAAA,QACA,UAAU;AAAA,UACR,WAAW,OAAO,KAAK;AAAA,UACvB,YAAY,OAAO;AAAA,QACrB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF,SAAS,KAAK;AACZ,WAAO;AAAA,MACL,eAAe,QAAQ,IAAI,UAAU;AAAA,IACvC;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/tools/google-ads/account-risks.ts"],
4
- "sourcesContent": ["import { error, object } from \"mcp-use/server\";\nimport { z } from \"zod\";\n\nimport {\n dateRegex,\n getFieldValue,\n microsToCurrency,\n parseGoogleAdsMetricValue,\n resolveGoogleAdsCustomerId,\n resolveGoogleAdsDeveloperToken,\n resolveGoogleAdsLoginCustomerId,\n round,\n toPercent,\n} from \"../../utils/google-ads.js\";\nimport { searchGoogleAds } from \"../../services/google-ads/google-ads-client.js\";\nimport { stripNulls } from \"../../utils/strip-payload.js\";\n\ntype RiskSeverity = \"low\" | \"medium\" | \"high\";\n\nfunction buildRisk(\n code: string,\n severity: RiskSeverity,\n title: string,\n detail: string,\n metricValue?: number\n) {\n return stripNulls({\n code,\n severity,\n title,\n detail,\n metric_value: metricValue !== undefined ? round(metricValue) : undefined,\n });\n}\n\nexport const googleAdsAccountRisksSchema = z.object({\n startDate: z.string().regex(dateRegex).describe(\"Start date in YYYY-MM-DD format.\"),\n endDate: z.string().regex(dateRegex).describe(\"End date in YYYY-MM-DD format.\"),\n customerId: z\n .string()\n .optional()\n .describe(\"Google Ads customer ID to query. If omitted, resolves the profileId mapping. Accepts digits with or without hyphens.\"),\n profileId: z.string().optional().describe(\"Optional business profile ID used to resolve the mapped Google Ads customer ID.\"),\n loginCustomerId: z\n .string()\n .optional()\n .describe(\n \"Optional manager account ID used as login customer. If omitted, uses GOOGLE_ADS_LOGIN_CUSTOMER_ID when configured.\"\n ),\n});\n\nexport async function googleAdsAccountRisksHandler(\n params: z.infer<typeof googleAdsAccountRisksSchema>\n) {\n try {\n const customerId = await resolveGoogleAdsCustomerId(params.customerId, params.profileId);\n const loginCustomerId = resolveGoogleAdsLoginCustomerId(params.loginCustomerId);\n const developerToken = resolveGoogleAdsDeveloperToken();\n\n const [campaignsResult, channelsResult, seriesResult] = await Promise.all([\n searchGoogleAds(\n customerId,\n [\n \"SELECT\",\n \"customer.descriptive_name,\",\n \"customer.currency_code,\",\n \"campaign.id,\",\n \"campaign.name,\",\n \"campaign.advertising_channel_type,\",\n \"metrics.impressions,\",\n \"metrics.clicks,\",\n \"metrics.cost_micros,\",\n \"metrics.conversions,\",\n \"metrics.conversions_value\",\n \"FROM campaign\",\n `WHERE segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`,\n \"AND campaign.status = 'ENABLED'\",\n \"ORDER BY metrics.cost_micros DESC\",\n ].join(\" \"),\n developerToken,\n loginCustomerId\n ),\n searchGoogleAds(\n customerId,\n [\n \"SELECT\",\n \"campaign.advertising_channel_type,\",\n \"metrics.cost_micros,\",\n \"metrics.conversions_value\",\n \"FROM campaign\",\n `WHERE segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`,\n \"AND campaign.status = 'ENABLED'\",\n ].join(\" \"),\n developerToken,\n loginCustomerId\n ),\n searchGoogleAds(\n customerId,\n [\n \"SELECT\",\n \"segments.date,\",\n \"metrics.cost_micros,\",\n \"metrics.conversions_value,\",\n \"metrics.conversions\",\n \"FROM customer\",\n `WHERE segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`,\n \"ORDER BY segments.date\",\n ].join(\" \"),\n developerToken,\n loginCustomerId\n ),\n ]);\n\n const campaignRows = campaignsResult.rows.map((row) => ({\n campaign_name: getFieldValue(row, \"campaign.name\"),\n channel_type: getFieldValue(row, \"campaign.advertisingChannelType\"),\n cost: microsToCurrency(getFieldValue(row, \"metrics.costMicros\")),\n conversions: round(parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversions\"))),\n conversion_value: round(\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversionsValue\"))\n ),\n }));\n\n const totalCost = campaignRows.reduce((sum, row) => sum + row.cost, 0);\n const totalConversions = campaignRows.reduce((sum, row) => sum + row.conversions, 0);\n const totalConversionValue = campaignRows.reduce((sum, row) => sum + row.conversion_value, 0);\n const topCampaign = campaignRows[0];\n const topCampaignShare = round(toPercent(topCampaign?.cost ?? 0, totalCost));\n\n const channelCosts = new Map<string, number>();\n for (const row of channelsResult.rows) {\n const channelType = String(\n getFieldValue(row, \"campaign.advertisingChannelType\", \"UNKNOWN\") ?? \"UNKNOWN\"\n );\n channelCosts.set(\n channelType,\n (channelCosts.get(channelType) ?? 0) +\n microsToCurrency(getFieldValue(row, \"metrics.costMicros\"))\n );\n }\n const dominantChannel = Array.from(channelCosts.entries()).sort((a, b) => b[1] - a[1])[0];\n const dominantChannelShare = round(toPercent(dominantChannel?.[1] ?? 0, totalCost));\n\n const series = seriesResult.rows.map((row) => ({\n period_start: String(getFieldValue(row, \"segments.date\") ?? \"\"),\n cost: microsToCurrency(getFieldValue(row, \"metrics.costMicros\")),\n conversion_value: round(\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversionsValue\"))\n ),\n conversions: round(parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversions\"))),\n }));\n const midpoint = Math.ceil(series.length / 2);\n const firstHalf = series.slice(0, midpoint);\n const secondHalf = series.slice(midpoint);\n const firstHalfRoas = round(\n firstHalf.reduce((sum, item) => sum + item.conversion_value, 0) /\n firstHalf.reduce((sum, item) => sum + item.cost, 0)\n );\n const secondHalfRoas = round(\n secondHalf.reduce((sum, item) => sum + item.conversion_value, 0) /\n secondHalf.reduce((sum, item) => sum + item.cost, 0)\n );\n\n const risks = [];\n\n if (topCampaignShare >= 70) {\n risks.push(\n buildRisk(\n \"campaign_concentration_high\",\n \"high\",\n \"Concentraci\u00F3n alta en una campa\u00F1a\",\n `La campa\u00F1a principal concentra ${topCampaignShare}% del gasto.`,\n topCampaignShare\n )\n );\n } else if (topCampaignShare >= 50) {\n risks.push(\n buildRisk(\n \"campaign_concentration_medium\",\n \"medium\",\n \"Concentraci\u00F3n relevante en una campa\u00F1a\",\n `La campa\u00F1a principal concentra ${topCampaignShare}% del gasto.`,\n topCampaignShare\n )\n );\n }\n\n if (dominantChannelShare >= 80) {\n risks.push(\n buildRisk(\n \"channel_concentration_high\",\n \"high\",\n \"Dependencia alta de un tipo de campa\u00F1a\",\n `${dominantChannel?.[0] ?? \"UNKNOWN\"} concentra ${dominantChannelShare}% del gasto.`,\n dominantChannelShare\n )\n );\n } else if (dominantChannelShare >= 60) {\n risks.push(\n buildRisk(\n \"channel_concentration_medium\",\n \"medium\",\n \"Dependencia relevante de un tipo de campa\u00F1a\",\n `${dominantChannel?.[0] ?? \"UNKNOWN\"} concentra ${dominantChannelShare}% del gasto.`,\n dominantChannelShare\n )\n );\n }\n\n const overallRoas = round(totalConversionValue / totalCost);\n if (overallRoas < 1) {\n risks.push(\n buildRisk(\n \"low_roas\",\n \"high\",\n \"ROAS por debajo de 1\",\n `El ROAS agregado es ${overallRoas}. La cuenta est\u00E1 devolviendo menos valor que el gasto atribuido.`,\n overallRoas\n )\n );\n } else if (overallRoas < 1.5) {\n risks.push(\n buildRisk(\n \"borderline_roas\",\n \"medium\",\n \"ROAS ajustado al l\u00EDmite\",\n `El ROAS agregado es ${overallRoas}. Hay poco margen antes de entrar en zona de riesgo.`,\n overallRoas\n )\n );\n }\n\n if (totalConversions < 5) {\n risks.push(\n buildRisk(\n \"low_conversion_volume\",\n \"medium\",\n \"Volumen de conversiones bajo\",\n `La cuenta gener\u00F3 ${round(totalConversions)} conversiones en el per\u00EDodo. La se\u00F1al estad\u00EDstica es limitada.`,\n totalConversions\n )\n );\n }\n\n if (firstHalf.length > 0 && secondHalf.length > 0 && secondHalfRoas < firstHalfRoas * 0.7) {\n risks.push(\n buildRisk(\n \"roas_deteriorating\",\n \"medium\",\n \"Deterioro reciente de eficiencia\",\n `El ROAS cay\u00F3 de ${firstHalfRoas} en la primera mitad a ${secondHalfRoas} en la segunda mitad del per\u00EDodo.`,\n secondHalfRoas\n )\n );\n }\n\n const highSeverityCount = risks.filter((risk) => risk.severity === \"high\").length;\n const mediumSeverityCount = risks.filter((risk) => risk.severity === \"medium\").length;\n const overallSeverity: RiskSeverity =\n highSeverityCount > 0 ? \"high\" : mediumSeverityCount > 0 ? \"medium\" : \"low\";\n\n return object(\n stripNulls({\n customer_id: customerId,\n login_customer_id: loginCustomerId,\n customer_name: getFieldValue(campaignsResult.rows[0] ?? {}, \"customer.descriptiveName\"),\n currency_code: getFieldValue(campaignsResult.rows[0] ?? {}, \"customer.currencyCode\"),\n date_range: {\n start_date: params.startDate,\n end_date: params.endDate,\n },\n overview: {\n total_campaigns: campaignRows.length,\n total_cost: round(totalCost),\n total_conversions: round(totalConversions),\n total_conversion_value: round(totalConversionValue),\n roas: overallRoas,\n top_campaign_by_cost: topCampaign?.campaign_name,\n top_campaign_cost_share_percent: topCampaignShare,\n dominant_channel: dominantChannel?.[0],\n dominant_channel_cost_share_percent: dominantChannelShare,\n first_half_roas: firstHalfRoas,\n second_half_roas: secondHalfRoas,\n },\n risk_summary: {\n overall_severity: overallSeverity,\n high_severity_count: highSeverityCount,\n medium_severity_count: mediumSeverityCount,\n total_risks: risks.length,\n },\n risks,\n metadata: {\n request_ids: [campaignsResult.requestId, channelsResult.requestId, seriesResult.requestId],\n },\n })\n );\n } catch (err) {\n return error(err instanceof Error ? err.message : \"Failed to evaluate Google Ads account risks\");\n }\n}\n"],
4
+ "sourcesContent": ["import { error, object } from \"mcp-use/server\";\r\nimport { z } from \"zod\";\r\n\r\nimport {\r\n dateRegex,\r\n getFieldValue,\r\n microsToCurrency,\r\n parseGoogleAdsMetricValue,\r\n resolveGoogleAdsCustomerId,\r\n resolveGoogleAdsDeveloperToken,\r\n resolveGoogleAdsLoginCustomerId,\r\n round,\r\n toPercent,\r\n} from \"../../utils/google-ads.js\";\r\nimport { searchGoogleAds } from \"../../services/google-ads/google-ads-client.js\";\r\nimport { stripNulls } from \"../../utils/strip-payload.js\";\r\n\r\ntype RiskSeverity = \"low\" | \"medium\" | \"high\";\r\n\r\nfunction buildRisk(\r\n code: string,\r\n severity: RiskSeverity,\r\n title: string,\r\n detail: string,\r\n metricValue?: number\r\n) {\r\n return stripNulls({\r\n code,\r\n severity,\r\n title,\r\n detail,\r\n metric_value: metricValue !== undefined ? round(metricValue) : undefined,\r\n });\r\n}\r\n\r\nexport const googleAdsAccountRisksSchema = z.object({\r\n startDate: z.string().regex(dateRegex).describe(\"Start date in YYYY-MM-DD format.\"),\r\n endDate: z.string().regex(dateRegex).describe(\"End date in YYYY-MM-DD format.\"),\r\n customerId: z\r\n .string()\r\n .optional()\r\n .describe(\"Google Ads customer ID to query. If omitted, resolves the profileId mapping. Accepts digits with or without hyphens.\"),\r\n profileId: z.string().optional().describe(\"Optional business profile ID used to resolve the mapped Google Ads customer ID.\"),\r\n loginCustomerId: z\r\n .string()\r\n .optional()\r\n .describe(\r\n \"Optional manager account ID used as login customer. If omitted, uses GOOGLE_ADS_LOGIN_CUSTOMER_ID when configured.\"\r\n ),\r\n});\r\n\r\nexport async function googleAdsAccountRisksHandler(\r\n params: z.infer<typeof googleAdsAccountRisksSchema>\r\n) {\r\n try {\r\n const customerId = await resolveGoogleAdsCustomerId(params.customerId, params.profileId);\r\n const loginCustomerId = resolveGoogleAdsLoginCustomerId(params.loginCustomerId);\r\n const developerToken = resolveGoogleAdsDeveloperToken();\r\n\r\n const [campaignsResult, channelsResult, seriesResult] = await Promise.all([\r\n searchGoogleAds(\r\n customerId,\r\n [\r\n \"SELECT\",\r\n \"customer.descriptive_name,\",\r\n \"customer.currency_code,\",\r\n \"campaign.id,\",\r\n \"campaign.name,\",\r\n \"campaign.advertising_channel_type,\",\r\n \"metrics.impressions,\",\r\n \"metrics.clicks,\",\r\n \"metrics.cost_micros,\",\r\n \"metrics.conversions,\",\r\n \"metrics.conversions_value\",\r\n \"FROM campaign\",\r\n `WHERE segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`,\r\n \"AND campaign.status = 'ENABLED'\",\r\n \"ORDER BY metrics.cost_micros DESC\",\r\n ].join(\" \"),\r\n developerToken,\r\n loginCustomerId\r\n ),\r\n searchGoogleAds(\r\n customerId,\r\n [\r\n \"SELECT\",\r\n \"campaign.advertising_channel_type,\",\r\n \"metrics.cost_micros,\",\r\n \"metrics.conversions_value\",\r\n \"FROM campaign\",\r\n `WHERE segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`,\r\n \"AND campaign.status = 'ENABLED'\",\r\n ].join(\" \"),\r\n developerToken,\r\n loginCustomerId\r\n ),\r\n searchGoogleAds(\r\n customerId,\r\n [\r\n \"SELECT\",\r\n \"segments.date,\",\r\n \"metrics.cost_micros,\",\r\n \"metrics.conversions_value,\",\r\n \"metrics.conversions\",\r\n \"FROM customer\",\r\n `WHERE segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`,\r\n \"ORDER BY segments.date\",\r\n ].join(\" \"),\r\n developerToken,\r\n loginCustomerId\r\n ),\r\n ]);\r\n\r\n const campaignRows = campaignsResult.rows.map((row) => ({\r\n campaign_name: getFieldValue(row, \"campaign.name\"),\r\n channel_type: getFieldValue(row, \"campaign.advertisingChannelType\"),\r\n cost: microsToCurrency(getFieldValue(row, \"metrics.costMicros\")),\r\n conversions: round(parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversions\"))),\r\n conversion_value: round(\r\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversionsValue\"))\r\n ),\r\n }));\r\n\r\n const totalCost = campaignRows.reduce((sum, row) => sum + row.cost, 0);\r\n const totalConversions = campaignRows.reduce((sum, row) => sum + row.conversions, 0);\r\n const totalConversionValue = campaignRows.reduce((sum, row) => sum + row.conversion_value, 0);\r\n const topCampaign = campaignRows[0];\r\n const topCampaignShare = round(toPercent(topCampaign?.cost ?? 0, totalCost));\r\n\r\n const channelCosts = new Map<string, number>();\r\n for (const row of channelsResult.rows) {\r\n const channelType = String(\r\n getFieldValue(row, \"campaign.advertisingChannelType\", \"UNKNOWN\") ?? \"UNKNOWN\"\r\n );\r\n channelCosts.set(\r\n channelType,\r\n (channelCosts.get(channelType) ?? 0) +\r\n microsToCurrency(getFieldValue(row, \"metrics.costMicros\"))\r\n );\r\n }\r\n const dominantChannel = Array.from(channelCosts.entries()).sort((a, b) => b[1] - a[1])[0];\r\n const dominantChannelShare = round(toPercent(dominantChannel?.[1] ?? 0, totalCost));\r\n\r\n const series = seriesResult.rows.map((row) => ({\r\n period_start: String(getFieldValue(row, \"segments.date\") ?? \"\"),\r\n cost: microsToCurrency(getFieldValue(row, \"metrics.costMicros\")),\r\n conversion_value: round(\r\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversionsValue\"))\r\n ),\r\n conversions: round(parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversions\"))),\r\n }));\r\n const midpoint = Math.ceil(series.length / 2);\r\n const firstHalf = series.slice(0, midpoint);\r\n const secondHalf = series.slice(midpoint);\r\n const firstHalfRoas = round(\r\n firstHalf.reduce((sum, item) => sum + item.conversion_value, 0) /\r\n firstHalf.reduce((sum, item) => sum + item.cost, 0)\r\n );\r\n const secondHalfRoas = round(\r\n secondHalf.reduce((sum, item) => sum + item.conversion_value, 0) /\r\n secondHalf.reduce((sum, item) => sum + item.cost, 0)\r\n );\r\n\r\n const risks = [];\r\n\r\n if (topCampaignShare >= 70) {\r\n risks.push(\r\n buildRisk(\r\n \"campaign_concentration_high\",\r\n \"high\",\r\n \"Concentraci\u00F3n alta en una campa\u00F1a\",\r\n `La campa\u00F1a principal concentra ${topCampaignShare}% del gasto.`,\r\n topCampaignShare\r\n )\r\n );\r\n } else if (topCampaignShare >= 50) {\r\n risks.push(\r\n buildRisk(\r\n \"campaign_concentration_medium\",\r\n \"medium\",\r\n \"Concentraci\u00F3n relevante en una campa\u00F1a\",\r\n `La campa\u00F1a principal concentra ${topCampaignShare}% del gasto.`,\r\n topCampaignShare\r\n )\r\n );\r\n }\r\n\r\n if (dominantChannelShare >= 80) {\r\n risks.push(\r\n buildRisk(\r\n \"channel_concentration_high\",\r\n \"high\",\r\n \"Dependencia alta de un tipo de campa\u00F1a\",\r\n `${dominantChannel?.[0] ?? \"UNKNOWN\"} concentra ${dominantChannelShare}% del gasto.`,\r\n dominantChannelShare\r\n )\r\n );\r\n } else if (dominantChannelShare >= 60) {\r\n risks.push(\r\n buildRisk(\r\n \"channel_concentration_medium\",\r\n \"medium\",\r\n \"Dependencia relevante de un tipo de campa\u00F1a\",\r\n `${dominantChannel?.[0] ?? \"UNKNOWN\"} concentra ${dominantChannelShare}% del gasto.`,\r\n dominantChannelShare\r\n )\r\n );\r\n }\r\n\r\n const overallRoas = round(totalConversionValue / totalCost);\r\n if (overallRoas < 1) {\r\n risks.push(\r\n buildRisk(\r\n \"low_roas\",\r\n \"high\",\r\n \"ROAS por debajo de 1\",\r\n `El ROAS agregado es ${overallRoas}. La cuenta est\u00E1 devolviendo menos valor que el gasto atribuido.`,\r\n overallRoas\r\n )\r\n );\r\n } else if (overallRoas < 1.5) {\r\n risks.push(\r\n buildRisk(\r\n \"borderline_roas\",\r\n \"medium\",\r\n \"ROAS ajustado al l\u00EDmite\",\r\n `El ROAS agregado es ${overallRoas}. Hay poco margen antes de entrar en zona de riesgo.`,\r\n overallRoas\r\n )\r\n );\r\n }\r\n\r\n if (totalConversions < 5) {\r\n risks.push(\r\n buildRisk(\r\n \"low_conversion_volume\",\r\n \"medium\",\r\n \"Volumen de conversiones bajo\",\r\n `La cuenta gener\u00F3 ${round(totalConversions)} conversiones en el per\u00EDodo. La se\u00F1al estad\u00EDstica es limitada.`,\r\n totalConversions\r\n )\r\n );\r\n }\r\n\r\n if (firstHalf.length > 0 && secondHalf.length > 0 && secondHalfRoas < firstHalfRoas * 0.7) {\r\n risks.push(\r\n buildRisk(\r\n \"roas_deteriorating\",\r\n \"medium\",\r\n \"Deterioro reciente de eficiencia\",\r\n `El ROAS cay\u00F3 de ${firstHalfRoas} en la primera mitad a ${secondHalfRoas} en la segunda mitad del per\u00EDodo.`,\r\n secondHalfRoas\r\n )\r\n );\r\n }\r\n\r\n const highSeverityCount = risks.filter((risk) => risk.severity === \"high\").length;\r\n const mediumSeverityCount = risks.filter((risk) => risk.severity === \"medium\").length;\r\n const overallSeverity: RiskSeverity =\r\n highSeverityCount > 0 ? \"high\" : mediumSeverityCount > 0 ? \"medium\" : \"low\";\r\n\r\n return object(\r\n stripNulls({\r\n customer_id: customerId,\r\n login_customer_id: loginCustomerId,\r\n customer_name: getFieldValue(campaignsResult.rows[0] ?? {}, \"customer.descriptiveName\"),\r\n currency_code: getFieldValue(campaignsResult.rows[0] ?? {}, \"customer.currencyCode\"),\r\n date_range: {\r\n start_date: params.startDate,\r\n end_date: params.endDate,\r\n },\r\n overview: {\r\n total_campaigns: campaignRows.length,\r\n total_cost: round(totalCost),\r\n total_conversions: round(totalConversions),\r\n total_conversion_value: round(totalConversionValue),\r\n roas: overallRoas,\r\n top_campaign_by_cost: topCampaign?.campaign_name,\r\n top_campaign_cost_share_percent: topCampaignShare,\r\n dominant_channel: dominantChannel?.[0],\r\n dominant_channel_cost_share_percent: dominantChannelShare,\r\n first_half_roas: firstHalfRoas,\r\n second_half_roas: secondHalfRoas,\r\n },\r\n risk_summary: {\r\n overall_severity: overallSeverity,\r\n high_severity_count: highSeverityCount,\r\n medium_severity_count: mediumSeverityCount,\r\n total_risks: risks.length,\r\n },\r\n risks,\r\n metadata: {\r\n request_ids: [campaignsResult.requestId, channelsResult.requestId, seriesResult.requestId],\r\n },\r\n })\r\n );\r\n } catch (err) {\r\n return error(err instanceof Error ? err.message : \"Failed to evaluate Google Ads account risks\");\r\n }\r\n}\r\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/tools/google-ads/break-even-analysis.ts"],
4
- "sourcesContent": ["import { error, object } from \"mcp-use/server\";\nimport { z } from \"zod\";\n\nimport {\n dateRegex,\n getFieldValue,\n microsToCurrency,\n parseGoogleAdsMetricValue,\n resolveGoogleAdsCustomerId,\n resolveGoogleAdsDeveloperToken,\n resolveGoogleAdsLoginCustomerId,\n round,\n} from \"../../utils/google-ads.js\";\nimport { searchGoogleAds } from \"../../services/google-ads/google-ads-client.js\";\nimport { stripNulls } from \"../../utils/strip-payload.js\";\n\nexport const googleAdsBreakEvenAnalysisSchema = z.object({\n startDate: z.string().regex(dateRegex).describe(\"Start date in YYYY-MM-DD format.\"),\n endDate: z.string().regex(dateRegex).describe(\"End date in YYYY-MM-DD format.\"),\n customerId: z\n .string()\n .optional()\n .describe(\"Google Ads customer ID to query. If omitted, resolves the profileId mapping. Accepts digits with or without hyphens.\"),\n profileId: z.string().optional().describe(\"Optional business profile ID used to resolve the mapped Google Ads customer ID.\"),\n loginCustomerId: z\n .string()\n .optional()\n .describe(\n \"Optional manager account ID used as login customer. If omitted, uses GOOGLE_ADS_LOGIN_CUSTOMER_ID when configured.\"\n ),\n breakEvenRoas: z\n .number()\n .positive()\n .describe(\"Break-even ROAS for the business. Example: 2.5 means the account needs at least 2.5x return to break even.\"),\n targetCpa: z\n .number()\n .positive()\n .optional()\n .describe(\"Optional maximum acceptable CPA for the business.\"),\n});\n\nexport async function googleAdsBreakEvenAnalysisHandler(\n params: z.infer<typeof googleAdsBreakEvenAnalysisSchema>\n) {\n try {\n const customerId = await resolveGoogleAdsCustomerId(params.customerId, params.profileId);\n const loginCustomerId = resolveGoogleAdsLoginCustomerId(params.loginCustomerId);\n const developerToken = resolveGoogleAdsDeveloperToken();\n\n const result = await searchGoogleAds(\n customerId,\n [\n \"SELECT\",\n \"customer.descriptive_name,\",\n \"customer.currency_code,\",\n \"metrics.clicks,\",\n \"metrics.cost_micros,\",\n \"metrics.conversions,\",\n \"metrics.conversions_value\",\n \"FROM customer\",\n `WHERE segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`,\n ].join(\" \"),\n developerToken,\n loginCustomerId\n );\n\n const row = result.rows[0] ?? {};\n const clicks = parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.clicks\"));\n const cost = microsToCurrency(getFieldValue(row, \"metrics.costMicros\"));\n const conversions = round(\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversions\"))\n );\n const conversionValue = round(\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversionsValue\"))\n );\n const currentRoas = round(conversionValue / cost);\n const currentCpa = round(cost / conversions);\n const roasGap = round(currentRoas - params.breakEvenRoas);\n const roasGapPercent = round((currentRoas / params.breakEvenRoas - 1) * 100);\n\n return object(\n stripNulls({\n customer_id: customerId,\n login_customer_id: loginCustomerId,\n customer_name: getFieldValue(row, \"customer.descriptiveName\"),\n currency_code: getFieldValue(row, \"customer.currencyCode\"),\n date_range: {\n start_date: params.startDate,\n end_date: params.endDate,\n },\n inputs: {\n break_even_roas: params.breakEvenRoas,\n target_cpa: params.targetCpa,\n },\n overview: {\n clicks,\n cost,\n conversions,\n conversion_value: conversionValue,\n current_roas: currentRoas,\n current_cpa: currentCpa,\n break_even_roas: params.breakEvenRoas,\n roas_gap: roasGap,\n roas_gap_percent: roasGapPercent,\n is_above_break_even: currentRoas >= params.breakEvenRoas,\n cpa_vs_target_gap: params.targetCpa ? round(currentCpa - params.targetCpa) : undefined,\n is_cpa_within_target: params.targetCpa\n ? currentCpa > 0 && currentCpa <= params.targetCpa\n : undefined,\n },\n interpretation: {\n status:\n currentRoas >= params.breakEvenRoas\n ? \"above_break_even\"\n : \"below_break_even\",\n message:\n currentRoas >= params.breakEvenRoas\n ? `The account is above break-even by ${roasGap}.`\n : `The account is below break-even by ${Math.abs(roasGap)}.`,\n },\n metadata: {\n row_count: result.rows.length,\n request_id: result.requestId,\n },\n })\n );\n } catch (err) {\n return error(\n err instanceof Error ? err.message : \"Failed to run Google Ads break-even analysis\"\n );\n }\n}\n"],
4
+ "sourcesContent": ["import { error, object } from \"mcp-use/server\";\r\nimport { z } from \"zod\";\r\n\r\nimport {\r\n dateRegex,\r\n getFieldValue,\r\n microsToCurrency,\r\n parseGoogleAdsMetricValue,\r\n resolveGoogleAdsCustomerId,\r\n resolveGoogleAdsDeveloperToken,\r\n resolveGoogleAdsLoginCustomerId,\r\n round,\r\n} from \"../../utils/google-ads.js\";\r\nimport { searchGoogleAds } from \"../../services/google-ads/google-ads-client.js\";\r\nimport { stripNulls } from \"../../utils/strip-payload.js\";\r\n\r\nexport const googleAdsBreakEvenAnalysisSchema = z.object({\r\n startDate: z.string().regex(dateRegex).describe(\"Start date in YYYY-MM-DD format.\"),\r\n endDate: z.string().regex(dateRegex).describe(\"End date in YYYY-MM-DD format.\"),\r\n customerId: z\r\n .string()\r\n .optional()\r\n .describe(\"Google Ads customer ID to query. If omitted, resolves the profileId mapping. Accepts digits with or without hyphens.\"),\r\n profileId: z.string().optional().describe(\"Optional business profile ID used to resolve the mapped Google Ads customer ID.\"),\r\n loginCustomerId: z\r\n .string()\r\n .optional()\r\n .describe(\r\n \"Optional manager account ID used as login customer. If omitted, uses GOOGLE_ADS_LOGIN_CUSTOMER_ID when configured.\"\r\n ),\r\n breakEvenRoas: z\r\n .number()\r\n .positive()\r\n .describe(\"Break-even ROAS for the business. Example: 2.5 means the account needs at least 2.5x return to break even.\"),\r\n targetCpa: z\r\n .number()\r\n .positive()\r\n .optional()\r\n .describe(\"Optional maximum acceptable CPA for the business.\"),\r\n});\r\n\r\nexport async function googleAdsBreakEvenAnalysisHandler(\r\n params: z.infer<typeof googleAdsBreakEvenAnalysisSchema>\r\n) {\r\n try {\r\n const customerId = await resolveGoogleAdsCustomerId(params.customerId, params.profileId);\r\n const loginCustomerId = resolveGoogleAdsLoginCustomerId(params.loginCustomerId);\r\n const developerToken = resolveGoogleAdsDeveloperToken();\r\n\r\n const result = await searchGoogleAds(\r\n customerId,\r\n [\r\n \"SELECT\",\r\n \"customer.descriptive_name,\",\r\n \"customer.currency_code,\",\r\n \"metrics.clicks,\",\r\n \"metrics.cost_micros,\",\r\n \"metrics.conversions,\",\r\n \"metrics.conversions_value\",\r\n \"FROM customer\",\r\n `WHERE segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`,\r\n ].join(\" \"),\r\n developerToken,\r\n loginCustomerId\r\n );\r\n\r\n const row = result.rows[0] ?? {};\r\n const clicks = parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.clicks\"));\r\n const cost = microsToCurrency(getFieldValue(row, \"metrics.costMicros\"));\r\n const conversions = round(\r\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversions\"))\r\n );\r\n const conversionValue = round(\r\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversionsValue\"))\r\n );\r\n const currentRoas = round(conversionValue / cost);\r\n const currentCpa = round(cost / conversions);\r\n const roasGap = round(currentRoas - params.breakEvenRoas);\r\n const roasGapPercent = round((currentRoas / params.breakEvenRoas - 1) * 100);\r\n\r\n return object(\r\n stripNulls({\r\n customer_id: customerId,\r\n login_customer_id: loginCustomerId,\r\n customer_name: getFieldValue(row, \"customer.descriptiveName\"),\r\n currency_code: getFieldValue(row, \"customer.currencyCode\"),\r\n date_range: {\r\n start_date: params.startDate,\r\n end_date: params.endDate,\r\n },\r\n inputs: {\r\n break_even_roas: params.breakEvenRoas,\r\n target_cpa: params.targetCpa,\r\n },\r\n overview: {\r\n clicks,\r\n cost,\r\n conversions,\r\n conversion_value: conversionValue,\r\n current_roas: currentRoas,\r\n current_cpa: currentCpa,\r\n break_even_roas: params.breakEvenRoas,\r\n roas_gap: roasGap,\r\n roas_gap_percent: roasGapPercent,\r\n is_above_break_even: currentRoas >= params.breakEvenRoas,\r\n cpa_vs_target_gap: params.targetCpa ? round(currentCpa - params.targetCpa) : undefined,\r\n is_cpa_within_target: params.targetCpa\r\n ? currentCpa > 0 && currentCpa <= params.targetCpa\r\n : undefined,\r\n },\r\n interpretation: {\r\n status:\r\n currentRoas >= params.breakEvenRoas\r\n ? \"above_break_even\"\r\n : \"below_break_even\",\r\n message:\r\n currentRoas >= params.breakEvenRoas\r\n ? `The account is above break-even by ${roasGap}.`\r\n : `The account is below break-even by ${Math.abs(roasGap)}.`,\r\n },\r\n metadata: {\r\n row_count: result.rows.length,\r\n request_id: result.requestId,\r\n },\r\n })\r\n );\r\n } catch (err) {\r\n return error(\r\n err instanceof Error ? err.message : \"Failed to run Google Ads break-even analysis\"\r\n );\r\n }\r\n}\r\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/tools/google-ads/campaign-performance.ts"],
4
- "sourcesContent": ["import { error, object } from \"mcp-use/server\";\nimport { z } from \"zod\";\n\nimport {\n dateRegex,\n getFieldValue,\n microsToCurrency,\n parseGoogleAdsMetricValue,\n resolveGoogleAdsCustomerId,\n resolveGoogleAdsDeveloperToken,\n resolveGoogleAdsLoginCustomerId,\n round,\n toPercent,\n} from \"../../utils/google-ads.js\";\nimport { searchGoogleAds } from \"../../services/google-ads/google-ads-client.js\";\nimport { stripNulls } from \"../../utils/strip-payload.js\";\n\nconst campaignStatusEnum = z.enum([\"ENABLED\", \"PAUSED\", \"REMOVED\"]);\n\nexport const googleAdsCampaignPerformanceSchema = z.object({\n startDate: z.string().regex(dateRegex).describe(\"Start date in YYYY-MM-DD format.\"),\n endDate: z.string().regex(dateRegex).describe(\"End date in YYYY-MM-DD format.\"),\n customerId: z.string().optional().describe(\"Google Ads customer ID to query. If omitted, resolves the profileId mapping. Accepts digits with or without hyphens.\"),\n profileId: z.string().optional().describe(\"Optional business profile ID used to resolve the mapped Google Ads customer ID.\"),\n loginCustomerId: z\n .string()\n .optional()\n .describe(\n \"Optional manager account ID used as login customer. If omitted, uses GOOGLE_ADS_LOGIN_CUSTOMER_ID when configured.\"\n ),\n enabledOnly: z\n .boolean()\n .optional()\n .default(true)\n .describe(\"If true, only returns enabled campaigns unless statusFilter is explicitly provided.\"),\n statusFilter: z\n .array(campaignStatusEnum)\n .optional()\n .describe(\"Optional list of campaign statuses to include. Overrides enabledOnly when provided.\"),\n});\n\nexport async function googleAdsCampaignPerformanceHandler(\n params: z.infer<typeof googleAdsCampaignPerformanceSchema>\n) {\n try {\n const customerId = await resolveGoogleAdsCustomerId(params.customerId, params.profileId);\n const loginCustomerId = resolveGoogleAdsLoginCustomerId(params.loginCustomerId);\n const developerToken = resolveGoogleAdsDeveloperToken();\n const effectiveStatuses =\n params.statusFilter && params.statusFilter.length > 0\n ? params.statusFilter\n : params.enabledOnly !== false\n ? [\"ENABLED\"]\n : undefined;\n const whereClauses = [`segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`];\n\n if (effectiveStatuses?.length) {\n whereClauses.push(\n `campaign.status IN (${effectiveStatuses.map((status) => `'${status}'`).join(\", \")})`\n );\n }\n\n const result = await searchGoogleAds(\n customerId,\n [\n \"SELECT\",\n \"customer.descriptive_name,\",\n \"customer.currency_code,\",\n \"campaign.id,\",\n \"campaign.name,\",\n \"campaign.status,\",\n \"campaign.advertising_channel_type,\",\n \"metrics.impressions,\",\n \"metrics.clicks,\",\n \"metrics.cost_micros,\",\n \"metrics.conversions,\",\n \"metrics.conversions_value,\",\n \"metrics.ctr,\",\n \"metrics.average_cpc\",\n \"FROM campaign\",\n `WHERE ${whereClauses.join(\" AND \")}`,\n \"ORDER BY metrics.cost_micros DESC\",\n ].join(\" \"),\n developerToken,\n loginCustomerId\n );\n\n const campaigns = result.rows.map((row) => {\n const impressions = parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.impressions\"));\n const clicks = parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.clicks\"));\n const cost = microsToCurrency(getFieldValue(row, \"metrics.costMicros\"));\n const conversions = round(\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversions\"))\n );\n const conversionValue = round(\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversionsValue\"))\n );\n\n return {\n campaign_id: getFieldValue(row, \"campaign.id\"),\n campaign_name: getFieldValue(row, \"campaign.name\"),\n status: getFieldValue(row, \"campaign.status\"),\n channel_type: getFieldValue(row, \"campaign.advertisingChannelType\"),\n impressions,\n clicks,\n cost,\n conversions,\n conversion_value: conversionValue,\n ctr_percent: round(parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.ctr\")) * 100),\n average_cpc: microsToCurrency(getFieldValue(row, \"metrics.averageCpc\")),\n cpa: round(cost / conversions),\n roas: round(conversionValue / cost),\n conversion_rate_percent: round(toPercent(conversions, clicks)),\n cost_share_percent: 0,\n };\n });\n\n const totals = campaigns.reduce(\n (acc, campaign) => {\n acc.impressions += campaign.impressions;\n acc.clicks += campaign.clicks;\n acc.cost += campaign.cost;\n acc.conversions += campaign.conversions;\n acc.conversion_value += campaign.conversion_value;\n return acc;\n },\n {\n impressions: 0,\n clicks: 0,\n cost: 0,\n conversions: 0,\n conversion_value: 0,\n }\n );\n const campaignsWithShare = campaigns.map((campaign) => ({\n ...campaign,\n cost_share_percent: round(toPercent(campaign.cost, totals.cost)),\n }));\n\n return object(\n stripNulls({\n customer_id: customerId,\n login_customer_id: loginCustomerId,\n date_range: {\n start_date: params.startDate,\n end_date: params.endDate,\n },\n customer_name: getFieldValue(result.rows[0] ?? {}, \"customer.descriptiveName\"),\n currency_code: getFieldValue(result.rows[0] ?? {}, \"customer.currencyCode\"),\n applied_filters: {\n enabled_only: params.enabledOnly !== false,\n statuses: effectiveStatuses,\n },\n overview: {\n total_campaigns: campaignsWithShare.length,\n impressions: totals.impressions,\n clicks: totals.clicks,\n cost: round(totals.cost),\n conversions: round(totals.conversions),\n conversion_value: round(totals.conversion_value),\n ctr_percent: round(toPercent(totals.clicks, totals.impressions)),\n average_cpc: round(totals.cost / totals.clicks),\n cpa: round(totals.cost / totals.conversions),\n roas: round(totals.conversion_value / totals.cost),\n conversion_rate_percent: round(toPercent(totals.conversions, totals.clicks)),\n top_campaign_by_cost: campaignsWithShare[0]?.campaign_name,\n },\n campaigns: campaignsWithShare,\n metadata: {\n row_count: result.rows.length,\n request_id: result.requestId,\n },\n })\n );\n } catch (err) {\n return error(\n err instanceof Error ? err.message : \"Failed to fetch Google Ads campaign performance\"\n );\n }\n}\n"],
4
+ "sourcesContent": ["import { error, object } from \"mcp-use/server\";\r\nimport { z } from \"zod\";\r\n\r\nimport {\r\n dateRegex,\r\n getFieldValue,\r\n microsToCurrency,\r\n parseGoogleAdsMetricValue,\r\n resolveGoogleAdsCustomerId,\r\n resolveGoogleAdsDeveloperToken,\r\n resolveGoogleAdsLoginCustomerId,\r\n round,\r\n toPercent,\r\n} from \"../../utils/google-ads.js\";\r\nimport { searchGoogleAds } from \"../../services/google-ads/google-ads-client.js\";\r\nimport { stripNulls } from \"../../utils/strip-payload.js\";\r\n\r\nconst campaignStatusEnum = z.enum([\"ENABLED\", \"PAUSED\", \"REMOVED\"]);\r\n\r\nexport const googleAdsCampaignPerformanceSchema = z.object({\r\n startDate: z.string().regex(dateRegex).describe(\"Start date in YYYY-MM-DD format.\"),\r\n endDate: z.string().regex(dateRegex).describe(\"End date in YYYY-MM-DD format.\"),\r\n customerId: z.string().optional().describe(\"Google Ads customer ID to query. If omitted, resolves the profileId mapping. Accepts digits with or without hyphens.\"),\r\n profileId: z.string().optional().describe(\"Optional business profile ID used to resolve the mapped Google Ads customer ID.\"),\r\n loginCustomerId: z\r\n .string()\r\n .optional()\r\n .describe(\r\n \"Optional manager account ID used as login customer. If omitted, uses GOOGLE_ADS_LOGIN_CUSTOMER_ID when configured.\"\r\n ),\r\n enabledOnly: z\r\n .boolean()\r\n .optional()\r\n .default(true)\r\n .describe(\"If true, only returns enabled campaigns unless statusFilter is explicitly provided.\"),\r\n statusFilter: z\r\n .array(campaignStatusEnum)\r\n .optional()\r\n .describe(\"Optional list of campaign statuses to include. Overrides enabledOnly when provided.\"),\r\n});\r\n\r\nexport async function googleAdsCampaignPerformanceHandler(\r\n params: z.infer<typeof googleAdsCampaignPerformanceSchema>\r\n) {\r\n try {\r\n const customerId = await resolveGoogleAdsCustomerId(params.customerId, params.profileId);\r\n const loginCustomerId = resolveGoogleAdsLoginCustomerId(params.loginCustomerId);\r\n const developerToken = resolveGoogleAdsDeveloperToken();\r\n const effectiveStatuses =\r\n params.statusFilter && params.statusFilter.length > 0\r\n ? params.statusFilter\r\n : params.enabledOnly !== false\r\n ? [\"ENABLED\"]\r\n : undefined;\r\n const whereClauses = [`segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`];\r\n\r\n if (effectiveStatuses?.length) {\r\n whereClauses.push(\r\n `campaign.status IN (${effectiveStatuses.map((status) => `'${status}'`).join(\", \")})`\r\n );\r\n }\r\n\r\n const result = await searchGoogleAds(\r\n customerId,\r\n [\r\n \"SELECT\",\r\n \"customer.descriptive_name,\",\r\n \"customer.currency_code,\",\r\n \"campaign.id,\",\r\n \"campaign.name,\",\r\n \"campaign.status,\",\r\n \"campaign.advertising_channel_type,\",\r\n \"metrics.impressions,\",\r\n \"metrics.clicks,\",\r\n \"metrics.cost_micros,\",\r\n \"metrics.conversions,\",\r\n \"metrics.conversions_value,\",\r\n \"metrics.ctr,\",\r\n \"metrics.average_cpc\",\r\n \"FROM campaign\",\r\n `WHERE ${whereClauses.join(\" AND \")}`,\r\n \"ORDER BY metrics.cost_micros DESC\",\r\n ].join(\" \"),\r\n developerToken,\r\n loginCustomerId\r\n );\r\n\r\n const campaigns = result.rows.map((row) => {\r\n const impressions = parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.impressions\"));\r\n const clicks = parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.clicks\"));\r\n const cost = microsToCurrency(getFieldValue(row, \"metrics.costMicros\"));\r\n const conversions = round(\r\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversions\"))\r\n );\r\n const conversionValue = round(\r\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversionsValue\"))\r\n );\r\n\r\n return {\r\n campaign_id: getFieldValue(row, \"campaign.id\"),\r\n campaign_name: getFieldValue(row, \"campaign.name\"),\r\n status: getFieldValue(row, \"campaign.status\"),\r\n channel_type: getFieldValue(row, \"campaign.advertisingChannelType\"),\r\n impressions,\r\n clicks,\r\n cost,\r\n conversions,\r\n conversion_value: conversionValue,\r\n ctr_percent: round(parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.ctr\")) * 100),\r\n average_cpc: microsToCurrency(getFieldValue(row, \"metrics.averageCpc\")),\r\n cpa: round(cost / conversions),\r\n roas: round(conversionValue / cost),\r\n conversion_rate_percent: round(toPercent(conversions, clicks)),\r\n cost_share_percent: 0,\r\n };\r\n });\r\n\r\n const totals = campaigns.reduce(\r\n (acc, campaign) => {\r\n acc.impressions += campaign.impressions;\r\n acc.clicks += campaign.clicks;\r\n acc.cost += campaign.cost;\r\n acc.conversions += campaign.conversions;\r\n acc.conversion_value += campaign.conversion_value;\r\n return acc;\r\n },\r\n {\r\n impressions: 0,\r\n clicks: 0,\r\n cost: 0,\r\n conversions: 0,\r\n conversion_value: 0,\r\n }\r\n );\r\n const campaignsWithShare = campaigns.map((campaign) => ({\r\n ...campaign,\r\n cost_share_percent: round(toPercent(campaign.cost, totals.cost)),\r\n }));\r\n\r\n return object(\r\n stripNulls({\r\n customer_id: customerId,\r\n login_customer_id: loginCustomerId,\r\n date_range: {\r\n start_date: params.startDate,\r\n end_date: params.endDate,\r\n },\r\n customer_name: getFieldValue(result.rows[0] ?? {}, \"customer.descriptiveName\"),\r\n currency_code: getFieldValue(result.rows[0] ?? {}, \"customer.currencyCode\"),\r\n applied_filters: {\r\n enabled_only: params.enabledOnly !== false,\r\n statuses: effectiveStatuses,\r\n },\r\n overview: {\r\n total_campaigns: campaignsWithShare.length,\r\n impressions: totals.impressions,\r\n clicks: totals.clicks,\r\n cost: round(totals.cost),\r\n conversions: round(totals.conversions),\r\n conversion_value: round(totals.conversion_value),\r\n ctr_percent: round(toPercent(totals.clicks, totals.impressions)),\r\n average_cpc: round(totals.cost / totals.clicks),\r\n cpa: round(totals.cost / totals.conversions),\r\n roas: round(totals.conversion_value / totals.cost),\r\n conversion_rate_percent: round(toPercent(totals.conversions, totals.clicks)),\r\n top_campaign_by_cost: campaignsWithShare[0]?.campaign_name,\r\n },\r\n campaigns: campaignsWithShare,\r\n metadata: {\r\n row_count: result.rows.length,\r\n request_id: result.requestId,\r\n },\r\n })\r\n );\r\n } catch (err) {\r\n return error(\r\n err instanceof Error ? err.message : \"Failed to fetch Google Ads campaign performance\"\r\n );\r\n }\r\n}\r\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/tools/google-ads/channel-mix.ts"],
4
- "sourcesContent": ["import { error, object } from \"mcp-use/server\";\nimport { z } from \"zod\";\n\nimport {\n dateRegex,\n getFieldValue,\n microsToCurrency,\n parseGoogleAdsMetricValue,\n resolveGoogleAdsCustomerId,\n resolveGoogleAdsDeveloperToken,\n resolveGoogleAdsLoginCustomerId,\n round,\n toPercent,\n} from \"../../utils/google-ads.js\";\nimport { searchGoogleAds } from \"../../services/google-ads/google-ads-client.js\";\nimport { stripNulls } from \"../../utils/strip-payload.js\";\n\nexport const googleAdsChannelMixSchema = z.object({\n startDate: z.string().regex(dateRegex).describe(\"Start date in YYYY-MM-DD format.\"),\n endDate: z.string().regex(dateRegex).describe(\"End date in YYYY-MM-DD format.\"),\n customerId: z\n .string()\n .optional()\n .describe(\"Google Ads customer ID to query. If omitted, resolves the profileId mapping. Accepts digits with or without hyphens.\"),\n profileId: z.string().optional().describe(\"Optional business profile ID used to resolve the mapped Google Ads customer ID.\"),\n loginCustomerId: z\n .string()\n .optional()\n .describe(\n \"Optional manager account ID used as login customer. If omitted, uses GOOGLE_ADS_LOGIN_CUSTOMER_ID when configured.\"\n ),\n});\n\nexport async function googleAdsChannelMixHandler(\n params: z.infer<typeof googleAdsChannelMixSchema>\n) {\n try {\n const customerId = await resolveGoogleAdsCustomerId(params.customerId, params.profileId);\n const loginCustomerId = resolveGoogleAdsLoginCustomerId(params.loginCustomerId);\n const developerToken = resolveGoogleAdsDeveloperToken();\n\n const result = await searchGoogleAds(\n customerId,\n [\n \"SELECT\",\n \"customer.descriptive_name,\",\n \"customer.currency_code,\",\n \"campaign.advertising_channel_type,\",\n \"metrics.impressions,\",\n \"metrics.clicks,\",\n \"metrics.cost_micros,\",\n \"metrics.conversions,\",\n \"metrics.conversions_value\",\n \"FROM campaign\",\n `WHERE segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`,\n \"AND campaign.status = 'ENABLED'\",\n \"ORDER BY metrics.cost_micros DESC\",\n ].join(\" \"),\n developerToken,\n loginCustomerId\n );\n\n const grouped = new Map<\n string,\n {\n channel_type: string;\n impressions: number;\n clicks: number;\n cost: number;\n conversions: number;\n conversion_value: number;\n }\n >();\n\n for (const row of result.rows) {\n const channelType = String(\n getFieldValue(row, \"campaign.advertisingChannelType\", \"UNKNOWN\") ?? \"UNKNOWN\"\n );\n const current = grouped.get(channelType) ?? {\n channel_type: channelType,\n impressions: 0,\n clicks: 0,\n cost: 0,\n conversions: 0,\n conversion_value: 0,\n };\n\n current.impressions += parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.impressions\"));\n current.clicks += parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.clicks\"));\n current.cost += microsToCurrency(getFieldValue(row, \"metrics.costMicros\"));\n current.conversions += round(\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversions\"))\n );\n current.conversion_value += round(\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversionsValue\"))\n );\n\n grouped.set(channelType, current);\n }\n\n const totals = Array.from(grouped.values()).reduce(\n (acc, channel) => {\n acc.impressions += channel.impressions;\n acc.clicks += channel.clicks;\n acc.cost += channel.cost;\n acc.conversions += channel.conversions;\n acc.conversion_value += channel.conversion_value;\n return acc;\n },\n { impressions: 0, clicks: 0, cost: 0, conversions: 0, conversion_value: 0 }\n );\n\n const channels = Array.from(grouped.values())\n .map((channel) => ({\n ...channel,\n cost: round(channel.cost),\n conversions: round(channel.conversions),\n conversion_value: round(channel.conversion_value),\n ctr_percent: round(toPercent(channel.clicks, channel.impressions)),\n cost_share_percent: round(toPercent(channel.cost, totals.cost)),\n cpa: round(channel.cost / channel.conversions),\n roas: round(channel.conversion_value / channel.cost),\n }))\n .sort((a, b) => b.cost - a.cost);\n\n return object(\n stripNulls({\n customer_id: customerId,\n login_customer_id: loginCustomerId,\n customer_name: getFieldValue(result.rows[0] ?? {}, \"customer.descriptiveName\"),\n currency_code: getFieldValue(result.rows[0] ?? {}, \"customer.currencyCode\"),\n date_range: {\n start_date: params.startDate,\n end_date: params.endDate,\n },\n overview: {\n total_channels: channels.length,\n impressions: totals.impressions,\n clicks: totals.clicks,\n cost: round(totals.cost),\n conversions: round(totals.conversions),\n conversion_value: round(totals.conversion_value),\n dominant_channel: channels[0]?.channel_type,\n dominant_channel_cost_share_percent: channels[0]?.cost_share_percent,\n },\n channels,\n metadata: {\n row_count: result.rows.length,\n request_id: result.requestId,\n },\n })\n );\n } catch (err) {\n return error(err instanceof Error ? err.message : \"Failed to fetch Google Ads channel mix\");\n }\n}\n"],
4
+ "sourcesContent": ["import { error, object } from \"mcp-use/server\";\r\nimport { z } from \"zod\";\r\n\r\nimport {\r\n dateRegex,\r\n getFieldValue,\r\n microsToCurrency,\r\n parseGoogleAdsMetricValue,\r\n resolveGoogleAdsCustomerId,\r\n resolveGoogleAdsDeveloperToken,\r\n resolveGoogleAdsLoginCustomerId,\r\n round,\r\n toPercent,\r\n} from \"../../utils/google-ads.js\";\r\nimport { searchGoogleAds } from \"../../services/google-ads/google-ads-client.js\";\r\nimport { stripNulls } from \"../../utils/strip-payload.js\";\r\n\r\nexport const googleAdsChannelMixSchema = z.object({\r\n startDate: z.string().regex(dateRegex).describe(\"Start date in YYYY-MM-DD format.\"),\r\n endDate: z.string().regex(dateRegex).describe(\"End date in YYYY-MM-DD format.\"),\r\n customerId: z\r\n .string()\r\n .optional()\r\n .describe(\"Google Ads customer ID to query. If omitted, resolves the profileId mapping. Accepts digits with or without hyphens.\"),\r\n profileId: z.string().optional().describe(\"Optional business profile ID used to resolve the mapped Google Ads customer ID.\"),\r\n loginCustomerId: z\r\n .string()\r\n .optional()\r\n .describe(\r\n \"Optional manager account ID used as login customer. If omitted, uses GOOGLE_ADS_LOGIN_CUSTOMER_ID when configured.\"\r\n ),\r\n});\r\n\r\nexport async function googleAdsChannelMixHandler(\r\n params: z.infer<typeof googleAdsChannelMixSchema>\r\n) {\r\n try {\r\n const customerId = await resolveGoogleAdsCustomerId(params.customerId, params.profileId);\r\n const loginCustomerId = resolveGoogleAdsLoginCustomerId(params.loginCustomerId);\r\n const developerToken = resolveGoogleAdsDeveloperToken();\r\n\r\n const result = await searchGoogleAds(\r\n customerId,\r\n [\r\n \"SELECT\",\r\n \"customer.descriptive_name,\",\r\n \"customer.currency_code,\",\r\n \"campaign.advertising_channel_type,\",\r\n \"metrics.impressions,\",\r\n \"metrics.clicks,\",\r\n \"metrics.cost_micros,\",\r\n \"metrics.conversions,\",\r\n \"metrics.conversions_value\",\r\n \"FROM campaign\",\r\n `WHERE segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`,\r\n \"AND campaign.status = 'ENABLED'\",\r\n \"ORDER BY metrics.cost_micros DESC\",\r\n ].join(\" \"),\r\n developerToken,\r\n loginCustomerId\r\n );\r\n\r\n const grouped = new Map<\r\n string,\r\n {\r\n channel_type: string;\r\n impressions: number;\r\n clicks: number;\r\n cost: number;\r\n conversions: number;\r\n conversion_value: number;\r\n }\r\n >();\r\n\r\n for (const row of result.rows) {\r\n const channelType = String(\r\n getFieldValue(row, \"campaign.advertisingChannelType\", \"UNKNOWN\") ?? \"UNKNOWN\"\r\n );\r\n const current = grouped.get(channelType) ?? {\r\n channel_type: channelType,\r\n impressions: 0,\r\n clicks: 0,\r\n cost: 0,\r\n conversions: 0,\r\n conversion_value: 0,\r\n };\r\n\r\n current.impressions += parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.impressions\"));\r\n current.clicks += parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.clicks\"));\r\n current.cost += microsToCurrency(getFieldValue(row, \"metrics.costMicros\"));\r\n current.conversions += round(\r\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversions\"))\r\n );\r\n current.conversion_value += round(\r\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversionsValue\"))\r\n );\r\n\r\n grouped.set(channelType, current);\r\n }\r\n\r\n const totals = Array.from(grouped.values()).reduce(\r\n (acc, channel) => {\r\n acc.impressions += channel.impressions;\r\n acc.clicks += channel.clicks;\r\n acc.cost += channel.cost;\r\n acc.conversions += channel.conversions;\r\n acc.conversion_value += channel.conversion_value;\r\n return acc;\r\n },\r\n { impressions: 0, clicks: 0, cost: 0, conversions: 0, conversion_value: 0 }\r\n );\r\n\r\n const channels = Array.from(grouped.values())\r\n .map((channel) => ({\r\n ...channel,\r\n cost: round(channel.cost),\r\n conversions: round(channel.conversions),\r\n conversion_value: round(channel.conversion_value),\r\n ctr_percent: round(toPercent(channel.clicks, channel.impressions)),\r\n cost_share_percent: round(toPercent(channel.cost, totals.cost)),\r\n cpa: round(channel.cost / channel.conversions),\r\n roas: round(channel.conversion_value / channel.cost),\r\n }))\r\n .sort((a, b) => b.cost - a.cost);\r\n\r\n return object(\r\n stripNulls({\r\n customer_id: customerId,\r\n login_customer_id: loginCustomerId,\r\n customer_name: getFieldValue(result.rows[0] ?? {}, \"customer.descriptiveName\"),\r\n currency_code: getFieldValue(result.rows[0] ?? {}, \"customer.currencyCode\"),\r\n date_range: {\r\n start_date: params.startDate,\r\n end_date: params.endDate,\r\n },\r\n overview: {\r\n total_channels: channels.length,\r\n impressions: totals.impressions,\r\n clicks: totals.clicks,\r\n cost: round(totals.cost),\r\n conversions: round(totals.conversions),\r\n conversion_value: round(totals.conversion_value),\r\n dominant_channel: channels[0]?.channel_type,\r\n dominant_channel_cost_share_percent: channels[0]?.cost_share_percent,\r\n },\r\n channels,\r\n metadata: {\r\n row_count: result.rows.length,\r\n request_id: result.requestId,\r\n },\r\n })\r\n );\r\n } catch (err) {\r\n return error(err instanceof Error ? err.message : \"Failed to fetch Google Ads channel mix\");\r\n }\r\n}\r\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/tools/google-ads/compare-accounts.ts"],
4
- "sourcesContent": ["import { error, object } from \"mcp-use/server\";\nimport { z } from \"zod\";\n\nimport {\n dateRegex,\n getFieldValue,\n microsToCurrency,\n parseGoogleAdsMetricValue,\n resolveGoogleAdsCustomerId,\n resolveGoogleAdsDeveloperToken,\n resolveGoogleAdsLoginCustomerId,\n round,\n toPercent,\n} from \"../../utils/google-ads.js\";\nimport { searchGoogleAds } from \"../../services/google-ads/google-ads-client.js\";\nimport { stripNulls } from \"../../utils/strip-payload.js\";\n\nexport const googleAdsCompareAccountsSchema = z.object({\n startDate: z.string().regex(dateRegex).describe(\"Start date in YYYY-MM-DD format.\"),\n endDate: z.string().regex(dateRegex).describe(\"End date in YYYY-MM-DD format.\"),\n customerIds: z\n .array(z.string())\n .min(2)\n .max(10)\n .describe(\"Google Ads customer IDs to compare. Accepts digits with or without hyphens.\"),\n loginCustomerId: z\n .string()\n .optional()\n .describe(\n \"Optional manager account ID used as login customer. If omitted, uses GOOGLE_ADS_LOGIN_CUSTOMER_ID when configured.\"\n ),\n});\n\nexport async function googleAdsCompareAccountsHandler(\n params: z.infer<typeof googleAdsCompareAccountsSchema>\n) {\n try {\n const customerIds = await Promise.all(params.customerIds.map((id) => resolveGoogleAdsCustomerId(id)));\n const loginCustomerId = resolveGoogleAdsLoginCustomerId(params.loginCustomerId);\n const developerToken = resolveGoogleAdsDeveloperToken();\n\n const accountResults = await Promise.all(\n customerIds.map(async (customerId) => {\n const [overviewResult, campaignResult] = await Promise.all([\n searchGoogleAds(\n customerId,\n [\n \"SELECT\",\n \"customer.descriptive_name,\",\n \"customer.currency_code,\",\n \"metrics.clicks,\",\n \"metrics.cost_micros,\",\n \"metrics.conversions,\",\n \"metrics.conversions_value\",\n \"FROM customer\",\n `WHERE segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`,\n ].join(\" \"),\n developerToken,\n loginCustomerId\n ),\n searchGoogleAds(\n customerId,\n [\n \"SELECT\",\n \"metrics.cost_micros\",\n \"FROM campaign\",\n `WHERE segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`,\n \"AND campaign.status = 'ENABLED'\",\n \"ORDER BY metrics.cost_micros DESC\",\n ].join(\" \"),\n developerToken,\n loginCustomerId\n ),\n ]);\n\n const row = overviewResult.rows[0] ?? {};\n const cost = microsToCurrency(getFieldValue(row, \"metrics.costMicros\"));\n const conversions = round(\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversions\"))\n );\n const conversionValue = round(\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversionsValue\"))\n );\n const clicks = parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.clicks\"));\n const roas = round(conversionValue / cost);\n const cpa = round(cost / conversions);\n const topCampaignCost = microsToCurrency(\n getFieldValue(campaignResult.rows[0] ?? {}, \"metrics.costMicros\")\n );\n const topCampaignCostSharePercent = round(toPercent(topCampaignCost, cost));\n\n const riskScore =\n (roas < 1 ? 40 : roas < 1.5 ? 20 : 0) +\n (conversions < 5 ? 25 : 0) +\n (topCampaignCostSharePercent >= 70 ? 25 : topCampaignCostSharePercent >= 50 ? 10 : 0) +\n (cost <= 0 ? 10 : 0);\n\n return {\n customer_id: customerId,\n customer_name: getFieldValue(row, \"customer.descriptiveName\"),\n currency_code: getFieldValue(row, \"customer.currencyCode\"),\n cost,\n clicks,\n conversions,\n conversion_value: conversionValue,\n roas,\n cpa,\n top_campaign_cost_share_percent: topCampaignCostSharePercent,\n risk_score: riskScore,\n };\n })\n );\n\n const rankedAccounts = accountResults.sort((a, b) => b.risk_score - a.risk_score);\n\n return object(\n stripNulls({\n login_customer_id: loginCustomerId,\n date_range: {\n start_date: params.startDate,\n end_date: params.endDate,\n },\n overview: {\n compared_accounts: rankedAccounts.length,\n highest_risk_customer_id: rankedAccounts[0]?.customer_id,\n highest_risk_customer_name: rankedAccounts[0]?.customer_name,\n lowest_risk_customer_id: rankedAccounts.at(-1)?.customer_id,\n lowest_risk_customer_name: rankedAccounts.at(-1)?.customer_name,\n },\n accounts: rankedAccounts,\n interpretation: {\n worst_account: rankedAccounts[0]\n ? `${rankedAccounts[0].customer_name} appears to be the weakest account, mainly driven by ROAS ${rankedAccounts[0].roas}, ${rankedAccounts[0].conversions} conversions, and ${rankedAccounts[0].top_campaign_cost_share_percent}% spend concentration in the top campaign.`\n : undefined,\n },\n })\n );\n } catch (err) {\n return error(err instanceof Error ? err.message : \"Failed to compare Google Ads accounts\");\n }\n}\n"],
4
+ "sourcesContent": ["import { error, object } from \"mcp-use/server\";\r\nimport { z } from \"zod\";\r\n\r\nimport {\r\n dateRegex,\r\n getFieldValue,\r\n microsToCurrency,\r\n parseGoogleAdsMetricValue,\r\n resolveGoogleAdsCustomerId,\r\n resolveGoogleAdsDeveloperToken,\r\n resolveGoogleAdsLoginCustomerId,\r\n round,\r\n toPercent,\r\n} from \"../../utils/google-ads.js\";\r\nimport { searchGoogleAds } from \"../../services/google-ads/google-ads-client.js\";\r\nimport { stripNulls } from \"../../utils/strip-payload.js\";\r\n\r\nexport const googleAdsCompareAccountsSchema = z.object({\r\n startDate: z.string().regex(dateRegex).describe(\"Start date in YYYY-MM-DD format.\"),\r\n endDate: z.string().regex(dateRegex).describe(\"End date in YYYY-MM-DD format.\"),\r\n customerIds: z\r\n .array(z.string())\r\n .min(2)\r\n .max(10)\r\n .describe(\"Google Ads customer IDs to compare. Accepts digits with or without hyphens.\"),\r\n loginCustomerId: z\r\n .string()\r\n .optional()\r\n .describe(\r\n \"Optional manager account ID used as login customer. If omitted, uses GOOGLE_ADS_LOGIN_CUSTOMER_ID when configured.\"\r\n ),\r\n});\r\n\r\nexport async function googleAdsCompareAccountsHandler(\r\n params: z.infer<typeof googleAdsCompareAccountsSchema>\r\n) {\r\n try {\r\n const customerIds = await Promise.all(params.customerIds.map((id) => resolveGoogleAdsCustomerId(id)));\r\n const loginCustomerId = resolveGoogleAdsLoginCustomerId(params.loginCustomerId);\r\n const developerToken = resolveGoogleAdsDeveloperToken();\r\n\r\n const accountResults = await Promise.all(\r\n customerIds.map(async (customerId) => {\r\n const [overviewResult, campaignResult] = await Promise.all([\r\n searchGoogleAds(\r\n customerId,\r\n [\r\n \"SELECT\",\r\n \"customer.descriptive_name,\",\r\n \"customer.currency_code,\",\r\n \"metrics.clicks,\",\r\n \"metrics.cost_micros,\",\r\n \"metrics.conversions,\",\r\n \"metrics.conversions_value\",\r\n \"FROM customer\",\r\n `WHERE segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`,\r\n ].join(\" \"),\r\n developerToken,\r\n loginCustomerId\r\n ),\r\n searchGoogleAds(\r\n customerId,\r\n [\r\n \"SELECT\",\r\n \"metrics.cost_micros\",\r\n \"FROM campaign\",\r\n `WHERE segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`,\r\n \"AND campaign.status = 'ENABLED'\",\r\n \"ORDER BY metrics.cost_micros DESC\",\r\n ].join(\" \"),\r\n developerToken,\r\n loginCustomerId\r\n ),\r\n ]);\r\n\r\n const row = overviewResult.rows[0] ?? {};\r\n const cost = microsToCurrency(getFieldValue(row, \"metrics.costMicros\"));\r\n const conversions = round(\r\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversions\"))\r\n );\r\n const conversionValue = round(\r\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversionsValue\"))\r\n );\r\n const clicks = parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.clicks\"));\r\n const roas = round(conversionValue / cost);\r\n const cpa = round(cost / conversions);\r\n const topCampaignCost = microsToCurrency(\r\n getFieldValue(campaignResult.rows[0] ?? {}, \"metrics.costMicros\")\r\n );\r\n const topCampaignCostSharePercent = round(toPercent(topCampaignCost, cost));\r\n\r\n const riskScore =\r\n (roas < 1 ? 40 : roas < 1.5 ? 20 : 0) +\r\n (conversions < 5 ? 25 : 0) +\r\n (topCampaignCostSharePercent >= 70 ? 25 : topCampaignCostSharePercent >= 50 ? 10 : 0) +\r\n (cost <= 0 ? 10 : 0);\r\n\r\n return {\r\n customer_id: customerId,\r\n customer_name: getFieldValue(row, \"customer.descriptiveName\"),\r\n currency_code: getFieldValue(row, \"customer.currencyCode\"),\r\n cost,\r\n clicks,\r\n conversions,\r\n conversion_value: conversionValue,\r\n roas,\r\n cpa,\r\n top_campaign_cost_share_percent: topCampaignCostSharePercent,\r\n risk_score: riskScore,\r\n };\r\n })\r\n );\r\n\r\n const rankedAccounts = accountResults.sort((a, b) => b.risk_score - a.risk_score);\r\n\r\n return object(\r\n stripNulls({\r\n login_customer_id: loginCustomerId,\r\n date_range: {\r\n start_date: params.startDate,\r\n end_date: params.endDate,\r\n },\r\n overview: {\r\n compared_accounts: rankedAccounts.length,\r\n highest_risk_customer_id: rankedAccounts[0]?.customer_id,\r\n highest_risk_customer_name: rankedAccounts[0]?.customer_name,\r\n lowest_risk_customer_id: rankedAccounts.at(-1)?.customer_id,\r\n lowest_risk_customer_name: rankedAccounts.at(-1)?.customer_name,\r\n },\r\n accounts: rankedAccounts,\r\n interpretation: {\r\n worst_account: rankedAccounts[0]\r\n ? `${rankedAccounts[0].customer_name} appears to be the weakest account, mainly driven by ROAS ${rankedAccounts[0].roas}, ${rankedAccounts[0].conversions} conversions, and ${rankedAccounts[0].top_campaign_cost_share_percent}% spend concentration in the top campaign.`\r\n : undefined,\r\n },\r\n })\r\n );\r\n } catch (err) {\r\n return error(err instanceof Error ? err.message : \"Failed to compare Google Ads accounts\");\r\n }\r\n}\r\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/tools/google-ads/customer-clients.ts"],
4
- "sourcesContent": ["import { error, object } from \"mcp-use/server\";\nimport { z } from \"zod\";\n\nimport {\n getFieldValue,\n resolveGoogleAdsCustomerId,\n resolveGoogleAdsDeveloperToken,\n resolveGoogleAdsLoginCustomerId,\n} from \"../../utils/google-ads.js\";\nimport { searchGoogleAds } from \"../../services/google-ads/google-ads-client.js\";\nimport { stripNulls } from \"../../utils/strip-payload.js\";\n\nexport const googleAdsCustomerClientsSchema = z.object({\n customerId: z\n .string()\n .optional()\n .describe(\"Google Ads customer ID to inspect for visible child accounts. If omitted, resolves the profileId mapping.\"),\n profileId: z.string().optional().describe(\"Optional business profile ID used to resolve the mapped Google Ads customer ID.\"),\n loginCustomerId: z\n .string()\n .optional()\n .describe(\n \"Optional manager account ID used as login customer. If omitted, uses GOOGLE_ADS_LOGIN_CUSTOMER_ID when configured.\"\n ),\n});\n\nexport async function googleAdsCustomerClientsHandler(\n params: z.infer<typeof googleAdsCustomerClientsSchema>\n) {\n try {\n const customerId = await resolveGoogleAdsCustomerId(params.customerId, params.profileId);\n const loginCustomerId = resolveGoogleAdsLoginCustomerId(params.loginCustomerId);\n const developerToken = resolveGoogleAdsDeveloperToken();\n\n const result = await searchGoogleAds(\n customerId,\n [\n \"SELECT\",\n \"customer_client.client_customer,\",\n \"customer_client.descriptive_name,\",\n \"customer_client.currency_code,\",\n \"customer_client.time_zone,\",\n \"customer_client.manager,\",\n \"customer_client.test_account,\",\n \"customer_client.status\",\n \"FROM customer_client\",\n \"ORDER BY customer_client.descriptive_name\",\n ].join(\" \"),\n developerToken,\n loginCustomerId\n );\n\n const customers = result.rows.map((row) => ({\n customer_id: String(getFieldValue(row, \"customerClient.clientCustomer\") ?? \"\").replace(\n \"customers/\",\n \"\"\n ),\n descriptive_name: getFieldValue(row, \"customerClient.descriptiveName\"),\n currency_code: getFieldValue(row, \"customerClient.currencyCode\"),\n time_zone: getFieldValue(row, \"customerClient.timeZone\"),\n is_manager: getFieldValue<boolean>(row, \"customerClient.manager\"),\n is_test_account: getFieldValue<boolean>(row, \"customerClient.testAccount\"),\n status: getFieldValue(row, \"customerClient.status\"),\n }));\n\n return object(\n stripNulls({\n customer_id: customerId,\n login_customer_id: loginCustomerId,\n overview: {\n total_visible_customers: customers.length,\n manager_customers: customers.filter((customer) => customer.is_manager).length,\n client_customers: customers.filter((customer) => customer.is_manager === false).length,\n },\n customers,\n metadata: {\n row_count: result.rows.length,\n request_id: result.requestId,\n },\n })\n );\n } catch (err) {\n return error(\n err instanceof Error ? err.message : \"Failed to list visible Google Ads customer clients\"\n );\n }\n}\n"],
4
+ "sourcesContent": ["import { error, object } from \"mcp-use/server\";\r\nimport { z } from \"zod\";\r\n\r\nimport {\r\n getFieldValue,\r\n resolveGoogleAdsCustomerId,\r\n resolveGoogleAdsDeveloperToken,\r\n resolveGoogleAdsLoginCustomerId,\r\n} from \"../../utils/google-ads.js\";\r\nimport { searchGoogleAds } from \"../../services/google-ads/google-ads-client.js\";\r\nimport { stripNulls } from \"../../utils/strip-payload.js\";\r\n\r\nexport const googleAdsCustomerClientsSchema = z.object({\r\n customerId: z\r\n .string()\r\n .optional()\r\n .describe(\"Google Ads customer ID to inspect for visible child accounts. If omitted, resolves the profileId mapping.\"),\r\n profileId: z.string().optional().describe(\"Optional business profile ID used to resolve the mapped Google Ads customer ID.\"),\r\n loginCustomerId: z\r\n .string()\r\n .optional()\r\n .describe(\r\n \"Optional manager account ID used as login customer. If omitted, uses GOOGLE_ADS_LOGIN_CUSTOMER_ID when configured.\"\r\n ),\r\n});\r\n\r\nexport async function googleAdsCustomerClientsHandler(\r\n params: z.infer<typeof googleAdsCustomerClientsSchema>\r\n) {\r\n try {\r\n const customerId = await resolveGoogleAdsCustomerId(params.customerId, params.profileId);\r\n const loginCustomerId = resolveGoogleAdsLoginCustomerId(params.loginCustomerId);\r\n const developerToken = resolveGoogleAdsDeveloperToken();\r\n\r\n const result = await searchGoogleAds(\r\n customerId,\r\n [\r\n \"SELECT\",\r\n \"customer_client.client_customer,\",\r\n \"customer_client.descriptive_name,\",\r\n \"customer_client.currency_code,\",\r\n \"customer_client.time_zone,\",\r\n \"customer_client.manager,\",\r\n \"customer_client.test_account,\",\r\n \"customer_client.status\",\r\n \"FROM customer_client\",\r\n \"ORDER BY customer_client.descriptive_name\",\r\n ].join(\" \"),\r\n developerToken,\r\n loginCustomerId\r\n );\r\n\r\n const customers = result.rows.map((row) => ({\r\n customer_id: String(getFieldValue(row, \"customerClient.clientCustomer\") ?? \"\").replace(\r\n \"customers/\",\r\n \"\"\r\n ),\r\n descriptive_name: getFieldValue(row, \"customerClient.descriptiveName\"),\r\n currency_code: getFieldValue(row, \"customerClient.currencyCode\"),\r\n time_zone: getFieldValue(row, \"customerClient.timeZone\"),\r\n is_manager: getFieldValue<boolean>(row, \"customerClient.manager\"),\r\n is_test_account: getFieldValue<boolean>(row, \"customerClient.testAccount\"),\r\n status: getFieldValue(row, \"customerClient.status\"),\r\n }));\r\n\r\n return object(\r\n stripNulls({\r\n customer_id: customerId,\r\n login_customer_id: loginCustomerId,\r\n overview: {\r\n total_visible_customers: customers.length,\r\n manager_customers: customers.filter((customer) => customer.is_manager).length,\r\n client_customers: customers.filter((customer) => customer.is_manager === false).length,\r\n },\r\n customers,\r\n metadata: {\r\n row_count: result.rows.length,\r\n request_id: result.requestId,\r\n },\r\n })\r\n );\r\n } catch (err) {\r\n return error(\r\n err instanceof Error ? err.message : \"Failed to list visible Google Ads customer clients\"\r\n );\r\n }\r\n}\r\n"],
5
5
  "mappings": "AAAA,SAAS,OAAO,cAAc;AAC9B,SAAS,SAAS;AAElB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,uBAAuB;AAChC,SAAS,kBAAkB;AAEpB,MAAM,iCAAiC,EAAE,OAAO;AAAA,EACrD,YAAY,EACT,OAAO,EACP,SAAS,EACT,SAAS,2GAA2G;AAAA,EACvH,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,iFAAiF;AAAA,EAC3H,iBAAiB,EACd,OAAO,EACP,SAAS,EACT;AAAA,IACC;AAAA,EACF;AACJ,CAAC;AAED,eAAsB,gCACpB,QACA;AACA,MAAI;AACF,UAAM,aAAa,MAAM,2BAA2B,OAAO,YAAY,OAAO,SAAS;AACvF,UAAM,kBAAkB,gCAAgC,OAAO,eAAe;AAC9E,UAAM,iBAAiB,+BAA+B;AAEtD,UAAM,SAAS,MAAM;AAAA,MACnB;AAAA,MACA;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,GAAG;AAAA,MACV;AAAA,MACA;AAAA,IACF;AAEA,UAAM,YAAY,OAAO,KAAK,IAAI,CAAC,SAAS;AAAA,MAC1C,aAAa,OAAO,cAAc,KAAK,+BAA+B,KAAK,EAAE,EAAE;AAAA,QAC7E;AAAA,QACA;AAAA,MACF;AAAA,MACA,kBAAkB,cAAc,KAAK,gCAAgC;AAAA,MACrE,eAAe,cAAc,KAAK,6BAA6B;AAAA,MAC/D,WAAW,cAAc,KAAK,yBAAyB;AAAA,MACvD,YAAY,cAAuB,KAAK,wBAAwB;AAAA,MAChE,iBAAiB,cAAuB,KAAK,4BAA4B;AAAA,MACzE,QAAQ,cAAc,KAAK,uBAAuB;AAAA,IACpD,EAAE;AAEF,WAAO;AAAA,MACL,WAAW;AAAA,QACT,aAAa;AAAA,QACb,mBAAmB;AAAA,QACnB,UAAU;AAAA,UACR,yBAAyB,UAAU;AAAA,UACnC,mBAAmB,UAAU,OAAO,CAAC,aAAa,SAAS,UAAU,EAAE;AAAA,UACvE,kBAAkB,UAAU,OAAO,CAAC,aAAa,SAAS,eAAe,KAAK,EAAE;AAAA,QAClF;AAAA,QACA;AAAA,QACA,UAAU;AAAA,UACR,WAAW,OAAO,KAAK;AAAA,UACvB,YAAY,OAAO;AAAA,QACrB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF,SAAS,KAAK;AACZ,WAAO;AAAA,MACL,eAAe,QAAQ,IAAI,UAAU;AAAA,IACvC;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/tools/google-ads/customer-info.ts"],
4
- "sourcesContent": ["import { error, object } from \"mcp-use/server\";\nimport { z } from \"zod\";\n\nimport {\n getFieldValue,\n resolveGoogleAdsCustomerId,\n resolveGoogleAdsDeveloperToken,\n resolveGoogleAdsLoginCustomerId,\n} from \"../../utils/google-ads.js\";\nimport { searchGoogleAds } from \"../../services/google-ads/google-ads-client.js\";\nimport { stripNulls } from \"../../utils/strip-payload.js\";\n\nexport const googleAdsCustomerInfoSchema = z.object({\n customerId: z.string().optional().describe(\"Google Ads customer ID to inspect. If omitted, resolves the profileId mapping. Accepts digits with or without hyphens.\"),\n profileId: z.string().optional().describe(\"Optional business profile ID used to resolve the mapped Google Ads customer ID.\"),\n loginCustomerId: z\n .string()\n .optional()\n .describe(\n \"Optional manager account ID used as login customer. If omitted, uses GOOGLE_ADS_LOGIN_CUSTOMER_ID when configured.\"\n ),\n});\n\nexport async function googleAdsCustomerInfoHandler(\n params: z.infer<typeof googleAdsCustomerInfoSchema>\n) {\n try {\n const customerId = await resolveGoogleAdsCustomerId(params.customerId, params.profileId);\n const loginCustomerId = resolveGoogleAdsLoginCustomerId(params.loginCustomerId);\n const developerToken = resolveGoogleAdsDeveloperToken();\n\n const result = await searchGoogleAds(\n customerId,\n [\n \"SELECT\",\n \"customer.id,\",\n \"customer.descriptive_name,\",\n \"customer.currency_code,\",\n \"customer.time_zone,\",\n \"customer.manager,\",\n \"customer.test_account\",\n \"FROM customer\",\n ].join(\" \"),\n developerToken,\n loginCustomerId\n );\n\n const row = result.rows[0] ?? {};\n\n return object(\n stripNulls({\n customer_id: customerId,\n login_customer_id: loginCustomerId,\n customer: {\n id: getFieldValue(row, \"customer.id\"),\n descriptive_name: getFieldValue(row, \"customer.descriptiveName\"),\n currency_code: getFieldValue(row, \"customer.currencyCode\"),\n time_zone: getFieldValue(row, \"customer.timeZone\"),\n is_manager: getFieldValue(row, \"customer.manager\"),\n is_test_account: getFieldValue(row, \"customer.testAccount\"),\n },\n metadata: {\n row_count: result.rows.length,\n request_id: result.requestId,\n },\n })\n );\n } catch (err) {\n return error(err instanceof Error ? err.message : \"Failed to fetch Google Ads customer info\");\n }\n}\n"],
4
+ "sourcesContent": ["import { error, object } from \"mcp-use/server\";\r\nimport { z } from \"zod\";\r\n\r\nimport {\r\n getFieldValue,\r\n resolveGoogleAdsCustomerId,\r\n resolveGoogleAdsDeveloperToken,\r\n resolveGoogleAdsLoginCustomerId,\r\n} from \"../../utils/google-ads.js\";\r\nimport { searchGoogleAds } from \"../../services/google-ads/google-ads-client.js\";\r\nimport { stripNulls } from \"../../utils/strip-payload.js\";\r\n\r\nexport const googleAdsCustomerInfoSchema = z.object({\r\n customerId: z.string().optional().describe(\"Google Ads customer ID to inspect. If omitted, resolves the profileId mapping. Accepts digits with or without hyphens.\"),\r\n profileId: z.string().optional().describe(\"Optional business profile ID used to resolve the mapped Google Ads customer ID.\"),\r\n loginCustomerId: z\r\n .string()\r\n .optional()\r\n .describe(\r\n \"Optional manager account ID used as login customer. If omitted, uses GOOGLE_ADS_LOGIN_CUSTOMER_ID when configured.\"\r\n ),\r\n});\r\n\r\nexport async function googleAdsCustomerInfoHandler(\r\n params: z.infer<typeof googleAdsCustomerInfoSchema>\r\n) {\r\n try {\r\n const customerId = await resolveGoogleAdsCustomerId(params.customerId, params.profileId);\r\n const loginCustomerId = resolveGoogleAdsLoginCustomerId(params.loginCustomerId);\r\n const developerToken = resolveGoogleAdsDeveloperToken();\r\n\r\n const result = await searchGoogleAds(\r\n customerId,\r\n [\r\n \"SELECT\",\r\n \"customer.id,\",\r\n \"customer.descriptive_name,\",\r\n \"customer.currency_code,\",\r\n \"customer.time_zone,\",\r\n \"customer.manager,\",\r\n \"customer.test_account\",\r\n \"FROM customer\",\r\n ].join(\" \"),\r\n developerToken,\r\n loginCustomerId\r\n );\r\n\r\n const row = result.rows[0] ?? {};\r\n\r\n return object(\r\n stripNulls({\r\n customer_id: customerId,\r\n login_customer_id: loginCustomerId,\r\n customer: {\r\n id: getFieldValue(row, \"customer.id\"),\r\n descriptive_name: getFieldValue(row, \"customer.descriptiveName\"),\r\n currency_code: getFieldValue(row, \"customer.currencyCode\"),\r\n time_zone: getFieldValue(row, \"customer.timeZone\"),\r\n is_manager: getFieldValue(row, \"customer.manager\"),\r\n is_test_account: getFieldValue(row, \"customer.testAccount\"),\r\n },\r\n metadata: {\r\n row_count: result.rows.length,\r\n request_id: result.requestId,\r\n },\r\n })\r\n );\r\n } catch (err) {\r\n return error(err instanceof Error ? err.message : \"Failed to fetch Google Ads customer info\");\r\n }\r\n}\r\n"],
5
5
  "mappings": "AAAA,SAAS,OAAO,cAAc;AAC9B,SAAS,SAAS;AAElB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,uBAAuB;AAChC,SAAS,kBAAkB;AAEpB,MAAM,8BAA8B,EAAE,OAAO;AAAA,EAClD,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,wHAAwH;AAAA,EACnK,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,iFAAiF;AAAA,EAC3H,iBAAiB,EACd,OAAO,EACP,SAAS,EACT;AAAA,IACC;AAAA,EACF;AACJ,CAAC;AAED,eAAsB,6BACpB,QACA;AACA,MAAI;AACF,UAAM,aAAa,MAAM,2BAA2B,OAAO,YAAY,OAAO,SAAS;AACvF,UAAM,kBAAkB,gCAAgC,OAAO,eAAe;AAC9E,UAAM,iBAAiB,+BAA+B;AAEtD,UAAM,SAAS,MAAM;AAAA,MACnB;AAAA,MACA;AAAA,QACE;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,GAAG;AAAA,MACV;AAAA,MACA;AAAA,IACF;AAEA,UAAM,MAAM,OAAO,KAAK,CAAC,KAAK,CAAC;AAE/B,WAAO;AAAA,MACL,WAAW;AAAA,QACT,aAAa;AAAA,QACb,mBAAmB;AAAA,QACnB,UAAU;AAAA,UACR,IAAI,cAAc,KAAK,aAAa;AAAA,UACpC,kBAAkB,cAAc,KAAK,0BAA0B;AAAA,UAC/D,eAAe,cAAc,KAAK,uBAAuB;AAAA,UACzD,WAAW,cAAc,KAAK,mBAAmB;AAAA,UACjD,YAAY,cAAc,KAAK,kBAAkB;AAAA,UACjD,iBAAiB,cAAc,KAAK,sBAAsB;AAAA,QAC5D;AAAA,QACA,UAAU;AAAA,UACR,WAAW,OAAO,KAAK;AAAA,UACvB,YAAY,OAAO;AAAA,QACrB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF,SAAS,KAAK;AACZ,WAAO,MAAM,eAAe,QAAQ,IAAI,UAAU,0CAA0C;AAAA,EAC9F;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/tools/google-ads/index.ts"],
4
- "sourcesContent": ["export * from \"./customer-info.js\";\nexport * from \"./customer-clients.js\";\nexport * from \"./account-overview.js\";\nexport * from \"./campaign-performance.js\";\nexport * from \"./time-series.js\";\nexport * from \"./search-terms-summary.js\";\nexport * from \"./channel-mix.js\";\nexport * from \"./account-risks.js\";\nexport * from \"./break-even-analysis.js\";\nexport * from \"./scaling-health.js\";\nexport * from \"./compare-accounts.js\";\n"],
4
+ "sourcesContent": ["export * from \"./customer-info.js\";\r\nexport * from \"./customer-clients.js\";\r\nexport * from \"./account-overview.js\";\r\nexport * from \"./campaign-performance.js\";\r\nexport * from \"./time-series.js\";\r\nexport * from \"./search-terms-summary.js\";\r\nexport * from \"./channel-mix.js\";\r\nexport * from \"./account-risks.js\";\r\nexport * from \"./break-even-analysis.js\";\r\nexport * from \"./scaling-health.js\";\r\nexport * from \"./compare-accounts.js\";\r\n"],
5
5
  "mappings": "AAAA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/tools/google-ads/scaling-health.ts"],
4
- "sourcesContent": ["import { error, object } from \"mcp-use/server\";\nimport { z } from \"zod\";\n\nimport {\n dateRegex,\n getFieldValue,\n microsToCurrency,\n parseGoogleAdsMetricValue,\n resolveGoogleAdsCustomerId,\n resolveGoogleAdsDeveloperToken,\n resolveGoogleAdsLoginCustomerId,\n round,\n toPercent,\n} from \"../../utils/google-ads.js\";\nimport { searchGoogleAds } from \"../../services/google-ads/google-ads-client.js\";\nimport { stripNulls } from \"../../utils/strip-payload.js\";\n\nexport const googleAdsScalingHealthSchema = z.object({\n startDate: z.string().regex(dateRegex).describe(\"Start date in YYYY-MM-DD format.\"),\n endDate: z.string().regex(dateRegex).describe(\"End date in YYYY-MM-DD format.\"),\n customerId: z\n .string()\n .optional()\n .describe(\"Google Ads customer ID to query. If omitted, resolves the profileId mapping. Accepts digits with or without hyphens.\"),\n profileId: z.string().optional().describe(\"Optional business profile ID used to resolve the mapped Google Ads customer ID.\"),\n loginCustomerId: z\n .string()\n .optional()\n .describe(\n \"Optional manager account ID used as login customer. If omitted, uses GOOGLE_ADS_LOGIN_CUSTOMER_ID when configured.\"\n ),\n breakEvenRoas: z\n .number()\n .positive()\n .describe(\"Break-even ROAS for the business.\"),\n targetCpa: z\n .number()\n .positive()\n .optional()\n .describe(\"Optional maximum acceptable CPA for the business.\"),\n});\n\nexport async function googleAdsScalingHealthHandler(\n params: z.infer<typeof googleAdsScalingHealthSchema>\n) {\n try {\n const customerId = await resolveGoogleAdsCustomerId(params.customerId, params.profileId);\n const loginCustomerId = resolveGoogleAdsLoginCustomerId(params.loginCustomerId);\n const developerToken = resolveGoogleAdsDeveloperToken();\n\n const overviewResult = await searchGoogleAds(\n customerId,\n [\n \"SELECT\",\n \"customer.descriptive_name,\",\n \"customer.currency_code,\",\n \"metrics.clicks,\",\n \"metrics.cost_micros,\",\n \"metrics.conversions,\",\n \"metrics.conversions_value\",\n \"FROM customer\",\n `WHERE segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`,\n ].join(\" \"),\n developerToken,\n loginCustomerId\n );\n const campaignsResult = await searchGoogleAds(\n customerId,\n [\n \"SELECT\",\n \"campaign.name,\",\n \"metrics.cost_micros,\",\n \"metrics.conversions_value,\",\n \"metrics.conversions\",\n \"FROM campaign\",\n `WHERE segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`,\n \"AND campaign.status = 'ENABLED'\",\n \"ORDER BY metrics.cost_micros DESC\",\n ].join(\" \"),\n developerToken,\n loginCustomerId\n );\n const seriesResult = await searchGoogleAds(\n customerId,\n [\n \"SELECT\",\n \"segments.date,\",\n \"metrics.cost_micros,\",\n \"metrics.conversions_value,\",\n \"metrics.conversions\",\n \"FROM customer\",\n `WHERE segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`,\n \"ORDER BY segments.date\",\n ].join(\" \"),\n developerToken,\n loginCustomerId\n );\n\n const overviewRow = overviewResult.rows[0] ?? {};\n const cost = microsToCurrency(getFieldValue(overviewRow, \"metrics.costMicros\"));\n const conversions = round(\n parseGoogleAdsMetricValue(getFieldValue(overviewRow, \"metrics.conversions\"))\n );\n const conversionValue = round(\n parseGoogleAdsMetricValue(getFieldValue(overviewRow, \"metrics.conversionsValue\"))\n );\n const clicks = parseGoogleAdsMetricValue(getFieldValue(overviewRow, \"metrics.clicks\"));\n const currentRoas = round(conversionValue / cost);\n const currentCpa = round(cost / conversions);\n\n const campaignCosts = campaignsResult.rows.map((row) =>\n microsToCurrency(getFieldValue(row, \"metrics.costMicros\"))\n );\n const topCampaignCostShare = round(toPercent(campaignCosts[0] ?? 0, cost));\n\n const series = seriesResult.rows.map((row) => ({\n cost: microsToCurrency(getFieldValue(row, \"metrics.costMicros\")),\n conversion_value: round(\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversionsValue\"))\n ),\n conversions: round(parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversions\"))),\n }));\n const midpoint = Math.ceil(series.length / 2);\n const firstHalf = series.slice(0, midpoint);\n const secondHalf = series.slice(midpoint);\n const firstHalfRoas = round(\n firstHalf.reduce((sum, item) => sum + item.conversion_value, 0) /\n firstHalf.reduce((sum, item) => sum + item.cost, 0)\n );\n const secondHalfRoas = round(\n secondHalf.reduce((sum, item) => sum + item.conversion_value, 0) /\n secondHalf.reduce((sum, item) => sum + item.cost, 0)\n );\n\n let score = 0;\n if (currentRoas >= params.breakEvenRoas) score += 35;\n else if (currentRoas >= params.breakEvenRoas * 0.8) score += 20;\n\n if (!params.targetCpa || (currentCpa > 0 && currentCpa <= params.targetCpa)) score += 20;\n else if (params.targetCpa && currentCpa <= params.targetCpa * 1.2) score += 10;\n\n if (topCampaignCostShare < 50) score += 20;\n else if (topCampaignCostShare < 70) score += 10;\n\n if (secondHalfRoas >= firstHalfRoas * 0.9) score += 15;\n else if (secondHalfRoas >= firstHalfRoas * 0.75) score += 8;\n\n if (conversions >= 10) score += 10;\n else if (conversions >= 5) score += 5;\n\n const status =\n score >= 75 ? \"ready_to_scale\" : score >= 50 ? \"scale_with_caution\" : \"not_ready\";\n\n return object(\n stripNulls({\n customer_id: customerId,\n login_customer_id: loginCustomerId,\n customer_name: getFieldValue(overviewRow, \"customer.descriptiveName\"),\n currency_code: getFieldValue(overviewRow, \"customer.currencyCode\"),\n date_range: {\n start_date: params.startDate,\n end_date: params.endDate,\n },\n inputs: {\n break_even_roas: params.breakEvenRoas,\n target_cpa: params.targetCpa,\n },\n overview: {\n cost,\n clicks,\n conversions,\n conversion_value: conversionValue,\n current_roas: currentRoas,\n current_cpa: currentCpa,\n top_campaign_cost_share_percent: topCampaignCostShare,\n first_half_roas: firstHalfRoas,\n second_half_roas: secondHalfRoas,\n scaling_health_score: score,\n scaling_health_status: status,\n },\n checks: {\n above_break_even: currentRoas >= params.breakEvenRoas,\n cpa_within_target: params.targetCpa ? currentCpa <= params.targetCpa : undefined,\n concentration_acceptable: topCampaignCostShare < 70,\n recent_efficiency_stable: secondHalfRoas >= firstHalfRoas * 0.9,\n conversion_volume_acceptable: conversions >= 5,\n },\n interpretation: {\n status,\n message:\n status === \"ready_to_scale\"\n ? \"The account shows enough efficiency and stability to test scaling.\"\n : status === \"scale_with_caution\"\n ? \"The account has mixed signals. Scale only with tight monitoring.\"\n : \"The account is not ready to scale without increasing risk.\",\n },\n metadata: {\n request_ids: [overviewResult.requestId, campaignsResult.requestId, seriesResult.requestId],\n },\n })\n );\n } catch (err) {\n return error(\n err instanceof Error ? err.message : \"Failed to evaluate Google Ads scaling health\"\n );\n }\n}\n"],
4
+ "sourcesContent": ["import { error, object } from \"mcp-use/server\";\r\nimport { z } from \"zod\";\r\n\r\nimport {\r\n dateRegex,\r\n getFieldValue,\r\n microsToCurrency,\r\n parseGoogleAdsMetricValue,\r\n resolveGoogleAdsCustomerId,\r\n resolveGoogleAdsDeveloperToken,\r\n resolveGoogleAdsLoginCustomerId,\r\n round,\r\n toPercent,\r\n} from \"../../utils/google-ads.js\";\r\nimport { searchGoogleAds } from \"../../services/google-ads/google-ads-client.js\";\r\nimport { stripNulls } from \"../../utils/strip-payload.js\";\r\n\r\nexport const googleAdsScalingHealthSchema = z.object({\r\n startDate: z.string().regex(dateRegex).describe(\"Start date in YYYY-MM-DD format.\"),\r\n endDate: z.string().regex(dateRegex).describe(\"End date in YYYY-MM-DD format.\"),\r\n customerId: z\r\n .string()\r\n .optional()\r\n .describe(\"Google Ads customer ID to query. If omitted, resolves the profileId mapping. Accepts digits with or without hyphens.\"),\r\n profileId: z.string().optional().describe(\"Optional business profile ID used to resolve the mapped Google Ads customer ID.\"),\r\n loginCustomerId: z\r\n .string()\r\n .optional()\r\n .describe(\r\n \"Optional manager account ID used as login customer. If omitted, uses GOOGLE_ADS_LOGIN_CUSTOMER_ID when configured.\"\r\n ),\r\n breakEvenRoas: z\r\n .number()\r\n .positive()\r\n .describe(\"Break-even ROAS for the business.\"),\r\n targetCpa: z\r\n .number()\r\n .positive()\r\n .optional()\r\n .describe(\"Optional maximum acceptable CPA for the business.\"),\r\n});\r\n\r\nexport async function googleAdsScalingHealthHandler(\r\n params: z.infer<typeof googleAdsScalingHealthSchema>\r\n) {\r\n try {\r\n const customerId = await resolveGoogleAdsCustomerId(params.customerId, params.profileId);\r\n const loginCustomerId = resolveGoogleAdsLoginCustomerId(params.loginCustomerId);\r\n const developerToken = resolveGoogleAdsDeveloperToken();\r\n\r\n const overviewResult = await searchGoogleAds(\r\n customerId,\r\n [\r\n \"SELECT\",\r\n \"customer.descriptive_name,\",\r\n \"customer.currency_code,\",\r\n \"metrics.clicks,\",\r\n \"metrics.cost_micros,\",\r\n \"metrics.conversions,\",\r\n \"metrics.conversions_value\",\r\n \"FROM customer\",\r\n `WHERE segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`,\r\n ].join(\" \"),\r\n developerToken,\r\n loginCustomerId\r\n );\r\n const campaignsResult = await searchGoogleAds(\r\n customerId,\r\n [\r\n \"SELECT\",\r\n \"campaign.name,\",\r\n \"metrics.cost_micros,\",\r\n \"metrics.conversions_value,\",\r\n \"metrics.conversions\",\r\n \"FROM campaign\",\r\n `WHERE segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`,\r\n \"AND campaign.status = 'ENABLED'\",\r\n \"ORDER BY metrics.cost_micros DESC\",\r\n ].join(\" \"),\r\n developerToken,\r\n loginCustomerId\r\n );\r\n const seriesResult = await searchGoogleAds(\r\n customerId,\r\n [\r\n \"SELECT\",\r\n \"segments.date,\",\r\n \"metrics.cost_micros,\",\r\n \"metrics.conversions_value,\",\r\n \"metrics.conversions\",\r\n \"FROM customer\",\r\n `WHERE segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`,\r\n \"ORDER BY segments.date\",\r\n ].join(\" \"),\r\n developerToken,\r\n loginCustomerId\r\n );\r\n\r\n const overviewRow = overviewResult.rows[0] ?? {};\r\n const cost = microsToCurrency(getFieldValue(overviewRow, \"metrics.costMicros\"));\r\n const conversions = round(\r\n parseGoogleAdsMetricValue(getFieldValue(overviewRow, \"metrics.conversions\"))\r\n );\r\n const conversionValue = round(\r\n parseGoogleAdsMetricValue(getFieldValue(overviewRow, \"metrics.conversionsValue\"))\r\n );\r\n const clicks = parseGoogleAdsMetricValue(getFieldValue(overviewRow, \"metrics.clicks\"));\r\n const currentRoas = round(conversionValue / cost);\r\n const currentCpa = round(cost / conversions);\r\n\r\n const campaignCosts = campaignsResult.rows.map((row) =>\r\n microsToCurrency(getFieldValue(row, \"metrics.costMicros\"))\r\n );\r\n const topCampaignCostShare = round(toPercent(campaignCosts[0] ?? 0, cost));\r\n\r\n const series = seriesResult.rows.map((row) => ({\r\n cost: microsToCurrency(getFieldValue(row, \"metrics.costMicros\")),\r\n conversion_value: round(\r\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversionsValue\"))\r\n ),\r\n conversions: round(parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversions\"))),\r\n }));\r\n const midpoint = Math.ceil(series.length / 2);\r\n const firstHalf = series.slice(0, midpoint);\r\n const secondHalf = series.slice(midpoint);\r\n const firstHalfRoas = round(\r\n firstHalf.reduce((sum, item) => sum + item.conversion_value, 0) /\r\n firstHalf.reduce((sum, item) => sum + item.cost, 0)\r\n );\r\n const secondHalfRoas = round(\r\n secondHalf.reduce((sum, item) => sum + item.conversion_value, 0) /\r\n secondHalf.reduce((sum, item) => sum + item.cost, 0)\r\n );\r\n\r\n let score = 0;\r\n if (currentRoas >= params.breakEvenRoas) score += 35;\r\n else if (currentRoas >= params.breakEvenRoas * 0.8) score += 20;\r\n\r\n if (!params.targetCpa || (currentCpa > 0 && currentCpa <= params.targetCpa)) score += 20;\r\n else if (params.targetCpa && currentCpa <= params.targetCpa * 1.2) score += 10;\r\n\r\n if (topCampaignCostShare < 50) score += 20;\r\n else if (topCampaignCostShare < 70) score += 10;\r\n\r\n if (secondHalfRoas >= firstHalfRoas * 0.9) score += 15;\r\n else if (secondHalfRoas >= firstHalfRoas * 0.75) score += 8;\r\n\r\n if (conversions >= 10) score += 10;\r\n else if (conversions >= 5) score += 5;\r\n\r\n const status =\r\n score >= 75 ? \"ready_to_scale\" : score >= 50 ? \"scale_with_caution\" : \"not_ready\";\r\n\r\n return object(\r\n stripNulls({\r\n customer_id: customerId,\r\n login_customer_id: loginCustomerId,\r\n customer_name: getFieldValue(overviewRow, \"customer.descriptiveName\"),\r\n currency_code: getFieldValue(overviewRow, \"customer.currencyCode\"),\r\n date_range: {\r\n start_date: params.startDate,\r\n end_date: params.endDate,\r\n },\r\n inputs: {\r\n break_even_roas: params.breakEvenRoas,\r\n target_cpa: params.targetCpa,\r\n },\r\n overview: {\r\n cost,\r\n clicks,\r\n conversions,\r\n conversion_value: conversionValue,\r\n current_roas: currentRoas,\r\n current_cpa: currentCpa,\r\n top_campaign_cost_share_percent: topCampaignCostShare,\r\n first_half_roas: firstHalfRoas,\r\n second_half_roas: secondHalfRoas,\r\n scaling_health_score: score,\r\n scaling_health_status: status,\r\n },\r\n checks: {\r\n above_break_even: currentRoas >= params.breakEvenRoas,\r\n cpa_within_target: params.targetCpa ? currentCpa <= params.targetCpa : undefined,\r\n concentration_acceptable: topCampaignCostShare < 70,\r\n recent_efficiency_stable: secondHalfRoas >= firstHalfRoas * 0.9,\r\n conversion_volume_acceptable: conversions >= 5,\r\n },\r\n interpretation: {\r\n status,\r\n message:\r\n status === \"ready_to_scale\"\r\n ? \"The account shows enough efficiency and stability to test scaling.\"\r\n : status === \"scale_with_caution\"\r\n ? \"The account has mixed signals. Scale only with tight monitoring.\"\r\n : \"The account is not ready to scale without increasing risk.\",\r\n },\r\n metadata: {\r\n request_ids: [overviewResult.requestId, campaignsResult.requestId, seriesResult.requestId],\r\n },\r\n })\r\n );\r\n } catch (err) {\r\n return error(\r\n err instanceof Error ? err.message : \"Failed to evaluate Google Ads scaling health\"\r\n );\r\n }\r\n}\r\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/tools/google-ads/search-terms-summary.ts"],
4
- "sourcesContent": ["import { error, object } from \"mcp-use/server\";\nimport { z } from \"zod\";\n\nimport {\n dateRegex,\n getFieldValue,\n microsToCurrency,\n parseGoogleAdsMetricValue,\n resolveGoogleAdsCustomerId,\n resolveGoogleAdsDeveloperToken,\n resolveGoogleAdsLoginCustomerId,\n round,\n toPercent,\n} from \"../../utils/google-ads.js\";\nimport { searchGoogleAds } from \"../../services/google-ads/google-ads-client.js\";\nimport { stripNulls } from \"../../utils/strip-payload.js\";\n\nexport const googleAdsSearchTermsSummarySchema = z.object({\n startDate: z.string().regex(dateRegex).describe(\"Start date in YYYY-MM-DD format.\"),\n endDate: z.string().regex(dateRegex).describe(\"End date in YYYY-MM-DD format.\"),\n customerId: z.string().optional().describe(\"Google Ads customer ID to query. If omitted, resolves the profileId mapping. Accepts digits with or without hyphens.\"),\n profileId: z.string().optional().describe(\"Optional business profile ID used to resolve the mapped Google Ads customer ID.\"),\n loginCustomerId: z\n .string()\n .optional()\n .describe(\n \"Optional manager account ID used as login customer. If omitted, uses GOOGLE_ADS_LOGIN_CUSTOMER_ID when configured.\"\n ),\n limit: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .default(20)\n .describe(\"Maximum number of search terms to return, ordered by spend descending.\"),\n});\n\nexport async function googleAdsSearchTermsSummaryHandler(\n params: z.infer<typeof googleAdsSearchTermsSummarySchema>\n) {\n try {\n const customerId = await resolveGoogleAdsCustomerId(params.customerId, params.profileId);\n const loginCustomerId = resolveGoogleAdsLoginCustomerId(params.loginCustomerId);\n const developerToken = resolveGoogleAdsDeveloperToken();\n\n const result = await searchGoogleAds(\n customerId,\n [\n \"SELECT\",\n \"customer.descriptive_name,\",\n \"customer.currency_code,\",\n \"search_term_view.search_term,\",\n \"campaign.id,\",\n \"campaign.name,\",\n \"ad_group.id,\",\n \"ad_group.name,\",\n \"metrics.impressions,\",\n \"metrics.clicks,\",\n \"metrics.cost_micros,\",\n \"metrics.conversions,\",\n \"metrics.conversions_value\",\n \"FROM search_term_view\",\n `WHERE segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`,\n \"AND metrics.impressions > 0\",\n \"ORDER BY metrics.cost_micros DESC\",\n `LIMIT ${params.limit}`,\n ].join(\" \"),\n developerToken,\n loginCustomerId\n );\n\n const searchTerms = result.rows.map((row) => {\n const impressions = parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.impressions\"));\n const clicks = parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.clicks\"));\n const cost = microsToCurrency(getFieldValue(row, \"metrics.costMicros\"));\n const conversions = round(\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversions\"))\n );\n const conversionValue = round(\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversionsValue\"))\n );\n\n return {\n search_term: getFieldValue(row, \"searchTermView.searchTerm\"),\n campaign_id: getFieldValue(row, \"campaign.id\"),\n campaign_name: getFieldValue(row, \"campaign.name\"),\n ad_group_id: getFieldValue(row, \"adGroup.id\"),\n ad_group_name: getFieldValue(row, \"adGroup.name\"),\n impressions,\n clicks,\n cost,\n conversions,\n conversion_value: conversionValue,\n ctr_percent: round(toPercent(clicks, impressions)),\n cpa: round(cost / conversions),\n roas: round(conversionValue / cost),\n };\n });\n\n const totals = searchTerms.reduce(\n (acc, item) => {\n acc.impressions += item.impressions;\n acc.clicks += item.clicks;\n acc.cost += item.cost;\n acc.conversions += item.conversions;\n acc.conversion_value += item.conversion_value;\n return acc;\n },\n { impressions: 0, clicks: 0, cost: 0, conversions: 0, conversion_value: 0 }\n );\n\n return object(\n stripNulls({\n customer_id: customerId,\n login_customer_id: loginCustomerId,\n customer_name: getFieldValue(result.rows[0] ?? {}, \"customer.descriptiveName\"),\n currency_code: getFieldValue(result.rows[0] ?? {}, \"customer.currencyCode\"),\n date_range: {\n start_date: params.startDate,\n end_date: params.endDate,\n },\n overview: {\n returned_terms: searchTerms.length,\n impressions: totals.impressions,\n clicks: totals.clicks,\n cost: round(totals.cost),\n conversions: round(totals.conversions),\n conversion_value: round(totals.conversion_value),\n ctr_percent: round(toPercent(totals.clicks, totals.impressions)),\n cpa: round(totals.cost / totals.conversions),\n roas: round(totals.conversion_value / totals.cost),\n top_term_by_cost: searchTerms[0]?.search_term,\n },\n search_terms: searchTerms,\n metadata: {\n row_count: result.rows.length,\n request_id: result.requestId,\n },\n })\n );\n } catch (err) {\n return error(\n err instanceof Error ? err.message : \"Failed to fetch Google Ads search terms summary\"\n );\n }\n}\n"],
4
+ "sourcesContent": ["import { error, object } from \"mcp-use/server\";\r\nimport { z } from \"zod\";\r\n\r\nimport {\r\n dateRegex,\r\n getFieldValue,\r\n microsToCurrency,\r\n parseGoogleAdsMetricValue,\r\n resolveGoogleAdsCustomerId,\r\n resolveGoogleAdsDeveloperToken,\r\n resolveGoogleAdsLoginCustomerId,\r\n round,\r\n toPercent,\r\n} from \"../../utils/google-ads.js\";\r\nimport { searchGoogleAds } from \"../../services/google-ads/google-ads-client.js\";\r\nimport { stripNulls } from \"../../utils/strip-payload.js\";\r\n\r\nexport const googleAdsSearchTermsSummarySchema = z.object({\r\n startDate: z.string().regex(dateRegex).describe(\"Start date in YYYY-MM-DD format.\"),\r\n endDate: z.string().regex(dateRegex).describe(\"End date in YYYY-MM-DD format.\"),\r\n customerId: z.string().optional().describe(\"Google Ads customer ID to query. If omitted, resolves the profileId mapping. Accepts digits with or without hyphens.\"),\r\n profileId: z.string().optional().describe(\"Optional business profile ID used to resolve the mapped Google Ads customer ID.\"),\r\n loginCustomerId: z\r\n .string()\r\n .optional()\r\n .describe(\r\n \"Optional manager account ID used as login customer. If omitted, uses GOOGLE_ADS_LOGIN_CUSTOMER_ID when configured.\"\r\n ),\r\n limit: z\r\n .number()\r\n .int()\r\n .min(1)\r\n .max(50)\r\n .optional()\r\n .default(20)\r\n .describe(\"Maximum number of search terms to return, ordered by spend descending.\"),\r\n});\r\n\r\nexport async function googleAdsSearchTermsSummaryHandler(\r\n params: z.infer<typeof googleAdsSearchTermsSummarySchema>\r\n) {\r\n try {\r\n const customerId = await resolveGoogleAdsCustomerId(params.customerId, params.profileId);\r\n const loginCustomerId = resolveGoogleAdsLoginCustomerId(params.loginCustomerId);\r\n const developerToken = resolveGoogleAdsDeveloperToken();\r\n\r\n const result = await searchGoogleAds(\r\n customerId,\r\n [\r\n \"SELECT\",\r\n \"customer.descriptive_name,\",\r\n \"customer.currency_code,\",\r\n \"search_term_view.search_term,\",\r\n \"campaign.id,\",\r\n \"campaign.name,\",\r\n \"ad_group.id,\",\r\n \"ad_group.name,\",\r\n \"metrics.impressions,\",\r\n \"metrics.clicks,\",\r\n \"metrics.cost_micros,\",\r\n \"metrics.conversions,\",\r\n \"metrics.conversions_value\",\r\n \"FROM search_term_view\",\r\n `WHERE segments.date BETWEEN '${params.startDate}' AND '${params.endDate}'`,\r\n \"AND metrics.impressions > 0\",\r\n \"ORDER BY metrics.cost_micros DESC\",\r\n `LIMIT ${params.limit}`,\r\n ].join(\" \"),\r\n developerToken,\r\n loginCustomerId\r\n );\r\n\r\n const searchTerms = result.rows.map((row) => {\r\n const impressions = parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.impressions\"));\r\n const clicks = parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.clicks\"));\r\n const cost = microsToCurrency(getFieldValue(row, \"metrics.costMicros\"));\r\n const conversions = round(\r\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversions\"))\r\n );\r\n const conversionValue = round(\r\n parseGoogleAdsMetricValue(getFieldValue(row, \"metrics.conversionsValue\"))\r\n );\r\n\r\n return {\r\n search_term: getFieldValue(row, \"searchTermView.searchTerm\"),\r\n campaign_id: getFieldValue(row, \"campaign.id\"),\r\n campaign_name: getFieldValue(row, \"campaign.name\"),\r\n ad_group_id: getFieldValue(row, \"adGroup.id\"),\r\n ad_group_name: getFieldValue(row, \"adGroup.name\"),\r\n impressions,\r\n clicks,\r\n cost,\r\n conversions,\r\n conversion_value: conversionValue,\r\n ctr_percent: round(toPercent(clicks, impressions)),\r\n cpa: round(cost / conversions),\r\n roas: round(conversionValue / cost),\r\n };\r\n });\r\n\r\n const totals = searchTerms.reduce(\r\n (acc, item) => {\r\n acc.impressions += item.impressions;\r\n acc.clicks += item.clicks;\r\n acc.cost += item.cost;\r\n acc.conversions += item.conversions;\r\n acc.conversion_value += item.conversion_value;\r\n return acc;\r\n },\r\n { impressions: 0, clicks: 0, cost: 0, conversions: 0, conversion_value: 0 }\r\n );\r\n\r\n return object(\r\n stripNulls({\r\n customer_id: customerId,\r\n login_customer_id: loginCustomerId,\r\n customer_name: getFieldValue(result.rows[0] ?? {}, \"customer.descriptiveName\"),\r\n currency_code: getFieldValue(result.rows[0] ?? {}, \"customer.currencyCode\"),\r\n date_range: {\r\n start_date: params.startDate,\r\n end_date: params.endDate,\r\n },\r\n overview: {\r\n returned_terms: searchTerms.length,\r\n impressions: totals.impressions,\r\n clicks: totals.clicks,\r\n cost: round(totals.cost),\r\n conversions: round(totals.conversions),\r\n conversion_value: round(totals.conversion_value),\r\n ctr_percent: round(toPercent(totals.clicks, totals.impressions)),\r\n cpa: round(totals.cost / totals.conversions),\r\n roas: round(totals.conversion_value / totals.cost),\r\n top_term_by_cost: searchTerms[0]?.search_term,\r\n },\r\n search_terms: searchTerms,\r\n metadata: {\r\n row_count: result.rows.length,\r\n request_id: result.requestId,\r\n },\r\n })\r\n );\r\n } catch (err) {\r\n return error(\r\n err instanceof Error ? err.message : \"Failed to fetch Google Ads search terms summary\"\r\n );\r\n }\r\n}\r\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }