@yoryoboy/bi-mcp 1.0.1
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.
- package/README.md +346 -0
- package/bin/bi-mcp.js +2 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/index.js +768 -0
- package/dist/index.js.map +7 -0
- package/dist/mcp-use.json +7 -0
- package/dist/public/favicon.ico +0 -0
- package/dist/public/icon.svg +6 -0
- package/dist/src/analytics/ga4-channel-groups.js +20 -0
- package/dist/src/analytics/ga4-channel-groups.js.map +7 -0
- package/dist/src/analytics/ga4-report-utils.js +117 -0
- package/dist/src/analytics/ga4-report-utils.js.map +7 -0
- package/dist/src/config/benchmarks.js +128 -0
- package/dist/src/config/benchmarks.js.map +7 -0
- package/dist/src/config/google.js +41 -0
- package/dist/src/config/google.js.map +7 -0
- package/dist/src/config/vtex.js +26 -0
- package/dist/src/config/vtex.js.map +7 -0
- package/dist/src/google-ads/report-utils.js +78 -0
- package/dist/src/google-ads/report-utils.js.map +7 -0
- package/dist/src/prompts/reporte-ventas.js +75 -0
- package/dist/src/prompts/reporte-ventas.js.map +7 -0
- package/dist/src/search-console/search-console-utils.js +275 -0
- package/dist/src/search-console/search-console-utils.js.map +7 -0
- package/dist/src/services/analytics/ga4-client.js +69 -0
- package/dist/src/services/analytics/ga4-client.js.map +7 -0
- package/dist/src/services/analytics/oauth.js +30 -0
- package/dist/src/services/analytics/oauth.js.map +7 -0
- package/dist/src/services/google-ads/google-ads-client.js +54 -0
- package/dist/src/services/google-ads/google-ads-client.js.map +7 -0
- package/dist/src/services/search-console/search-console-client.js +45 -0
- package/dist/src/services/search-console/search-console-client.js.map +7 -0
- package/dist/src/services/vtex/vtex-api.js +51 -0
- package/dist/src/services/vtex/vtex-api.js.map +7 -0
- package/dist/src/services/vtex/vtex-catalog.js +18 -0
- package/dist/src/services/vtex/vtex-catalog.js.map +7 -0
- package/dist/src/services/vtex/vtex-logistics.js +151 -0
- package/dist/src/services/vtex/vtex-logistics.js.map +7 -0
- package/dist/src/services/vtex/vtex-orders.js +143 -0
- package/dist/src/services/vtex/vtex-orders.js.map +7 -0
- package/dist/src/services/vtex/vtex-pricing.js +17 -0
- package/dist/src/services/vtex/vtex-pricing.js.map +7 -0
- package/dist/src/tools/analytics/attribution-gaps.js +109 -0
- package/dist/src/tools/analytics/attribution-gaps.js.map +7 -0
- package/dist/src/tools/analytics/channel-mix.js +74 -0
- package/dist/src/tools/analytics/channel-mix.js.map +7 -0
- package/dist/src/tools/analytics/ecommerce-tracking-health.js +89 -0
- package/dist/src/tools/analytics/ecommerce-tracking-health.js.map +7 -0
- package/dist/src/tools/analytics/engagement-overview.js +71 -0
- package/dist/src/tools/analytics/engagement-overview.js.map +7 -0
- package/dist/src/tools/analytics/index.js +12 -0
- package/dist/src/tools/analytics/index.js.map +7 -0
- package/dist/src/tools/analytics/list-accessible-properties.js +46 -0
- package/dist/src/tools/analytics/list-accessible-properties.js.map +7 -0
- package/dist/src/tools/analytics/property-info.js +54 -0
- package/dist/src/tools/analytics/property-info.js.map +7 -0
- package/dist/src/tools/analytics/revenue-by-channel.js +70 -0
- package/dist/src/tools/analytics/revenue-by-channel.js.map +7 -0
- package/dist/src/tools/analytics/revenue-overview.js +77 -0
- package/dist/src/tools/analytics/revenue-overview.js.map +7 -0
- package/dist/src/tools/analytics/revenue-trend.js +69 -0
- package/dist/src/tools/analytics/revenue-trend.js.map +7 -0
- package/dist/src/tools/analytics/source-medium-breakdown.js +86 -0
- package/dist/src/tools/analytics/source-medium-breakdown.js.map +7 -0
- package/dist/src/tools/analytics/top-landing-pages.js +79 -0
- package/dist/src/tools/analytics/top-landing-pages.js.map +7 -0
- package/dist/src/tools/google-ads/account-overview.js +103 -0
- package/dist/src/tools/google-ads/account-overview.js.map +7 -0
- package/dist/src/tools/google-ads/account-risks.js +267 -0
- package/dist/src/tools/google-ads/account-risks.js.map +7 -0
- package/dist/src/tools/google-ads/break-even-analysis.js +107 -0
- package/dist/src/tools/google-ads/break-even-analysis.js.map +7 -0
- package/dist/src/tools/google-ads/campaign-performance.js +157 -0
- package/dist/src/tools/google-ads/campaign-performance.js.map +7 -0
- package/dist/src/tools/google-ads/channel-mix.js +129 -0
- package/dist/src/tools/google-ads/channel-mix.js.map +7 -0
- package/dist/src/tools/google-ads/compare-accounts.js +122 -0
- package/dist/src/tools/google-ads/compare-accounts.js.map +7 -0
- package/dist/src/tools/google-ads/customer-clients.js +77 -0
- package/dist/src/tools/google-ads/customer-clients.js.map +7 -0
- package/dist/src/tools/google-ads/customer-info.js +64 -0
- package/dist/src/tools/google-ads/customer-info.js.map +7 -0
- package/dist/src/tools/google-ads/index.js +12 -0
- package/dist/src/tools/google-ads/index.js.map +7 -0
- package/dist/src/tools/google-ads/scaling-health.js +174 -0
- package/dist/src/tools/google-ads/scaling-health.js.map +7 -0
- package/dist/src/tools/google-ads/search-terms-summary.js +131 -0
- package/dist/src/tools/google-ads/search-terms-summary.js.map +7 -0
- package/dist/src/tools/google-ads/time-series.js +126 -0
- package/dist/src/tools/google-ads/time-series.js.map +7 -0
- package/dist/src/tools/index.js +5 -0
- package/dist/src/tools/index.js.map +7 -0
- package/dist/src/tools/search-console/country-breakdown.js +85 -0
- package/dist/src/tools/search-console/country-breakdown.js.map +7 -0
- package/dist/src/tools/search-console/device-breakdown.js +85 -0
- package/dist/src/tools/search-console/device-breakdown.js.map +7 -0
- package/dist/src/tools/search-console/high-impression-low-click-queries.js +95 -0
- package/dist/src/tools/search-console/high-impression-low-click-queries.js.map +7 -0
- package/dist/src/tools/search-console/index.js +15 -0
- package/dist/src/tools/search-console/index.js.map +7 -0
- package/dist/src/tools/search-console/list-accessible-sites.js +42 -0
- package/dist/src/tools/search-console/list-accessible-sites.js.map +7 -0
- package/dist/src/tools/search-console/low-ctr-opportunities.js +98 -0
- package/dist/src/tools/search-console/low-ctr-opportunities.js.map +7 -0
- package/dist/src/tools/search-console/page-performance.js +104 -0
- package/dist/src/tools/search-console/page-performance.js.map +7 -0
- package/dist/src/tools/search-console/product-demand-low-capture-queries.js +93 -0
- package/dist/src/tools/search-console/product-demand-low-capture-queries.js.map +7 -0
- package/dist/src/tools/search-console/query-page-matrix.js +99 -0
- package/dist/src/tools/search-console/query-page-matrix.js.map +7 -0
- package/dist/src/tools/search-console/query-performance.js +109 -0
- package/dist/src/tools/search-console/query-performance.js.map +7 -0
- package/dist/src/tools/search-console/quick-win-opportunities.js +93 -0
- package/dist/src/tools/search-console/quick-win-opportunities.js.map +7 -0
- package/dist/src/tools/search-console/rising-non-brand-queries.js +121 -0
- package/dist/src/tools/search-console/rising-non-brand-queries.js.map +7 -0
- package/dist/src/tools/search-console/search-performance.js +89 -0
- package/dist/src/tools/search-console/search-performance.js.map +7 -0
- package/dist/src/tools/search-console/site-context.js +43 -0
- package/dist/src/tools/search-console/site-context.js.map +7 -0
- package/dist/src/tools/search-console/visibility-declines.js +146 -0
- package/dist/src/tools/search-console/visibility-declines.js.map +7 -0
- package/dist/src/tools/vtex/computed-price.js +48 -0
- package/dist/src/tools/vtex/computed-price.js.map +7 -0
- package/dist/src/tools/vtex/index.js +11 -0
- package/dist/src/tools/vtex/index.js.map +7 -0
- package/dist/src/tools/vtex/inventory-check.js +148 -0
- package/dist/src/tools/vtex/inventory-check.js.map +7 -0
- package/dist/src/tools/vtex/order-details.js +56 -0
- package/dist/src/tools/vtex/order-details.js.map +7 -0
- package/dist/src/tools/vtex/orders-summary.js +83 -0
- package/dist/src/tools/vtex/orders-summary.js.map +7 -0
- package/dist/src/tools/vtex/product-offers.js +28 -0
- package/dist/src/tools/vtex/product-offers.js.map +7 -0
- package/dist/src/tools/vtex/sku-offers.js +30 -0
- package/dist/src/tools/vtex/sku-offers.js.map +7 -0
- package/dist/src/tools/vtex/sku-price.js +42 -0
- package/dist/src/tools/vtex/sku-price.js.map +7 -0
- package/dist/src/tools/vtex/update-inventory.js +43 -0
- package/dist/src/tools/vtex/update-inventory.js.map +7 -0
- package/dist/src/tools/vtex/update-lead-time.js +32 -0
- package/dist/src/tools/vtex/update-lead-time.js.map +7 -0
- package/dist/src/tools/vtex/warehouse-inventory.js +42 -0
- package/dist/src/tools/vtex/warehouse-inventory.js.map +7 -0
- package/dist/src/utils/case-conversion.js +21 -0
- package/dist/src/utils/case-conversion.js.map +7 -0
- package/dist/src/utils/currency.js +52 -0
- package/dist/src/utils/currency.js.map +7 -0
- package/dist/src/utils/format-order-details.js +137 -0
- package/dist/src/utils/format-order-details.js.map +7 -0
- package/dist/src/utils/google-ads.js +78 -0
- package/dist/src/utils/google-ads.js.map +7 -0
- package/dist/src/utils/money.js +83 -0
- package/dist/src/utils/money.js.map +7 -0
- package/dist/src/utils/order-status.js +11 -0
- package/dist/src/utils/order-status.js.map +7 -0
- package/dist/src/utils/pagination.js +45 -0
- package/dist/src/utils/pagination.js.map +7 -0
- package/dist/src/utils/strip-payload.js +40 -0
- package/dist/src/utils/strip-payload.js.map +7 -0
- package/dist/src/utils/type-guards.js +7 -0
- package/dist/src/utils/type-guards.js.map +7 -0
- package/package.json +66 -0
- package/public/favicon.ico +0 -0
- package/public/icon.svg +6 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/tools/analytics/attribution-gaps.ts"],
|
|
4
|
+
"sourcesContent": ["import { error, object } from \"mcp-use/server\";\nimport { z } from \"zod\";\n\nimport {\n detectSeverity,\n extractQuotaSnapshot,\n getDimensionValue,\n getMetricValueByName,\n isProblematicAttributionValue,\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 attributionGapsSchema = 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, uses GA4_PROPERTY_ID\"),\n limit: z.number().int().min(10).max(100).optional().describe(\"Maximum source / medium rows to inspect\"),\n});\n\nexport async function attributionGapsHandler(params: z.infer<typeof attributionGapsSchema>) {\n try {\n const propertyId = resolveGa4PropertyId(params.propertyId);\n const limit = params.limit ?? 50;\n\n const [channelReport, sourceMediumReport, totalReport] = await Promise.all([\n runReport(propertyId, {\n dateRanges: [{ startDate: params.startDate, endDate: params.endDate }],\n dimensions: [{ name: \"sessionDefaultChannelGroup\" }],\n metrics: [{ name: \"sessions\" }, { name: \"totalUsers\" }],\n }),\n runReport(propertyId, {\n dateRanges: [{ startDate: params.startDate, endDate: params.endDate }],\n dimensions: [{ name: \"sessionSourceMedium\" }],\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\" }],\n }),\n ]);\n\n const totalSessions = getMetricValueByName(\n totalReport.rows?.[0] ?? {},\n totalReport.metricHeaders,\n \"sessions\"\n );\n\n const unassignedChannel = (channelReport.rows ?? []).find(\n (row) => getDimensionValue(row) === \"Unassigned\"\n );\n const unassignedSessions = getMetricValueByName(\n unassignedChannel ?? {},\n channelReport.metricHeaders,\n \"sessions\"\n );\n\n const problematicSourceMediumRows = (sourceMediumReport.rows ?? [])\n .filter((row) => isProblematicAttributionValue(getDimensionValue(row)))\n .map((row) => {\n const sessions = getMetricValueByName(row, sourceMediumReport.metricHeaders, \"sessions\");\n const users = getMetricValueByName(row, sourceMediumReport.metricHeaders, \"totalUsers\");\n\n return {\n source_medium: getDimensionValue(row),\n sessions,\n users,\n session_share_percent: round(toPercent(sessions, totalSessions)),\n };\n });\n\n const problematicSourceMediumSessions = problematicSourceMediumRows.reduce(\n (total, row) => total + row.sessions,\n 0\n );\n const unassignedSharePercent = round(toPercent(unassignedSessions, totalSessions));\n const problematicSourceMediumSharePercent = round(\n toPercent(problematicSourceMediumSessions, totalSessions)\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 attribution_warning: \"Las se\u00F1ales se reportan por separado para evitar doble conteo entre dimensiones distintas de GA4.\",\n },\n unassigned_channel_group: {\n sessions: unassignedSessions,\n session_share_percent: unassignedSharePercent,\n severity: detectSeverity(unassignedSharePercent),\n },\n problematic_source_medium: {\n sessions: problematicSourceMediumSessions,\n session_share_percent: problematicSourceMediumSharePercent,\n severity: detectSeverity(problematicSourceMediumSharePercent),\n },\n problematic_source_mediums: problematicSourceMediumRows,\n quota: extractQuotaSnapshot(sourceMediumReport),\n })\n );\n } catch (err) {\n return error(err instanceof Error ? err.message : \"Failed to detect GA4 attribution gaps\");\n }\n}\n"],
|
|
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,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,mDAAmD;AAAA,EAC9F,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,GAAG,EAAE,SAAS,EAAE,SAAS,yCAAyC;AACxG,CAAC;AAED,eAAsB,uBAAuB,QAA+C;AAC1F,MAAI;AACF,UAAM,aAAa,qBAAqB,OAAO,UAAU;AACzD,UAAM,QAAQ,OAAO,SAAS;AAE9B,UAAM,CAAC,eAAe,oBAAoB,WAAW,IAAI,MAAM,QAAQ,IAAI;AAAA,MACzE,UAAU,YAAY;AAAA,QACpB,YAAY,CAAC,EAAE,WAAW,OAAO,WAAW,SAAS,OAAO,QAAQ,CAAC;AAAA,QACrE,YAAY,CAAC,EAAE,MAAM,6BAA6B,CAAC;AAAA,QACnD,SAAS,CAAC,EAAE,MAAM,WAAW,GAAG,EAAE,MAAM,aAAa,CAAC;AAAA,MACxD,CAAC;AAAA,MACD,UAAU,YAAY;AAAA,QACpB,YAAY,CAAC,EAAE,WAAW,OAAO,WAAW,SAAS,OAAO,QAAQ,CAAC;AAAA,QACrE,YAAY,CAAC,EAAE,MAAM,sBAAsB,CAAC;AAAA,QAC5C,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,CAAC;AAAA,MAChC,CAAC;AAAA,IACH,CAAC;AAED,UAAM,gBAAgB;AAAA,MACpB,YAAY,OAAO,CAAC,KAAK,CAAC;AAAA,MAC1B,YAAY;AAAA,MACZ;AAAA,IACF;AAEA,UAAM,qBAAqB,cAAc,QAAQ,CAAC,GAAG;AAAA,MACnD,CAAC,QAAQ,kBAAkB,GAAG,MAAM;AAAA,IACtC;AACA,UAAM,qBAAqB;AAAA,MACzB,qBAAqB,CAAC;AAAA,MACtB,cAAc;AAAA,MACd;AAAA,IACF;AAEA,UAAM,+BAA+B,mBAAmB,QAAQ,CAAC,GAC9D,OAAO,CAAC,QAAQ,8BAA8B,kBAAkB,GAAG,CAAC,CAAC,EACrE,IAAI,CAAC,QAAQ;AACZ,YAAM,WAAW,qBAAqB,KAAK,mBAAmB,eAAe,UAAU;AACvF,YAAM,QAAQ,qBAAqB,KAAK,mBAAmB,eAAe,YAAY;AAEtF,aAAO;AAAA,QACL,eAAe,kBAAkB,GAAG;AAAA,QACpC;AAAA,QACA;AAAA,QACA,uBAAuB,MAAM,UAAU,UAAU,aAAa,CAAC;AAAA,MACjE;AAAA,IACF,CAAC;AAEH,UAAM,kCAAkC,4BAA4B;AAAA,MAClE,CAAC,OAAO,QAAQ,QAAQ,IAAI;AAAA,MAC5B;AAAA,IACF;AACA,UAAM,yBAAyB,MAAM,UAAU,oBAAoB,aAAa,CAAC;AACjF,UAAM,sCAAsC;AAAA,MAC1C,UAAU,iCAAiC,aAAa;AAAA,IAC1D;AAEA,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,qBAAqB;AAAA,QACvB;AAAA,QACA,0BAA0B;AAAA,UACxB,UAAU;AAAA,UACV,uBAAuB;AAAA,UACvB,UAAU,eAAe,sBAAsB;AAAA,QACjD;AAAA,QACA,2BAA2B;AAAA,UACzB,UAAU;AAAA,UACV,uBAAuB;AAAA,UACvB,UAAU,eAAe,mCAAmC;AAAA,QAC9D;AAAA,QACA,4BAA4B;AAAA,QAC5B,OAAO,qBAAqB,kBAAkB;AAAA,MAChD,CAAC;AAAA,IACH;AAAA,EACF,SAAS,KAAK;AACZ,WAAO,MAAM,eAAe,QAAQ,IAAI,UAAU,uCAAuC;AAAA,EAC3F;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { error, object } from "mcp-use/server";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import {
|
|
4
|
+
extractQuotaSnapshot,
|
|
5
|
+
getDimensionValue,
|
|
6
|
+
getMetricValueByName,
|
|
7
|
+
resolveGa4PropertyId,
|
|
8
|
+
round,
|
|
9
|
+
sumMetricValues,
|
|
10
|
+
toPercent
|
|
11
|
+
} from "../../analytics/ga4-report-utils.js";
|
|
12
|
+
import { isPaidDefaultChannelGroup } from "../../analytics/ga4-channel-groups.js";
|
|
13
|
+
import { runReport } from "../../services/analytics/ga4-client.js";
|
|
14
|
+
import { stripNulls } from "../../utils/strip-payload.js";
|
|
15
|
+
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
|
|
16
|
+
const channelMixSchema = z.object({
|
|
17
|
+
startDate: z.string().regex(dateRegex).describe("Start date in YYYY-MM-DD format"),
|
|
18
|
+
endDate: z.string().regex(dateRegex).describe("End date in YYYY-MM-DD format"),
|
|
19
|
+
propertyId: z.string().optional().describe("GA4 property ID. If omitted, uses GA4_PROPERTY_ID")
|
|
20
|
+
});
|
|
21
|
+
async function channelMixHandler(params) {
|
|
22
|
+
try {
|
|
23
|
+
const propertyId = resolveGa4PropertyId(params.propertyId);
|
|
24
|
+
const report = await runReport(propertyId, {
|
|
25
|
+
dateRanges: [{ startDate: params.startDate, endDate: params.endDate }],
|
|
26
|
+
dimensions: [{ name: "sessionDefaultChannelGroup" }],
|
|
27
|
+
metrics: [{ name: "sessions" }, { name: "totalUsers" }],
|
|
28
|
+
orderBys: [{ metric: { metricName: "sessions" }, desc: true }],
|
|
29
|
+
returnPropertyQuota: true
|
|
30
|
+
});
|
|
31
|
+
const totalSessions = sumMetricValues(report.rows, report.metricHeaders, "sessions");
|
|
32
|
+
const totalUsers = sumMetricValues(report.rows, report.metricHeaders, "totalUsers");
|
|
33
|
+
const channels = (report.rows ?? []).map((row) => {
|
|
34
|
+
const sessions = getMetricValueByName(row, report.metricHeaders, "sessions");
|
|
35
|
+
const users = getMetricValueByName(row, report.metricHeaders, "totalUsers");
|
|
36
|
+
return {
|
|
37
|
+
channel: getDimensionValue(row),
|
|
38
|
+
sessions,
|
|
39
|
+
users,
|
|
40
|
+
session_share_percent: round(toPercent(sessions, totalSessions)),
|
|
41
|
+
user_share_percent: round(toPercent(users, totalUsers))
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
const paidSessions = channels.filter((channel) => isPaidDefaultChannelGroup(channel.channel)).reduce((total, channel) => total + channel.sessions, 0);
|
|
45
|
+
const organicSessions = channels.filter((channel) => channel.channel.startsWith("Organic")).reduce((total, channel) => total + channel.sessions, 0);
|
|
46
|
+
const directSessions = channels.filter((channel) => channel.channel === "Direct").reduce((total, channel) => total + channel.sessions, 0);
|
|
47
|
+
return object(
|
|
48
|
+
stripNulls({
|
|
49
|
+
property_id: propertyId,
|
|
50
|
+
date_range: {
|
|
51
|
+
start_date: params.startDate,
|
|
52
|
+
end_date: params.endDate
|
|
53
|
+
},
|
|
54
|
+
overview: {
|
|
55
|
+
total_sessions: totalSessions,
|
|
56
|
+
total_users: totalUsers,
|
|
57
|
+
top_channel: channels[0]?.channel,
|
|
58
|
+
paid_session_share_percent: round(toPercent(paidSessions, totalSessions)),
|
|
59
|
+
organic_session_share_percent: round(toPercent(organicSessions, totalSessions)),
|
|
60
|
+
direct_session_share_percent: round(toPercent(directSessions, totalSessions))
|
|
61
|
+
},
|
|
62
|
+
channels,
|
|
63
|
+
quota: extractQuotaSnapshot(report)
|
|
64
|
+
})
|
|
65
|
+
);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
return error(err instanceof Error ? err.message : "Failed to fetch GA4 channel mix");
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export {
|
|
71
|
+
channelMixHandler,
|
|
72
|
+
channelMixSchema
|
|
73
|
+
};
|
|
74
|
+
//# sourceMappingURL=channel-mix.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/tools/analytics/channel-mix.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 sumMetricValues,\n toPercent,\n} from \"../../analytics/ga4-report-utils.js\";\nimport { isPaidDefaultChannelGroup } from \"../../analytics/ga4-channel-groups.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 channelMixSchema = 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, uses GA4_PROPERTY_ID\"),\n});\n\nexport async function channelMixHandler(params: z.infer<typeof channelMixSchema>) {\n try {\n const propertyId = resolveGa4PropertyId(params.propertyId);\n\n const report = await runReport(propertyId, {\n dateRanges: [{ startDate: params.startDate, endDate: params.endDate }],\n dimensions: [{ name: \"sessionDefaultChannelGroup\" }],\n metrics: [{ name: \"sessions\" }, { name: \"totalUsers\" }],\n orderBys: [{ metric: { metricName: \"sessions\" }, desc: true }],\n returnPropertyQuota: true,\n });\n\n const totalSessions = sumMetricValues(report.rows, report.metricHeaders, \"sessions\");\n const totalUsers = sumMetricValues(report.rows, report.metricHeaders, \"totalUsers\");\n\n const channels = (report.rows ?? []).map((row) => {\n const sessions = getMetricValueByName(row, report.metricHeaders, \"sessions\");\n const users = getMetricValueByName(row, report.metricHeaders, \"totalUsers\");\n\n return {\n channel: getDimensionValue(row),\n sessions,\n users,\n session_share_percent: round(toPercent(sessions, totalSessions)),\n user_share_percent: round(toPercent(users, totalUsers)),\n };\n });\n\n const paidSessions = channels\n .filter((channel) => isPaidDefaultChannelGroup(channel.channel))\n .reduce((total, channel) => total + channel.sessions, 0);\n const organicSessions = channels\n .filter((channel) => channel.channel.startsWith(\"Organic\"))\n .reduce((total, channel) => total + channel.sessions, 0);\n const directSessions = channels\n .filter((channel) => channel.channel === \"Direct\")\n .reduce((total, channel) => total + channel.sessions, 0);\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 total_users: totalUsers,\n top_channel: channels[0]?.channel,\n paid_session_share_percent: round(toPercent(paidSessions, totalSessions)),\n organic_session_share_percent: round(toPercent(organicSessions, totalSessions)),\n direct_session_share_percent: round(toPercent(directSessions, totalSessions)),\n },\n channels,\n quota: extractQuotaSnapshot(report),\n })\n );\n } catch (err) {\n return error(err instanceof Error ? err.message : \"Failed to fetch GA4 channel mix\");\n }\n}\n"],
|
|
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,OACK;AACP,SAAS,iCAAiC;AAC1C,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAE3B,MAAM,YAAY;AAEX,MAAM,mBAAmB,EAAE,OAAO;AAAA,EACvC,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,mDAAmD;AAChG,CAAC;AAED,eAAsB,kBAAkB,QAA0C;AAChF,MAAI;AACF,UAAM,aAAa,qBAAqB,OAAO,UAAU;AAEzD,UAAM,SAAS,MAAM,UAAU,YAAY;AAAA,MACzC,YAAY,CAAC,EAAE,WAAW,OAAO,WAAW,SAAS,OAAO,QAAQ,CAAC;AAAA,MACrE,YAAY,CAAC,EAAE,MAAM,6BAA6B,CAAC;AAAA,MACnD,SAAS,CAAC,EAAE,MAAM,WAAW,GAAG,EAAE,MAAM,aAAa,CAAC;AAAA,MACtD,UAAU,CAAC,EAAE,QAAQ,EAAE,YAAY,WAAW,GAAG,MAAM,KAAK,CAAC;AAAA,MAC7D,qBAAqB;AAAA,IACvB,CAAC;AAED,UAAM,gBAAgB,gBAAgB,OAAO,MAAM,OAAO,eAAe,UAAU;AACnF,UAAM,aAAa,gBAAgB,OAAO,MAAM,OAAO,eAAe,YAAY;AAElF,UAAM,YAAY,OAAO,QAAQ,CAAC,GAAG,IAAI,CAAC,QAAQ;AAChD,YAAM,WAAW,qBAAqB,KAAK,OAAO,eAAe,UAAU;AAC3E,YAAM,QAAQ,qBAAqB,KAAK,OAAO,eAAe,YAAY;AAE1E,aAAO;AAAA,QACL,SAAS,kBAAkB,GAAG;AAAA,QAC9B;AAAA,QACA;AAAA,QACA,uBAAuB,MAAM,UAAU,UAAU,aAAa,CAAC;AAAA,QAC/D,oBAAoB,MAAM,UAAU,OAAO,UAAU,CAAC;AAAA,MACxD;AAAA,IACF,CAAC;AAED,UAAM,eAAe,SAClB,OAAO,CAAC,YAAY,0BAA0B,QAAQ,OAAO,CAAC,EAC9D,OAAO,CAAC,OAAO,YAAY,QAAQ,QAAQ,UAAU,CAAC;AACzD,UAAM,kBAAkB,SACrB,OAAO,CAAC,YAAY,QAAQ,QAAQ,WAAW,SAAS,CAAC,EACzD,OAAO,CAAC,OAAO,YAAY,QAAQ,QAAQ,UAAU,CAAC;AACzD,UAAM,iBAAiB,SACpB,OAAO,CAAC,YAAY,QAAQ,YAAY,QAAQ,EAChD,OAAO,CAAC,OAAO,YAAY,QAAQ,QAAQ,UAAU,CAAC;AAEzD,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,aAAa;AAAA,UACb,aAAa,SAAS,CAAC,GAAG;AAAA,UAC1B,4BAA4B,MAAM,UAAU,cAAc,aAAa,CAAC;AAAA,UACxE,+BAA+B,MAAM,UAAU,iBAAiB,aAAa,CAAC;AAAA,UAC9E,8BAA8B,MAAM,UAAU,gBAAgB,aAAa,CAAC;AAAA,QAC9E;AAAA,QACA;AAAA,QACA,OAAO,qBAAqB,MAAM;AAAA,MACpC,CAAC;AAAA,IACH;AAAA,EACF,SAAS,KAAK;AACZ,WAAO,MAAM,eAAe,QAAQ,IAAI,UAAU,iCAAiC;AAAA,EACrF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { error, object } from "mcp-use/server";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import {
|
|
4
|
+
extractQuotaSnapshot,
|
|
5
|
+
getMetricValueByName,
|
|
6
|
+
resolveGa4PropertyId,
|
|
7
|
+
round,
|
|
8
|
+
toPercent
|
|
9
|
+
} from "../../analytics/ga4-report-utils.js";
|
|
10
|
+
import { runReport } from "../../services/analytics/ga4-client.js";
|
|
11
|
+
import { stripNulls } from "../../utils/strip-payload.js";
|
|
12
|
+
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
|
|
13
|
+
const ecommerceTrackingHealthSchema = z.object({
|
|
14
|
+
startDate: z.string().regex(dateRegex).describe("Start date in YYYY-MM-DD format"),
|
|
15
|
+
endDate: z.string().regex(dateRegex).describe("End date in YYYY-MM-DD format"),
|
|
16
|
+
propertyId: z.string().optional().describe("GA4 property ID. If omitted, uses GA4_PROPERTY_ID")
|
|
17
|
+
});
|
|
18
|
+
async function ecommerceTrackingHealthHandler(params) {
|
|
19
|
+
try {
|
|
20
|
+
const propertyId = resolveGa4PropertyId(params.propertyId);
|
|
21
|
+
const report = await runReport(propertyId, {
|
|
22
|
+
dateRanges: [{ startDate: params.startDate, endDate: params.endDate }],
|
|
23
|
+
metrics: [
|
|
24
|
+
{ name: "itemViewEvents" },
|
|
25
|
+
{ name: "addToCarts" },
|
|
26
|
+
{ name: "checkouts" },
|
|
27
|
+
{ name: "transactions" },
|
|
28
|
+
{ name: "purchaseRevenue" },
|
|
29
|
+
{ name: "refundAmount" }
|
|
30
|
+
],
|
|
31
|
+
returnPropertyQuota: true
|
|
32
|
+
});
|
|
33
|
+
const row = report.rows?.[0] ?? {};
|
|
34
|
+
const itemViews = getMetricValueByName(row, report.metricHeaders, "itemViewEvents");
|
|
35
|
+
const addToCarts = getMetricValueByName(row, report.metricHeaders, "addToCarts");
|
|
36
|
+
const checkouts = getMetricValueByName(row, report.metricHeaders, "checkouts");
|
|
37
|
+
const transactions = getMetricValueByName(row, report.metricHeaders, "transactions");
|
|
38
|
+
const purchaseRevenue = getMetricValueByName(row, report.metricHeaders, "purchaseRevenue");
|
|
39
|
+
const refundAmount = getMetricValueByName(row, report.metricHeaders, "refundAmount");
|
|
40
|
+
const warnings = [];
|
|
41
|
+
if (itemViews === 0) {
|
|
42
|
+
warnings.push("No se detectaron view_item events en el rango consultado.");
|
|
43
|
+
}
|
|
44
|
+
if (addToCarts === 0 && itemViews > 0) {
|
|
45
|
+
warnings.push("Hay view_item pero no add_to_cart. Revisar tagging de add_to_cart.");
|
|
46
|
+
}
|
|
47
|
+
if (checkouts === 0 && addToCarts > 0) {
|
|
48
|
+
warnings.push("Hay add_to_cart pero no begin_checkout. Revisar continuidad del funnel.");
|
|
49
|
+
}
|
|
50
|
+
if (transactions === 0 && checkouts > 0) {
|
|
51
|
+
warnings.push("Hay begin_checkout pero no purchase/transactions. Revisar tagging de compra.");
|
|
52
|
+
}
|
|
53
|
+
if (purchaseRevenue > 0 && transactions === 0) {
|
|
54
|
+
warnings.push("Hay revenue pero no transactions. Revisar consistencia de ecommerce events.");
|
|
55
|
+
}
|
|
56
|
+
return object(
|
|
57
|
+
stripNulls({
|
|
58
|
+
property_id: propertyId,
|
|
59
|
+
date_range: {
|
|
60
|
+
start_date: params.startDate,
|
|
61
|
+
end_date: params.endDate
|
|
62
|
+
},
|
|
63
|
+
currency_code: report.metadata?.currencyCode,
|
|
64
|
+
funnel: {
|
|
65
|
+
item_view_events: itemViews,
|
|
66
|
+
add_to_carts: addToCarts,
|
|
67
|
+
checkouts,
|
|
68
|
+
transactions,
|
|
69
|
+
purchase_revenue: round(purchaseRevenue),
|
|
70
|
+
refund_amount: round(refundAmount),
|
|
71
|
+
add_to_cart_rate_percent: round(toPercent(addToCarts, itemViews)),
|
|
72
|
+
checkout_rate_percent: round(toPercent(checkouts, addToCarts)),
|
|
73
|
+
purchase_rate_percent: round(toPercent(transactions, checkouts))
|
|
74
|
+
},
|
|
75
|
+
warnings,
|
|
76
|
+
quota: extractQuotaSnapshot(report)
|
|
77
|
+
})
|
|
78
|
+
);
|
|
79
|
+
} catch (err) {
|
|
80
|
+
return error(
|
|
81
|
+
err instanceof Error ? err.message : "Failed to evaluate GA4 ecommerce tracking health"
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export {
|
|
86
|
+
ecommerceTrackingHealthHandler,
|
|
87
|
+
ecommerceTrackingHealthSchema
|
|
88
|
+
};
|
|
89
|
+
//# sourceMappingURL=ecommerce-tracking-health.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/tools/analytics/ecommerce-tracking-health.ts"],
|
|
4
|
+
"sourcesContent": ["import { error, object } from \"mcp-use/server\";\nimport { z } from \"zod\";\n\nimport {\n extractQuotaSnapshot,\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 ecommerceTrackingHealthSchema = 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, uses GA4_PROPERTY_ID\"),\n});\n\nexport async function ecommerceTrackingHealthHandler(\n params: z.infer<typeof ecommerceTrackingHealthSchema>\n) {\n try {\n const propertyId = resolveGa4PropertyId(params.propertyId);\n const report = await runReport(propertyId, {\n dateRanges: [{ startDate: params.startDate, endDate: params.endDate }],\n metrics: [\n { name: \"itemViewEvents\" },\n { name: \"addToCarts\" },\n { name: \"checkouts\" },\n { name: \"transactions\" },\n { name: \"purchaseRevenue\" },\n { name: \"refundAmount\" },\n ],\n returnPropertyQuota: true,\n });\n\n const row = report.rows?.[0] ?? {};\n const itemViews = getMetricValueByName(row, report.metricHeaders, \"itemViewEvents\");\n const addToCarts = getMetricValueByName(row, report.metricHeaders, \"addToCarts\");\n const checkouts = getMetricValueByName(row, report.metricHeaders, \"checkouts\");\n const transactions = getMetricValueByName(row, report.metricHeaders, \"transactions\");\n const purchaseRevenue = getMetricValueByName(row, report.metricHeaders, \"purchaseRevenue\");\n const refundAmount = getMetricValueByName(row, report.metricHeaders, \"refundAmount\");\n\n const warnings: string[] = [];\n if (itemViews === 0) {\n warnings.push(\"No se detectaron view_item events en el rango consultado.\");\n }\n if (addToCarts === 0 && itemViews > 0) {\n warnings.push(\"Hay view_item pero no add_to_cart. Revisar tagging de add_to_cart.\");\n }\n if (checkouts === 0 && addToCarts > 0) {\n warnings.push(\"Hay add_to_cart pero no begin_checkout. Revisar continuidad del funnel.\");\n }\n if (transactions === 0 && checkouts > 0) {\n warnings.push(\"Hay begin_checkout pero no purchase/transactions. Revisar tagging de compra.\");\n }\n if (purchaseRevenue > 0 && transactions === 0) {\n warnings.push(\"Hay revenue pero no transactions. Revisar consistencia de ecommerce events.\");\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 currency_code: report.metadata?.currencyCode,\n funnel: {\n item_view_events: itemViews,\n add_to_carts: addToCarts,\n checkouts,\n transactions,\n purchase_revenue: round(purchaseRevenue),\n refund_amount: round(refundAmount),\n add_to_cart_rate_percent: round(toPercent(addToCarts, itemViews)),\n checkout_rate_percent: round(toPercent(checkouts, addToCarts)),\n purchase_rate_percent: round(toPercent(transactions, checkouts)),\n },\n warnings,\n quota: extractQuotaSnapshot(report),\n })\n );\n } catch (err) {\n return error(\n err instanceof Error ? err.message : \"Failed to evaluate GA4 ecommerce tracking health\"\n );\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,OAAO,cAAc;AAC9B,SAAS,SAAS;AAElB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAE3B,MAAM,YAAY;AAEX,MAAM,gCAAgC,EAAE,OAAO;AAAA,EACpD,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,mDAAmD;AAChG,CAAC;AAED,eAAsB,+BACpB,QACA;AACA,MAAI;AACF,UAAM,aAAa,qBAAqB,OAAO,UAAU;AACzD,UAAM,SAAS,MAAM,UAAU,YAAY;AAAA,MACzC,YAAY,CAAC,EAAE,WAAW,OAAO,WAAW,SAAS,OAAO,QAAQ,CAAC;AAAA,MACrE,SAAS;AAAA,QACP,EAAE,MAAM,iBAAiB;AAAA,QACzB,EAAE,MAAM,aAAa;AAAA,QACrB,EAAE,MAAM,YAAY;AAAA,QACpB,EAAE,MAAM,eAAe;AAAA,QACvB,EAAE,MAAM,kBAAkB;AAAA,QAC1B,EAAE,MAAM,eAAe;AAAA,MACzB;AAAA,MACA,qBAAqB;AAAA,IACvB,CAAC;AAED,UAAM,MAAM,OAAO,OAAO,CAAC,KAAK,CAAC;AACjC,UAAM,YAAY,qBAAqB,KAAK,OAAO,eAAe,gBAAgB;AAClF,UAAM,aAAa,qBAAqB,KAAK,OAAO,eAAe,YAAY;AAC/E,UAAM,YAAY,qBAAqB,KAAK,OAAO,eAAe,WAAW;AAC7E,UAAM,eAAe,qBAAqB,KAAK,OAAO,eAAe,cAAc;AACnF,UAAM,kBAAkB,qBAAqB,KAAK,OAAO,eAAe,iBAAiB;AACzF,UAAM,eAAe,qBAAqB,KAAK,OAAO,eAAe,cAAc;AAEnF,UAAM,WAAqB,CAAC;AAC5B,QAAI,cAAc,GAAG;AACnB,eAAS,KAAK,2DAA2D;AAAA,IAC3E;AACA,QAAI,eAAe,KAAK,YAAY,GAAG;AACrC,eAAS,KAAK,oEAAoE;AAAA,IACpF;AACA,QAAI,cAAc,KAAK,aAAa,GAAG;AACrC,eAAS,KAAK,yEAAyE;AAAA,IACzF;AACA,QAAI,iBAAiB,KAAK,YAAY,GAAG;AACvC,eAAS,KAAK,8EAA8E;AAAA,IAC9F;AACA,QAAI,kBAAkB,KAAK,iBAAiB,GAAG;AAC7C,eAAS,KAAK,6EAA6E;AAAA,IAC7F;AAEA,WAAO;AAAA,MACL,WAAW;AAAA,QACT,aAAa;AAAA,QACb,YAAY;AAAA,UACV,YAAY,OAAO;AAAA,UACnB,UAAU,OAAO;AAAA,QACnB;AAAA,QACA,eAAe,OAAO,UAAU;AAAA,QAChC,QAAQ;AAAA,UACN,kBAAkB;AAAA,UAClB,cAAc;AAAA,UACd;AAAA,UACA;AAAA,UACA,kBAAkB,MAAM,eAAe;AAAA,UACvC,eAAe,MAAM,YAAY;AAAA,UACjC,0BAA0B,MAAM,UAAU,YAAY,SAAS,CAAC;AAAA,UAChE,uBAAuB,MAAM,UAAU,WAAW,UAAU,CAAC;AAAA,UAC7D,uBAAuB,MAAM,UAAU,cAAc,SAAS,CAAC;AAAA,QACjE;AAAA,QACA;AAAA,QACA,OAAO,qBAAqB,MAAM;AAAA,MACpC,CAAC;AAAA,IACH;AAAA,EACF,SAAS,KAAK;AACZ,WAAO;AAAA,MACL,eAAe,QAAQ,IAAI,UAAU;AAAA,IACvC;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { error, object } from "mcp-use/server";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import {
|
|
4
|
+
extractQuotaSnapshot,
|
|
5
|
+
getMetricValueByName,
|
|
6
|
+
resolveGa4PropertyId,
|
|
7
|
+
round
|
|
8
|
+
} from "../../analytics/ga4-report-utils.js";
|
|
9
|
+
import { runReport } from "../../services/analytics/ga4-client.js";
|
|
10
|
+
import { stripNulls } from "../../utils/strip-payload.js";
|
|
11
|
+
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
|
|
12
|
+
const engagementOverviewSchema = z.object({
|
|
13
|
+
startDate: z.string().regex(dateRegex).describe("Start date in YYYY-MM-DD format"),
|
|
14
|
+
endDate: z.string().regex(dateRegex).describe("End date in YYYY-MM-DD format"),
|
|
15
|
+
propertyId: z.string().optional().describe("GA4 property ID. If omitted, uses GA4_PROPERTY_ID")
|
|
16
|
+
});
|
|
17
|
+
async function engagementOverviewHandler(params) {
|
|
18
|
+
try {
|
|
19
|
+
const propertyId = resolveGa4PropertyId(params.propertyId);
|
|
20
|
+
const report = await runReport(propertyId, {
|
|
21
|
+
dateRanges: [{ startDate: params.startDate, endDate: params.endDate }],
|
|
22
|
+
metrics: [
|
|
23
|
+
{ name: "sessions" },
|
|
24
|
+
{ name: "engagedSessions" },
|
|
25
|
+
{ name: "engagementRate" },
|
|
26
|
+
{ name: "averageSessionDuration" },
|
|
27
|
+
{ name: "screenPageViews" },
|
|
28
|
+
{ name: "screenPageViewsPerSession" },
|
|
29
|
+
{ name: "bounceRate" },
|
|
30
|
+
{ name: "userEngagementDuration" }
|
|
31
|
+
],
|
|
32
|
+
returnPropertyQuota: true
|
|
33
|
+
});
|
|
34
|
+
const row = report.rows?.[0] ?? {};
|
|
35
|
+
return object(
|
|
36
|
+
stripNulls({
|
|
37
|
+
property_id: propertyId,
|
|
38
|
+
date_range: {
|
|
39
|
+
start_date: params.startDate,
|
|
40
|
+
end_date: params.endDate
|
|
41
|
+
},
|
|
42
|
+
overview: {
|
|
43
|
+
sessions: getMetricValueByName(row, report.metricHeaders, "sessions"),
|
|
44
|
+
engaged_sessions: getMetricValueByName(row, report.metricHeaders, "engagedSessions"),
|
|
45
|
+
engagement_rate_percent: round(
|
|
46
|
+
getMetricValueByName(row, report.metricHeaders, "engagementRate") * 100
|
|
47
|
+
),
|
|
48
|
+
bounce_rate_percent: round(getMetricValueByName(row, report.metricHeaders, "bounceRate") * 100),
|
|
49
|
+
average_session_duration_seconds: round(
|
|
50
|
+
getMetricValueByName(row, report.metricHeaders, "averageSessionDuration")
|
|
51
|
+
),
|
|
52
|
+
views: getMetricValueByName(row, report.metricHeaders, "screenPageViews"),
|
|
53
|
+
views_per_session: round(
|
|
54
|
+
getMetricValueByName(row, report.metricHeaders, "screenPageViewsPerSession")
|
|
55
|
+
),
|
|
56
|
+
user_engagement_duration_seconds: round(
|
|
57
|
+
getMetricValueByName(row, report.metricHeaders, "userEngagementDuration")
|
|
58
|
+
)
|
|
59
|
+
},
|
|
60
|
+
quota: extractQuotaSnapshot(report)
|
|
61
|
+
})
|
|
62
|
+
);
|
|
63
|
+
} catch (err) {
|
|
64
|
+
return error(err instanceof Error ? err.message : "Failed to fetch GA4 engagement overview");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export {
|
|
68
|
+
engagementOverviewHandler,
|
|
69
|
+
engagementOverviewSchema
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=engagement-overview.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/tools/analytics/engagement-overview.ts"],
|
|
4
|
+
"sourcesContent": ["import { error, object } from \"mcp-use/server\";\nimport { z } from \"zod\";\n\nimport {\n extractQuotaSnapshot,\n getMetricValueByName,\n resolveGa4PropertyId,\n round,\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 engagementOverviewSchema = 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, uses GA4_PROPERTY_ID\"),\n});\n\nexport async function engagementOverviewHandler(params: z.infer<typeof engagementOverviewSchema>) {\n try {\n const propertyId = resolveGa4PropertyId(params.propertyId);\n const report = await runReport(propertyId, {\n dateRanges: [{ startDate: params.startDate, endDate: params.endDate }],\n metrics: [\n { name: \"sessions\" },\n { name: \"engagedSessions\" },\n { name: \"engagementRate\" },\n { name: \"averageSessionDuration\" },\n { name: \"screenPageViews\" },\n { name: \"screenPageViewsPerSession\" },\n { name: \"bounceRate\" },\n { name: \"userEngagementDuration\" },\n ],\n returnPropertyQuota: true,\n });\n\n const row = report.rows?.[0] ?? {};\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 sessions: getMetricValueByName(row, report.metricHeaders, \"sessions\"),\n engaged_sessions: getMetricValueByName(row, report.metricHeaders, \"engagedSessions\"),\n engagement_rate_percent: round(\n getMetricValueByName(row, report.metricHeaders, \"engagementRate\") * 100\n ),\n bounce_rate_percent: round(getMetricValueByName(row, report.metricHeaders, \"bounceRate\") * 100),\n average_session_duration_seconds: round(\n getMetricValueByName(row, report.metricHeaders, \"averageSessionDuration\")\n ),\n views: getMetricValueByName(row, report.metricHeaders, \"screenPageViews\"),\n views_per_session: round(\n getMetricValueByName(row, report.metricHeaders, \"screenPageViewsPerSession\")\n ),\n user_engagement_duration_seconds: round(\n getMetricValueByName(row, report.metricHeaders, \"userEngagementDuration\")\n ),\n },\n quota: extractQuotaSnapshot(report),\n })\n );\n } catch (err) {\n return error(err instanceof Error ? err.message : \"Failed to fetch GA4 engagement overview\");\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,OAAO,cAAc;AAC9B,SAAS,SAAS;AAElB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAE3B,MAAM,YAAY;AAEX,MAAM,2BAA2B,EAAE,OAAO;AAAA,EAC/C,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,mDAAmD;AAChG,CAAC;AAED,eAAsB,0BAA0B,QAAkD;AAChG,MAAI;AACF,UAAM,aAAa,qBAAqB,OAAO,UAAU;AACzD,UAAM,SAAS,MAAM,UAAU,YAAY;AAAA,MACzC,YAAY,CAAC,EAAE,WAAW,OAAO,WAAW,SAAS,OAAO,QAAQ,CAAC;AAAA,MACrE,SAAS;AAAA,QACP,EAAE,MAAM,WAAW;AAAA,QACnB,EAAE,MAAM,kBAAkB;AAAA,QAC1B,EAAE,MAAM,iBAAiB;AAAA,QACzB,EAAE,MAAM,yBAAyB;AAAA,QACjC,EAAE,MAAM,kBAAkB;AAAA,QAC1B,EAAE,MAAM,4BAA4B;AAAA,QACpC,EAAE,MAAM,aAAa;AAAA,QACrB,EAAE,MAAM,yBAAyB;AAAA,MACnC;AAAA,MACA,qBAAqB;AAAA,IACvB,CAAC;AAED,UAAM,MAAM,OAAO,OAAO,CAAC,KAAK,CAAC;AAEjC,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,UAAU,qBAAqB,KAAK,OAAO,eAAe,UAAU;AAAA,UACpE,kBAAkB,qBAAqB,KAAK,OAAO,eAAe,iBAAiB;AAAA,UACnF,yBAAyB;AAAA,YACvB,qBAAqB,KAAK,OAAO,eAAe,gBAAgB,IAAI;AAAA,UACtE;AAAA,UACA,qBAAqB,MAAM,qBAAqB,KAAK,OAAO,eAAe,YAAY,IAAI,GAAG;AAAA,UAC9F,kCAAkC;AAAA,YAChC,qBAAqB,KAAK,OAAO,eAAe,wBAAwB;AAAA,UAC1E;AAAA,UACA,OAAO,qBAAqB,KAAK,OAAO,eAAe,iBAAiB;AAAA,UACxE,mBAAmB;AAAA,YACjB,qBAAqB,KAAK,OAAO,eAAe,2BAA2B;AAAA,UAC7E;AAAA,UACA,kCAAkC;AAAA,YAChC,qBAAqB,KAAK,OAAO,eAAe,wBAAwB;AAAA,UAC1E;AAAA,QACF;AAAA,QACA,OAAO,qBAAqB,MAAM;AAAA,MACpC,CAAC;AAAA,IACH;AAAA,EACF,SAAS,KAAK;AACZ,WAAO,MAAM,eAAe,QAAQ,IAAI,UAAU,yCAAyC;AAAA,EAC7F;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from "./list-accessible-properties.js";
|
|
2
|
+
export * from "./property-info.js";
|
|
3
|
+
export * from "./channel-mix.js";
|
|
4
|
+
export * from "./source-medium-breakdown.js";
|
|
5
|
+
export * from "./revenue-overview.js";
|
|
6
|
+
export * from "./revenue-trend.js";
|
|
7
|
+
export * from "./revenue-by-channel.js";
|
|
8
|
+
export * from "./top-landing-pages.js";
|
|
9
|
+
export * from "./engagement-overview.js";
|
|
10
|
+
export * from "./attribution-gaps.js";
|
|
11
|
+
export * from "./ecommerce-tracking-health.js";
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/tools/analytics/index.ts"],
|
|
4
|
+
"sourcesContent": ["export * from \"./list-accessible-properties.js\";\nexport * from \"./property-info.js\";\nexport * from \"./channel-mix.js\";\nexport * from \"./source-medium-breakdown.js\";\nexport * from \"./revenue-overview.js\";\nexport * from \"./revenue-trend.js\";\nexport * from \"./revenue-by-channel.js\";\nexport * from \"./top-landing-pages.js\";\nexport * from \"./engagement-overview.js\";\nexport * from \"./attribution-gaps.js\";\nexport * from \"./ecommerce-tracking-health.js\";\n"],
|
|
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
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { error, object } from "mcp-use/server";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { listAccountSummaries } from "../../services/analytics/ga4-client.js";
|
|
4
|
+
import { getDefaultGa4PropertyId } from "../../config/google.js";
|
|
5
|
+
import { stripNulls } from "../../utils/strip-payload.js";
|
|
6
|
+
const listAccessiblePropertiesSchema = z.object({});
|
|
7
|
+
async function listAccessiblePropertiesHandler(_params) {
|
|
8
|
+
try {
|
|
9
|
+
const { accountSummaries, nextPageToken } = await listAccountSummaries();
|
|
10
|
+
const defaultPropertyId = getDefaultGa4PropertyId();
|
|
11
|
+
const accounts = accountSummaries.map((account) => ({
|
|
12
|
+
account_id: account.account.replace("accounts/", ""),
|
|
13
|
+
account_name: account.displayName,
|
|
14
|
+
properties: (account.propertySummaries ?? []).map((property) => {
|
|
15
|
+
const propertyId = property.property.replace("properties/", "");
|
|
16
|
+
return {
|
|
17
|
+
property_id: propertyId,
|
|
18
|
+
property_name: property.displayName,
|
|
19
|
+
property_type: property.propertyType,
|
|
20
|
+
is_default_property: propertyId === defaultPropertyId
|
|
21
|
+
};
|
|
22
|
+
})
|
|
23
|
+
}));
|
|
24
|
+
const totalProperties = accounts.reduce((total, account) => total + account.properties.length, 0);
|
|
25
|
+
return object(
|
|
26
|
+
stripNulls({
|
|
27
|
+
metadata: {
|
|
28
|
+
is_partial: Boolean(nextPageToken),
|
|
29
|
+
next_page_token: nextPageToken,
|
|
30
|
+
continuation_instructions: nextPageToken ? "This response includes only the first page of accessible GA4 account summaries. Do not assume the account or property list is complete. If you need the full universe, request the next page using next_page_token." : void 0
|
|
31
|
+
},
|
|
32
|
+
total_accounts: accounts.length,
|
|
33
|
+
total_properties: totalProperties,
|
|
34
|
+
default_property_id: defaultPropertyId,
|
|
35
|
+
accounts
|
|
36
|
+
})
|
|
37
|
+
);
|
|
38
|
+
} catch (err) {
|
|
39
|
+
return error(err instanceof Error ? err.message : "Failed to list accessible GA4 properties");
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export {
|
|
43
|
+
listAccessiblePropertiesHandler,
|
|
44
|
+
listAccessiblePropertiesSchema
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=list-accessible-properties.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/tools/analytics/list-accessible-properties.ts"],
|
|
4
|
+
"sourcesContent": ["import { error, object } from \"mcp-use/server\";\nimport { z } from \"zod\";\n\nimport { listAccountSummaries } from \"../../services/analytics/ga4-client.js\";\nimport { getDefaultGa4PropertyId } from \"../../config/google.js\";\nimport { stripNulls } from \"../../utils/strip-payload.js\";\n\nexport const listAccessiblePropertiesSchema = z.object({});\n\nexport async function listAccessiblePropertiesHandler(\n _params: z.infer<typeof listAccessiblePropertiesSchema>\n) {\n try {\n const { accountSummaries, nextPageToken } = await listAccountSummaries();\n const defaultPropertyId = getDefaultGa4PropertyId();\n\n const accounts = accountSummaries.map((account) => ({\n account_id: account.account.replace(\"accounts/\", \"\"),\n account_name: account.displayName,\n properties: (account.propertySummaries ?? []).map((property) => {\n const propertyId = property.property.replace(\"properties/\", \"\");\n\n return {\n property_id: propertyId,\n property_name: property.displayName,\n property_type: property.propertyType,\n is_default_property: propertyId === defaultPropertyId,\n };\n }),\n }));\n\n const totalProperties = accounts.reduce((total, account) => total + account.properties.length, 0);\n\n return object(\n stripNulls({\n metadata: {\n is_partial: Boolean(nextPageToken),\n next_page_token: nextPageToken,\n continuation_instructions: nextPageToken\n ? \"This response includes only the first page of accessible GA4 account summaries. Do not assume the account or property list is complete. If you need the full universe, request the next page using next_page_token.\"\n : undefined,\n },\n total_accounts: accounts.length,\n total_properties: totalProperties,\n default_property_id: defaultPropertyId,\n accounts,\n })\n );\n } catch (err) {\n return error(err instanceof Error ? err.message : \"Failed to list accessible GA4 properties\");\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,OAAO,cAAc;AAC9B,SAAS,SAAS;AAElB,SAAS,4BAA4B;AACrC,SAAS,+BAA+B;AACxC,SAAS,kBAAkB;AAEpB,MAAM,iCAAiC,EAAE,OAAO,CAAC,CAAC;AAEzD,eAAsB,gCACpB,SACA;AACA,MAAI;AACF,UAAM,EAAE,kBAAkB,cAAc,IAAI,MAAM,qBAAqB;AACvE,UAAM,oBAAoB,wBAAwB;AAElD,UAAM,WAAW,iBAAiB,IAAI,CAAC,aAAa;AAAA,MAClD,YAAY,QAAQ,QAAQ,QAAQ,aAAa,EAAE;AAAA,MACnD,cAAc,QAAQ;AAAA,MACtB,aAAa,QAAQ,qBAAqB,CAAC,GAAG,IAAI,CAAC,aAAa;AAC9D,cAAM,aAAa,SAAS,SAAS,QAAQ,eAAe,EAAE;AAE9D,eAAO;AAAA,UACL,aAAa;AAAA,UACb,eAAe,SAAS;AAAA,UACxB,eAAe,SAAS;AAAA,UACxB,qBAAqB,eAAe;AAAA,QACtC;AAAA,MACF,CAAC;AAAA,IACH,EAAE;AAEF,UAAM,kBAAkB,SAAS,OAAO,CAAC,OAAO,YAAY,QAAQ,QAAQ,WAAW,QAAQ,CAAC;AAEhG,WAAO;AAAA,MACL,WAAW;AAAA,QACT,UAAU;AAAA,UACR,YAAY,QAAQ,aAAa;AAAA,UACjC,iBAAiB;AAAA,UACjB,2BAA2B,gBACvB,wNACA;AAAA,QACN;AAAA,QACA,gBAAgB,SAAS;AAAA,QACzB,kBAAkB;AAAA,QAClB,qBAAqB;AAAA,QACrB;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF,SAAS,KAAK;AACZ,WAAO,MAAM,eAAe,QAAQ,IAAI,UAAU,0CAA0C;AAAA,EAC9F;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { error, object } from "mcp-use/server";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { findPropertySummary, resolveGa4PropertyId } from "../../analytics/ga4-report-utils.js";
|
|
4
|
+
import { getMetadata, listAccountSummaries } from "../../services/analytics/ga4-client.js";
|
|
5
|
+
import { stripNulls } from "../../utils/strip-payload.js";
|
|
6
|
+
const propertyInfoSchema = z.object({
|
|
7
|
+
propertyId: z.string().optional().describe("GA4 property ID. If omitted, uses GA4_PROPERTY_ID")
|
|
8
|
+
});
|
|
9
|
+
async function propertyInfoHandler(params) {
|
|
10
|
+
try {
|
|
11
|
+
const propertyId = resolveGa4PropertyId(params.propertyId);
|
|
12
|
+
const [{ accountSummaries }, metadata] = await Promise.all([
|
|
13
|
+
listAccountSummaries(),
|
|
14
|
+
getMetadata(propertyId)
|
|
15
|
+
]);
|
|
16
|
+
const propertySummary = findPropertySummary(accountSummaries, propertyId);
|
|
17
|
+
const dimensions = metadata.dimensions ?? [];
|
|
18
|
+
const metrics = metadata.metrics ?? [];
|
|
19
|
+
return object(
|
|
20
|
+
stripNulls({
|
|
21
|
+
property: {
|
|
22
|
+
property_id: propertyId,
|
|
23
|
+
property_name: propertySummary?.propertyName,
|
|
24
|
+
property_type: propertySummary?.propertyType,
|
|
25
|
+
account_id: propertySummary?.accountId,
|
|
26
|
+
account_name: propertySummary?.accountName
|
|
27
|
+
},
|
|
28
|
+
capabilities: {
|
|
29
|
+
supports_ecommerce: metrics.some((metric) => metric.apiName === "purchaseRevenue") && metrics.some((metric) => metric.apiName === "transactions"),
|
|
30
|
+
supports_google_ads_dimensions: dimensions.some(
|
|
31
|
+
(dimension) => dimension.apiName.startsWith("sessionGoogleAds")
|
|
32
|
+
),
|
|
33
|
+
supports_search_console_metrics: metrics.some(
|
|
34
|
+
(metric) => metric.apiName.startsWith("organicGoogleSearch")
|
|
35
|
+
),
|
|
36
|
+
custom_channel_groups: dimensions.filter((dimension) => dimension.apiName.includes("CustomChannelGroup")).map((dimension) => dimension.apiName)
|
|
37
|
+
},
|
|
38
|
+
metadata_summary: {
|
|
39
|
+
total_dimensions: dimensions.length,
|
|
40
|
+
total_metrics: metrics.length,
|
|
41
|
+
sample_dimensions: dimensions.slice(0, 12).map((dimension) => dimension.apiName),
|
|
42
|
+
sample_metrics: metrics.slice(0, 12).map((metric) => metric.apiName)
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
return error(err instanceof Error ? err.message : "Failed to fetch GA4 property info");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export {
|
|
51
|
+
propertyInfoHandler,
|
|
52
|
+
propertyInfoSchema
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=property-info.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/tools/analytics/property-info.ts"],
|
|
4
|
+
"sourcesContent": ["import { error, object } from \"mcp-use/server\";\nimport { z } from \"zod\";\n\nimport { findPropertySummary, resolveGa4PropertyId } from \"../../analytics/ga4-report-utils.js\";\nimport { getMetadata, listAccountSummaries } from \"../../services/analytics/ga4-client.js\";\nimport { stripNulls } from \"../../utils/strip-payload.js\";\n\nexport const propertyInfoSchema = z.object({\n propertyId: z.string().optional().describe(\"GA4 property ID. If omitted, uses GA4_PROPERTY_ID\"),\n});\n\nexport async function propertyInfoHandler(params: z.infer<typeof propertyInfoSchema>) {\n try {\n const propertyId = resolveGa4PropertyId(params.propertyId);\n\n const [{ accountSummaries }, metadata] = await Promise.all([\n listAccountSummaries(),\n getMetadata(propertyId),\n ]);\n\n const propertySummary = findPropertySummary(accountSummaries, propertyId);\n const dimensions = metadata.dimensions ?? [];\n const metrics = metadata.metrics ?? [];\n\n return object(\n stripNulls({\n property: {\n property_id: propertyId,\n property_name: propertySummary?.propertyName,\n property_type: propertySummary?.propertyType,\n account_id: propertySummary?.accountId,\n account_name: propertySummary?.accountName,\n },\n capabilities: {\n supports_ecommerce:\n metrics.some((metric) => metric.apiName === \"purchaseRevenue\") &&\n metrics.some((metric) => metric.apiName === \"transactions\"),\n supports_google_ads_dimensions: dimensions.some((dimension) =>\n dimension.apiName.startsWith(\"sessionGoogleAds\")\n ),\n supports_search_console_metrics: metrics.some((metric) =>\n metric.apiName.startsWith(\"organicGoogleSearch\")\n ),\n custom_channel_groups: dimensions\n .filter((dimension) => dimension.apiName.includes(\"CustomChannelGroup\"))\n .map((dimension) => dimension.apiName),\n },\n metadata_summary: {\n total_dimensions: dimensions.length,\n total_metrics: metrics.length,\n sample_dimensions: dimensions.slice(0, 12).map((dimension) => dimension.apiName),\n sample_metrics: metrics.slice(0, 12).map((metric) => metric.apiName),\n },\n })\n );\n } catch (err) {\n return error(err instanceof Error ? err.message : \"Failed to fetch GA4 property info\");\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,OAAO,cAAc;AAC9B,SAAS,SAAS;AAElB,SAAS,qBAAqB,4BAA4B;AAC1D,SAAS,aAAa,4BAA4B;AAClD,SAAS,kBAAkB;AAEpB,MAAM,qBAAqB,EAAE,OAAO;AAAA,EACzC,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,mDAAmD;AAChG,CAAC;AAED,eAAsB,oBAAoB,QAA4C;AACpF,MAAI;AACF,UAAM,aAAa,qBAAqB,OAAO,UAAU;AAEzD,UAAM,CAAC,EAAE,iBAAiB,GAAG,QAAQ,IAAI,MAAM,QAAQ,IAAI;AAAA,MACzD,qBAAqB;AAAA,MACrB,YAAY,UAAU;AAAA,IACxB,CAAC;AAED,UAAM,kBAAkB,oBAAoB,kBAAkB,UAAU;AACxE,UAAM,aAAa,SAAS,cAAc,CAAC;AAC3C,UAAM,UAAU,SAAS,WAAW,CAAC;AAErC,WAAO;AAAA,MACL,WAAW;AAAA,QACT,UAAU;AAAA,UACR,aAAa;AAAA,UACb,eAAe,iBAAiB;AAAA,UAChC,eAAe,iBAAiB;AAAA,UAChC,YAAY,iBAAiB;AAAA,UAC7B,cAAc,iBAAiB;AAAA,QACjC;AAAA,QACA,cAAc;AAAA,UACZ,oBACE,QAAQ,KAAK,CAAC,WAAW,OAAO,YAAY,iBAAiB,KAC7D,QAAQ,KAAK,CAAC,WAAW,OAAO,YAAY,cAAc;AAAA,UAC5D,gCAAgC,WAAW;AAAA,YAAK,CAAC,cAC/C,UAAU,QAAQ,WAAW,kBAAkB;AAAA,UACjD;AAAA,UACA,iCAAiC,QAAQ;AAAA,YAAK,CAAC,WAC7C,OAAO,QAAQ,WAAW,qBAAqB;AAAA,UACjD;AAAA,UACA,uBAAuB,WACpB,OAAO,CAAC,cAAc,UAAU,QAAQ,SAAS,oBAAoB,CAAC,EACtE,IAAI,CAAC,cAAc,UAAU,OAAO;AAAA,QACzC;AAAA,QACA,kBAAkB;AAAA,UAChB,kBAAkB,WAAW;AAAA,UAC7B,eAAe,QAAQ;AAAA,UACvB,mBAAmB,WAAW,MAAM,GAAG,EAAE,EAAE,IAAI,CAAC,cAAc,UAAU,OAAO;AAAA,UAC/E,gBAAgB,QAAQ,MAAM,GAAG,EAAE,EAAE,IAAI,CAAC,WAAW,OAAO,OAAO;AAAA,QACrE;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF,SAAS,KAAK;AACZ,WAAO,MAAM,eAAe,QAAQ,IAAI,UAAU,mCAAmC;AAAA,EACvF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { error, object } from "mcp-use/server";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import {
|
|
4
|
+
extractQuotaSnapshot,
|
|
5
|
+
getDimensionValue,
|
|
6
|
+
getMetricValueByName,
|
|
7
|
+
resolveGa4PropertyId,
|
|
8
|
+
round,
|
|
9
|
+
sumMetricValues,
|
|
10
|
+
toPercent
|
|
11
|
+
} from "../../analytics/ga4-report-utils.js";
|
|
12
|
+
import { runReport } from "../../services/analytics/ga4-client.js";
|
|
13
|
+
import { stripNulls } from "../../utils/strip-payload.js";
|
|
14
|
+
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
|
|
15
|
+
const revenueByChannelSchema = z.object({
|
|
16
|
+
startDate: z.string().regex(dateRegex).describe("Start date in YYYY-MM-DD format"),
|
|
17
|
+
endDate: z.string().regex(dateRegex).describe("End date in YYYY-MM-DD format"),
|
|
18
|
+
propertyId: z.string().optional().describe("GA4 property ID. If omitted, uses GA4_PROPERTY_ID")
|
|
19
|
+
});
|
|
20
|
+
async function revenueByChannelHandler(params) {
|
|
21
|
+
try {
|
|
22
|
+
const propertyId = resolveGa4PropertyId(params.propertyId);
|
|
23
|
+
const report = await runReport(propertyId, {
|
|
24
|
+
dateRanges: [{ startDate: params.startDate, endDate: params.endDate }],
|
|
25
|
+
dimensions: [{ name: "sessionDefaultChannelGroup" }],
|
|
26
|
+
metrics: [{ name: "transactions" }, { name: "purchaseRevenue" }, { name: "sessions" }],
|
|
27
|
+
orderBys: [{ metric: { metricName: "purchaseRevenue" }, desc: true }],
|
|
28
|
+
returnPropertyQuota: true
|
|
29
|
+
});
|
|
30
|
+
const totalRevenue = sumMetricValues(report.rows, report.metricHeaders, "purchaseRevenue");
|
|
31
|
+
const totalTransactions = sumMetricValues(report.rows, report.metricHeaders, "transactions");
|
|
32
|
+
const channels = (report.rows ?? []).map((row) => {
|
|
33
|
+
const revenue = getMetricValueByName(row, report.metricHeaders, "purchaseRevenue");
|
|
34
|
+
const transactions = getMetricValueByName(row, report.metricHeaders, "transactions");
|
|
35
|
+
const sessions = getMetricValueByName(row, report.metricHeaders, "sessions");
|
|
36
|
+
return {
|
|
37
|
+
channel: getDimensionValue(row),
|
|
38
|
+
sessions,
|
|
39
|
+
transactions,
|
|
40
|
+
purchase_revenue: round(revenue),
|
|
41
|
+
revenue_share_percent: round(toPercent(revenue, totalRevenue)),
|
|
42
|
+
transaction_share_percent: round(toPercent(transactions, totalTransactions))
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
return object(
|
|
46
|
+
stripNulls({
|
|
47
|
+
property_id: propertyId,
|
|
48
|
+
date_range: {
|
|
49
|
+
start_date: params.startDate,
|
|
50
|
+
end_date: params.endDate
|
|
51
|
+
},
|
|
52
|
+
currency_code: report.metadata?.currencyCode,
|
|
53
|
+
overview: {
|
|
54
|
+
total_purchase_revenue: round(totalRevenue),
|
|
55
|
+
total_transactions: totalTransactions,
|
|
56
|
+
top_revenue_channel: channels[0]?.channel
|
|
57
|
+
},
|
|
58
|
+
channels,
|
|
59
|
+
quota: extractQuotaSnapshot(report)
|
|
60
|
+
})
|
|
61
|
+
);
|
|
62
|
+
} catch (err) {
|
|
63
|
+
return error(err instanceof Error ? err.message : "Failed to fetch GA4 revenue by channel");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export {
|
|
67
|
+
revenueByChannelHandler,
|
|
68
|
+
revenueByChannelSchema
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=revenue-by-channel.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/tools/analytics/revenue-by-channel.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 sumMetricValues,\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 revenueByChannelSchema = 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, uses GA4_PROPERTY_ID\"),\n});\n\nexport async function revenueByChannelHandler(params: z.infer<typeof revenueByChannelSchema>) {\n try {\n const propertyId = resolveGa4PropertyId(params.propertyId);\n const report = await runReport(propertyId, {\n dateRanges: [{ startDate: params.startDate, endDate: params.endDate }],\n dimensions: [{ name: \"sessionDefaultChannelGroup\" }],\n metrics: [{ name: \"transactions\" }, { name: \"purchaseRevenue\" }, { name: \"sessions\" }],\n orderBys: [{ metric: { metricName: \"purchaseRevenue\" }, desc: true }],\n returnPropertyQuota: true,\n });\n\n const totalRevenue = sumMetricValues(report.rows, report.metricHeaders, \"purchaseRevenue\");\n const totalTransactions = sumMetricValues(report.rows, report.metricHeaders, \"transactions\");\n\n const channels = (report.rows ?? []).map((row) => {\n const revenue = getMetricValueByName(row, report.metricHeaders, \"purchaseRevenue\");\n const transactions = getMetricValueByName(row, report.metricHeaders, \"transactions\");\n const sessions = getMetricValueByName(row, report.metricHeaders, \"sessions\");\n\n return {\n channel: getDimensionValue(row),\n sessions,\n transactions,\n purchase_revenue: round(revenue),\n revenue_share_percent: round(toPercent(revenue, totalRevenue)),\n transaction_share_percent: round(toPercent(transactions, totalTransactions)),\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 currency_code: report.metadata?.currencyCode,\n overview: {\n total_purchase_revenue: round(totalRevenue),\n total_transactions: totalTransactions,\n top_revenue_channel: channels[0]?.channel,\n },\n channels,\n quota: extractQuotaSnapshot(report),\n })\n );\n } catch (err) {\n return error(err instanceof Error ? err.message : \"Failed to fetch GA4 revenue by channel\");\n }\n}\n"],
|
|
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,OACK;AACP,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAE3B,MAAM,YAAY;AAEX,MAAM,yBAAyB,EAAE,OAAO;AAAA,EAC7C,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,mDAAmD;AAChG,CAAC;AAED,eAAsB,wBAAwB,QAAgD;AAC5F,MAAI;AACF,UAAM,aAAa,qBAAqB,OAAO,UAAU;AACzD,UAAM,SAAS,MAAM,UAAU,YAAY;AAAA,MACzC,YAAY,CAAC,EAAE,WAAW,OAAO,WAAW,SAAS,OAAO,QAAQ,CAAC;AAAA,MACrE,YAAY,CAAC,EAAE,MAAM,6BAA6B,CAAC;AAAA,MACnD,SAAS,CAAC,EAAE,MAAM,eAAe,GAAG,EAAE,MAAM,kBAAkB,GAAG,EAAE,MAAM,WAAW,CAAC;AAAA,MACrF,UAAU,CAAC,EAAE,QAAQ,EAAE,YAAY,kBAAkB,GAAG,MAAM,KAAK,CAAC;AAAA,MACpE,qBAAqB;AAAA,IACvB,CAAC;AAED,UAAM,eAAe,gBAAgB,OAAO,MAAM,OAAO,eAAe,iBAAiB;AACzF,UAAM,oBAAoB,gBAAgB,OAAO,MAAM,OAAO,eAAe,cAAc;AAE3F,UAAM,YAAY,OAAO,QAAQ,CAAC,GAAG,IAAI,CAAC,QAAQ;AAChD,YAAM,UAAU,qBAAqB,KAAK,OAAO,eAAe,iBAAiB;AACjF,YAAM,eAAe,qBAAqB,KAAK,OAAO,eAAe,cAAc;AACnF,YAAM,WAAW,qBAAqB,KAAK,OAAO,eAAe,UAAU;AAE3E,aAAO;AAAA,QACL,SAAS,kBAAkB,GAAG;AAAA,QAC9B;AAAA,QACA;AAAA,QACA,kBAAkB,MAAM,OAAO;AAAA,QAC/B,uBAAuB,MAAM,UAAU,SAAS,YAAY,CAAC;AAAA,QAC7D,2BAA2B,MAAM,UAAU,cAAc,iBAAiB,CAAC;AAAA,MAC7E;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,eAAe,OAAO,UAAU;AAAA,QAChC,UAAU;AAAA,UACR,wBAAwB,MAAM,YAAY;AAAA,UAC1C,oBAAoB;AAAA,UACpB,qBAAqB,SAAS,CAAC,GAAG;AAAA,QACpC;AAAA,QACA;AAAA,QACA,OAAO,qBAAqB,MAAM;AAAA,MACpC,CAAC;AAAA,IACH;AAAA,EACF,SAAS,KAAK;AACZ,WAAO,MAAM,eAAe,QAAQ,IAAI,UAAU,wCAAwC;AAAA,EAC5F;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { error, object } from "mcp-use/server";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import {
|
|
4
|
+
extractQuotaSnapshot,
|
|
5
|
+
getMetricValueByName,
|
|
6
|
+
resolveGa4PropertyId,
|
|
7
|
+
round,
|
|
8
|
+
toPercent
|
|
9
|
+
} from "../../analytics/ga4-report-utils.js";
|
|
10
|
+
import { runReport } from "../../services/analytics/ga4-client.js";
|
|
11
|
+
import { stripNulls } from "../../utils/strip-payload.js";
|
|
12
|
+
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
|
|
13
|
+
const revenueOverviewSchema = z.object({
|
|
14
|
+
startDate: z.string().regex(dateRegex).describe("Start date in YYYY-MM-DD format"),
|
|
15
|
+
endDate: z.string().regex(dateRegex).describe("End date in YYYY-MM-DD format"),
|
|
16
|
+
propertyId: z.string().optional().describe("GA4 property ID. If omitted, uses GA4_PROPERTY_ID")
|
|
17
|
+
});
|
|
18
|
+
async function revenueOverviewHandler(params) {
|
|
19
|
+
try {
|
|
20
|
+
const propertyId = resolveGa4PropertyId(params.propertyId);
|
|
21
|
+
const report = await runReport(propertyId, {
|
|
22
|
+
dateRanges: [{ startDate: params.startDate, endDate: params.endDate }],
|
|
23
|
+
metrics: [
|
|
24
|
+
{ name: "sessions" },
|
|
25
|
+
{ name: "totalUsers" },
|
|
26
|
+
{ name: "transactions" },
|
|
27
|
+
{ name: "purchaseRevenue" },
|
|
28
|
+
{ name: "averagePurchaseRevenue" },
|
|
29
|
+
{ name: "totalPurchasers" },
|
|
30
|
+
{ name: "purchaserRate" }
|
|
31
|
+
],
|
|
32
|
+
returnPropertyQuota: true
|
|
33
|
+
});
|
|
34
|
+
const row = report.rows?.[0] ?? {};
|
|
35
|
+
const sessions = getMetricValueByName(row, report.metricHeaders, "sessions");
|
|
36
|
+
const totalUsers = getMetricValueByName(row, report.metricHeaders, "totalUsers");
|
|
37
|
+
const transactions = getMetricValueByName(row, report.metricHeaders, "transactions");
|
|
38
|
+
const purchaseRevenue = getMetricValueByName(row, report.metricHeaders, "purchaseRevenue");
|
|
39
|
+
const averagePurchaseRevenue = getMetricValueByName(
|
|
40
|
+
row,
|
|
41
|
+
report.metricHeaders,
|
|
42
|
+
"averagePurchaseRevenue"
|
|
43
|
+
);
|
|
44
|
+
const totalPurchasers = getMetricValueByName(row, report.metricHeaders, "totalPurchasers");
|
|
45
|
+
const purchaserRate = getMetricValueByName(row, report.metricHeaders, "purchaserRate");
|
|
46
|
+
return object(
|
|
47
|
+
stripNulls({
|
|
48
|
+
property_id: propertyId,
|
|
49
|
+
date_range: {
|
|
50
|
+
start_date: params.startDate,
|
|
51
|
+
end_date: params.endDate
|
|
52
|
+
},
|
|
53
|
+
currency_code: report.metadata?.currencyCode,
|
|
54
|
+
overview: {
|
|
55
|
+
sessions,
|
|
56
|
+
total_users: totalUsers,
|
|
57
|
+
transactions,
|
|
58
|
+
total_purchasers: totalPurchasers,
|
|
59
|
+
purchase_revenue: round(purchaseRevenue),
|
|
60
|
+
average_purchase_revenue: round(averagePurchaseRevenue),
|
|
61
|
+
purchaser_rate_percent: round(purchaserRate * 100),
|
|
62
|
+
transactions_per_session_percent: round(toPercent(transactions, sessions)),
|
|
63
|
+
transactions_per_user_percent: round(toPercent(transactions, totalUsers)),
|
|
64
|
+
revenue_per_session: round(sessions > 0 ? purchaseRevenue / sessions : 0)
|
|
65
|
+
},
|
|
66
|
+
quota: extractQuotaSnapshot(report)
|
|
67
|
+
})
|
|
68
|
+
);
|
|
69
|
+
} catch (err) {
|
|
70
|
+
return error(err instanceof Error ? err.message : "Failed to fetch GA4 revenue overview");
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export {
|
|
74
|
+
revenueOverviewHandler,
|
|
75
|
+
revenueOverviewSchema
|
|
76
|
+
};
|
|
77
|
+
//# sourceMappingURL=revenue-overview.js.map
|