@getmcpads/google-analytics-mcp-server 1.0.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.
- package/LICENSE +201 -0
- package/NOTICE +16 -0
- package/README.md +327 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +4488 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +4471 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server.ts","../src/platforms/ga4/tools.ts","../src/platforms/ga4/types.ts","../src/core/logger.ts","../src/core/errors.ts","../src/core/rate-limiter.ts","../src/platforms/ga4/client.ts","../src/platforms/ga4/metric-catalog.ts","../src/platforms/ga4/dimension-catalog.ts","../src/platforms/ga4/compatibility-rules.ts","../src/platforms/ga4/calculated-metrics.ts","../src/platforms/ga4/filter-catalog.ts","../src/platforms/ga4/query-planner.ts","../src/platforms/ga4/surface-tools.ts","../src/platforms/ga4/privacy.ts","../src/platforms/ga4/resources.ts","../src/platforms/ga4/index.ts","../src/config.ts"],"sourcesContent":["/**\n * google-analytics-mcp-server: an open-source MCP server for Google Analytics 4.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { GA4Config } from \"./config.js\";\nimport { registerGA4 } from \"./platforms/ga4/index.js\";\nimport { logger } from \"./core/logger.js\";\n\nexport const PACKAGE_VERSION = \"1.0.0\";\n\nexport function createServer(config: GA4Config): McpServer {\n const server = new McpServer(\n { name: \"google-analytics-mcp\", version: PACKAGE_VERSION },\n { capabilities: { tools: { listChanged: true }, resources: { subscribe: false, listChanged: true } } },\n );\n registerGA4(server, config);\n logger.system(`google-analytics-mcp v${PACKAGE_VERSION} ready, read-only`);\n return server;\n}\n","/**\n * google-analytics-mcp-server: an open-source MCP server for Google Analytics 4.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { GA4Client } from \"./client.js\";\nimport { planGA4Query } from \"./query-planner.js\";\nimport { validateGA4QuerySelection } from \"./compatibility-rules.js\";\nimport { enrichWithCalculatedMetrics } from \"./calculated-metrics.js\";\nimport { formatMcpToolError } from \"../../core/errors.js\";\nimport type { GA4Config } from \"../../config.js\";\nimport type {\n GA4DateRange,\n GA4FilterExpression,\n GA4InsightRow,\n GA4Property,\n GA4RunFunnelReportRequest,\n GA4RunReportRequest,\n GA4RunReportResponse,\n} from \"./types.js\";\nimport { resolveDatePreset } from \"./types.js\";\nimport { getMetricByKey } from \"./metric-catalog.js\";\nimport { getDimensionByKey } from \"./dimension-catalog.js\";\nimport { registerGA4SurfaceTools } from \"./surface-tools.js\";\nimport { redactGA4AudienceExportResponse } from \"./privacy.js\";\n\nconst propertyIdSchema = z.string().describe(\"GA4 property ID (numeric, e.g., 123456789)\");\nconst datePresetValues = [\"today\", \"yesterday\", \"last7days\", \"last28days\", \"last30days\", \"last90days\", \"last12months\", \"thisMonth\", \"lastMonth\", \"thisYear\"] as const;\nconst datePresetSchema = z.enum(datePresetValues);\nconst matchTypeSchema = z.enum([\"EXACT\", \"BEGINS_WITH\", \"ENDS_WITH\", \"CONTAINS\", \"FULL_REGEXP\", \"PARTIAL_REGEXP\"]);\nconst funnelStepSchema = z.object({\n name: z.string().optional().describe(\"Optional display name for this funnel step\"),\n eventName: z.string().optional().describe(\"GA4 eventName to count for this step\"),\n pagePath: z.string().optional().describe(\"GA4 pagePath to count for this step\"),\n matchType: matchTypeSchema.optional().default(\"EXACT\").describe(\"String match type for pagePath. eventName always uses EXACT.\"),\n}).refine((step) => Boolean(step.eventName || step.pagePath), {\n message: \"Each funnel step must include eventName or pagePath\",\n});\nconst advancedFunnelStepSchema = z.object({\n name: z.string().optional().describe(\"Optional display name for this funnel step\"),\n eventName: z.string().optional().describe(\"GA4 event name matched by a FunnelEventFilter\"),\n pagePath: z.string().optional().describe(\"Shortcut for fieldName=pagePath string filter\"),\n fieldName: z.string().optional().describe(\"Optional GA4 field name for a FunnelFieldFilter, e.g. pagePath, pageTitle, deviceCategory\"),\n fieldValue: z.string().optional().describe(\"String value to match for fieldName\"),\n matchType: matchTypeSchema.optional().default(\"EXACT\").describe(\"String match type for pagePath or fieldName\"),\n isDirectlyFollowedBy: z.boolean().optional().describe(\"Require this step to directly follow the previous step\"),\n withinSecondsFromPriorStep: z.number().int().min(1).max(60 * 60 * 24 * 30).optional().describe(\"Maximum seconds from the prior step\"),\n}).refine((step) => Boolean(step.eventName || step.pagePath || (step.fieldName && step.fieldValue)), {\n message: \"Each advanced funnel step must include eventName, pagePath, or fieldName plus fieldValue\",\n});\nconst funnelVisualizationTypeSchema = z.enum([\"STANDARD_FUNNEL\", \"TRENDED_FUNNEL\"]);\nconst GA4_RESPONSE_API_VERSION = \"data_api_v1beta/admin_api_v1beta_v1alpha\";\n\ntype MetadataKind = \"dimensions\" | \"metrics\";\n\ntype MetadataField = {\n apiName: string;\n uiName?: string;\n description?: string;\n category?: string;\n customDefinition?: boolean;\n type?: string;\n deprecatedApiNames?: string[];\n};\n\ntype MetadataSummary = {\n dimensions: MetadataField[];\n metrics: MetadataField[];\n};\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction hasOwnField(value: Record<string, unknown>, field: string): boolean {\n return Object.prototype.hasOwnProperty.call(value, field);\n}\n\nfunction getDebugRecord(payload: Record<string, unknown>): Record<string, unknown> {\n return isRecord(payload[\"debug\"]) ? payload[\"debug\"] : {};\n}\n\nfunction getRequestCount(payload: Record<string, unknown>): number {\n const debug = getDebugRecord(payload);\n const requestCount = debug[\"requestCount\"];\n return typeof requestCount === \"number\" ? requestCount : 1;\n}\n\nfunction getWarnings(payload: Record<string, unknown>): unknown[] {\n if (Array.isArray(payload[\"warnings\"])) return payload[\"warnings\"];\n\n const debug = getDebugRecord(payload);\n return Array.isArray(debug[\"warnings\"]) ? debug[\"warnings\"] : [];\n}\n\nfunction withAgentResponseContract(data: unknown): unknown {\n if (!isRecord(data)) return data;\n\n return {\n ...data,\n warnings: hasOwnField(data, \"warnings\") ? data[\"warnings\"] : getWarnings(data),\n limitations: hasOwnField(data, \"limitations\") ? data[\"limitations\"] : [],\n nextActions: hasOwnField(data, \"nextActions\") ? data[\"nextActions\"] : [],\n debug: {\n ...getDebugRecord(data),\n source: \"ga4\",\n apiVersion: GA4_RESPONSE_API_VERSION,\n requestCount: getRequestCount(data),\n },\n };\n}\n\nfunction ok(data: unknown) {\n return { content: [{ type: \"text\" as const, text: JSON.stringify(withAgentResponseContract(data), null, 2) }] };\n}\n\nfunction stringOrUndefined(value: unknown): string | undefined {\n return typeof value === \"string\" ? value : undefined;\n}\n\nfunction stringArrayOrUndefined(value: unknown): string[] | undefined {\n return Array.isArray(value) ? value.filter((item): item is string => typeof item === \"string\") : undefined;\n}\n\nfunction normalizeMetadataField(value: unknown): MetadataField | null {\n if (!isRecord(value) || typeof value[\"apiName\"] !== \"string\") {\n return null;\n }\n\n return {\n apiName: value[\"apiName\"],\n uiName: stringOrUndefined(value[\"uiName\"]),\n description: stringOrUndefined(value[\"description\"]),\n category: stringOrUndefined(value[\"category\"]),\n customDefinition: value[\"customDefinition\"] === true,\n type: stringOrUndefined(value[\"type\"]),\n deprecatedApiNames: stringArrayOrUndefined(value[\"deprecatedApiNames\"]),\n };\n}\n\nfunction extractMetadataFields(metadata: Record<string, unknown>, kind: MetadataKind): MetadataField[] {\n const raw = Array.isArray(metadata[kind]) ? metadata[kind] : [];\n return raw\n .map(normalizeMetadataField)\n .filter((field): field is MetadataField => field !== null);\n}\n\nfunction summarizeMetadata(metadata: Record<string, unknown>): MetadataSummary {\n return {\n dimensions: extractMetadataFields(metadata, \"dimensions\"),\n metrics: extractMetadataFields(metadata, \"metrics\"),\n };\n}\n\nfunction inferCustomScope(apiName: string): string {\n if (apiName.startsWith(\"customEvent:\")) return \"event\";\n if (apiName.startsWith(\"customUser:\")) return \"user\";\n if (apiName.startsWith(\"customItem:\")) return \"item\";\n return \"custom\";\n}\n\nfunction serializeMetadataField(field: MetadataField) {\n return {\n apiName: field.apiName,\n uiName: field.uiName,\n description: field.description,\n category: field.category,\n type: field.type,\n customDefinition: field.customDefinition === true,\n scope: inferCustomScope(field.apiName),\n ...(field.deprecatedApiNames && field.deprecatedApiNames.length > 0 && { deprecatedApiNames: field.deprecatedApiNames }),\n };\n}\n\nfunction getCustomDefinitions(summary: MetadataSummary) {\n const isCustom = (field: MetadataField) =>\n field.customDefinition === true\n || field.apiName.startsWith(\"customEvent:\")\n || field.apiName.startsWith(\"customUser:\")\n || field.apiName.startsWith(\"customItem:\");\n\n return {\n customDimensions: summary.dimensions.filter(isCustom).map(serializeMetadataField),\n customMetrics: summary.metrics.filter(isCustom).map(serializeMetadataField),\n };\n}\n\nfunction fieldMatchesCandidate(field: MetadataField, candidate: string): boolean {\n return field.apiName === candidate || Boolean(field.deprecatedApiNames?.includes(candidate));\n}\n\nfunction pickAvailableField(summary: MetadataSummary | undefined, kind: MetadataKind, candidates: string[]): string | undefined {\n if (!summary || summary[kind].length === 0) {\n return candidates[0];\n }\n\n for (const candidate of candidates) {\n const match = summary[kind].find((field) => fieldMatchesCandidate(field, candidate));\n if (match) return match.apiName;\n }\n\n return undefined;\n}\n\nfunction filterAvailableFields(summary: MetadataSummary | undefined, kind: MetadataKind, candidates: string[]): string[] {\n if (!summary || summary[kind].length === 0) {\n return candidates;\n }\n\n return candidates.flatMap((candidate) => {\n const match = summary[kind].find((field) => fieldMatchesCandidate(field, candidate));\n return match ? [match.apiName] : [];\n });\n}\n\nfunction toMetrics(metrics: string[]) {\n return metrics.map((metric) => ({ name: getMetricByKey(metric)?.apiField ?? metric }));\n}\n\nfunction toDimensions(dimensions: string[]) {\n return dimensions.map((dimension) => ({ name: getDimensionByKey(dimension)?.apiField ?? dimension }));\n}\n\nfunction resolveInputDateRange(startDate?: string, endDate?: string, datePreset?: typeof datePresetValues[number]): GA4DateRange {\n if (startDate || endDate) {\n return {\n startDate: startDate ?? \"28daysAgo\",\n endDate: endDate ?? \"today\",\n };\n }\n\n return resolveDatePreset(datePreset ?? \"last28days\");\n}\n\nfunction buildStringFilter(fieldName: string, value: string, matchType: z.infer<typeof matchTypeSchema> = \"EXACT\"): GA4FilterExpression {\n return {\n filter: {\n fieldName,\n stringFilter: {\n matchType,\n value,\n caseSensitive: false,\n },\n },\n };\n}\n\nfunction buildInListFilter(fieldName: string, values: string[]): GA4FilterExpression {\n return {\n filter: {\n fieldName,\n inListFilter: {\n values,\n caseSensitive: false,\n },\n },\n };\n}\n\nfunction buildMetricGreaterThanFilter(fieldName: string, value: number): GA4FilterExpression {\n return {\n filter: {\n fieldName,\n numericFilter: {\n operation: \"GREATER_THAN\",\n value: { doubleValue: value },\n },\n },\n };\n}\n\nfunction combineFilters(expressions: GA4FilterExpression[]): GA4FilterExpression | undefined {\n if (expressions.length === 0) return undefined;\n if (expressions.length === 1) return expressions[0];\n return { andGroup: { expressions } };\n}\n\nfunction numericValue(row: GA4InsightRow | undefined, fieldNames: string[]): number {\n if (!row) return 0;\n\n for (const fieldName of fieldNames) {\n const value = row[fieldName];\n if (typeof value === \"number\") return value;\n if (typeof value === \"string\" && value.trim() !== \"\" && !Number.isNaN(Number(value))) {\n return Number(value);\n }\n }\n\n return 0;\n}\n\nfunction numericValueOrNull(row: GA4InsightRow | undefined, fieldNames: string[]): number | null {\n if (!row) return null;\n\n for (const fieldName of fieldNames) {\n if (!Object.prototype.hasOwnProperty.call(row, fieldName)) continue;\n const value = row[fieldName];\n if (typeof value === \"number\") return value;\n if (typeof value === \"string\" && value.trim() !== \"\" && !Number.isNaN(Number(value))) {\n return Number(value);\n }\n }\n\n return null;\n}\n\nfunction stringValue(row: GA4InsightRow, fieldName: string): string {\n const value = row[fieldName];\n return value === null || value === undefined ? \"\" : String(value);\n}\n\nfunction isTruthyGA4Flag(value: unknown): boolean {\n if (typeof value === \"boolean\") return value;\n if (typeof value === \"number\") return value > 0;\n if (typeof value !== \"string\") return false;\n return [\"true\", \"yes\", \"1\", \"key event\", \"conversion\"].includes(value.trim().toLowerCase());\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nasync function runReportRows(client: GA4Client, propertyId: string, request: GA4RunReportRequest) {\n const response = await client.runReport(propertyId, request);\n return {\n response,\n rows: client.flattenResponse(response),\n };\n}\n\ntype AdvancedFunnelStep = z.infer<typeof advancedFunnelStepSchema>;\n\nfunction recordString(value: Record<string, unknown>, key: string): string | undefined {\n return typeof value[key] === \"string\" ? value[key] : undefined;\n}\n\nfunction recordNumber(value: Record<string, unknown>, key: string): number | undefined {\n return typeof value[key] === \"number\" ? value[key] : undefined;\n}\n\nfunction countByStringField(records: Record<string, unknown>[], fieldName: string): Record<string, number> {\n return records.reduce<Record<string, number>>((acc, record) => {\n const value = recordString(record, fieldName) ?? \"UNKNOWN\";\n acc[value] = (acc[value] ?? 0) + 1;\n return acc;\n }, {});\n}\n\nfunction summarizeAudienceExport(exportRecord: Record<string, unknown>) {\n const dimensions = Array.isArray(exportRecord[\"dimensions\"]) ? exportRecord[\"dimensions\"] : [];\n return {\n name: recordString(exportRecord, \"name\"),\n audience: recordString(exportRecord, \"audience\"),\n audienceDisplayName: recordString(exportRecord, \"audienceDisplayName\"),\n state: recordString(exportRecord, \"state\"),\n rowCount: recordNumber(exportRecord, \"rowCount\"),\n percentageCompleted: recordNumber(exportRecord, \"percentageCompleted\"),\n beginCreatingTime: recordString(exportRecord, \"beginCreatingTime\"),\n errorMessage: recordString(exportRecord, \"errorMessage\"),\n dimensions,\n dimensionCount: dimensions.length,\n creationQuotaTokensCharged: recordNumber(exportRecord, \"creationQuotaTokensCharged\"),\n };\n}\n\nfunction summarizeAudience(audience: Record<string, unknown>, includeDefinition: boolean) {\n const filterClauses = Array.isArray(audience[\"filterClauses\"]) ? audience[\"filterClauses\"] : [];\n const eventTrigger = isRecord(audience[\"eventTrigger\"]) ? audience[\"eventTrigger\"] : undefined;\n const summary = {\n name: recordString(audience, \"name\"),\n displayName: recordString(audience, \"displayName\"),\n description: recordString(audience, \"description\"),\n membershipDurationDays: recordNumber(audience, \"membershipDurationDays\"),\n adsPersonalizationEnabled: audience[\"adsPersonalizationEnabled\"],\n filterClauseCount: filterClauses.length,\n hasEventTrigger: Boolean(eventTrigger),\n eventTrigger: eventTrigger ? {\n eventName: recordString(eventTrigger, \"eventName\"),\n logCondition: recordString(eventTrigger, \"logCondition\"),\n } : undefined,\n };\n\n return includeDefinition ? { ...summary, definition: audience } : summary;\n}\n\nfunction summarizeRecurringAudienceList(list: Record<string, unknown>) {\n const dimensions = Array.isArray(list[\"dimensions\"]) ? list[\"dimensions\"] : [];\n return {\n name: recordString(list, \"name\"),\n audience: recordString(list, \"audience\"),\n audienceDisplayName: recordString(list, \"audienceDisplayName\"),\n dimensions,\n dimensionCount: dimensions.length,\n audienceLists: stringArrayOrUndefined(list[\"audienceLists\"]) ?? [],\n activeDaysRemaining: recordNumber(list, \"activeDaysRemaining\"),\n hasWebhookNotification: isRecord(list[\"webhookNotification\"]),\n };\n}\n\nfunction summarizeBigQueryLink(link: Record<string, unknown>) {\n return {\n name: recordString(link, \"name\"),\n project: recordString(link, \"project\"),\n datasetLocation: recordString(link, \"datasetLocation\"),\n createTime: recordString(link, \"createTime\"),\n dailyExportEnabled: link[\"dailyExportEnabled\"] === true,\n streamingExportEnabled: link[\"streamingExportEnabled\"] === true,\n freshDailyExportEnabled: link[\"freshDailyExportEnabled\"] === true,\n includeAdvertisingId: link[\"includeAdvertisingId\"] === true,\n exportStreams: stringArrayOrUndefined(link[\"exportStreams\"]) ?? [],\n excludedEvents: stringArrayOrUndefined(link[\"excludedEvents\"]) ?? [],\n };\n}\n\nfunction summarizeDataStream(stream: Record<string, unknown>) {\n const webStreamData = isRecord(stream[\"webStreamData\"]) ? stream[\"webStreamData\"] : undefined;\n const androidAppStreamData = isRecord(stream[\"androidAppStreamData\"]) ? stream[\"androidAppStreamData\"] : undefined;\n const iosAppStreamData = isRecord(stream[\"iosAppStreamData\"]) ? stream[\"iosAppStreamData\"] : undefined;\n\n return {\n name: recordString(stream, \"name\"),\n type: recordString(stream, \"type\"),\n displayName: recordString(stream, \"displayName\"),\n createTime: recordString(stream, \"createTime\"),\n updateTime: recordString(stream, \"updateTime\"),\n webStreamData: webStreamData ? {\n measurementId: recordString(webStreamData, \"measurementId\"),\n defaultUri: recordString(webStreamData, \"defaultUri\"),\n } : undefined,\n androidAppStreamData: androidAppStreamData ? {\n packageName: recordString(androidAppStreamData, \"packageName\"),\n firebaseAppId: recordString(androidAppStreamData, \"firebaseAppId\"),\n } : undefined,\n iosAppStreamData: iosAppStreamData ? {\n bundleId: recordString(iosAppStreamData, \"bundleId\"),\n firebaseAppId: recordString(iosAppStreamData, \"firebaseAppId\"),\n } : undefined,\n };\n}\n\nfunction buildFunnelFieldFilter(fieldName: string, value: string, matchType: z.infer<typeof matchTypeSchema>) {\n return {\n funnelFieldFilter: {\n fieldName,\n stringFilter: {\n matchType,\n value,\n caseSensitive: false,\n },\n },\n };\n}\n\nfunction buildAdvancedFunnelFilterExpression(step: AdvancedFunnelStep): Record<string, unknown> {\n const expressions: Record<string, unknown>[] = [];\n\n if (step.eventName) {\n expressions.push({ funnelEventFilter: { eventName: step.eventName } });\n }\n if (step.pagePath) {\n expressions.push(buildFunnelFieldFilter(\"pagePath\", step.pagePath, step.matchType ?? \"EXACT\"));\n }\n if (step.fieldName && step.fieldValue) {\n expressions.push(buildFunnelFieldFilter(step.fieldName, step.fieldValue, step.matchType ?? \"EXACT\"));\n }\n\n return expressions.length === 1 ? expressions[0] : { andGroup: { expressions } };\n}\n\nfunction buildAdvancedFunnelReportFilter(step: AdvancedFunnelStep): GA4FilterExpression | undefined {\n const expressions: GA4FilterExpression[] = [];\n\n if (step.eventName) {\n expressions.push(buildStringFilter(\"eventName\", step.eventName, \"EXACT\"));\n }\n if (step.pagePath) {\n expressions.push(buildStringFilter(\"pagePath\", step.pagePath, step.matchType ?? \"EXACT\"));\n }\n if (step.fieldName && step.fieldValue) {\n expressions.push(buildStringFilter(step.fieldName, step.fieldValue, step.matchType ?? \"EXACT\"));\n }\n\n return combineFilters(expressions);\n}\n\nfunction flattenFunnelSubReport(client: GA4Client, subReport: GA4RunReportResponse | undefined): GA4InsightRow[] {\n return subReport ? client.flattenResponse(subReport) : [];\n}\n\nasync function runAdvancedFunnelStepCountFallback(\n client: GA4Client,\n propertyId: string,\n dateRange: GA4DateRange,\n steps: AdvancedFunnelStep[]\n) {\n const rows: Array<{\n index: number;\n name: string;\n value: number;\n conversionRateFromPrevious: number | null;\n conversionRateFromFirst: number | null;\n }> = [];\n\n for (let index = 0; index < steps.length; index++) {\n const step = steps[index];\n const report = await runReportRows(client, propertyId, {\n dateRanges: [dateRange],\n metrics: toMetrics([\"activeUsers\"]),\n dimensionFilter: buildAdvancedFunnelReportFilter(step),\n keepEmptyRows: true,\n limit: 1,\n });\n\n const value = numericValue(report.rows[0], [\"activeUsers\"]);\n const previousValue = rows[index - 1]?.value ?? null;\n const firstValue = rows[0]?.value ?? value;\n\n rows.push({\n index: index + 1,\n name: step.name ?? step.eventName ?? step.pagePath ?? step.fieldName ?? `Step ${index + 1}`,\n value,\n conversionRateFromPrevious: previousValue && previousValue > 0 ? value / previousValue : null,\n conversionRateFromFirst: firstValue > 0 ? value / firstValue : null,\n });\n }\n\n return rows;\n}\n\nexport function registerGA4Tools(server: McpServer, config: GA4Config): void {\n const client = new GA4Client({ clientId: config.clientId, clientSecret: config.clientSecret, refreshToken: config.refreshToken });\n\n // 1. ga4_health_check\n server.tool(\n \"ga4_health_check\",\n \"Read-only GA4 health check. Verifies configured credentials, accessible properties, and metadata/property access without exposing OAuth tokens.\",\n { propertyId: propertyIdSchema.optional().describe(\"Optional property ID to check metadata and property details for\") },\n async ({ propertyId }) => {\n const warnings: string[] = [];\n const actions: string[] = [];\n const checks: Array<{ name: string; status: \"ok\" | \"warning\" | \"error\"; detail?: string }> = [];\n\n try {\n checks.push({\n name: \"credentials_configured\",\n status: config.clientId && config.clientSecret && config.refreshToken ? \"ok\" : \"error\",\n detail: \"Presence checked only; credential values are never returned.\",\n });\n\n let properties: GA4Property[] = [];\n try {\n properties = await client.listProperties();\n checks.push({ name: \"list_properties\", status: \"ok\", detail: `${properties.length} properties accessible` });\n if (properties.length === 0) {\n warnings.push(\"No GA4 properties were returned for these credentials.\");\n actions.push(\"Grant the authenticated Google user Viewer access to at least one GA4 property.\");\n }\n } catch (error) {\n checks.push({ name: \"list_properties\", status: \"error\", detail: errorMessage(error) });\n actions.push(\"Verify GA4_CLIENT_ID, GA4_CLIENT_SECRET, GA4_REFRESH_TOKEN, and Analytics Admin API access.\");\n return ok({\n status: \"error\",\n checks,\n warnings,\n actions,\n credentials: {\n clientIdConfigured: Boolean(config.clientId),\n clientSecretConfigured: Boolean(config.clientSecret),\n refreshTokenConfigured: Boolean(config.refreshToken),\n },\n debug: { requestCount: 1 },\n tokenExposure: \"No access token or refresh token is returned by this tool.\",\n });\n }\n\n const configuredDefaultPropertyId = config.defaultPropertyId;\n const selectedPropertyId = propertyId ?? configuredDefaultPropertyId ?? properties[0]?.propertyId;\n if (configuredDefaultPropertyId && !propertyId) {\n const foundConfiguredDefault = properties.some((property) => property.propertyId === configuredDefaultPropertyId);\n if (foundConfiguredDefault) {\n checks.push({ name: \"default_property_configured\", status: \"ok\", detail: `Using GA4_PROPERTY_ID=${configuredDefaultPropertyId}` });\n } else {\n warnings.push(`GA4_PROPERTY_ID ${configuredDefaultPropertyId} was not present in accountSummaries; direct property/metadata checks will still be attempted.`);\n checks.push({ name: \"default_property_configured\", status: \"warning\", detail: `GA4_PROPERTY_ID=${configuredDefaultPropertyId} not found in account summaries` });\n }\n }\n if (propertyId && properties.length > 0 && !properties.some((property) => property.propertyId === propertyId)) {\n warnings.push(`Property ${propertyId} was not present in accountSummaries; direct property/metadata checks will still be attempted.`);\n }\n\n let propertyDetail: Record<string, unknown> | null = null;\n let metadataSummary: MetadataSummary | null = null;\n\n if (selectedPropertyId) {\n try {\n propertyDetail = await client.getProperty(selectedPropertyId);\n checks.push({ name: \"get_property\", status: \"ok\", detail: `Property ${selectedPropertyId} is readable` });\n } catch (error) {\n checks.push({ name: \"get_property\", status: \"warning\", detail: errorMessage(error) });\n warnings.push(`Could not read Admin API property details for ${selectedPropertyId}.`);\n actions.push(\"Confirm the authenticated user can access the GA4 property in Google Analytics Admin.\");\n }\n\n try {\n metadataSummary = summarizeMetadata(await client.getMetadata(selectedPropertyId));\n checks.push({\n name: \"get_metadata\",\n status: \"ok\",\n detail: `${metadataSummary.dimensions.length} dimensions, ${metadataSummary.metrics.length} metrics`,\n });\n } catch (error) {\n checks.push({ name: \"get_metadata\", status: \"warning\", detail: errorMessage(error) });\n warnings.push(`Could not read Data API metadata for ${selectedPropertyId}.`);\n actions.push(\"Enable the Google Analytics Data API and verify property-level Viewer access.\");\n }\n } else {\n checks.push({ name: \"get_property\", status: \"warning\", detail: \"Skipped because no property was available.\" });\n checks.push({ name: \"get_metadata\", status: \"warning\", detail: \"Skipped because no property was available.\" });\n }\n\n const customDefinitions = metadataSummary ? getCustomDefinitions(metadataSummary) : null;\n const status = checks.some((check) => check.status === \"error\")\n ? \"error\"\n : warnings.length > 0 ? \"warning\" : \"ok\";\n\n return ok({\n status,\n checks,\n warnings,\n actions,\n credentials: {\n clientIdConfigured: Boolean(config.clientId),\n clientSecretConfigured: Boolean(config.clientSecret),\n refreshTokenConfigured: Boolean(config.refreshToken),\n defaultPropertyIdConfigured: Boolean(config.defaultPropertyId),\n },\n properties: {\n count: properties.length,\n sample: properties.slice(0, 10).map((property) => ({\n propertyId: property.propertyId,\n displayName: property.displayName,\n timeZone: property.timeZone,\n currencyCode: property.currencyCode,\n propertyType: property.propertyType,\n parent: property.parent,\n })),\n },\n selectedPropertyId,\n defaultPropertyId: configuredDefaultPropertyId ?? null,\n property: propertyDetail,\n metadata: metadataSummary ? {\n dimensions: metadataSummary.dimensions.length,\n metrics: metadataSummary.metrics.length,\n customDimensions: customDefinitions?.customDimensions.length ?? 0,\n customMetrics: customDefinitions?.customMetrics.length ?? 0,\n } : null,\n debug: { requestCount: 1 + (propertyDetail ? 1 : 0) + (metadataSummary ? 1 : 0) },\n tokenExposure: \"No access token or refresh token is returned by this tool.\",\n });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // 2. ga4_list_properties\n server.tool(\n \"ga4_list_properties\",\n \"List all GA4 properties accessible with the current credentials. Returns property ID, display name, timezone, and currency.\",\n {},\n async () => {\n try {\n const properties = await client.listProperties();\n return ok({ properties, count: properties.length });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // 3. ga4_run_report\n server.tool(\n \"ga4_run_report\",\n `Run a GA4 analytics report with intelligent query planning. Supports 52 metrics, 63 dimensions, auto-pagination.\nUse ga4://metrics and ga4://dimensions resources for available fields. Max 9 dimensions + 10 metrics per request.`,\n {\n propertyId: propertyIdSchema,\n metrics: z.array(z.string()).min(1).describe(\"Metric keys (e.g., sessions, activeUsers, totalRevenue)\"),\n dimensions: z.array(z.string()).optional().describe(\"Dimension keys (e.g., date, sessionSource, country)\"),\n startDate: z.string().optional().describe(\"Start date YYYY-MM-DD or relative (7daysAgo, yesterday)\"),\n endDate: z.string().optional().describe(\"End date YYYY-MM-DD or relative (today, yesterday)\"),\n datePreset: z.enum([\"today\", \"yesterday\", \"last7days\", \"last28days\", \"last30days\", \"last90days\", \"last12months\", \"thisMonth\", \"lastMonth\", \"thisYear\"]).optional(),\n orderBy: z.string().optional().describe(\"Metric or dimension key to sort by\"),\n orderDirection: z.enum([\"ASC\", \"DESC\"]).optional().default(\"DESC\"),\n limit: z.number().int().min(1).max(250000).optional().default(1000),\n offset: z.number().int().min(0).optional().describe(\"Optional 0-based row offset for explicit pagination.\"),\n autoPaginate: z.boolean().optional().default(false).describe(\"When true, omit the limit so the GA4 client paginates through all rows using offset.\"),\n },\n async ({ propertyId, metrics, dimensions, startDate, endDate, datePreset, orderBy, orderDirection, limit, offset, autoPaginate }) => {\n try {\n const startTime = Date.now();\n const plan = planGA4Query({\n propertyId,\n metrics,\n dimensions,\n dateRange: startDate && endDate ? { startDate, endDate } : undefined,\n datePreset,\n orderBy,\n orderDirection,\n limit: autoPaginate ? undefined : limit,\n });\n if (typeof offset === \"number\") plan.request.offset = offset;\n\n if (plan.errors.length > 0) {\n return ok({ error: \"Query validation failed\", errors: plan.errors, warnings: plan.warnings });\n }\n\n const response = await client.runReport(propertyId, plan.request);\n let data = client.flattenResponse(response);\n\n if (plan.calculatedMetrics.length > 0) {\n data = enrichWithCalculatedMetrics(data, plan.calculatedMetrics);\n }\n\n return ok({\n data,\n rowCount: data.length,\n totalRowCount: response.rowCount,\n debug: {\n requestCount: 1,\n executionTimeMs: Date.now() - startTime,\n warnings: plan.warnings,\n calculatedMetrics: plan.calculatedMetrics,\n apiMetrics: plan.apiMetrics,\n pagination: {\n mode: autoPaginate ? \"auto_offset\" : \"single_page\",\n autoPaginate,\n offset: offset ?? 0,\n limit: autoPaginate ? null : limit,\n totalRowCount: response.rowCount,\n },\n },\n });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // 4. ga4_run_realtime_report\n server.tool(\n \"ga4_run_realtime_report\",\n \"Run a read-only GA4 Data API realtime report. Realtime supports only a limited subset of dimensions and metrics.\",\n {\n propertyId: propertyIdSchema,\n metrics: z.array(z.string()).min(1).max(10).optional().default([\"activeUsers\"]).describe(\"Realtime metric API names, e.g. activeUsers, eventCount\"),\n dimensions: z.array(z.string()).max(9).optional().describe(\"Realtime dimension API names, e.g. eventName, city, deviceCategory\"),\n dimensionFilter: z.record(z.unknown()).optional().describe(\"Native realtime-compatible GA4 FilterExpression JSON.\"),\n metricFilter: z.record(z.unknown()).optional().describe(\"Native realtime-compatible GA4 FilterExpression JSON.\"),\n minuteRanges: z.array(z.object({\n name: z.string().optional(),\n startMinutesAgo: z.number().int().min(0).max(59).optional(),\n endMinutesAgo: z.number().int().min(0).max(59).optional(),\n })).max(2).optional().describe(\"Optional named realtime minute ranges; zero is the current minute.\"),\n orderBys: z.array(z.record(z.unknown())).max(10).optional().describe(\"Native GA4 OrderBy JSON.\"),\n limit: z.number().int().min(1).max(10000).optional().default(100),\n keepEmptyRows: z.boolean().optional().default(false),\n },\n async ({ propertyId, metrics, dimensions, dimensionFilter, metricFilter, minuteRanges, orderBys, limit, keepEmptyRows }) => {\n try {\n const response = await client.runRealtimeReport(propertyId, {\n metrics: toMetrics(metrics),\n ...(dimensions && dimensions.length > 0 && { dimensions: toDimensions(dimensions) }),\n ...(dimensionFilter && { dimensionFilter: dimensionFilter as GA4FilterExpression }),\n ...(metricFilter && { metricFilter: metricFilter as GA4FilterExpression }),\n ...(minuteRanges && { minuteRanges }),\n ...(orderBys && { orderBys: orderBys as import(\"./types.js\").GA4OrderBy[] }),\n limit,\n keepEmptyRows,\n returnPropertyQuota: true,\n });\n\n const data = client.flattenResponse(response);\n return ok({\n data,\n rowCount: data.length,\n totalRowCount: response.rowCount,\n warnings: [\n \"GA4 realtime reports support a limited subset of dimensions and metrics. If a field fails, check ga4_get_metadata or retry with core realtime fields such as activeUsers, eventCount, eventName, city, and deviceCategory.\",\n ],\n propertyQuota: response.propertyQuota,\n });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // 5. ga4_get_metadata\n server.tool(\n \"ga4_get_metadata\",\n \"Get the complete list of dimensions and metrics available for a specific GA4 property. Useful for discovering custom dimensions/metrics.\",\n { propertyId: propertyIdSchema },\n async ({ propertyId }) => {\n try {\n const metadata = await client.getMetadata(propertyId);\n return ok(metadata);\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // 6. ga4_get_custom_definitions\n server.tool(\n \"ga4_get_custom_definitions\",\n \"List custom dimensions and custom metrics exposed by GA4 Data API metadata. Read-only fallback for custom definition inventory.\",\n { propertyId: propertyIdSchema },\n async ({ propertyId }) => {\n try {\n const metadata = await client.getMetadata(propertyId);\n const summary = summarizeMetadata(metadata);\n const customDefinitions = getCustomDefinitions(summary);\n\n return ok({\n propertyId,\n source: \"data_api_metadata\",\n customDimensions: customDefinitions.customDimensions,\n customMetrics: customDefinitions.customMetrics,\n counts: {\n customDimensions: customDefinitions.customDimensions.length,\n customMetrics: customDefinitions.customMetrics.length,\n metadataDimensions: summary.dimensions.length,\n metadataMetrics: summary.metrics.length,\n },\n limitations: [\n \"This server does not call Admin API mutate endpoints.\",\n \"Data API metadata exposes reportable custom definitions but may omit Admin-only details such as archived state or exact creation metadata.\",\n ],\n });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // 7. ga4_get_key_events\n server.tool(\n \"ga4_get_key_events\",\n \"Inventory GA4 key events/conversions using metadata-aware Data API reports. Returns best-effort event names and counts.\",\n {\n propertyId: propertyIdSchema,\n startDate: z.string().optional().describe(\"Start date YYYY-MM-DD or relative (7daysAgo, yesterday)\"),\n endDate: z.string().optional().describe(\"End date YYYY-MM-DD or relative (today, yesterday)\"),\n datePreset: datePresetSchema.optional().default(\"last28days\"),\n limit: z.number().int().min(1).max(1000).optional().default(100),\n },\n async ({ propertyId, startDate, endDate, datePreset, limit }) => {\n try {\n const warnings: string[] = [];\n const attempts: string[] = [];\n const dateRange = resolveInputDateRange(startDate, endDate, datePreset);\n\n let metadataSummary: MetadataSummary | undefined;\n try {\n metadataSummary = summarizeMetadata(await client.getMetadata(propertyId));\n } catch (error) {\n warnings.push(`Metadata lookup failed; continuing with standard key event fields. ${errorMessage(error)}`);\n }\n\n const eventNameField = pickAvailableField(metadataSummary, \"dimensions\", [\"eventName\"]) ?? \"eventName\";\n const flagField = pickAvailableField(metadataSummary, \"dimensions\", [\"isKeyEvent\", \"isConversionEvent\"]);\n const metricField = pickAvailableField(metadataSummary, \"metrics\", [\"keyEvents\", \"conversions\"]);\n let rows: GA4InsightRow[] = [];\n let source = \"none\";\n\n if (flagField && metricField) {\n attempts.push(`eventName + ${flagField} + ${metricField}`);\n try {\n const report = await runReportRows(client, propertyId, {\n dateRanges: [dateRange],\n dimensions: toDimensions([eventNameField, flagField]),\n metrics: toMetrics([metricField]),\n orderBys: [{ metric: { metricName: metricField }, desc: true }],\n limit,\n });\n rows = report.rows.filter((row) => isTruthyGA4Flag(row[flagField]) || numericValue(row, [metricField]) > 0);\n source = `report:${eventNameField}+${flagField}+${metricField}`;\n } catch (error) {\n warnings.push(`Could not report with key event flag (${flagField}). ${errorMessage(error)}`);\n }\n }\n\n if (rows.length === 0 && metricField) {\n attempts.push(`eventName + ${metricField} > 0`);\n try {\n const report = await runReportRows(client, propertyId, {\n dateRanges: [dateRange],\n dimensions: toDimensions([eventNameField]),\n metrics: toMetrics([metricField]),\n metricFilter: buildMetricGreaterThanFilter(metricField, 0),\n orderBys: [{ metric: { metricName: metricField }, desc: true }],\n limit,\n });\n rows = report.rows.filter((row) => numericValue(row, [metricField]) > 0);\n source = `report:${eventNameField}+${metricField}`;\n } catch (error) {\n warnings.push(`Could not report key event metric (${metricField}). ${errorMessage(error)}`);\n }\n }\n\n if (!metricField) {\n warnings.push(\"No keyEvents or conversions metric was found in metadata. Key event inventory cannot be determined from Data API reports.\");\n }\n\n const keyEvents = rows.map((row) => {\n const count = metricField ? numericValue(row, [metricField]) : 0;\n return {\n eventName: stringValue(row, eventNameField),\n ...(flagField && { flagField, flagValue: row[flagField], isKeyEvent: isTruthyGA4Flag(row[flagField]) }),\n ...(metricField && { metric: metricField, value: count }),\n };\n }).filter((event) => event.eventName);\n\n return ok({\n propertyId,\n dateRange,\n source,\n attempts,\n keyEvents,\n count: keyEvents.length,\n warnings,\n limitations: [\n \"This is a read-only Data API inventory. Admin-only key event configuration details may require Admin API support.\",\n \"If the property uses the newer key events naming, keyEvents is preferred; conversions is used as a fallback.\",\n ],\n });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // 8. ga4_get_ecommerce_diagnostics\n server.tool(\n \"ga4_get_ecommerce_diagnostics\",\n \"Read-only ecommerce coverage diagnostics for core ecommerce events, revenue metrics, and item-level reporting over a date range.\",\n {\n propertyId: propertyIdSchema,\n startDate: z.string().optional().describe(\"Start date YYYY-MM-DD or relative (7daysAgo, yesterday)\"),\n endDate: z.string().optional().describe(\"End date YYYY-MM-DD or relative (today, yesterday)\"),\n datePreset: datePresetSchema.optional().default(\"last28days\"),\n limit: z.number().int().min(1).max(1000).optional().default(100),\n },\n async ({ propertyId, startDate, endDate, datePreset, limit }) => {\n try {\n const warnings: string[] = [];\n const dateRange = resolveInputDateRange(startDate, endDate, datePreset);\n const coreEvents = [\"view_item\", \"add_to_cart\", \"begin_checkout\", \"purchase\"];\n const diagnosticEvents = [...coreEvents, \"view_item_list\", \"select_item\", \"add_shipping_info\", \"add_payment_info\"];\n\n let metadataSummary: MetadataSummary | undefined;\n try {\n metadataSummary = summarizeMetadata(await client.getMetadata(propertyId));\n } catch (error) {\n warnings.push(`Metadata lookup failed; continuing with standard ecommerce fields. ${errorMessage(error)}`);\n }\n\n const eventNameField = pickAvailableField(metadataSummary, \"dimensions\", [\"eventName\"]) ?? \"eventName\";\n const eventCountMetric = pickAvailableField(metadataSummary, \"metrics\", [\"eventCount\"]) ?? \"eventCount\";\n let eventRows: GA4InsightRow[] = [];\n\n try {\n const report = await runReportRows(client, propertyId, {\n dateRanges: [dateRange],\n dimensions: toDimensions([eventNameField]),\n metrics: toMetrics([eventCountMetric]),\n dimensionFilter: buildInListFilter(eventNameField, diagnosticEvents),\n orderBys: [{ metric: { metricName: eventCountMetric }, desc: true }],\n limit,\n });\n eventRows = report.rows;\n } catch (error) {\n warnings.push(`Could not read ecommerce event coverage. ${errorMessage(error)}`);\n }\n\n const totalMetrics = filterAvailableFields(metadataSummary, \"metrics\", [\n \"totalRevenue\",\n \"purchaseRevenue\",\n \"itemRevenue\",\n \"transactions\",\n \"ecommercePurchases\",\n \"addToCarts\",\n \"checkouts\",\n \"itemsPurchased\",\n \"itemsViewed\",\n \"itemsAddedToCart\",\n \"itemsCheckedOut\",\n ]);\n let totalsRow: GA4InsightRow | undefined;\n if (totalMetrics.length > 0) {\n totalsRow = {};\n for (const metric of totalMetrics) {\n try {\n const report = await runReportRows(client, propertyId, {\n dateRanges: [dateRange],\n metrics: toMetrics([metric]),\n limit: 1,\n });\n totalsRow = { ...totalsRow, ...(report.rows[0] ?? {}) };\n } catch (error) {\n warnings.push(`Could not read ecommerce aggregate metric ${metric}. ${errorMessage(error)}`);\n }\n }\n }\n\n const itemDimensions = filterAvailableFields(metadataSummary, \"dimensions\", [\"itemId\", \"itemName\"]);\n const itemMetrics = filterAvailableFields(metadataSummary, \"metrics\", [\n \"itemsViewed\",\n \"itemsAddedToCart\",\n \"itemsCheckedOut\",\n \"itemsPurchased\",\n \"itemRevenue\",\n ]);\n let itemRows: GA4InsightRow[] = [];\n if (itemDimensions.length > 0 && itemMetrics.length > 0) {\n try {\n const report = await runReportRows(client, propertyId, {\n dateRanges: [dateRange],\n dimensions: toDimensions(itemDimensions.slice(0, 2)),\n metrics: toMetrics(itemMetrics),\n orderBys: [{ metric: { metricName: itemMetrics[0] }, desc: true }],\n limit,\n });\n itemRows = report.rows;\n } catch (error) {\n warnings.push(`Could not read item-level ecommerce report. ${errorMessage(error)}`);\n }\n } else {\n warnings.push(\"Item-level dimensions or metrics were not available in metadata.\");\n }\n\n const eventCoverage = diagnosticEvents.map((eventName) => {\n const row = eventRows.find((candidate) => stringValue(candidate, eventNameField) === eventName);\n const eventCount = numericValue(row, [eventCountMetric]);\n return {\n eventName,\n present: eventCount > 0,\n eventCount,\n };\n });\n const missingCoreEvents = eventCoverage.filter((event) => coreEvents.includes(event.eventName) && !event.present).map((event) => event.eventName);\n const totalRevenue = numericValueOrNull(totalsRow, [\"totalRevenue\"]);\n const purchaseRevenue = numericValueOrNull(totalsRow, [\"purchaseRevenue\"]);\n const purchases = numericValueOrNull(totalsRow, [\"ecommercePurchases\", \"transactions\"]);\n const purchaseEventCount = eventCoverage.find((event) => event.eventName === \"purchase\")?.eventCount ?? 0;\n\n if (missingCoreEvents.length > 0) {\n warnings.push(`Missing core ecommerce event coverage: ${missingCoreEvents.join(\", \")}.`);\n }\n if (purchaseEventCount > 0 && purchases === null) {\n warnings.push(\"Purchase events are present but aggregate purchase metrics were unavailable; summary.purchases is null rather than inferred as 0.\");\n }\n if (purchases !== null && purchases > 0 && totalRevenue === 0 && purchaseRevenue === 0) {\n warnings.push(\"Purchase events are present but revenue metrics are zero; verify value/currency ecommerce parameters.\");\n }\n if (itemRows.length === 0) {\n warnings.push(\"No item-level rows returned; verify items array parameters such as item_id and item_name.\");\n }\n\n return ok({\n propertyId,\n dateRange,\n summary: {\n coreEventsPresent: coreEvents.filter((eventName) => eventCoverage.some((event) => event.eventName === eventName && event.present)),\n missingCoreEvents,\n totalRevenue,\n purchaseRevenue,\n purchases,\n itemRows: itemRows.length,\n },\n eventCoverage,\n aggregateMetrics: totalsRow ?? {},\n itemBreakdown: itemRows,\n warnings,\n limitations: [\n \"Diagnostics are based on reportable Data API fields and do not inspect raw event payloads.\",\n \"Use BigQuery export or DebugView to validate unregistered ecommerce parameters at event ingestion time.\",\n ],\n });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // 9. ga4_get_event_parameters\n server.tool(\n \"ga4_get_event_parameters\",\n \"Best-effort event parameter inventory from GA4 metadata plus event reports. Documents Data API limitations for raw parameters.\",\n {\n propertyId: propertyIdSchema,\n eventNames: z.array(z.string()).optional().describe(\"Optional event names to sample, e.g. purchase, add_to_cart\"),\n startDate: z.string().optional().describe(\"Start date YYYY-MM-DD or relative (7daysAgo, yesterday)\"),\n endDate: z.string().optional().describe(\"End date YYYY-MM-DD or relative (today, yesterday)\"),\n datePreset: datePresetSchema.optional().default(\"last28days\"),\n limit: z.number().int().min(1).max(1000).optional().default(50),\n },\n async ({ propertyId, eventNames, startDate, endDate, datePreset, limit }) => {\n try {\n const warnings: string[] = [];\n const dateRange = resolveInputDateRange(startDate, endDate, datePreset);\n const metadata = await client.getMetadata(propertyId);\n const summary = summarizeMetadata(metadata);\n const customDefinitions = getCustomDefinitions(summary);\n const eventNameField = pickAvailableField(summary, \"dimensions\", [\"eventName\"]) ?? \"eventName\";\n const eventCountMetric = pickAvailableField(summary, \"metrics\", [\"eventCount\"]) ?? \"eventCount\";\n\n let eventRows: GA4InsightRow[] = [];\n try {\n const report = await runReportRows(client, propertyId, {\n dateRanges: [dateRange],\n dimensions: toDimensions([eventNameField]),\n metrics: toMetrics([eventCountMetric]),\n ...(eventNames && eventNames.length > 0 && { dimensionFilter: buildInListFilter(eventNameField, eventNames) }),\n orderBys: [{ metric: { metricName: eventCountMetric }, desc: true }],\n limit,\n });\n eventRows = report.rows;\n } catch (error) {\n warnings.push(`Could not sample events for parameter context. ${errorMessage(error)}`);\n }\n\n const registeredEventParameters = [\n ...customDefinitions.customDimensions,\n ...customDefinitions.customMetrics,\n ].filter((definition) => definition.scope === \"event\").map((definition) => ({\n ...definition,\n parameterName: definition.apiName.includes(\":\") ? definition.apiName.split(\":\").slice(1).join(\":\") : definition.apiName,\n }));\n\n if (registeredEventParameters.length === 0) {\n warnings.push(\"No registered event-scoped custom dimensions or metrics were exposed by metadata.\");\n }\n\n return ok({\n propertyId,\n dateRange,\n registeredEventParameters,\n eventSamples: eventRows,\n warnings,\n limitations: [\n \"GA4 Data API metadata only exposes parameters registered as reportable custom dimensions or metrics.\",\n \"Unregistered raw event parameters and parameter values are not enumerable through this read-only Data API path.\",\n \"For exhaustive raw parameter discovery, use GA4 BigQuery export or instrumentation logs.\",\n ],\n });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // 10. ga4_run_funnel_recipe\n server.tool(\n \"ga4_run_funnel_recipe\",\n \"Run a read-only configurable funnel recipe using one GA4 report per eventName/pagePath step and return simple step counts.\",\n {\n propertyId: propertyIdSchema,\n steps: z.array(funnelStepSchema).min(2).max(10).describe(\"Ordered funnel steps. Each step needs eventName or pagePath.\"),\n metric: z.enum([\"activeUsers\", \"totalUsers\", \"sessions\", \"eventCount\", \"screenPageViews\"]).optional().default(\"activeUsers\"),\n startDate: z.string().optional().describe(\"Start date YYYY-MM-DD or relative (7daysAgo, yesterday)\"),\n endDate: z.string().optional().describe(\"End date YYYY-MM-DD or relative (today, yesterday)\"),\n datePreset: datePresetSchema.optional().default(\"last28days\"),\n },\n async ({ propertyId, steps, metric, startDate, endDate, datePreset }) => {\n try {\n const dateRange = resolveInputDateRange(startDate, endDate, datePreset);\n const metricField = getMetricByKey(metric)?.apiField ?? metric;\n const results: Array<{\n index: number;\n name: string;\n filter: { eventName?: string; pagePath?: string; matchType?: string };\n value: number;\n conversionRateFromPrevious: number | null;\n conversionRateFromFirst: number | null;\n dropoffFromPrevious: number | null;\n }> = [];\n\n for (let index = 0; index < steps.length; index++) {\n const step = steps[index];\n const filters: GA4FilterExpression[] = [];\n if (step.eventName) {\n filters.push(buildStringFilter(\"eventName\", step.eventName, \"EXACT\"));\n }\n if (step.pagePath) {\n filters.push(buildStringFilter(\"pagePath\", step.pagePath, step.matchType ?? \"EXACT\"));\n }\n\n const report = await runReportRows(client, propertyId, {\n dateRanges: [dateRange],\n metrics: toMetrics([metricField]),\n dimensionFilter: combineFilters(filters),\n keepEmptyRows: true,\n limit: 1,\n });\n\n const value = numericValue(report.rows[0], [metricField]);\n const previousValue = results[index - 1]?.value ?? null;\n const firstValue = results[0]?.value ?? value;\n\n results.push({\n index: index + 1,\n name: step.name ?? step.eventName ?? step.pagePath ?? `Step ${index + 1}`,\n filter: {\n ...(step.eventName && { eventName: step.eventName }),\n ...(step.pagePath && { pagePath: step.pagePath, matchType: step.matchType ?? \"EXACT\" }),\n },\n value,\n conversionRateFromPrevious: previousValue && previousValue > 0 ? value / previousValue : null,\n conversionRateFromFirst: firstValue > 0 ? value / firstValue : null,\n dropoffFromPrevious: previousValue === null ? null : Math.max(previousValue - value, 0),\n });\n }\n\n return ok({\n propertyId,\n dateRange,\n metric: metricField,\n steps: results,\n recipe: {\n type: \"simple_step_counts\",\n readOnly: true,\n reportsRun: results.length,\n },\n limitations: [\n \"This is a simple read-only funnel recipe, not GA4 Explore funnel semantics.\",\n \"Each step is counted independently with one runReport call; counts are not user-sequenced or deduplicated across steps.\",\n ],\n });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // 11. ga4_get_audience_export_diagnostics\n server.tool(\n \"ga4_get_audience_export_diagnostics\",\n \"Read-only Audience Export diagnostics. Lists existing audience exports, state counts, and optionally samples rows from an existing export.\",\n {\n propertyId: propertyIdSchema,\n audienceExportName: z.string().optional().describe(\"Optional resource name, e.g. properties/123456789/audienceExports/abc123\"),\n pageSize: z.number().int().min(1).max(200).optional().default(100),\n includeRowSample: z.boolean().optional().default(false).describe(\"If true, query a small row sample from audienceExportName. Can include user-level export dimensions.\"),\n sampleLimit: z.number().int().min(1).max(10).optional().default(5),\n includePersonalIdentifiers: z.boolean().optional().default(false)\n .describe(\"Explicitly include user/device identifiers in the optional sample. False redacts them by default.\"),\n },\n async ({ propertyId, audienceExportName, pageSize, includeRowSample, sampleLimit, includePersonalIdentifiers }) => {\n try {\n const warnings: string[] = [];\n const exports = await client.listAudienceExports(propertyId, pageSize);\n const summarizedExports = exports.map(summarizeAudienceExport);\n const failedExports = summarizedExports.filter((exportRecord) => exportRecord.state === \"FAILED\");\n let selectedExport: Record<string, unknown> | null = null;\n let rowSample: Record<string, unknown> | null = null;\n\n if (audienceExportName) {\n try {\n selectedExport = summarizeAudienceExport(await client.getAudienceExport(audienceExportName));\n } catch (error) {\n warnings.push(`Could not get audience export ${audienceExportName}. ${errorMessage(error)}`);\n }\n\n if (includeRowSample) {\n try {\n const response = await client.queryAudienceExport(audienceExportName, { limit: String(sampleLimit), offset: \"0\" });\n rowSample = includePersonalIdentifiers\n ? response\n : redactGA4AudienceExportResponse(response);\n warnings.push(includePersonalIdentifiers\n ? \"SENSITIVE PERSONAL IDENTIFIERS INCLUDED BY EXPLICIT OPT-IN: Audience Export sample user/device identifiers are present. Restrict storage, sharing, and model output.\"\n : \"Audience Export sample user/device identifiers were redacted by default. Set includePersonalIdentifiers=true only with explicit authorization.\");\n } catch (error) {\n warnings.push(`Could not query row sample for ${audienceExportName}. ${errorMessage(error)}`);\n }\n }\n } else if (includeRowSample) {\n warnings.push(\"includeRowSample was ignored because audienceExportName was not provided.\");\n }\n\n if (exports.length === 0) {\n warnings.push(\"No existing Audience Exports were found. This server intentionally does not create new exports.\");\n }\n if (failedExports.length > 0) {\n warnings.push(`${failedExports.length} Audience Export(s) are FAILED. Inspect errorMessage for root cause.`);\n }\n\n return ok({\n propertyId,\n source: \"data_api_v1beta_audienceExports_list\",\n count: exports.length,\n stateCounts: countByStringField(exports, \"state\"),\n audienceExports: summarizedExports,\n selectedExport,\n rowSample,\n warnings,\n limitations: [\n \"This tool only lists/gets/queries existing Audience Exports; it never calls audienceExports.create.\",\n \"Row sampling is disabled by default, and identifier values are redacted unless includePersonalIdentifiers=true is explicitly supplied.\",\n ],\n });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // 12. ga4_get_audience_diagnostics\n server.tool(\n \"ga4_get_audience_diagnostics\",\n \"Read-only audience diagnostics from Admin API audiences, Data API recurring audience lists, and observed audienceName report fallback.\",\n {\n propertyId: propertyIdSchema,\n recurringAudienceListName: z.string().optional().describe(\"Optional resource name, e.g. properties/123456789/recurringAudienceLists/abc123\"),\n includeDefinitions: z.boolean().optional().default(false).describe(\"Include full Admin API audience definitions when available\"),\n startDate: z.string().optional().describe(\"Start date YYYY-MM-DD or relative (7daysAgo, yesterday)\"),\n endDate: z.string().optional().describe(\"End date YYYY-MM-DD or relative (today, yesterday)\"),\n datePreset: datePresetSchema.optional().default(\"last28days\"),\n limit: z.number().int().min(1).max(1000).optional().default(100),\n },\n async ({ propertyId, recurringAudienceListName, includeDefinitions, startDate, endDate, datePreset, limit }) => {\n try {\n const warnings: string[] = [];\n const dateRange = resolveInputDateRange(startDate, endDate, datePreset);\n let audiences: Record<string, unknown>[] = [];\n let recurringAudienceLists: Record<string, unknown>[] = [];\n let selectedRecurringAudienceList: Record<string, unknown> | null = null;\n let observedAudienceRows: GA4InsightRow[] = [];\n\n try {\n audiences = await client.listAudiences(propertyId);\n } catch (error) {\n warnings.push(`Admin API audiences.list was unavailable. ${errorMessage(error)}`);\n }\n\n try {\n recurringAudienceLists = await client.listRecurringAudienceLists(propertyId);\n } catch (error) {\n warnings.push(`Data API recurringAudienceLists.list was unavailable. ${errorMessage(error)}`);\n }\n\n if (recurringAudienceListName) {\n try {\n selectedRecurringAudienceList = summarizeRecurringAudienceList(await client.getRecurringAudienceList(recurringAudienceListName));\n } catch (error) {\n warnings.push(`Could not get recurring audience list ${recurringAudienceListName}. ${errorMessage(error)}`);\n }\n }\n\n try {\n const summary = summarizeMetadata(await client.getMetadata(propertyId));\n const audienceNameField = pickAvailableField(summary, \"dimensions\", [\"audienceName\"]);\n const activeUsersMetric = pickAvailableField(summary, \"metrics\", [\"activeUsers\"]) ?? \"activeUsers\";\n\n if (audienceNameField) {\n const report = await runReportRows(client, propertyId, {\n dateRanges: [dateRange],\n dimensions: toDimensions([audienceNameField]),\n metrics: toMetrics([activeUsersMetric]),\n orderBys: [{ metric: { metricName: activeUsersMetric }, desc: true }],\n limit,\n });\n observedAudienceRows = report.rows;\n } else {\n warnings.push(\"audienceName was not available in Data API metadata, so observed audience membership fallback was skipped.\");\n }\n } catch (error) {\n warnings.push(`Observed audienceName report fallback failed. ${errorMessage(error)}`);\n }\n\n return ok({\n propertyId,\n dateRange,\n adminAudiences: audiences.map((audience) => summarizeAudience(audience, includeDefinitions)),\n recurringAudienceLists: recurringAudienceLists.map(summarizeRecurringAudienceList),\n selectedRecurringAudienceList,\n observedAudienceRows,\n counts: {\n adminAudiences: audiences.length,\n recurringAudienceLists: recurringAudienceLists.length,\n observedAudienceRows: observedAudienceRows.length,\n },\n warnings,\n limitations: [\n \"Admin API audiences.list is alpha and may omit pre-2020 or default audience filter definitions.\",\n \"Recurring Audience Lists are listed read-only; this server never calls recurringAudienceLists.create.\",\n \"The observed audienceName report is a traffic fallback, not an Admin configuration inventory.\",\n ],\n });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // 13. ga4_get_bigquery_export_diagnostics\n server.tool(\n \"ga4_get_bigquery_export_diagnostics\",\n \"Detect GA4 BigQuery export links and diagnose export modes, stream coverage, excluded events, and dataset location.\",\n {\n propertyId: propertyIdSchema,\n includeDataStreams: z.boolean().optional().default(true).describe(\"Include data stream summaries to compare against BigQuery exportStreams\"),\n },\n async ({ propertyId, includeDataStreams }) => {\n try {\n const warnings: string[] = [];\n let links: Record<string, unknown>[] = [];\n let streams: Record<string, unknown>[] = [];\n\n try {\n links = await client.listBigQueryLinks(propertyId);\n } catch (error) {\n warnings.push(`Admin API bigQueryLinks.list was unavailable. ${errorMessage(error)}`);\n }\n\n if (includeDataStreams) {\n try {\n streams = await client.listDataStreams(propertyId);\n } catch (error) {\n warnings.push(`Admin API dataStreams.list was unavailable. ${errorMessage(error)}`);\n }\n }\n\n const bigQueryLinks = links.map(summarizeBigQueryLink);\n const exportedStreamNames = new Set(bigQueryLinks.flatMap((link) => link.exportStreams));\n const dataStreams = streams.map(summarizeDataStream);\n const streamsNotExplicitlyExported = exportedStreamNames.size > 0\n ? dataStreams.filter((stream) => stream.name && !exportedStreamNames.has(stream.name))\n : [];\n\n if (bigQueryLinks.length === 0) {\n warnings.push(\"No BigQuery links were detected for this property.\");\n }\n if (bigQueryLinks.some((link) => !link.dailyExportEnabled && !link.streamingExportEnabled && !link.freshDailyExportEnabled)) {\n warnings.push(\"At least one BigQuery link has no daily, streaming, or fresh daily export mode enabled.\");\n }\n if (streamsNotExplicitlyExported.length > 0) {\n warnings.push(`${streamsNotExplicitlyExported.length} stream(s) were not present in explicit BigQuery exportStreams.`);\n }\n\n return ok({\n propertyId,\n detected: bigQueryLinks.length > 0,\n bigQueryLinks,\n dataStreams,\n summary: {\n linkCount: bigQueryLinks.length,\n dailyExportLinks: bigQueryLinks.filter((link) => link.dailyExportEnabled).length,\n streamingExportLinks: bigQueryLinks.filter((link) => link.streamingExportEnabled).length,\n freshDailyExportLinks: bigQueryLinks.filter((link) => link.freshDailyExportEnabled).length,\n datasetLocations: Array.from(new Set(bigQueryLinks.map((link) => link.datasetLocation).filter(Boolean))),\n excludedEventCount: bigQueryLinks.reduce((total, link) => total + link.excludedEvents.length, 0),\n explicitExportStreamCount: exportedStreamNames.size,\n streamsNotExplicitlyExported: streamsNotExplicitlyExported.length,\n },\n warnings,\n limitations: [\n \"This detects GA4 Admin API BigQueryLink configuration only; it does not query BigQuery datasets or table freshness.\",\n \"When exportStreams is empty, Google may apply property-level defaults; stream coverage should be confirmed in GA4 Admin UI or BigQuery.\",\n ],\n });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // 14. ga4_get_server_side_tagging_diagnostics\n server.tool(\n \"ga4_get_server_side_tagging_diagnostics\",\n \"Best-effort read-only server-side tagging diagnostics from data streams, Measurement Protocol secrets, event rules, and stream settings.\",\n {\n propertyId: propertyIdSchema,\n includeSettings: z.boolean().optional().default(true).describe(\"Fetch enhanced measurement and data redaction settings for web streams\"),\n includeRules: z.boolean().optional().default(true).describe(\"Fetch event create/edit rules for web streams\"),\n },\n async ({ propertyId, includeSettings, includeRules }) => {\n try {\n const warnings: string[] = [];\n const streams = await client.listDataStreams(propertyId);\n const streamDiagnostics: Array<Record<string, unknown>> = [];\n\n for (const stream of streams) {\n const streamName = recordString(stream, \"name\");\n const streamSummary = summarizeDataStream(stream);\n const streamWarnings: string[] = [];\n let measurementProtocolSecrets: Record<string, unknown>[] = [];\n let enhancedMeasurementSettings: Record<string, unknown> | null = null;\n let dataRedactionSettings: Record<string, unknown> | null = null;\n let eventCreateRules: Record<string, unknown>[] = [];\n let eventEditRules: Record<string, unknown>[] = [];\n\n if (streamName) {\n try {\n measurementProtocolSecrets = await client.listMeasurementProtocolSecrets(streamName);\n } catch (error) {\n streamWarnings.push(`measurementProtocolSecrets.list unavailable. ${errorMessage(error)}`);\n }\n\n if (includeSettings && streamSummary.type === \"WEB_DATA_STREAM\") {\n try {\n enhancedMeasurementSettings = await client.getEnhancedMeasurementSettings(streamName);\n } catch (error) {\n streamWarnings.push(`enhancedMeasurementSettings unavailable. ${errorMessage(error)}`);\n }\n\n try {\n dataRedactionSettings = await client.getDataRedactionSettings(streamName);\n } catch (error) {\n streamWarnings.push(`dataRedactionSettings unavailable. ${errorMessage(error)}`);\n }\n }\n\n if (includeRules && streamSummary.type === \"WEB_DATA_STREAM\") {\n try {\n eventCreateRules = await client.listEventCreateRules(streamName);\n } catch (error) {\n streamWarnings.push(`eventCreateRules.list unavailable. ${errorMessage(error)}`);\n }\n\n try {\n eventEditRules = await client.listEventEditRules(streamName);\n } catch (error) {\n streamWarnings.push(`eventEditRules.list unavailable. ${errorMessage(error)}`);\n }\n }\n }\n\n streamDiagnostics.push({\n ...streamSummary,\n measurementProtocolSecrets,\n measurementProtocolSecretCount: measurementProtocolSecrets.length,\n enhancedMeasurementSettings,\n dataRedactionSettings,\n eventCreateRuleCount: eventCreateRules.length,\n eventEditRuleCount: eventEditRules.length,\n eventCreateRules,\n eventEditRules,\n warnings: streamWarnings,\n });\n }\n\n const measurementProtocolSecretCount = streamDiagnostics.reduce((total, stream) => {\n return total + (typeof stream.measurementProtocolSecretCount === \"number\" ? stream.measurementProtocolSecretCount : 0);\n }, 0);\n const eventRuleCount = streamDiagnostics.reduce((total, stream) => {\n const createRules = typeof stream.eventCreateRuleCount === \"number\" ? stream.eventCreateRuleCount : 0;\n const editRules = typeof stream.eventEditRuleCount === \"number\" ? stream.eventEditRuleCount : 0;\n return total + createRules + editRules;\n }, 0);\n\n if (measurementProtocolSecretCount === 0) {\n warnings.push(\"No Measurement Protocol secrets were detected. This does not prove server-side tagging is absent.\");\n }\n warnings.push(\"GA4 Admin API does not expose GTM server container routing or tagging server URLs; server-side tagging is inferred, not confirmed.\");\n\n return ok({\n propertyId,\n streamDiagnostics,\n summary: {\n streamCount: streams.length,\n webStreamCount: streamDiagnostics.filter((stream) => stream.type === \"WEB_DATA_STREAM\").length,\n measurementProtocolSecretCount,\n eventRuleCount,\n possibleServerSideOrServerToServerSignals: measurementProtocolSecretCount > 0 || eventRuleCount > 0,\n },\n warnings,\n safety: {\n measurementProtocolSecretValues: \"Redacted before returning tool output.\",\n },\n limitations: [\n \"Measurement Protocol secrets can support server-to-server collection, but are not proof of GTM server-side tagging.\",\n \"Direct GTM server container configuration is outside the GA4 Admin/Data APIs used by this read-only server.\",\n ],\n });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // 15. ga4_run_advanced_funnel_report\n server.tool(\n \"ga4_run_advanced_funnel_report\",\n \"Run the GA4 Data API v1alpha runFunnelReport endpoint with optional breakdown/next-action, falling back to read-only step counts if unavailable.\",\n {\n propertyId: propertyIdSchema,\n steps: z.array(advancedFunnelStepSchema).min(2).max(10).describe(\"Ordered funnel steps using eventName, pagePath, or fieldName+fieldValue\"),\n isOpenFunnel: z.boolean().optional().default(false),\n visualizationType: funnelVisualizationTypeSchema.optional().default(\"STANDARD_FUNNEL\"),\n breakdownDimension: z.string().optional().describe(\"Optional breakdown dimension, e.g. deviceCategory or sessionDefaultChannelGroup\"),\n breakdownLimit: z.number().int().min(1).max(50).optional().default(10),\n nextActionDimension: z.string().optional().describe(\"Optional next action dimension, commonly eventName\"),\n nextActionLimit: z.number().int().min(1).max(50).optional().default(10),\n startDate: z.string().optional().describe(\"Start date YYYY-MM-DD or relative (7daysAgo, yesterday)\"),\n endDate: z.string().optional().describe(\"End date YYYY-MM-DD or relative (today, yesterday)\"),\n datePreset: datePresetSchema.optional().default(\"last28days\"),\n limit: z.number().int().min(1).max(250000).optional().default(10000),\n fallbackToStepCounts: z.boolean().optional().default(true),\n },\n async ({\n propertyId,\n steps,\n isOpenFunnel,\n visualizationType,\n breakdownDimension,\n breakdownLimit,\n nextActionDimension,\n nextActionLimit,\n startDate,\n endDate,\n datePreset,\n limit,\n fallbackToStepCounts,\n }) => {\n try {\n const dateRange = resolveInputDateRange(startDate, endDate, datePreset);\n const warnings: string[] = [];\n const request: GA4RunFunnelReportRequest = {\n dateRanges: [dateRange],\n funnel: {\n isOpenFunnel,\n steps: steps.map((step, index) => ({\n name: step.name ?? step.eventName ?? step.pagePath ?? step.fieldName ?? `Step ${index + 1}`,\n ...(step.isDirectlyFollowedBy !== undefined && { isDirectlyFollowedBy: step.isDirectlyFollowedBy }),\n ...(step.withinSecondsFromPriorStep !== undefined && index > 0 && { withinDurationFromPriorStep: `${step.withinSecondsFromPriorStep}s` }),\n filterExpression: buildAdvancedFunnelFilterExpression(step),\n })),\n },\n funnelVisualizationType: visualizationType,\n limit: String(limit),\n returnPropertyQuota: true,\n ...(breakdownDimension && {\n funnelBreakdown: {\n breakdownDimension: { name: getDimensionByKey(breakdownDimension)?.apiField ?? breakdownDimension },\n limit: String(breakdownLimit),\n },\n }),\n ...(nextActionDimension && {\n funnelNextAction: {\n nextActionDimension: { name: getDimensionByKey(nextActionDimension)?.apiField ?? nextActionDimension },\n limit: String(nextActionLimit),\n },\n }),\n };\n\n try {\n const response = await client.runFunnelReport(propertyId, request);\n return ok({\n propertyId,\n dateRange,\n source: \"data_api_v1alpha_runFunnelReport\",\n funnelTable: flattenFunnelSubReport(client, response.funnelTable),\n funnelVisualization: flattenFunnelSubReport(client, response.funnelVisualization),\n rawResponse: response,\n request,\n warnings,\n limitations: [\n \"runFunnelReport is a Data API v1alpha read-only report endpoint and may have alpha limitations.\",\n \"Returned rows are report aggregates; this tool does not mutate GA4 Explore reports or property configuration.\",\n ],\n });\n } catch (error) {\n if (!fallbackToStepCounts) {\n throw error;\n }\n\n warnings.push(`runFunnelReport failed; used independent step-count fallback. ${errorMessage(error)}`);\n const fallbackSteps = await runAdvancedFunnelStepCountFallback(client, propertyId, dateRange, steps);\n return ok({\n propertyId,\n dateRange,\n source: \"data_api_runReport_step_count_fallback\",\n steps: fallbackSteps,\n request,\n warnings,\n limitations: [\n \"Fallback counts each step independently with activeUsers and does not reproduce GA4 Explore/runFunnelReport sequence semantics.\",\n \"Disable fallbackToStepCounts to surface runFunnelReport errors directly.\",\n ],\n });\n }\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // 16. ga4_get_channel_groups\n server.tool(\n \"ga4_get_channel_groups\",\n \"List custom channel groups defined for a GA4 property.\",\n { propertyId: propertyIdSchema },\n async ({ propertyId }) => {\n try {\n const groups = await client.listChannelGroups(propertyId);\n return ok({ channelGroups: groups });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n // 17. ga4_validate_query\n server.tool(\n \"ga4_validate_query\",\n \"Validate a metric/dimension combination BEFORE executing. Checks max limits (9 dims, 10 metrics), ecommerce rules, and dimension compatibility.\",\n {\n metrics: z.array(z.string()).min(1).describe(\"Metric keys to validate\"),\n dimensions: z.array(z.string()).optional().describe(\"Dimension keys to validate\"),\n },\n async ({ metrics, dimensions }) => {\n try {\n const result = validateGA4QuerySelection(metrics, dimensions ?? []);\n return ok({\n ...result,\n debug: {\n requestCount: 0,\n },\n });\n } catch (e) { return formatMcpToolError(e); }\n },\n );\n\n registerGA4SurfaceTools(server, client);\n}\n","/**\n * google-analytics-mcp-server: an open-source MCP server for Google Analytics 4.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\n// ============================================\n// GOOGLE ANALYTICS 4 DATA API TYPES\n// Complete TypeScript interfaces for GA4 integration\n// ============================================\n\n// ============================================\n// CORE ENUMS & TYPE ALIASES\n// ============================================\n\nexport type GA4MetricCategory =\n | \"traffic\"\n | \"engagement\"\n | \"revenue\"\n | \"ecommerce\"\n | \"conversions\"\n | \"ads\"\n | \"user\"\n | \"calculated\";\n\nexport type GA4DimensionCategory =\n | \"time\"\n | \"traffic_source\"\n | \"geography\"\n | \"device\"\n | \"page\"\n | \"event\"\n | \"ecommerce\"\n | \"user\";\n\nexport type GA4MetricFormat =\n | \"number\"\n | \"currency\"\n | \"percent\"\n | \"duration\"\n | \"ratio\";\n\nexport type GA4FilterMatchType =\n | \"EXACT\"\n | \"BEGINS_WITH\"\n | \"ENDS_WITH\"\n | \"CONTAINS\"\n | \"FULL_REGEXP\"\n | \"PARTIAL_REGEXP\";\n\nexport type GA4NumericOperation =\n | \"EQUAL\"\n | \"LESS_THAN\"\n | \"LESS_THAN_OR_EQUAL\"\n | \"GREATER_THAN\"\n | \"GREATER_THAN_OR_EQUAL\";\n\nexport type GA4DatePreset =\n | \"today\"\n | \"yesterday\"\n | \"last7days\"\n | \"last28days\"\n | \"last30days\"\n | \"last90days\"\n | \"last12months\"\n | \"thisMonth\"\n | \"lastMonth\"\n | \"thisYear\";\n\n// ============================================\n// PROPERTY TYPES\n// ============================================\n\nexport interface GA4Property {\n propertyId: string;\n displayName: string;\n timeZone: string;\n currencyCode: string;\n industryCategory?: string;\n propertyType?: string;\n parent?: string; // account resource name\n}\n\nexport interface GA4AccountSummary {\n name: string; // e.g. \"accountSummaries/123\"\n account: string; // e.g. \"accounts/123\"\n displayName: string;\n propertySummaries?: Array<{\n property: string; // e.g. \"properties/123\"\n displayName: string;\n propertyType?: string;\n parent?: string;\n }>;\n}\n\n// ============================================\n// CHANNEL GROUP TYPES\n// ============================================\n\nexport interface GA4ChannelGroup {\n name: string;\n displayName: string;\n description?: string;\n primary?: boolean;\n}\n\n// ============================================\n// QUERY REQUEST TYPES\n// ============================================\n\nexport interface GA4DateRange {\n startDate: string; // \"2026-01-01\" or \"7daysAgo\", \"yesterday\", \"today\"\n endDate: string;\n}\n\nexport interface GA4Dimension {\n name: string; // e.g. \"date\", \"sessionSource\", \"city\"\n}\n\nexport interface GA4Metric {\n name: string; // e.g. \"sessions\", \"activeUsers\", \"totalRevenue\"\n}\n\nexport interface GA4FilterExpression {\n andGroup?: { expressions: GA4FilterExpression[] };\n orGroup?: { expressions: GA4FilterExpression[] };\n notExpression?: GA4FilterExpression;\n filter?: {\n fieldName: string;\n stringFilter?: {\n matchType: GA4FilterMatchType;\n value: string;\n caseSensitive?: boolean;\n };\n inListFilter?: {\n values: string[];\n caseSensitive?: boolean;\n };\n numericFilter?: {\n operation: GA4NumericOperation;\n value: { int64Value?: string; doubleValue?: number };\n };\n betweenFilter?: {\n fromValue: { int64Value?: string; doubleValue?: number };\n toValue: { int64Value?: string; doubleValue?: number };\n };\n };\n}\n\nexport interface GA4OrderBy {\n dimension?: { dimensionName: string; orderType?: string };\n metric?: { metricName: string };\n desc?: boolean;\n}\n\nexport interface GA4RunReportRequest {\n property?: string;\n dateRanges?: GA4DateRange[];\n dimensions?: GA4Dimension[];\n metrics: GA4Metric[];\n dimensionFilter?: GA4FilterExpression;\n metricFilter?: GA4FilterExpression;\n orderBys?: GA4OrderBy[];\n limit?: number;\n offset?: number;\n keepEmptyRows?: boolean;\n metricAggregations?: Array<\"TOTAL\" | \"MINIMUM\" | \"MAXIMUM\" | \"COUNT\">;\n currencyCode?: string;\n returnPropertyQuota?: boolean;\n cohortSpec?: Record<string, unknown>;\n comparisons?: Array<Record<string, unknown>>;\n}\n\nexport interface GA4Pivot {\n fieldNames: string[];\n orderBys?: GA4OrderBy[];\n offset?: number;\n limit: number;\n metricAggregations?: Array<\"TOTAL\" | \"MINIMUM\" | \"MAXIMUM\" | \"COUNT\">;\n}\n\nexport interface GA4RunPivotReportRequest {\n property?: string;\n dateRanges?: GA4DateRange[];\n dimensions: GA4Dimension[];\n metrics: GA4Metric[];\n pivots: GA4Pivot[];\n dimensionFilter?: GA4FilterExpression;\n metricFilter?: GA4FilterExpression;\n currencyCode?: string;\n keepEmptyRows?: boolean;\n returnPropertyQuota?: boolean;\n cohortSpec?: Record<string, unknown>;\n comparisons?: Array<Record<string, unknown>>;\n}\n\nexport interface GA4RunPivotReportResponse extends GA4RunReportResponse {\n pivotHeaders?: Array<Record<string, unknown>>;\n aggregates?: GA4Row[];\n kind?: string;\n}\n\nexport interface GA4BatchRunReportsResponse {\n reports?: GA4RunReportResponse[];\n kind?: string;\n}\n\nexport interface GA4BatchRunPivotReportsResponse {\n pivotReports?: GA4RunPivotReportResponse[];\n kind?: string;\n}\n\nexport interface GA4MinuteRange {\n name?: string;\n startMinutesAgo?: number;\n endMinutesAgo?: number;\n}\n\nexport interface GA4RunRealtimeReportRequest {\n dimensions?: GA4Dimension[];\n metrics: GA4Metric[];\n dimensionFilter?: GA4FilterExpression;\n metricFilter?: GA4FilterExpression;\n minuteRanges?: GA4MinuteRange[];\n orderBys?: GA4OrderBy[];\n limit?: number;\n keepEmptyRows?: boolean;\n returnPropertyQuota?: boolean;\n}\n\nexport interface GA4RunFunnelReportRequest {\n dateRanges?: GA4DateRange[];\n funnel: Record<string, unknown>;\n funnelBreakdown?: Record<string, unknown>;\n funnelNextAction?: Record<string, unknown>;\n funnelVisualizationType?: \"STANDARD_FUNNEL\" | \"TRENDED_FUNNEL\";\n limit?: string;\n dimensionFilter?: GA4FilterExpression;\n returnPropertyQuota?: boolean;\n}\n\nexport interface GA4QueryRequest {\n propertyId: string;\n dateRanges: GA4DateRange[];\n dimensions?: string[];\n metrics: string[];\n dimensionFilter?: GA4FilterExpression;\n metricFilter?: GA4FilterExpression;\n orderBys?: GA4OrderBy[];\n limit?: number;\n offset?: number;\n keepEmptyRows?: boolean;\n}\n\n// ============================================\n// API RESPONSE TYPES\n// ============================================\n\nexport interface GA4Row {\n dimensionValues?: Array<{ value: string }>;\n metricValues?: Array<{ value: string }>;\n}\n\nexport interface GA4RunReportResponse {\n dimensionHeaders?: Array<{ name: string }>;\n metricHeaders?: Array<{ name: string; type: string }>;\n rows?: GA4Row[];\n rowCount?: number;\n metadata?: { currencyCode: string; timeZone: string };\n totals?: GA4Row[];\n minimums?: GA4Row[];\n maximums?: GA4Row[];\n propertyQuota?: Record<string, unknown>;\n}\n\nexport interface GA4RunFunnelReportResponse {\n funnelTable?: GA4RunReportResponse;\n funnelVisualization?: GA4RunReportResponse;\n propertyQuota?: Record<string, unknown>;\n kind?: string;\n}\n\nexport interface GA4InsightRow {\n [key: string]: string | number | null;\n}\n\n// ============================================\n// METRIC CATALOG TYPES\n// ============================================\n\nexport interface GA4MetricDefinition {\n key: string;\n name: string;\n description: string;\n category: GA4MetricCategory;\n format: GA4MetricFormat;\n apiField: string;\n type: \"api\" | \"calculated\";\n formula?: string;\n dependencies?: string[];\n}\n\n// ============================================\n// DIMENSION CATALOG TYPES\n// ============================================\n\nexport interface GA4DimensionDefinition {\n key: string;\n name: string;\n description: string;\n category: GA4DimensionCategory;\n apiField: string;\n}\n\n// ============================================\n// FILTER CATALOG TYPES\n// ============================================\n\nexport interface GA4FilterDefinition {\n key: string;\n name: string;\n description: string;\n apiField: string;\n type: \"string\" | \"numeric\" | \"inList\";\n category: GA4DimensionCategory;\n}\n\n// ============================================\n// QUERY PLAN TYPES\n// ============================================\n\nexport interface GA4QueryPlan {\n request: GA4RunReportRequest;\n propertyId: string;\n warnings: string[];\n errors: string[];\n calculatedMetrics: string[];\n apiMetrics: string[];\n}\n\n// ============================================\n// API ERROR TYPES\n// ============================================\n\nexport class GA4ApiException extends Error {\n code: number;\n status: string;\n\n constructor(message: string, code: number, status?: string) {\n super(message);\n this.name = \"GA4ApiException\";\n this.code = code;\n this.status = status || \"UNKNOWN\";\n }\n\n get isAuthError(): boolean {\n return this.code === 401 || this.code === 403;\n }\n\n get isRateLimitError(): boolean {\n return this.code === 429;\n }\n\n get isQuotaError(): boolean {\n return this.code === 429 || this.status === \"RESOURCE_EXHAUSTED\";\n }\n\n get suggestion(): string {\n if (this.isAuthError) return \"Re-authenticate with Google Analytics\";\n if (this.isRateLimitError) return \"Rate limit exceeded, wait and retry\";\n if (this.isQuotaError) return \"Quota exceeded, reduce query frequency\";\n return \"Check the error details for more information\";\n }\n}\n\n// ============================================\n// CONSTANTS\n// ============================================\n\nexport const GA4_DATA_API_BASE = \"https://analyticsdata.googleapis.com/v1beta\";\nexport const GA4_DATA_API_ALPHA_BASE = \"https://analyticsdata.googleapis.com/v1alpha\";\nexport const GA4_ADMIN_API_BASE = \"https://analyticsadmin.googleapis.com/v1beta\";\nexport const GA4_ADMIN_API_ALPHA_BASE = \"https://analyticsadmin.googleapis.com/v1alpha\";\n\n// ============================================\n// UTILITY FUNCTIONS\n// ============================================\n\n/**\n * Resolve a GA4 date preset to start/end date strings\n */\nexport function resolveDatePreset(preset: GA4DatePreset): GA4DateRange {\n const now = new Date();\n const today = now.toISOString().split(\"T\")[0];\n const daysAgo = (n: number) => {\n const d = new Date(now);\n d.setDate(d.getDate() - n);\n return d.toISOString().split(\"T\")[0];\n };\n\n switch (preset) {\n case \"today\": return { startDate: \"today\", endDate: \"today\" };\n case \"yesterday\": return { startDate: \"yesterday\", endDate: \"yesterday\" };\n case \"last7days\": return { startDate: \"7daysAgo\", endDate: \"today\" };\n case \"last28days\": return { startDate: \"28daysAgo\", endDate: \"today\" };\n case \"last30days\": return { startDate: \"30daysAgo\", endDate: \"today\" };\n case \"last90days\": return { startDate: \"90daysAgo\", endDate: \"today\" };\n case \"last12months\": return { startDate: daysAgo(365), endDate: today };\n case \"thisMonth\": {\n const firstOfMonth = new Date(now.getFullYear(), now.getMonth(), 1).toISOString().split(\"T\")[0];\n return { startDate: firstOfMonth, endDate: today };\n }\n case \"lastMonth\": {\n const first = new Date(now.getFullYear(), now.getMonth() - 1, 1);\n const last = new Date(now.getFullYear(), now.getMonth(), 0);\n return { startDate: first.toISOString().split(\"T\")[0], endDate: last.toISOString().split(\"T\")[0] };\n }\n case \"thisYear\": {\n const firstOfYear = new Date(now.getFullYear(), 0, 1).toISOString().split(\"T\")[0];\n return { startDate: firstOfYear, endDate: today };\n }\n default: return { startDate: \"28daysAgo\", endDate: \"today\" };\n }\n}\n","/**\n * google-analytics-mcp-server: an open-source MCP server for Google Analytics 4.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\ntype LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\nconst LEVEL_ORDER: Record<LogLevel, number> = { debug: 0, info: 1, warn: 2, error: 3 };\nlet currentLevel: LogLevel = (process.env[\"LOG_LEVEL\"] as LogLevel) ?? \"info\";\nfunction log(level: LogLevel, platform: string | null, msg: string, data?: unknown): void {\n if (LEVEL_ORDER[level] < LEVEL_ORDER[currentLevel]) return;\n console.error(JSON.stringify({ ts: new Date().toISOString(), level, ...(platform && { platform }), msg, ...(data !== undefined && { data }) }));\n}\nexport const logger = {\n debug: (p: string, m: string, d?: unknown) => log(\"debug\", p, m, d),\n info: (p: string, m: string, d?: unknown) => log(\"info\", p, m, d),\n warn: (p: string, m: string, d?: unknown) => log(\"warn\", p, m, d),\n error: (p: string, m: string, d?: unknown) => log(\"error\", p, m, d),\n system: (m: string, d?: unknown) => log(\"info\", null, m, d),\n setLevel: (l: LogLevel) => { currentLevel = l; },\n};\n","/**\n * google-analytics-mcp-server: an open-source MCP server for Google Analytics 4.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nexport class PlatformApiError extends Error {\n constructor(public readonly platform: string, public readonly code: number, message: string,\n public readonly isRateLimit: boolean = false, public readonly isAuth: boolean = false,\n public readonly isPermission: boolean = false, public readonly suggestion: string = \"\",\n public readonly retryAfter?: number) { super(message); this.name = \"PlatformApiError\"; }\n toMcpError() {\n return { error: this.message, platform: this.platform, code: this.code,\n isRateLimit: this.isRateLimit, isAuth: this.isAuth, suggestion: this.suggestion,\n ...(this.retryAfter !== undefined && { retryAfter: this.retryAfter }) };\n }\n}\nexport class RateLimitError extends PlatformApiError {\n constructor(retryAfter?: number) { super(\"ga4\", 429, \"Rate limit exceeded\", true, false, false, \"Wait and retry with backoff\", retryAfter); }\n}\nexport class AuthError extends PlatformApiError {\n constructor(msg?: string) { super(\"ga4\", 401, msg ?? \"Auth failed. Check GA4 credentials.\", false, true, false, \"Verify GA4_CLIENT_ID, GA4_CLIENT_SECRET, GA4_REFRESH_TOKEN\"); }\n}\nexport function formatMcpToolError(error: unknown): { content: Array<{ type: \"text\"; text: string }>; isError: true } {\n if (error instanceof PlatformApiError) return { content: [{ type: \"text\", text: JSON.stringify(error.toMcpError(), null, 2) }], isError: true };\n const msg = error instanceof Error ? error.message : String(error);\n return { content: [{ type: \"text\", text: JSON.stringify({ error: msg }, null, 2) }], isError: true };\n}\n","/**\n * google-analytics-mcp-server: an open-source MCP server for Google Analytics 4.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { RateLimitError } from \"./errors.js\";\nimport { logger } from \"./logger.js\";\nexport class RateLimiter {\n private timestamps: number[] = [];\n async acquire(): Promise<void> {\n const now = Date.now();\n this.timestamps = this.timestamps.filter(t => now - t < 60_000);\n const lastSec = this.timestamps.filter(t => now - t < 1000);\n if (lastSec.length >= 10) { const w = 1000 - (now - lastSec[0]!) + 50; logger.debug(\"ga4\", `Rate limit: ${w}ms`); await new Promise(r => setTimeout(r, w)); }\n if (this.timestamps.length >= 600) { const w = 60_000 - (now - this.timestamps[0]!) + 100; await new Promise(r => setTimeout(r, w)); }\n this.timestamps.push(Date.now());\n }\n async execute<T>(fn: () => Promise<T>): Promise<T> {\n for (let i = 0; i <= 3; i++) {\n await this.acquire();\n try { return await fn(); } catch (e) {\n const isRL = e instanceof RateLimitError || (e instanceof Error && e.message.toLowerCase().includes(\"rate limit\"));\n if (isRL && i < 3) { await new Promise(r => setTimeout(r, Math.min(1000 * Math.pow(2, i) + Math.floor(Math.random() * 500), 30_000))); continue; }\n throw e;\n }\n }\n throw new RateLimitError();\n }\n}\n","/**\n * google-analytics-mcp-server: an open-source MCP server for Google Analytics 4.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\n// ============================================\n// GOOGLE ANALYTICS 4 DATA API CLIENT\n// Client for GA4 Data API v1beta integration\n// Standalone MCP server version with auto-refresh\n// ============================================\n\nimport {\n GA4Property,\n GA4AccountSummary,\n GA4ChannelGroup,\n GA4RunReportRequest,\n GA4RunRealtimeReportRequest,\n GA4RunFunnelReportRequest,\n GA4RunPivotReportRequest,\n GA4RunReportResponse,\n GA4RunFunnelReportResponse,\n GA4RunPivotReportResponse,\n GA4BatchRunReportsResponse,\n GA4BatchRunPivotReportsResponse,\n GA4InsightRow,\n GA4ApiException,\n GA4_DATA_API_BASE,\n GA4_DATA_API_ALPHA_BASE,\n GA4_ADMIN_API_BASE,\n GA4_ADMIN_API_ALPHA_BASE,\n} from \"./types.js\";\nimport { logger } from \"../../core/logger.js\";\nimport { RateLimiter } from \"../../core/rate-limiter.js\";\n\nexport { GA4ApiException } from \"./types.js\";\n\nconst GOOGLE_OAUTH_TOKEN_URL = \"https://oauth2.googleapis.com/token\";\n\nexport const GA4_ADMIN_COLLECTIONS = {\n accessBindings: { version: \"v1alpha\", collectionKey: \"accessBindings\" },\n adSenseLinks: { version: \"v1alpha\", collectionKey: \"adSenseLinks\" },\n audiences: { version: \"v1alpha\", collectionKey: \"audiences\" },\n bigQueryLinks: { version: \"v1alpha\", collectionKey: \"bigQueryLinks\" },\n calculatedMetrics: { version: \"v1alpha\", collectionKey: \"calculatedMetrics\" },\n channelGroups: { version: \"v1alpha\", collectionKey: \"channelGroups\" },\n customDimensions: { version: \"v1beta\", collectionKey: \"customDimensions\" },\n customMetrics: { version: \"v1beta\", collectionKey: \"customMetrics\" },\n dataStreams: { version: \"v1beta\", collectionKey: \"dataStreams\" },\n displayVideo360AdvertiserLinkProposals: { version: \"v1alpha\", collectionKey: \"displayVideo360AdvertiserLinkProposals\" },\n displayVideo360AdvertiserLinks: { version: \"v1alpha\", collectionKey: \"displayVideo360AdvertiserLinks\" },\n expandedDataSets: { version: \"v1alpha\", collectionKey: \"expandedDataSets\" },\n firebaseLinks: { version: \"v1beta\", collectionKey: \"firebaseLinks\" },\n googleAdsLinks: { version: \"v1beta\", collectionKey: \"googleAdsLinks\" },\n keyEvents: { version: \"v1beta\", collectionKey: \"keyEvents\" },\n reportingDataAnnotations: { version: \"v1alpha\", collectionKey: \"reportingDataAnnotations\" },\n rollupPropertySourceLinks: { version: \"v1alpha\", collectionKey: \"rollupPropertySourceLinks\" },\n searchAds360Links: { version: \"v1alpha\", collectionKey: \"searchAds360Links\" },\n subpropertyEventFilters: { version: \"v1alpha\", collectionKey: \"subpropertyEventFilters\" },\n subpropertySyncConfigs: { version: \"v1alpha\", collectionKey: \"subpropertySyncConfigs\" },\n} as const;\n\nexport type GA4AdminCollection = keyof typeof GA4_ADMIN_COLLECTIONS;\n\nexport type GA4PropertySetting =\n | \"attributionSettings\"\n | \"dataRetentionSettings\"\n | \"googleSignalsSettings\"\n | \"reportingIdentitySettings\"\n | \"userProvidedDataSettings\";\n\n// ============================================\n// GA4 CLIENT CLASS\n// ============================================\n\nexport class GA4Client {\n private accessToken = \"\";\n private tokenExpiresAt = 0;\n private clientId: string;\n private clientSecret: string;\n private refreshToken: string;\n private rateLimiter: RateLimiter;\n\n constructor(config: { clientId: string; clientSecret: string; refreshToken: string }) {\n this.clientId = config.clientId;\n this.clientSecret = config.clientSecret;\n this.refreshToken = config.refreshToken;\n this.rateLimiter = new RateLimiter();\n }\n\n // ============================================\n // PRIVATE METHODS\n // ============================================\n\n /**\n * Refresh access token using refresh token.\n * Google refresh tokens never expire.\n */\n private async refreshAccessToken(): Promise<void> {\n logger.debug(\"ga4\", \"Refreshing access token\");\n\n const response = await fetch(GOOGLE_OAUTH_TOKEN_URL, {\n method: \"POST\",\n redirect: \"error\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n },\n body: new URLSearchParams({\n grant_type: \"refresh_token\",\n refresh_token: this.refreshToken,\n client_id: this.clientId,\n client_secret: this.clientSecret,\n }),\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({})) as Record<string, string>;\n logger.error(\"ga4\", \"Token refresh failed\", { status: response.status, error });\n throw new GA4ApiException(\n error.error_description || error.error || \"Failed to refresh token\",\n response.status,\n error.error\n );\n }\n\n const data = await response.json() as { access_token: string; expires_in: number };\n this.accessToken = data.access_token;\n // Expire 60 seconds early to avoid edge cases\n this.tokenExpiresAt = Date.now() + (data.expires_in - 60) * 1000;\n logger.debug(\"ga4\", \"Access token refreshed successfully\");\n }\n\n /**\n * Ensure we have a valid access token before making API calls.\n */\n private async ensureValidToken(): Promise<void> {\n if (!this.accessToken || Date.now() >= this.tokenExpiresAt) {\n await this.refreshAccessToken();\n }\n }\n\n private async request<T>(url: string, options: RequestInit = {}): Promise<T> {\n await this.ensureValidToken();\n\n // 90-second timeout to prevent hanging requests\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), 90_000);\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n // Forced after the spread: once a bearer token is attached, a redirect\n // must never be followed, or the credential would be forwarded to\n // whatever host the redirect names.\n redirect: \"error\",\n headers: {\n Authorization: `Bearer ${this.accessToken}`,\n \"Content-Type\": \"application/json\",\n ...options.headers,\n },\n });\n\n if (!response.ok) {\n const errorBody = await response.json().catch(() => ({})) as Record<string, unknown>;\n const errorDetails = errorBody?.error as Record<string, string> | undefined;\n\n logger.error(\"ga4\", \"API Error\", { status: response.status, error: errorBody });\n\n const detailedMessage = errorDetails?.message\n || `Request failed: ${response.statusText}`;\n\n throw new GA4ApiException(\n detailedMessage,\n response.status,\n errorDetails?.status\n );\n }\n\n return response.json() as Promise<T>;\n } catch (error) {\n if (error instanceof DOMException && error.name === \"AbortError\") {\n throw new GA4ApiException(\n \"GA4 API request timed out after 90 seconds\",\n 408,\n \"TIMEOUT\"\n );\n }\n throw error;\n } finally {\n clearTimeout(timeout);\n }\n }\n\n private cleanPropertyId(propertyId: string): string {\n return propertyId.replace(\"properties/\", \"\");\n }\n\n private cleanResourceName(resourceName: string): string {\n return resourceName.replace(/^\\/+/, \"\");\n }\n\n private async listPagedRecords<T extends Record<string, unknown>>(\n baseUrl: string,\n collectionKey: string,\n pageSize: number,\n maxPages = 50\n ): Promise<T[]> {\n const records: T[] = [];\n let pageToken: string | undefined;\n let pagesRead = 0;\n\n do {\n const params = new URLSearchParams({ pageSize: String(pageSize) });\n if (pageToken) {\n params.set(\"pageToken\", pageToken);\n }\n\n const separator = baseUrl.includes(\"?\") ? \"&\" : \"?\";\n const response = await this.rateLimiter.execute(() =>\n this.request<Record<string, unknown>>(`${baseUrl}${separator}${params.toString()}`)\n );\n\n const collection = response[collectionKey];\n if (Array.isArray(collection)) {\n records.push(...collection.filter((item): item is T => typeof item === \"object\" && item !== null && !Array.isArray(item)) as T[]);\n }\n\n pageToken = typeof response.nextPageToken === \"string\" ? response.nextPageToken : undefined;\n pagesRead += 1;\n } while (pageToken && pagesRead < maxPages);\n\n return records;\n }\n\n private redactSecretRecord(record: Record<string, unknown>): Record<string, unknown> {\n const safeRecord = { ...record };\n if (\"secretValue\" in safeRecord) {\n safeRecord.secretValue = \"[REDACTED]\";\n safeRecord.secretValueRedacted = true;\n }\n return safeRecord;\n }\n\n // ============================================\n // PROPERTY MANAGEMENT\n // ============================================\n\n /**\n * List all GA4 properties accessible to the authenticated user\n * Calls GET /v1beta/accountSummaries with pagination\n * Returns flattened array of GA4Property\n */\n async listProperties(): Promise<GA4Property[]> {\n const properties: GA4Property[] = [];\n let pageToken: string | undefined;\n\n do {\n const params = new URLSearchParams({ pageSize: \"200\" });\n if (pageToken) {\n params.set(\"pageToken\", pageToken);\n }\n\n const url = `${GA4_ADMIN_API_BASE}/accountSummaries?${params.toString()}`;\n\n const response = await this.rateLimiter.execute(() =>\n this.request<{\n accountSummaries?: GA4AccountSummary[];\n nextPageToken?: string;\n }>(url)\n );\n\n if (response.accountSummaries) {\n for (const account of response.accountSummaries) {\n if (account.propertySummaries) {\n for (const prop of account.propertySummaries) {\n // Extract numeric property ID from \"properties/123456\" format\n const propertyId = prop.property.replace(\"properties/\", \"\");\n\n properties.push({\n propertyId,\n displayName: prop.displayName,\n timeZone: \"\",\n currencyCode: \"\",\n propertyType: prop.propertyType,\n parent: account.account,\n });\n }\n }\n }\n }\n\n pageToken = response.nextPageToken;\n } while (pageToken);\n\n // Enrich properties with currency and timezone from the property detail endpoint\n // GET /v1beta/properties/{propertyId} returns currencyCode, timeZone, etc.\n await Promise.all(\n properties.map(async (prop) => {\n try {\n const detail = await this.rateLimiter.execute(() =>\n this.request<{\n currencyCode?: string;\n timeZone?: string;\n industryCategory?: string;\n }>(`${GA4_ADMIN_API_BASE}/properties/${prop.propertyId}`)\n );\n prop.currencyCode = detail.currencyCode || \"\";\n prop.timeZone = detail.timeZone || \"\";\n if (detail.industryCategory) {\n prop.industryCategory = detail.industryCategory;\n }\n } catch {\n // Non-critical, so keep empty values\n logger.warn(\"ga4\", `Failed to enrich property ${prop.propertyId}`);\n }\n })\n );\n\n return properties;\n }\n\n // ============================================\n // REPORTING\n // ============================================\n\n /**\n * Run a GA4 report with automatic pagination\n * POST /v1beta/properties/{propertyId}:runReport\n * Handles pagination via offset, capped at 250,000 rows per request\n */\n async runReport(\n propertyId: string,\n request: GA4RunReportRequest\n ): Promise<GA4RunReportResponse> {\n const cleanPropertyId = this.cleanPropertyId(propertyId);\n const url = `${GA4_DATA_API_BASE}/properties/${cleanPropertyId}:runReport`;\n\n const MAX_ROWS_PER_REQUEST = 250000;\n\n // First request\n const firstResponse = await this.rateLimiter.execute(() =>\n this.request<GA4RunReportResponse>(url, {\n method: \"POST\",\n body: JSON.stringify({\n ...request,\n limit: request.limit ?? MAX_ROWS_PER_REQUEST,\n offset: request.offset ?? 0,\n }),\n })\n );\n\n // If no pagination needed, return as-is\n const totalRows = firstResponse.rowCount ?? 0;\n const returnedRows = firstResponse.rows?.length ?? 0;\n const startOffset = request.offset ?? 0;\n\n // If the caller specified a limit, respect it and skip auto-pagination\n if (request.limit) {\n return firstResponse;\n }\n\n // If all rows were returned in the first request, no pagination needed\n if (startOffset + returnedRows >= totalRows) {\n return firstResponse;\n }\n\n // Paginate to collect all rows\n const allRows = [...(firstResponse.rows ?? [])];\n let currentOffset = startOffset + returnedRows;\n\n while (currentOffset < totalRows) {\n const pageResponse = await this.rateLimiter.execute(() =>\n this.request<GA4RunReportResponse>(url, {\n method: \"POST\",\n body: JSON.stringify({\n ...request,\n limit: MAX_ROWS_PER_REQUEST,\n offset: currentOffset,\n }),\n })\n );\n\n if (pageResponse.rows) {\n allRows.push(...pageResponse.rows);\n }\n\n const pageRowCount = pageResponse.rows?.length ?? 0;\n if (pageRowCount === 0) break; // Safety: no more rows returned\n\n currentOffset += pageRowCount;\n }\n\n // Return merged response with all rows\n return {\n ...firstResponse,\n rows: allRows,\n };\n }\n\n /**\n * Run a GA4 realtime report.\n * POST /v1beta/properties/{propertyId}:runRealtimeReport\n */\n async runRealtimeReport(\n propertyId: string,\n request: GA4RunRealtimeReportRequest\n ): Promise<GA4RunReportResponse> {\n const cleanPropertyId = this.cleanPropertyId(propertyId);\n const url = `${GA4_DATA_API_BASE}/properties/${cleanPropertyId}:runRealtimeReport`;\n\n return this.rateLimiter.execute(() =>\n this.request<GA4RunReportResponse>(url, {\n method: \"POST\",\n body: JSON.stringify(request),\n })\n );\n }\n\n /** POST /v1beta/properties/{propertyId}:batchRunReports (maximum five reports). */\n async batchRunReports(\n propertyId: string,\n requests: GA4RunReportRequest[]\n ): Promise<GA4BatchRunReportsResponse> {\n const cleanPropertyId = this.cleanPropertyId(propertyId);\n const url = `${GA4_DATA_API_BASE}/properties/${cleanPropertyId}:batchRunReports`;\n return this.rateLimiter.execute(() =>\n this.request<GA4BatchRunReportsResponse>(url, {\n method: \"POST\",\n body: JSON.stringify({ requests }),\n })\n );\n }\n\n /** POST /v1beta/properties/{propertyId}:runPivotReport. */\n async runPivotReport(\n propertyId: string,\n request: GA4RunPivotReportRequest\n ): Promise<GA4RunPivotReportResponse> {\n const cleanPropertyId = this.cleanPropertyId(propertyId);\n const url = `${GA4_DATA_API_BASE}/properties/${cleanPropertyId}:runPivotReport`;\n return this.rateLimiter.execute(() =>\n this.request<GA4RunPivotReportResponse>(url, {\n method: \"POST\",\n body: JSON.stringify(request),\n })\n );\n }\n\n /** POST /v1beta/properties/{propertyId}:batchRunPivotReports (maximum five reports). */\n async batchRunPivotReports(\n propertyId: string,\n requests: GA4RunPivotReportRequest[]\n ): Promise<GA4BatchRunPivotReportsResponse> {\n const cleanPropertyId = this.cleanPropertyId(propertyId);\n const url = `${GA4_DATA_API_BASE}/properties/${cleanPropertyId}:batchRunPivotReports`;\n return this.rateLimiter.execute(() =>\n this.request<GA4BatchRunPivotReportsResponse>(url, {\n method: \"POST\",\n body: JSON.stringify({ requests }),\n })\n );\n }\n\n /** POST /v1beta/properties/{propertyId}:checkCompatibility for Core reports. */\n async checkCompatibility(\n propertyId: string,\n request: Record<string, unknown>\n ): Promise<Record<string, unknown>> {\n const cleanPropertyId = this.cleanPropertyId(propertyId);\n const url = `${GA4_DATA_API_BASE}/properties/${cleanPropertyId}:checkCompatibility`;\n return this.rateLimiter.execute(() =>\n this.request<Record<string, unknown>>(url, {\n method: \"POST\",\n body: JSON.stringify(request),\n })\n );\n }\n\n /** GET /v1alpha/properties/{propertyId}/propertyQuotasSnapshot. */\n async getPropertyQuotasSnapshot(propertyId: string): Promise<Record<string, unknown>> {\n const cleanPropertyId = this.cleanPropertyId(propertyId);\n return this.rateLimiter.execute(() =>\n this.request<Record<string, unknown>>(\n `${GA4_DATA_API_ALPHA_BASE}/properties/${cleanPropertyId}/propertyQuotasSnapshot`\n )\n );\n }\n\n /**\n * Run a GA4 funnel report.\n * POST /v1alpha/properties/{propertyId}:runFunnelReport\n */\n async runFunnelReport(\n propertyId: string,\n request: GA4RunFunnelReportRequest\n ): Promise<GA4RunFunnelReportResponse> {\n const cleanPropertyId = this.cleanPropertyId(propertyId);\n const url = `${GA4_DATA_API_ALPHA_BASE}/properties/${cleanPropertyId}:runFunnelReport`;\n\n return this.rateLimiter.execute(() =>\n this.request<GA4RunFunnelReportResponse>(url, {\n method: \"POST\",\n body: JSON.stringify(request),\n })\n );\n }\n\n // ============================================\n // METADATA\n // ============================================\n\n /**\n * Get metadata for a GA4 property (available dimensions & metrics)\n * GET /v1beta/properties/{propertyId}/metadata\n */\n async getMetadata(propertyId: string): Promise<Record<string, unknown>> {\n const cleanPropertyId = this.cleanPropertyId(propertyId);\n const url = `${GA4_DATA_API_BASE}/properties/${cleanPropertyId}/metadata`;\n\n return this.rateLimiter.execute(() =>\n this.request<Record<string, unknown>>(url)\n );\n }\n\n /**\n * Get Admin API property details.\n * GET /v1beta/properties/{propertyId}\n */\n async getProperty(propertyId: string): Promise<Record<string, unknown>> {\n const cleanPropertyId = this.cleanPropertyId(propertyId);\n const url = `${GA4_ADMIN_API_BASE}/properties/${cleanPropertyId}`;\n\n return this.rateLimiter.execute(() =>\n this.request<Record<string, unknown>>(url)\n );\n }\n\n /** List raw Analytics Admin accounts accessible to the caller. */\n async listAccounts(pageSize = 200): Promise<Record<string, unknown>[]> {\n return this.listPagedRecords<Record<string, unknown>>(\n `${GA4_ADMIN_API_BASE}/accounts`,\n \"accounts\",\n pageSize\n );\n }\n\n /**\n * List an allowlisted Analytics Admin collection for a property.\n * Every route in the allowlist is a GET-only list method.\n */\n async listAdminPropertyResources(\n propertyId: string,\n collection: GA4AdminCollection,\n pageSize = 200\n ): Promise<Record<string, unknown>[]> {\n const cleanPropertyId = this.cleanPropertyId(propertyId);\n const definition = GA4_ADMIN_COLLECTIONS[collection];\n const base = definition.version === \"v1beta\" ? GA4_ADMIN_API_BASE : GA4_ADMIN_API_ALPHA_BASE;\n return this.listPagedRecords<Record<string, unknown>>(\n `${base}/properties/${cleanPropertyId}/${collection}`,\n definition.collectionKey,\n pageSize\n );\n }\n\n /** Get an allowlisted property singleton setting through a GET-only Admin API method. */\n async getPropertySetting(propertyId: string, setting: GA4PropertySetting): Promise<Record<string, unknown>> {\n const cleanPropertyId = this.cleanPropertyId(propertyId);\n const endpoints: Record<GA4PropertySetting, string> = {\n attributionSettings: `${GA4_ADMIN_API_ALPHA_BASE}/properties/${cleanPropertyId}/attributionSettings`,\n dataRetentionSettings: `${GA4_ADMIN_API_BASE}/properties/${cleanPropertyId}/dataRetentionSettings`,\n googleSignalsSettings: `${GA4_ADMIN_API_ALPHA_BASE}/properties/${cleanPropertyId}/googleSignalsSettings`,\n reportingIdentitySettings: `${GA4_ADMIN_API_ALPHA_BASE}/properties/${cleanPropertyId}/reportingIdentitySettings`,\n userProvidedDataSettings: `${GA4_ADMIN_API_ALPHA_BASE}/properties/${cleanPropertyId}/userProvidedDataSettings`,\n };\n return this.rateLimiter.execute(() => this.request<Record<string, unknown>>(endpoints[setting]));\n }\n\n // ============================================\n // AUDIENCES & AUDIENCE EXPORTS\n // ============================================\n\n /**\n * List Audience Export snapshots for a property.\n * GET /v1beta/properties/{propertyId}/audienceExports\n */\n async listAudienceExports(propertyId: string, pageSize = 100): Promise<Record<string, unknown>[]> {\n const cleanPropertyId = this.cleanPropertyId(propertyId);\n return this.listPagedRecords<Record<string, unknown>>(\n `${GA4_DATA_API_BASE}/properties/${cleanPropertyId}/audienceExports`,\n \"audienceExports\",\n pageSize\n );\n }\n\n /**\n * Get Audience Export metadata.\n * GET /v1beta/properties/{propertyId}/audienceExports/{audienceExport}\n */\n async getAudienceExport(audienceExportName: string): Promise<Record<string, unknown>> {\n const resourceName = this.cleanResourceName(audienceExportName);\n return this.rateLimiter.execute(() =>\n this.request<Record<string, unknown>>(`${GA4_DATA_API_BASE}/${resourceName}`)\n );\n }\n\n /**\n * Query rows from an existing Audience Export. This is read-only but can contain user identifiers.\n * POST /v1beta/properties/{propertyId}/audienceExports/{audienceExport}:query\n */\n async queryAudienceExport(\n audienceExportName: string,\n request: { offset?: string; limit?: string }\n ): Promise<Record<string, unknown>> {\n const resourceName = this.cleanResourceName(audienceExportName);\n return this.rateLimiter.execute(() =>\n this.request<Record<string, unknown>>(`${GA4_DATA_API_BASE}/${resourceName}:query`, {\n method: \"POST\",\n body: JSON.stringify(request),\n })\n );\n }\n\n /**\n * List configured Admin API Audiences for a property.\n * GET /v1alpha/properties/{propertyId}/audiences\n */\n async listAudiences(propertyId: string, pageSize = 200): Promise<Record<string, unknown>[]> {\n const cleanPropertyId = this.cleanPropertyId(propertyId);\n return this.listPagedRecords<Record<string, unknown>>(\n `${GA4_ADMIN_API_ALPHA_BASE}/properties/${cleanPropertyId}/audiences`,\n \"audiences\",\n pageSize\n );\n }\n\n /**\n * List Recurring Audience Lists for a property.\n * GET /v1alpha/properties/{propertyId}/recurringAudienceLists\n */\n async listRecurringAudienceLists(propertyId: string, pageSize = 100): Promise<Record<string, unknown>[]> {\n const cleanPropertyId = this.cleanPropertyId(propertyId);\n return this.listPagedRecords<Record<string, unknown>>(\n `${GA4_DATA_API_ALPHA_BASE}/properties/${cleanPropertyId}/recurringAudienceLists`,\n \"recurringAudienceLists\",\n pageSize\n );\n }\n\n /**\n * Get Recurring Audience List metadata.\n * GET /v1alpha/properties/{propertyId}/recurringAudienceLists/{recurringAudienceList}\n */\n async getRecurringAudienceList(recurringAudienceListName: string): Promise<Record<string, unknown>> {\n const resourceName = this.cleanResourceName(recurringAudienceListName);\n return this.rateLimiter.execute(() =>\n this.request<Record<string, unknown>>(`${GA4_DATA_API_ALPHA_BASE}/${resourceName}`)\n );\n }\n\n // ============================================\n // EXPORTS & TAGGING DIAGNOSTICS\n // ============================================\n\n /**\n * List BigQuery links configured for a property.\n * GET /v1alpha/properties/{propertyId}/bigQueryLinks\n */\n async listBigQueryLinks(propertyId: string, pageSize = 200): Promise<Record<string, unknown>[]> {\n const cleanPropertyId = this.cleanPropertyId(propertyId);\n return this.listPagedRecords<Record<string, unknown>>(\n `${GA4_ADMIN_API_ALPHA_BASE}/properties/${cleanPropertyId}/bigQueryLinks`,\n \"bigQueryLinks\",\n pageSize\n );\n }\n\n /**\n * List data streams configured for a property.\n * GET /v1alpha/properties/{propertyId}/dataStreams\n */\n async listDataStreams(propertyId: string, pageSize = 200): Promise<Record<string, unknown>[]> {\n const cleanPropertyId = this.cleanPropertyId(propertyId);\n return this.listPagedRecords<Record<string, unknown>>(\n `${GA4_ADMIN_API_ALPHA_BASE}/properties/${cleanPropertyId}/dataStreams`,\n \"dataStreams\",\n pageSize\n );\n }\n\n /**\n * List Measurement Protocol secrets for a stream with secret values redacted.\n * GET /v1alpha/properties/{propertyId}/dataStreams/{dataStream}/measurementProtocolSecrets\n */\n async listMeasurementProtocolSecrets(dataStreamName: string, pageSize = 10): Promise<Record<string, unknown>[]> {\n const resourceName = this.cleanResourceName(dataStreamName);\n const secrets = await this.listPagedRecords<Record<string, unknown>>(\n `${GA4_ADMIN_API_ALPHA_BASE}/${resourceName}/measurementProtocolSecrets`,\n \"measurementProtocolSecrets\",\n pageSize\n );\n return secrets.map((secret) => this.redactSecretRecord(secret));\n }\n\n /**\n * Get enhanced measurement settings for a web stream.\n * GET /v1alpha/properties/{propertyId}/dataStreams/{dataStream}/enhancedMeasurementSettings\n */\n async getEnhancedMeasurementSettings(dataStreamName: string): Promise<Record<string, unknown>> {\n const resourceName = this.cleanResourceName(dataStreamName);\n return this.rateLimiter.execute(() =>\n this.request<Record<string, unknown>>(`${GA4_ADMIN_API_ALPHA_BASE}/${resourceName}/enhancedMeasurementSettings`)\n );\n }\n\n /**\n * Get data redaction settings for a web stream.\n * GET /v1alpha/properties/{propertyId}/dataStreams/{dataStream}/dataRedactionSettings\n */\n async getDataRedactionSettings(dataStreamName: string): Promise<Record<string, unknown>> {\n const resourceName = this.cleanResourceName(dataStreamName);\n return this.rateLimiter.execute(() =>\n this.request<Record<string, unknown>>(`${GA4_ADMIN_API_ALPHA_BASE}/${resourceName}/dataRedactionSettings`)\n );\n }\n\n /**\n * List event create rules for a web stream.\n * GET /v1alpha/properties/{propertyId}/dataStreams/{dataStream}/eventCreateRules\n */\n async listEventCreateRules(dataStreamName: string, pageSize = 200): Promise<Record<string, unknown>[]> {\n const resourceName = this.cleanResourceName(dataStreamName);\n return this.listPagedRecords<Record<string, unknown>>(\n `${GA4_ADMIN_API_ALPHA_BASE}/${resourceName}/eventCreateRules`,\n \"eventCreateRules\",\n pageSize\n );\n }\n\n /**\n * List event edit rules for a web stream.\n * GET /v1alpha/properties/{propertyId}/dataStreams/{dataStream}/eventEditRules\n */\n async listEventEditRules(dataStreamName: string, pageSize = 200): Promise<Record<string, unknown>[]> {\n const resourceName = this.cleanResourceName(dataStreamName);\n return this.listPagedRecords<Record<string, unknown>>(\n `${GA4_ADMIN_API_ALPHA_BASE}/${resourceName}/eventEditRules`,\n \"eventEditRules\",\n pageSize\n );\n }\n\n // ============================================\n // CHANNEL GROUPS\n // ============================================\n\n /**\n * List custom channel groups for a GA4 property\n * GET /v1alpha/properties/{propertyId}/channelGroups\n * NOTE: channelGroups endpoint is only available in v1alpha, not v1beta\n */\n async listChannelGroups(propertyId: string): Promise<GA4ChannelGroup[]> {\n const cleanPropertyId = this.cleanPropertyId(propertyId);\n // Channel groups are only in v1alpha of the Admin API\n const url = `https://analyticsadmin.googleapis.com/v1alpha/properties/${cleanPropertyId}/channelGroups`;\n\n const response = await this.rateLimiter.execute(() =>\n this.request<{\n channelGroups?: GA4ChannelGroup[];\n }>(url)\n );\n\n return response.channelGroups ?? [];\n }\n\n // ============================================\n // RESPONSE FLATTENING\n // ============================================\n\n /**\n * Convert a GA4RunReportResponse into flat GA4InsightRow[]\n * Maps dimension and metric headers to row values\n * Parses numeric strings to numbers for metric values\n *\n * Input:\n * dimensionHeaders: [{ name: \"date\" }, { name: \"sessionSource\" }]\n * metricHeaders: [{ name: \"sessions\", type: \"TYPE_INTEGER\" }, { name: \"totalRevenue\", type: \"TYPE_CURRENCY\" }]\n * rows: [{ dimensionValues: [{ value: \"20260301\" }, { value: \"google\" }], metricValues: [{ value: \"150\" }, { value: \"1234.56\" }] }]\n *\n * Output:\n * [{ date: \"20260301\", sessionSource: \"google\", sessions: 150, totalRevenue: 1234.56 }]\n */\n flattenResponse(response: GA4RunReportResponse): GA4InsightRow[] {\n if (!response.rows || response.rows.length === 0) {\n return [];\n }\n\n const dimensionHeaders = response.dimensionHeaders ?? [];\n const metricHeaders = response.metricHeaders ?? [];\n\n return response.rows.map((row) => {\n const flat: GA4InsightRow = {};\n\n // Map dimension values\n if (row.dimensionValues) {\n for (let i = 0; i < dimensionHeaders.length; i++) {\n const header = dimensionHeaders[i];\n const value = row.dimensionValues[i]?.value ?? null;\n flat[header.name] = value;\n }\n }\n\n // Map metric values, parsing numeric strings to numbers\n if (row.metricValues) {\n for (let i = 0; i < metricHeaders.length; i++) {\n const header = metricHeaders[i];\n const rawValue = row.metricValues[i]?.value ?? null;\n\n if (rawValue === null) {\n flat[header.name] = null;\n } else if (/^-?\\d+(\\.\\d+)?$/.test(rawValue)) {\n flat[header.name] = parseFloat(rawValue);\n } else {\n flat[header.name] = rawValue;\n }\n }\n }\n\n return flat;\n });\n }\n}\n","/**\n * google-analytics-mcp-server: an open-source MCP server for Google Analytics 4.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\n// ============================================\n// GOOGLE ANALYTICS 4 DATA API METRIC CATALOG\n// Complete catalog of ~60 GA4 metrics\n// Based on GA4 Data API v1beta metric fields\n// ============================================\n\nimport type { GA4MetricDefinition } from \"./types.js\";\n\n// ============================================\n// TRAFFIC METRICS\n// ============================================\n\nconst TRAFFIC_METRICS: GA4MetricDefinition[] = [\n {\n key: \"sessions\",\n name: \"Sessions\",\n description:\n \"The number of sessions that began on your site or app.\",\n category: \"traffic\",\n format: \"number\",\n apiField: \"sessions\",\n type: \"api\",\n },\n {\n key: \"activeUsers\",\n name: \"Active Users\",\n description:\n \"The number of distinct users who visited your site or app.\",\n category: \"traffic\",\n format: \"number\",\n apiField: \"activeUsers\",\n type: \"api\",\n },\n {\n key: \"newUsers\",\n name: \"New Users\",\n description:\n \"The number of users who interacted with your site or app for the first time.\",\n category: \"traffic\",\n format: \"number\",\n apiField: \"newUsers\",\n type: \"api\",\n },\n {\n key: \"totalUsers\",\n name: \"Total Users\",\n description:\n \"The total number of unique users who logged an event.\",\n category: \"traffic\",\n format: \"number\",\n apiField: \"totalUsers\",\n type: \"api\",\n },\n {\n key: \"screenPageViews\",\n name: \"Page Views\",\n description:\n \"The number of app screens or web pages your users viewed. Includes repeated views of a single page or screen.\",\n category: \"traffic\",\n format: \"number\",\n apiField: \"screenPageViews\",\n type: \"api\",\n },\n {\n key: \"bounceRate\",\n name: \"Bounce Rate\",\n description:\n \"The percentage of sessions that were not engaged (lasted less than 10 seconds, had no conversion events, and had fewer than 2 page/screen views).\",\n category: \"traffic\",\n format: \"percent\",\n apiField: \"bounceRate\",\n type: \"api\",\n },\n {\n key: \"sessionsPerUser\",\n name: \"Sessions per User\",\n description:\n \"The average number of sessions per user.\",\n category: \"traffic\",\n format: \"ratio\",\n apiField: \"sessionsPerUser\",\n type: \"api\",\n },\n {\n key: \"engagedSessions\",\n name: \"Engaged Sessions\",\n description:\n \"The number of sessions that lasted 10 seconds or longer, had a conversion event, or had 2 or more page/screen views.\",\n category: \"traffic\",\n format: \"number\",\n apiField: \"engagedSessions\",\n type: \"api\",\n },\n {\n key: \"crashAffectedUsers\",\n name: \"Crash Affected Users\",\n description:\n \"The number of users who experienced a crash in your app.\",\n category: \"traffic\",\n format: \"number\",\n apiField: \"crashAffectedUsers\",\n type: \"api\",\n },\n];\n\n// ============================================\n// ENGAGEMENT METRICS\n// ============================================\n\nconst ENGAGEMENT_METRICS: GA4MetricDefinition[] = [\n {\n key: \"averageSessionDuration\",\n name: \"Avg. Session Duration\",\n description:\n \"The average duration (in seconds) of users' sessions.\",\n category: \"engagement\",\n format: \"duration\",\n apiField: \"averageSessionDuration\",\n type: \"api\",\n },\n {\n key: \"engagementRate\",\n name: \"Engagement Rate\",\n description:\n \"The percentage of engaged sessions (sessions that lasted 10 seconds or longer, had a conversion event, or had 2 or more page/screen views).\",\n category: \"engagement\",\n format: \"percent\",\n apiField: \"engagementRate\",\n type: \"api\",\n },\n {\n key: \"screenPageViewsPerSession\",\n name: \"Pages per Session\",\n description:\n \"The average number of pages viewed per session.\",\n category: \"engagement\",\n format: \"ratio\",\n apiField: \"screenPageViewsPerSession\",\n type: \"api\",\n },\n {\n key: \"eventCount\",\n name: \"Event Count\",\n description:\n \"The total number of events triggered by users.\",\n category: \"engagement\",\n format: \"number\",\n apiField: \"eventCount\",\n type: \"api\",\n },\n {\n key: \"eventsPerSession\",\n name: \"Events per Session\",\n description:\n \"The average number of events triggered per session.\",\n category: \"engagement\",\n format: \"ratio\",\n apiField: \"eventsPerSession\",\n type: \"api\",\n },\n {\n key: \"userEngagementDuration\",\n name: \"User Engagement Duration\",\n description:\n \"The total amount of time (in seconds) your app was in the foreground or your website had focus in the browser.\",\n category: \"engagement\",\n format: \"duration\",\n apiField: \"userEngagementDuration\",\n type: \"api\",\n },\n {\n key: \"scrolledUsers\",\n name: \"Scrolled Users\",\n description:\n \"The number of unique users who scrolled down at least 90% of the page.\",\n category: \"engagement\",\n format: \"number\",\n apiField: \"scrolledUsers\",\n type: \"api\",\n },\n];\n\n// ============================================\n// REVENUE METRICS\n// ============================================\n\nconst REVENUE_METRICS: GA4MetricDefinition[] = [\n {\n key: \"totalRevenue\",\n name: \"Total Revenue\",\n description:\n \"The sum of revenue from purchases, subscriptions, and advertising (purchase revenue plus subscription revenue plus ad revenue).\",\n category: \"revenue\",\n format: \"currency\",\n apiField: \"totalRevenue\",\n type: \"api\",\n },\n {\n key: \"purchaseRevenue\",\n name: \"Purchase Revenue\",\n description:\n \"The sum of revenue from purchases made on your site or app.\",\n category: \"revenue\",\n format: \"currency\",\n apiField: \"purchaseRevenue\",\n type: \"api\",\n },\n {\n key: \"ecommercePurchases\",\n name: \"Purchases\",\n description:\n \"The number of times users completed a purchase.\",\n category: \"revenue\",\n format: \"number\",\n apiField: \"ecommercePurchases\",\n type: \"api\",\n },\n {\n key: \"transactions\",\n name: \"Transactions\",\n description:\n \"The count of transaction events with purchase revenue.\",\n category: \"revenue\",\n format: \"number\",\n apiField: \"transactions\",\n type: \"api\",\n },\n {\n key: \"averagePurchaseRevenue\",\n name: \"Avg. Purchase Revenue\",\n description:\n \"The average revenue per purchase (total purchase revenue divided by the number of purchases).\",\n category: \"revenue\",\n format: \"currency\",\n apiField: \"averagePurchaseRevenue\",\n type: \"api\",\n },\n {\n key: \"averageRevenuePerUser\",\n name: \"ARPU\",\n description:\n \"Average revenue per user (total revenue divided by total users).\",\n category: \"revenue\",\n format: \"currency\",\n apiField: \"averageRevenuePerUser\",\n type: \"api\",\n },\n {\n key: \"itemRevenue\",\n name: \"Item Revenue\",\n description:\n \"The total revenue from items only (product sales revenue).\",\n category: \"revenue\",\n format: \"currency\",\n apiField: \"itemRevenue\",\n type: \"api\",\n },\n {\n key: \"itemsPurchased\",\n name: \"Items Purchased\",\n description:\n \"The number of units purchased across all items.\",\n category: \"revenue\",\n format: \"number\",\n apiField: \"itemsPurchased\",\n type: \"api\",\n },\n {\n key: \"refundAmount\",\n name: \"Refund Amount\",\n description:\n \"The total amount of refunds issued.\",\n category: \"revenue\",\n format: \"currency\",\n apiField: \"refundAmount\",\n type: \"api\",\n },\n {\n key: \"shippingAmount\",\n name: \"Shipping Amount\",\n description:\n \"The total shipping amount associated with transactions.\",\n category: \"revenue\",\n format: \"currency\",\n apiField: \"shippingAmount\",\n type: \"api\",\n },\n {\n key: \"taxAmount\",\n name: \"Tax Amount\",\n description:\n \"The total tax amount associated with transactions.\",\n category: \"revenue\",\n format: \"currency\",\n apiField: \"taxAmount\",\n type: \"api\",\n },\n];\n\n// ============================================\n// ECOMMERCE METRICS\n// ============================================\n\nconst ECOMMERCE_METRICS: GA4MetricDefinition[] = [\n {\n key: \"addToCarts\",\n name: \"Add to Carts\",\n description:\n \"The number of times users added items to their shopping carts.\",\n category: \"ecommerce\",\n format: \"number\",\n apiField: \"addToCarts\",\n type: \"api\",\n },\n {\n key: \"checkouts\",\n name: \"Checkouts\",\n description:\n \"The number of times users started the checkout process.\",\n category: \"ecommerce\",\n format: \"number\",\n apiField: \"checkouts\",\n type: \"api\",\n },\n {\n key: \"itemsViewed\",\n name: \"Items Viewed\",\n description:\n \"The number of items viewed by users.\",\n category: \"ecommerce\",\n format: \"number\",\n apiField: \"itemsViewed\",\n type: \"api\",\n },\n {\n key: \"itemsAddedToCart\",\n name: \"Items Added to Cart\",\n description:\n \"The number of units added to cart across all items.\",\n category: \"ecommerce\",\n format: \"number\",\n apiField: \"itemsAddedToCart\",\n type: \"api\",\n },\n {\n key: \"itemsCheckedOut\",\n name: \"Items Checked Out\",\n description:\n \"The number of units included in checkout across all items.\",\n category: \"ecommerce\",\n format: \"number\",\n apiField: \"itemsCheckedOut\",\n type: \"api\",\n },\n {\n key: \"cartToViewRate\",\n name: \"Cart-to-View Rate\",\n description:\n \"The percentage of users who added items to their cart after viewing them.\",\n category: \"ecommerce\",\n format: \"percent\",\n apiField: \"cartToViewRate\",\n type: \"api\",\n },\n {\n key: \"purchaseToViewRate\",\n name: \"Purchase-to-View Rate\",\n description:\n \"The percentage of users who purchased items after viewing them.\",\n category: \"ecommerce\",\n format: \"percent\",\n apiField: \"purchaseToViewRate\",\n type: \"api\",\n },\n {\n key: \"itemViewEvents\",\n name: \"Item View Events\",\n description:\n \"The number of times item details were viewed (view_item events).\",\n category: \"ecommerce\",\n format: \"number\",\n apiField: \"itemViewEvents\",\n type: \"api\",\n },\n {\n key: \"itemListClickEvents\",\n name: \"Item List Click Events\",\n description:\n \"The number of times users clicked an item in an item list (select_item events).\",\n category: \"ecommerce\",\n format: \"number\",\n apiField: \"itemListClickEvents\",\n type: \"api\",\n },\n {\n key: \"itemListViewEvents\",\n name: \"Item List View Events\",\n description:\n \"The number of times an item list was viewed (view_item_list events).\",\n category: \"ecommerce\",\n format: \"number\",\n apiField: \"itemListViewEvents\",\n type: \"api\",\n },\n {\n key: \"itemListClickThroughRate\",\n name: \"Item List CTR\",\n description:\n \"The percentage of users who clicked through after viewing an item list.\",\n category: \"ecommerce\",\n format: \"percent\",\n apiField: \"itemListClickThroughRate\",\n type: \"api\",\n },\n {\n key: \"itemPromotionClickThroughRate\",\n name: \"Promotion CTR\",\n description:\n \"The percentage of users who clicked through after viewing an item promotion.\",\n category: \"ecommerce\",\n format: \"percent\",\n apiField: \"itemPromotionClickThroughRate\",\n type: \"api\",\n },\n];\n\n// ============================================\n// CONVERSION METRICS\n// ============================================\n\nconst CONVERSION_METRICS: GA4MetricDefinition[] = [\n {\n key: \"conversions\",\n name: \"Conversions\",\n description:\n \"The total count of conversion events.\",\n category: \"conversions\",\n format: \"number\",\n apiField: \"conversions\",\n type: \"api\",\n },\n {\n key: \"keyEvents\",\n name: \"Key Events\",\n description:\n \"The count of key events (formerly conversions) triggered by users.\",\n category: \"conversions\",\n format: \"number\",\n apiField: \"keyEvents\",\n type: \"api\",\n },\n {\n key: \"firstTimePurchasers\",\n name: \"First-time Purchasers\",\n description:\n \"The number of users who completed their first purchase.\",\n category: \"conversions\",\n format: \"number\",\n apiField: \"firstTimePurchasers\",\n type: \"api\",\n },\n {\n key: \"firstTimePurchaserRate\",\n name: \"First-time Purchaser Rate\",\n description:\n \"The percentage of active users who made their first purchase.\",\n category: \"conversions\",\n format: \"percent\",\n apiField: \"firstTimePurchaserConversionRate\",\n type: \"api\",\n },\n];\n\n// ============================================\n// ADS METRICS\n// ============================================\n\nconst ADS_METRICS: GA4MetricDefinition[] = [\n {\n key: \"publisherAdClicks\",\n name: \"Publisher Ad Clicks\",\n description:\n \"The number of times users clicked on an ad served by a publisher on your site or app.\",\n category: \"ads\",\n format: \"number\",\n apiField: \"publisherAdClicks\",\n type: \"api\",\n },\n {\n key: \"publisherAdImpressions\",\n name: \"Publisher Ad Impressions\",\n description:\n \"The number of ad impressions served by a publisher on your site or app.\",\n category: \"ads\",\n format: \"number\",\n apiField: \"publisherAdImpressions\",\n type: \"api\",\n },\n {\n key: \"totalAdRevenue\",\n name: \"Total Ad Revenue\",\n description:\n \"The total revenue earned from ads served on your site or app.\",\n category: \"ads\",\n format: \"currency\",\n apiField: \"totalAdRevenue\",\n type: \"api\",\n },\n {\n key: \"returnOnAdSpend\",\n name: \"Return on Ad Spend\",\n description:\n \"The return on ad spend (total revenue divided by advertiser ad cost).\",\n category: \"ads\",\n format: \"ratio\",\n apiField: \"returnOnAdSpend\",\n type: \"api\",\n },\n];\n\n// ============================================\n// USER METRICS\n// ============================================\n\nconst USER_METRICS: GA4MetricDefinition[] = [\n {\n key: \"dauPerMau\",\n name: \"DAU/MAU\",\n description:\n \"The rolling ratio of daily active users to monthly active users. A measure of user stickiness.\",\n category: \"user\",\n format: \"ratio\",\n apiField: \"dauPerMau\",\n type: \"api\",\n },\n {\n key: \"dauPerWau\",\n name: \"DAU/WAU\",\n description:\n \"The rolling ratio of daily active users to weekly active users.\",\n category: \"user\",\n format: \"ratio\",\n apiField: \"dauPerWau\",\n type: \"api\",\n },\n {\n key: \"wauPerMau\",\n name: \"WAU/MAU\",\n description:\n \"The rolling ratio of weekly active users to monthly active users.\",\n category: \"user\",\n format: \"ratio\",\n apiField: \"wauPerMau\",\n type: \"api\",\n },\n {\n key: \"crashFreeUsersRate\",\n name: \"Crash-free Users Rate\",\n description:\n \"The percentage of users who did not experience a crash during the selected time period.\",\n category: \"user\",\n format: \"percent\",\n apiField: \"crashFreeUsersRate\",\n type: \"api\",\n },\n];\n\n// ============================================\n// COMBINE ALL METRICS\n// ============================================\n\nexport const GA4_METRIC_CATALOG: GA4MetricDefinition[] = [\n ...TRAFFIC_METRICS,\n ...ENGAGEMENT_METRICS,\n ...REVENUE_METRICS,\n ...ECOMMERCE_METRICS,\n ...CONVERSION_METRICS,\n ...ADS_METRICS,\n ...USER_METRICS,\n];\n\n// ============================================\n// HELPER FUNCTIONS\n// ============================================\n\n/**\n * Find a metric definition by its key or apiField name.\n */\nexport function getMetricByKey(\n key: string,\n): GA4MetricDefinition | undefined {\n return GA4_METRIC_CATALOG.find((m) => m.key === key || m.apiField === key);\n}\n","/**\n * google-analytics-mcp-server: an open-source MCP server for Google Analytics 4.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\n// ============================================\n// GOOGLE ANALYTICS 4 DIMENSION CATALOG\n// Complete catalog of GA4 dimensions\n// Organized by category for the Data API\n// ============================================\n\nimport type { GA4DimensionDefinition } from \"./types.js\";\n\n// ============================================\n// TIME DIMENSIONS\n// ============================================\n\nconst TIME_DIMENSIONS: GA4DimensionDefinition[] = [\n {\n key: \"date\",\n name: \"Date\",\n description: \"The date of the event (YYYYMMDD format)\",\n category: \"time\",\n apiField: \"date\",\n },\n {\n key: \"dateHour\",\n name: \"Date + Hour\",\n description: \"The combined date and hour of the event (YYYYMMDDHH format)\",\n category: \"time\",\n apiField: \"dateHour\",\n },\n {\n key: \"dayOfWeek\",\n name: \"Day of Week\",\n description: \"The day of the week as a number (0 = Sunday through 6 = Saturday)\",\n category: \"time\",\n apiField: \"dayOfWeek\",\n },\n {\n key: \"dayOfWeekName\",\n name: \"Day Name\",\n description: \"The name of the day of the week (e.g., Sunday, Monday)\",\n category: \"time\",\n apiField: \"dayOfWeekName\",\n },\n {\n key: \"month\",\n name: \"Month\",\n description: \"The month of the event (two-digit number, 01-12)\",\n category: \"time\",\n apiField: \"month\",\n },\n {\n key: \"year\",\n name: \"Year\",\n description: \"The four-digit year of the event (e.g., 2026)\",\n category: \"time\",\n apiField: \"year\",\n },\n {\n key: \"nthDay\",\n name: \"Nth Day\",\n description: \"The number of days since the start of the date range\",\n category: \"time\",\n apiField: \"nthDay\",\n },\n {\n key: \"hour\",\n name: \"Hour\",\n description: \"The hour of the event (two-digit number, 00-23)\",\n category: \"time\",\n apiField: \"hour\",\n },\n {\n key: \"isoWeek\",\n name: \"ISO Week\",\n description: \"The ISO week number of the year (01-53)\",\n category: \"time\",\n apiField: \"isoWeek\",\n },\n {\n key: \"isoYearIsoWeek\",\n name: \"Year + ISO Week\",\n description: \"The combined ISO year and ISO week number (e.g., 202610)\",\n category: \"time\",\n apiField: \"isoYearIsoWeek\",\n },\n {\n key: \"yearMonth\",\n name: \"Year + Month\",\n description: \"The combined year and month of the event (YYYYMM format)\",\n category: \"time\",\n apiField: \"yearMonth\",\n },\n];\n\n// ============================================\n// TRAFFIC SOURCE DIMENSIONS\n// ============================================\n\nconst TRAFFIC_SOURCE_DIMENSIONS: GA4DimensionDefinition[] = [\n {\n key: \"sessionSource\",\n name: \"Session Source\",\n description: \"The source that initiated the session (e.g., google, facebook, direct)\",\n category: \"traffic_source\",\n apiField: \"sessionSource\",\n },\n {\n key: \"sessionMedium\",\n name: \"Session Medium\",\n description: \"The medium of the session (e.g., organic, cpc, referral, email)\",\n category: \"traffic_source\",\n apiField: \"sessionMedium\",\n },\n {\n key: \"sessionCampaignName\",\n name: \"Session Campaign\",\n description: \"The campaign name associated with the session\",\n category: \"traffic_source\",\n apiField: \"sessionCampaignName\",\n },\n {\n key: \"sessionDefaultChannelGroup\",\n name: \"Session Default Channel Group\",\n description: \"The default channel grouping for the session (e.g., Organic Search, Paid Search, Direct)\",\n category: \"traffic_source\",\n apiField: \"sessionDefaultChannelGroup\",\n },\n {\n key: \"sessionPrimaryChannelGroup\",\n name: \"Session Primary Channel Group (Custom)\",\n description: \"The custom/primary channel group for the session. Uses custom channel group rules configured in GA4 Admin. Use this dimension (not sessionDefaultChannelGroup) when the user asks for custom or personalized channel groups.\",\n category: \"traffic_source\",\n apiField: \"sessionPrimaryChannelGroup\",\n },\n {\n key: \"sessionSourceMedium\",\n name: \"Session Source / Medium\",\n description: \"The combined source and medium of the session (e.g., google / organic)\",\n category: \"traffic_source\",\n apiField: \"sessionSourceMedium\",\n },\n {\n key: \"firstUserSource\",\n name: \"First User Source\",\n description: \"The source that first acquired the user\",\n category: \"traffic_source\",\n apiField: \"firstUserSource\",\n },\n {\n key: \"firstUserMedium\",\n name: \"First User Medium\",\n description: \"The medium that first acquired the user\",\n category: \"traffic_source\",\n apiField: \"firstUserMedium\",\n },\n {\n key: \"firstUserCampaignName\",\n name: \"First User Campaign\",\n description: \"The campaign that first acquired the user\",\n category: \"traffic_source\",\n apiField: \"firstUserCampaignName\",\n },\n {\n key: \"firstUserDefaultChannelGroup\",\n name: \"First User Default Channel Group\",\n description: \"The default channel grouping that first acquired the user\",\n category: \"traffic_source\",\n apiField: \"firstUserDefaultChannelGroup\",\n },\n {\n key: \"firstUserPrimaryChannelGroup\",\n name: \"First User Primary Channel Group (Custom)\",\n description: \"The custom/primary channel group that first acquired the user. Uses custom channel group rules configured in GA4 Admin.\",\n category: \"traffic_source\",\n apiField: \"firstUserPrimaryChannelGroup\",\n },\n {\n key: \"firstUserSourceMedium\",\n name: \"First User Source / Medium\",\n description: \"The combined source and medium that first acquired the user\",\n category: \"traffic_source\",\n apiField: \"firstUserSourceMedium\",\n },\n];\n\n// ============================================\n// GEOGRAPHY DIMENSIONS\n// ============================================\n\nconst GEOGRAPHY_DIMENSIONS: GA4DimensionDefinition[] = [\n {\n key: \"country\",\n name: \"Country\",\n description: \"The country from which the user activity originated\",\n category: \"geography\",\n apiField: \"country\",\n },\n {\n key: \"city\",\n name: \"City\",\n description: \"The city from which the user activity originated\",\n category: \"geography\",\n apiField: \"city\",\n },\n {\n key: \"region\",\n name: \"Region\",\n description: \"The geographic region (state/province) from which the user activity originated\",\n category: \"geography\",\n apiField: \"region\",\n },\n {\n key: \"continent\",\n name: \"Continent\",\n description: \"The continent from which the user activity originated\",\n category: \"geography\",\n apiField: \"continent\",\n },\n {\n key: \"countryId\",\n name: \"Country ID\",\n description: \"The ISO 3166 country code (e.g., US, GB, FR)\",\n category: \"geography\",\n apiField: \"countryId\",\n },\n {\n key: \"subContinent\",\n name: \"Sub Continent\",\n description: \"The sub-continent from which the user activity originated (e.g., Northern America, Western Europe)\",\n category: \"geography\",\n apiField: \"subContinent\",\n },\n];\n\n// ============================================\n// DEVICE DIMENSIONS\n// ============================================\n\nconst DEVICE_DIMENSIONS: GA4DimensionDefinition[] = [\n {\n key: \"deviceCategory\",\n name: \"Device Category\",\n description: \"The type of device (desktop, mobile, or tablet)\",\n category: \"device\",\n apiField: \"deviceCategory\",\n },\n {\n key: \"operatingSystem\",\n name: \"Operating System\",\n description: \"The operating system used by the visitor (e.g., Android, iOS, Windows, Macintosh)\",\n category: \"device\",\n apiField: \"operatingSystem\",\n },\n {\n key: \"browser\",\n name: \"Browser\",\n description: \"The browser used by the visitor (e.g., Chrome, Safari, Firefox, Edge)\",\n category: \"device\",\n apiField: \"browser\",\n },\n {\n key: \"screenResolution\",\n name: \"Screen Resolution\",\n description: \"The screen resolution of the user's device (e.g., 1920x1080)\",\n category: \"device\",\n apiField: \"screenResolution\",\n },\n {\n key: \"mobileDeviceModel\",\n name: \"Mobile Device Model\",\n description: \"The model name of the mobile device (e.g., iPhone 15, Pixel 8)\",\n category: \"device\",\n apiField: \"mobileDeviceModel\",\n },\n {\n key: \"mobileDeviceBranding\",\n name: \"Mobile Device Brand\",\n description: \"The brand or manufacturer of the mobile device (e.g., Apple, Samsung, Google)\",\n category: \"device\",\n apiField: \"mobileDeviceBranding\",\n },\n {\n key: \"platform\",\n name: \"Platform\",\n description: \"The platform on which the app or website was accessed (web, iOS, Android)\",\n category: \"device\",\n apiField: \"platform\",\n },\n {\n key: \"language\",\n name: \"Language\",\n description: \"The language setting of the user's browser or device (e.g., en-us, fr, de)\",\n category: \"device\",\n apiField: \"language\",\n },\n];\n\n// ============================================\n// PAGE DIMENSIONS\n// ============================================\n\nconst PAGE_DIMENSIONS: GA4DimensionDefinition[] = [\n {\n key: \"pagePath\",\n name: \"Page Path\",\n description: \"The URL path of the page (without query string or hostname)\",\n category: \"page\",\n apiField: \"pagePath\",\n },\n {\n key: \"pageTitle\",\n name: \"Page Title\",\n description: \"The title of the page as set in the HTML <title> tag\",\n category: \"page\",\n apiField: \"pageTitle\",\n },\n {\n key: \"landingPage\",\n name: \"Landing Page\",\n description: \"The page path of the first page viewed in a session\",\n category: \"page\",\n apiField: \"landingPage\",\n },\n {\n key: \"hostname\",\n name: \"Hostname\",\n description: \"The hostname of the URL (e.g., www.example.com)\",\n category: \"page\",\n apiField: \"hostname\",\n },\n {\n key: \"pageReferrer\",\n name: \"Page Referrer\",\n description: \"The full referring URL including hostname and path\",\n category: \"page\",\n apiField: \"pageReferrer\",\n },\n {\n key: \"landingPagePlusQueryString\",\n name: \"Landing Page + Query\",\n description: \"The landing page path including the query string parameters\",\n category: \"page\",\n apiField: \"landingPagePlusQueryString\",\n },\n {\n key: \"pagePathPlusQueryString\",\n name: \"Page Path + Query\",\n description: \"The page path including the query string parameters\",\n category: \"page\",\n apiField: \"pagePathPlusQueryString\",\n },\n];\n\n// ============================================\n// EVENT DIMENSIONS\n// ============================================\n\nconst EVENT_DIMENSIONS: GA4DimensionDefinition[] = [\n {\n key: \"eventName\",\n name: \"Event Name\",\n description: \"The name of the event (e.g., page_view, purchase, click, scroll)\",\n category: \"event\",\n apiField: \"eventName\",\n },\n {\n key: \"isConversionEvent\",\n name: \"Is Conversion Event\",\n description: \"Whether the event is marked as a conversion (true or false)\",\n category: \"event\",\n apiField: \"isConversionEvent\",\n },\n];\n\n// ============================================\n// ECOMMERCE DIMENSIONS\n// ============================================\n\nconst ECOMMERCE_DIMENSIONS: GA4DimensionDefinition[] = [\n {\n key: \"itemId\",\n name: \"Item ID\",\n description: \"The ID of the ecommerce item\",\n category: \"ecommerce\",\n apiField: \"itemId\",\n },\n {\n key: \"itemName\",\n name: \"Item Name\",\n description: \"The name of the ecommerce item\",\n category: \"ecommerce\",\n apiField: \"itemName\",\n },\n {\n key: \"itemBrand\",\n name: \"Item Brand\",\n description: \"The brand of the ecommerce item\",\n category: \"ecommerce\",\n apiField: \"itemBrand\",\n },\n {\n key: \"itemCategory\",\n name: \"Item Category\",\n description: \"The primary category of the ecommerce item\",\n category: \"ecommerce\",\n apiField: \"itemCategory\",\n },\n {\n key: \"itemCategory2\",\n name: \"Item Category 2\",\n description: \"The second-level category of the ecommerce item\",\n category: \"ecommerce\",\n apiField: \"itemCategory2\",\n },\n {\n key: \"itemCategory3\",\n name: \"Item Category 3\",\n description: \"The third-level category of the ecommerce item\",\n category: \"ecommerce\",\n apiField: \"itemCategory3\",\n },\n {\n key: \"itemVariant\",\n name: \"Item Variant\",\n description: \"The variant of the ecommerce item (e.g., size, color)\",\n category: \"ecommerce\",\n apiField: \"itemVariant\",\n },\n {\n key: \"itemListName\",\n name: \"Item List Name\",\n description: \"The name of the list in which the item was presented to the user\",\n category: \"ecommerce\",\n apiField: \"itemListName\",\n },\n {\n key: \"itemPromotionName\",\n name: \"Item Promotion Name\",\n description: \"The name of the promotion applied to the ecommerce item\",\n category: \"ecommerce\",\n apiField: \"itemPromotionName\",\n },\n {\n key: \"transactionId\",\n name: \"Transaction ID\",\n description: \"The unique identifier of the ecommerce transaction\",\n category: \"ecommerce\",\n apiField: \"transactionId\",\n },\n {\n key: \"orderCoupon\",\n name: \"Order Coupon\",\n description: \"The coupon code applied to the order\",\n category: \"ecommerce\",\n apiField: \"orderCoupon\",\n },\n {\n key: \"shippingTier\",\n name: \"Shipping Tier\",\n description: \"The shipping tier selected for the order (e.g., Ground, Express, Next Day)\",\n category: \"ecommerce\",\n apiField: \"shippingTier\",\n },\n];\n\n// ============================================\n// USER DIMENSIONS\n// ============================================\n\nconst USER_DIMENSIONS: GA4DimensionDefinition[] = [\n {\n key: \"newVsReturning\",\n name: \"New vs Returning\",\n description: \"Whether the user is new or returning to the property\",\n category: \"user\",\n apiField: \"newVsReturning\",\n },\n {\n key: \"userAgeBracket\",\n name: \"User Age Bracket\",\n description: \"The age bracket of the user (e.g., 18-24, 25-34, 35-44)\",\n category: \"user\",\n apiField: \"userAgeBracket\",\n },\n {\n key: \"userGender\",\n name: \"User Gender\",\n description: \"The gender of the user (male, female)\",\n category: \"user\",\n apiField: \"userGender\",\n },\n {\n key: \"audienceName\",\n name: \"Audience Name\",\n description: \"The name of the audience the user belongs to\",\n category: \"user\",\n apiField: \"audienceName\",\n },\n];\n\n// ============================================\n// COMBINE ALL DIMENSIONS\n// ============================================\n\nexport const GA4_DIMENSION_CATALOG: GA4DimensionDefinition[] = [\n ...TIME_DIMENSIONS,\n ...TRAFFIC_SOURCE_DIMENSIONS,\n ...GEOGRAPHY_DIMENSIONS,\n ...DEVICE_DIMENSIONS,\n ...PAGE_DIMENSIONS,\n ...EVENT_DIMENSIONS,\n ...ECOMMERCE_DIMENSIONS,\n ...USER_DIMENSIONS,\n];\n\n// ============================================\n// HELPER FUNCTIONS\n// ============================================\n\n/**\n * Get a dimension definition by its key or apiField\n */\nexport function getDimensionByKey(key: string): GA4DimensionDefinition | undefined {\n return GA4_DIMENSION_CATALOG.find((d) => d.key === key || d.apiField === key);\n}\n","/**\n * google-analytics-mcp-server: an open-source MCP server for Google Analytics 4.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\n// ============================================\n// GA4 COMPATIBILITY RULES\n// Validation logic for metric/dimension combinations\n// ============================================\n\n// ============================================\n// DIMENSION-METRIC COMPATIBILITY\n// ============================================\n\n/**\n * Ecommerce dimensions that only work with ecommerce metrics.\n * Using these dimensions without ecommerce metrics returns empty rows.\n */\nexport const ECOMMERCE_ONLY_DIMENSIONS = new Set([\n \"itemId\",\n \"itemName\",\n \"itemBrand\",\n \"itemCategory\",\n \"itemCategory2\",\n \"itemCategory3\",\n \"itemVariant\",\n \"itemListName\",\n \"itemListId\",\n \"itemPromotionName\",\n \"itemPromotionId\",\n \"orderCoupon\",\n \"shippingTier\",\n \"itemAffiliation\",\n \"transactionId\",\n]);\n\n/**\n * Ecommerce metrics that work with ecommerce dimensions.\n */\nexport const ECOMMERCE_METRICS = new Set([\n \"addToCarts\",\n \"checkouts\",\n \"ecommercePurchases\",\n \"itemRevenue\",\n \"itemsPurchased\",\n \"itemsViewed\",\n \"itemsAddedToCart\",\n \"itemsCheckedOut\",\n \"cartToViewRate\",\n \"purchaseToViewRate\",\n \"itemViewEvents\",\n \"itemListClickEvents\",\n \"itemListViewEvents\",\n \"itemListClickThroughRate\",\n \"itemPromotionClickThroughRate\",\n \"totalRevenue\",\n \"purchaseRevenue\",\n \"averagePurchaseRevenue\",\n \"transactions\",\n \"refundAmount\",\n \"shippingAmount\",\n \"taxAmount\",\n]);\n\n/**\n * Dimensions that cannot be combined with each other (mutually exclusive groups).\n * GA4 is generally permissive, but some combinations produce empty results.\n */\nexport const INCOMPATIBLE_DIMENSION_PAIRS: Array<[string, string]> = [\n // Session-scoped vs user-scoped source dimensions\n [\"sessionSource\", \"firstUserSource\"],\n [\"sessionMedium\", \"firstUserMedium\"],\n [\"sessionCampaignName\", \"firstUserCampaignName\"],\n [\"sessionDefaultChannelGroup\", \"firstUserDefaultChannelGroup\"],\n];\n\n// ============================================\n// VALIDATION FUNCTIONS\n// ============================================\n\nexport interface GA4ValidationResult {\n valid: boolean;\n errors: string[];\n warnings: string[];\n}\n\n/**\n * Validate a GA4 query selection (metrics + dimensions)\n */\nexport function validateGA4QuerySelection(\n metricKeys: string[],\n dimensionKeys: string[]\n): GA4ValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n // 1. Check ecommerce dimensions without ecommerce metrics\n const hasEcommerceDimensions = dimensionKeys.some((d) => ECOMMERCE_ONLY_DIMENSIONS.has(d));\n const hasEcommerceMetrics = metricKeys.some((m) => ECOMMERCE_METRICS.has(m));\n\n if (hasEcommerceDimensions && !hasEcommerceMetrics) {\n warnings.push(\n \"Ecommerce dimensions (itemName, itemBrand, etc.) require ecommerce metrics to return data. Add metrics like itemRevenue, addToCarts, or ecommercePurchases.\"\n );\n }\n\n // 2. Check incompatible dimension pairs\n for (const [dim1, dim2] of INCOMPATIBLE_DIMENSION_PAIRS) {\n if (dimensionKeys.includes(dim1) && dimensionKeys.includes(dim2)) {\n warnings.push(\n `Dimensions \"${dim1}\" and \"${dim2}\" are session-scoped vs user-scoped. Using both may produce unexpected results.`\n );\n }\n }\n\n // 3. Warn about high-cardinality combinations\n const highCardinalityDims = [\"pagePath\", \"pageTitle\", \"pagePathPlusQueryString\", \"landingPagePlusQueryString\"];\n const hasHighCardinality = dimensionKeys.some((d) => highCardinalityDims.includes(d));\n if (hasHighCardinality && dimensionKeys.length > 2) {\n warnings.push(\n \"High-cardinality dimensions (pagePath, pageTitle) combined with other dimensions may produce very large result sets.\"\n );\n }\n\n // 4. Must have at least one metric\n if (metricKeys.length === 0) {\n errors.push(\"At least one metric is required.\");\n }\n\n // 5. GA4 limit: max 9 dimensions per request\n if (dimensionKeys.length > 9) {\n errors.push(\n `GA4 supports a maximum of 9 dimensions per request. You selected ${dimensionKeys.length}.`\n );\n }\n\n // 6. GA4 limit: max 10 metrics per request\n if (metricKeys.length > 10) {\n errors.push(\n `GA4 supports a maximum of 10 metrics per request. You selected ${metricKeys.length}.`\n );\n }\n\n // 7. No time dimension selected\n const timeDimensions = [\"date\", \"dateHour\", \"dayOfWeek\", \"month\", \"year\", \"hour\", \"nthDay\", \"isoWeek\", \"yearMonth\"];\n if (!dimensionKeys.some((d) => timeDimensions.includes(d))) {\n warnings.push(\n \"No time dimension selected. Results will be aggregated over the entire date range.\"\n );\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n}\n","/**\n * google-analytics-mcp-server: an open-source MCP server for Google Analytics 4.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\n// ============================================\n// GA4 CALCULATED METRICS\n// Client-side metric computation\n// ============================================\n\nimport type { GA4InsightRow, GA4MetricDefinition } from \"./types.js\";\n\n// ============================================\n// CALCULATED METRIC DEFINITIONS\n// ============================================\n\nexport const GA4_CALCULATED_METRIC_DEFINITIONS: GA4MetricDefinition[] = [\n {\n key: \"revenuePerSession\",\n name: \"Revenue per Session\",\n description: \"Total revenue divided by total sessions\",\n category: \"calculated\",\n format: \"currency\",\n apiField: \"\",\n type: \"calculated\",\n formula: \"totalRevenue / sessions\",\n dependencies: [\"totalRevenue\", \"sessions\"],\n },\n {\n key: \"conversionRate\",\n name: \"Conversion Rate\",\n description: \"Conversions divided by sessions (percentage)\",\n category: \"calculated\",\n format: \"percent\",\n apiField: \"\",\n type: \"calculated\",\n formula: \"(conversions / sessions) * 100\",\n dependencies: [\"conversions\", \"sessions\"],\n },\n {\n key: \"pagesPerSession\",\n name: \"Pages per Session\",\n description: \"Page views divided by sessions\",\n category: \"calculated\",\n format: \"ratio\",\n apiField: \"\",\n type: \"calculated\",\n formula: \"screenPageViews / sessions\",\n dependencies: [\"screenPageViews\", \"sessions\"],\n },\n {\n key: \"aov\",\n name: \"Average Order Value\",\n description: \"Purchase revenue divided by number of purchases\",\n category: \"calculated\",\n format: \"currency\",\n apiField: \"\",\n type: \"calculated\",\n formula: \"purchaseRevenue / ecommercePurchases\",\n dependencies: [\"purchaseRevenue\", \"ecommercePurchases\"],\n },\n {\n key: \"revenuePerUser\",\n name: \"Revenue per User\",\n description: \"Total revenue divided by total users\",\n category: \"calculated\",\n format: \"currency\",\n apiField: \"\",\n type: \"calculated\",\n formula: \"totalRevenue / totalUsers\",\n dependencies: [\"totalRevenue\", \"totalUsers\"],\n },\n {\n key: \"addToCartRate\",\n name: \"Add to Cart Rate\",\n description: \"Add to carts divided by sessions (percentage)\",\n category: \"calculated\",\n format: \"percent\",\n apiField: \"\",\n type: \"calculated\",\n formula: \"(addToCarts / sessions) * 100\",\n dependencies: [\"addToCarts\", \"sessions\"],\n },\n {\n key: \"checkoutRate\",\n name: \"Checkout Rate\",\n description: \"Checkouts divided by sessions (percentage)\",\n category: \"calculated\",\n format: \"percent\",\n apiField: \"\",\n type: \"calculated\",\n formula: \"(checkouts / sessions) * 100\",\n dependencies: [\"checkouts\", \"sessions\"],\n },\n {\n key: \"purchaseRate\",\n name: \"Purchase Rate\",\n description: \"Purchases divided by sessions (percentage)\",\n category: \"calculated\",\n format: \"percent\",\n apiField: \"\",\n type: \"calculated\",\n formula: \"(ecommercePurchases / sessions) * 100\",\n dependencies: [\"ecommercePurchases\", \"sessions\"],\n },\n {\n key: \"cartAbandonmentRate\",\n name: \"Cart Abandonment Rate\",\n description: \"Percentage of checkouts not completed as purchases\",\n category: \"calculated\",\n format: \"percent\",\n apiField: \"\",\n type: \"calculated\",\n formula: \"(1 - ecommercePurchases / checkouts) * 100\",\n dependencies: [\"ecommercePurchases\", \"checkouts\"],\n },\n];\n\n// ============================================\n// INDIVIDUAL METRIC CALCULATORS\n// ============================================\n\nfunction calcRevenuePerSession(row: GA4InsightRow): number | null {\n const revenue = row[\"totalRevenue\"];\n const sessions = row[\"sessions\"];\n if (typeof revenue !== \"number\" || typeof sessions !== \"number\") return null;\n if (sessions <= 0) return null;\n return revenue / sessions;\n}\n\nfunction calcConversionRate(row: GA4InsightRow): number | null {\n const conversions = row[\"conversions\"] ?? row[\"keyEvents\"];\n const sessions = row[\"sessions\"];\n if (typeof conversions !== \"number\" || typeof sessions !== \"number\") return null;\n if (sessions <= 0) return null;\n return (conversions / sessions) * 100;\n}\n\nfunction calcPagesPerSession(row: GA4InsightRow): number | null {\n const pageViews = row[\"screenPageViews\"];\n const sessions = row[\"sessions\"];\n if (typeof pageViews !== \"number\" || typeof sessions !== \"number\") return null;\n if (sessions <= 0) return null;\n return pageViews / sessions;\n}\n\nfunction calcAov(row: GA4InsightRow): number | null {\n const revenue = row[\"purchaseRevenue\"] ?? row[\"totalRevenue\"];\n const purchases = row[\"ecommercePurchases\"] ?? row[\"transactions\"];\n if (typeof revenue !== \"number\" || typeof purchases !== \"number\") return null;\n if (purchases <= 0) return null;\n return revenue / purchases;\n}\n\nfunction calcRevenuePerUser(row: GA4InsightRow): number | null {\n const revenue = row[\"totalRevenue\"];\n const users = row[\"totalUsers\"];\n if (typeof revenue !== \"number\" || typeof users !== \"number\") return null;\n if (users <= 0) return null;\n return revenue / users;\n}\n\nfunction calcAddToCartRate(row: GA4InsightRow): number | null {\n const addToCarts = row[\"addToCarts\"];\n const sessions = row[\"sessions\"];\n if (typeof addToCarts !== \"number\" || typeof sessions !== \"number\") return null;\n if (sessions <= 0) return null;\n return (addToCarts / sessions) * 100;\n}\n\nfunction calcCheckoutRate(row: GA4InsightRow): number | null {\n const checkouts = row[\"checkouts\"];\n const sessions = row[\"sessions\"];\n if (typeof checkouts !== \"number\" || typeof sessions !== \"number\") return null;\n if (sessions <= 0) return null;\n return (checkouts / sessions) * 100;\n}\n\nfunction calcPurchaseRate(row: GA4InsightRow): number | null {\n const purchases = row[\"ecommercePurchases\"];\n const sessions = row[\"sessions\"];\n if (typeof purchases !== \"number\" || typeof sessions !== \"number\") return null;\n if (sessions <= 0) return null;\n return (purchases / sessions) * 100;\n}\n\nfunction calcCartAbandonmentRate(row: GA4InsightRow): number | null {\n const purchases = row[\"ecommercePurchases\"];\n const checkouts = row[\"checkouts\"];\n if (typeof purchases !== \"number\" || typeof checkouts !== \"number\") return null;\n if (checkouts <= 0) return null;\n return (1 - purchases / checkouts) * 100;\n}\n\n// ============================================\n// MAIN CALCULATION FUNCTION\n// ============================================\n\n/**\n * Calculate all custom metrics for a single row\n */\nexport function calculateMetrics(\n row: GA4InsightRow\n): Record<string, number | null> {\n return {\n revenuePerSession: calcRevenuePerSession(row),\n conversionRate: calcConversionRate(row),\n pagesPerSession: calcPagesPerSession(row),\n aov: calcAov(row),\n revenuePerUser: calcRevenuePerUser(row),\n addToCartRate: calcAddToCartRate(row),\n checkoutRate: calcCheckoutRate(row),\n purchaseRate: calcPurchaseRate(row),\n cartAbandonmentRate: calcCartAbandonmentRate(row),\n };\n}\n\n/**\n * Enrich rows with calculated metrics\n */\nexport function enrichWithCalculatedMetrics(\n rows: GA4InsightRow[],\n requestedCalculated?: string[]\n): GA4InsightRow[] {\n const requested = requestedCalculated ? new Set(requestedCalculated) : null;\n\n return rows.map((row) => {\n const all = calculateMetrics(row);\n const picked: Record<string, string | number | null> = {};\n\n for (const [key, value] of Object.entries(all)) {\n if (value === null) continue;\n if (requested && !requested.has(key)) continue;\n picked[key] = Math.round(value * 100) / 100;\n }\n\n return { ...row, ...picked };\n });\n}\n\n/**\n * Get dependencies for calculated metrics\n */\nexport function getCalculatedMetricDependencies(metricKeys: string[]): string[] {\n const deps = new Set<string>();\n\n for (const key of metricKeys) {\n const def = GA4_CALCULATED_METRIC_DEFINITIONS.find((m) => m.key === key);\n if (def?.dependencies) {\n for (const dep of def.dependencies) {\n deps.add(dep);\n }\n }\n }\n\n return Array.from(deps);\n}\n\n/**\n * Check if a metric key is a calculated metric\n */\nexport function isCalculatedMetric(key: string): boolean {\n return GA4_CALCULATED_METRIC_DEFINITIONS.some((m) => m.key === key);\n}\n","/**\n * google-analytics-mcp-server: an open-source MCP server for Google Analytics 4.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\n// ============================================\n// GA4 FILTER CATALOG\n// Definitions for GA4 dimension and metric filters\n// ============================================\n\nimport type { GA4FilterDefinition, GA4FilterExpression } from \"./types.js\";\n\n// ============================================\n// FILTER CATALOG\n// ============================================\n\nexport const GA4_FILTER_CATALOG: GA4FilterDefinition[] = [\n // ========================================\n // TRAFFIC SOURCE FILTERS\n // ========================================\n {\n key: \"sessionSource\",\n name: \"Session Source\",\n description: \"Filter by traffic source (e.g., google, facebook, direct)\",\n apiField: \"sessionSource\",\n type: \"string\",\n category: \"traffic_source\",\n },\n {\n key: \"sessionMedium\",\n name: \"Session Medium\",\n description: \"Filter by traffic medium (e.g., organic, cpc, referral)\",\n apiField: \"sessionMedium\",\n type: \"string\",\n category: \"traffic_source\",\n },\n {\n key: \"sessionCampaignName\",\n name: \"Session Campaign\",\n description: \"Filter by campaign name\",\n apiField: \"sessionCampaignName\",\n type: \"string\",\n category: \"traffic_source\",\n },\n {\n key: \"sessionDefaultChannelGroup\",\n name: \"Default Channel Group\",\n description: \"Filter by default channel group (e.g., Organic Search, Paid Social)\",\n apiField: \"sessionDefaultChannelGroup\",\n type: \"string\",\n category: \"traffic_source\",\n },\n\n // ========================================\n // GEOGRAPHY FILTERS\n // ========================================\n {\n key: \"country\",\n name: \"Country\",\n description: \"Filter by country name\",\n apiField: \"country\",\n type: \"string\",\n category: \"geography\",\n },\n {\n key: \"city\",\n name: \"City\",\n description: \"Filter by city name\",\n apiField: \"city\",\n type: \"string\",\n category: \"geography\",\n },\n {\n key: \"region\",\n name: \"Region\",\n description: \"Filter by region/state\",\n apiField: \"region\",\n type: \"string\",\n category: \"geography\",\n },\n {\n key: \"continent\",\n name: \"Continent\",\n description: \"Filter by continent\",\n apiField: \"continent\",\n type: \"string\",\n category: \"geography\",\n },\n\n // ========================================\n // DEVICE FILTERS\n // ========================================\n {\n key: \"deviceCategory\",\n name: \"Device Category\",\n description: \"Filter by device type (desktop, mobile, tablet)\",\n apiField: \"deviceCategory\",\n type: \"inList\",\n category: \"device\",\n },\n {\n key: \"operatingSystem\",\n name: \"Operating System\",\n description: \"Filter by OS (iOS, Android, Windows, Macintosh)\",\n apiField: \"operatingSystem\",\n type: \"string\",\n category: \"device\",\n },\n {\n key: \"browser\",\n name: \"Browser\",\n description: \"Filter by browser name (Chrome, Safari, Firefox)\",\n apiField: \"browser\",\n type: \"string\",\n category: \"device\",\n },\n {\n key: \"platform\",\n name: \"Platform\",\n description: \"Filter by platform (web, ios, android)\",\n apiField: \"platform\",\n type: \"string\",\n category: \"device\",\n },\n\n // ========================================\n // PAGE FILTERS\n // ========================================\n {\n key: \"pagePath\",\n name: \"Page Path\",\n description: \"Filter by page URL path\",\n apiField: \"pagePath\",\n type: \"string\",\n category: \"page\",\n },\n {\n key: \"pageTitle\",\n name: \"Page Title\",\n description: \"Filter by page title\",\n apiField: \"pageTitle\",\n type: \"string\",\n category: \"page\",\n },\n {\n key: \"landingPage\",\n name: \"Landing Page\",\n description: \"Filter by landing page path\",\n apiField: \"landingPage\",\n type: \"string\",\n category: \"page\",\n },\n {\n key: \"hostname\",\n name: \"Hostname\",\n description: \"Filter by hostname/domain\",\n apiField: \"hostname\",\n type: \"string\",\n category: \"page\",\n },\n\n // ========================================\n // EVENT FILTERS\n // ========================================\n {\n key: \"eventName\",\n name: \"Event Name\",\n description: \"Filter by event name (e.g., page_view, purchase, click)\",\n apiField: \"eventName\",\n type: \"string\",\n category: \"event\",\n },\n\n // ========================================\n // ECOMMERCE FILTERS\n // ========================================\n {\n key: \"itemName\",\n name: \"Item Name\",\n description: \"Filter by product/item name\",\n apiField: \"itemName\",\n type: \"string\",\n category: \"ecommerce\",\n },\n {\n key: \"itemBrand\",\n name: \"Item Brand\",\n description: \"Filter by product brand\",\n apiField: \"itemBrand\",\n type: \"string\",\n category: \"ecommerce\",\n },\n {\n key: \"itemCategory\",\n name: \"Item Category\",\n description: \"Filter by product category\",\n apiField: \"itemCategory\",\n type: \"string\",\n category: \"ecommerce\",\n },\n {\n key: \"transactionId\",\n name: \"Transaction ID\",\n description: \"Filter by transaction ID\",\n apiField: \"transactionId\",\n type: \"string\",\n category: \"ecommerce\",\n },\n\n // ========================================\n // USER FILTERS\n // ========================================\n {\n key: \"newVsReturning\",\n name: \"New vs Returning\",\n description: \"Filter by new or returning users\",\n apiField: \"newVsReturning\",\n type: \"inList\",\n category: \"user\",\n },\n {\n key: \"audienceName\",\n name: \"Audience Name\",\n description: \"Filter by GA4 audience name\",\n apiField: \"audienceName\",\n type: \"string\",\n category: \"user\",\n },\n];\n\n// ============================================\n// FILTER BUILDER HELPERS\n// ============================================\n\n/**\n * Build a GA4 string filter expression\n */\nexport function buildStringFilter(\n fieldName: string,\n matchType: string,\n value: string,\n caseSensitive: boolean = false\n): GA4FilterExpression {\n return {\n filter: {\n fieldName,\n stringFilter: {\n matchType: matchType as GA4FilterExpression[\"filter\"] extends { stringFilter?: { matchType: infer T } } ? T : never,\n value,\n caseSensitive,\n },\n },\n };\n}\n\n/**\n * Build a GA4 in-list filter expression\n */\nexport function buildInListFilter(\n fieldName: string,\n values: string[],\n caseSensitive: boolean = false\n): GA4FilterExpression {\n return {\n filter: {\n fieldName,\n inListFilter: {\n values,\n caseSensitive,\n },\n },\n };\n}\n\n/**\n * Build a GA4 numeric filter expression\n */\nexport function buildNumericFilter(\n fieldName: string,\n operation: string,\n value: number\n): GA4FilterExpression {\n return {\n filter: {\n fieldName,\n numericFilter: {\n operation: operation as GA4FilterExpression[\"filter\"] extends { numericFilter?: { operation: infer T } } ? T : never,\n value: { doubleValue: value },\n },\n },\n };\n}\n\n/**\n * Combine multiple filter expressions with AND\n */\nexport function buildAndFilter(expressions: GA4FilterExpression[]): GA4FilterExpression {\n if (expressions.length === 1) return expressions[0];\n return { andGroup: { expressions } };\n}\n\n/**\n * Combine multiple filter expressions with OR\n */\nexport function buildOrFilter(expressions: GA4FilterExpression[]): GA4FilterExpression {\n if (expressions.length === 1) return expressions[0];\n return { orGroup: { expressions } };\n}\n\n/**\n * Build a GA4 filter from simple copilot filter params\n */\nexport function buildFilterFromParams(\n filters: Array<{ field: string; operator: string; value: unknown }>\n): GA4FilterExpression | undefined {\n if (filters.length === 0) return undefined;\n\n const expressions: GA4FilterExpression[] = filters.map((f) => {\n const op = f.operator.toUpperCase();\n\n if (op === \"IN\" && Array.isArray(f.value)) {\n return buildInListFilter(f.field, f.value.map(String));\n }\n\n if ([\"GREATER_THAN\", \"LESS_THAN\", \"EQUAL\", \"GREATER_THAN_OR_EQUAL\", \"LESS_THAN_OR_EQUAL\"].includes(op)) {\n return buildNumericFilter(f.field, op, Number(f.value));\n }\n\n // Default: string filter\n const matchType = op === \"CONTAINS\" ? \"CONTAINS\"\n : op === \"BEGINS_WITH\" ? \"BEGINS_WITH\"\n : op === \"ENDS_WITH\" ? \"ENDS_WITH\"\n : op === \"REGEXP\" || op === \"FULL_REGEXP\" ? \"FULL_REGEXP\"\n : op === \"PARTIAL_REGEXP\" ? \"PARTIAL_REGEXP\"\n : \"EXACT\";\n\n return buildStringFilter(f.field, matchType, String(f.value));\n });\n\n return buildAndFilter(expressions);\n}\n","/**\n * google-analytics-mcp-server: an open-source MCP server for Google Analytics 4.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\n// ============================================\n// GA4 QUERY PLANNER\n// Converts copilot params to GA4RunReportRequest\n// ============================================\n\nimport type {\n GA4QueryRequest,\n GA4QueryPlan,\n GA4RunReportRequest,\n GA4DateRange,\n GA4Dimension,\n GA4Metric,\n GA4OrderBy,\n GA4FilterExpression,\n GA4DatePreset,\n} from \"./types.js\";\nimport { resolveDatePreset } from \"./types.js\";\nimport { getMetricByKey } from \"./metric-catalog.js\";\nimport { getDimensionByKey } from \"./dimension-catalog.js\";\nimport { validateGA4QuerySelection } from \"./compatibility-rules.js\";\nimport { isCalculatedMetric, getCalculatedMetricDependencies } from \"./calculated-metrics.js\";\nimport { buildFilterFromParams } from \"./filter-catalog.js\";\n\n// ============================================\n// MAIN PLANNER\n// ============================================\n\n/**\n * Build a GA4 report request from copilot-style params.\n *\n * This function:\n * 1. Separates calculated vs API metrics\n * 2. Resolves calculated metric dependencies\n * 3. Validates compatibility\n * 4. Builds the GA4 API request body\n */\nexport function planGA4Query(params: {\n propertyId: string;\n metrics: string[];\n dimensions?: string[];\n filters?: Array<{ field: string; operator: string; value: unknown }>;\n dateRange?: { startDate: string; endDate: string };\n datePreset?: string;\n orderBy?: string;\n orderDirection?: string;\n limit?: number;\n}): GA4QueryPlan {\n const warnings: string[] = [];\n const errors: string[] = [];\n\n // --- 1. Separate calculated vs API metrics ---\n const calculatedMetrics: string[] = [];\n const apiMetricKeys: string[] = [];\n\n for (const key of params.metrics) {\n if (isCalculatedMetric(key)) {\n calculatedMetrics.push(key);\n } else {\n apiMetricKeys.push(key);\n }\n }\n\n // --- 2. Resolve calculated metric dependencies ---\n if (calculatedMetrics.length > 0) {\n const deps = getCalculatedMetricDependencies(calculatedMetrics);\n for (const dep of deps) {\n if (!apiMetricKeys.includes(dep)) {\n apiMetricKeys.push(dep);\n }\n }\n }\n\n // --- 3. Resolve metric API field names ---\n const apiMetrics: GA4Metric[] = [];\n for (const key of apiMetricKeys) {\n const def = getMetricByKey(key);\n if (def) {\n apiMetrics.push({ name: def.apiField });\n } else {\n // Assume the key IS the API field name\n apiMetrics.push({ name: key });\n }\n }\n\n // --- 4. Resolve dimension API field names ---\n const dimensionKeys = params.dimensions || [];\n const apiDimensions: GA4Dimension[] = [];\n for (const key of dimensionKeys) {\n const def = getDimensionByKey(key);\n if (def) {\n apiDimensions.push({ name: def.apiField });\n } else {\n apiDimensions.push({ name: key });\n }\n }\n\n // --- 5. Validate compatibility ---\n const validation = validateGA4QuerySelection(\n apiMetricKeys,\n dimensionKeys\n );\n errors.push(...validation.errors);\n warnings.push(...validation.warnings);\n\n // --- 6. Resolve date range ---\n let dateRanges: GA4DateRange[];\n if (params.dateRange) {\n dateRanges = [{\n startDate: params.dateRange.startDate,\n endDate: params.dateRange.endDate,\n }];\n } else if (params.datePreset) {\n dateRanges = [resolveDatePreset(params.datePreset as GA4DatePreset)];\n } else {\n // Default: last 28 days\n dateRanges = [{ startDate: \"28daysAgo\", endDate: \"today\" }];\n }\n\n // --- 7. Build order by ---\n const orderBys: GA4OrderBy[] = [];\n if (params.orderBy) {\n const metricDef = getMetricByKey(params.orderBy);\n const dimDef = getDimensionByKey(params.orderBy);\n\n if (metricDef) {\n orderBys.push({\n metric: { metricName: metricDef.apiField },\n desc: params.orderDirection !== \"ASC\",\n });\n } else if (dimDef) {\n orderBys.push({\n dimension: { dimensionName: dimDef.apiField },\n desc: params.orderDirection !== \"ASC\",\n });\n } else {\n // Try as raw API field\n orderBys.push({\n metric: { metricName: params.orderBy },\n desc: params.orderDirection !== \"ASC\",\n });\n }\n }\n\n // --- 8. Build filters ---\n let dimensionFilter: GA4FilterExpression | undefined;\n if (params.filters && params.filters.length > 0) {\n dimensionFilter = buildFilterFromParams(params.filters);\n }\n\n // --- 9. Assemble request ---\n const request: GA4RunReportRequest = {\n dateRanges,\n metrics: apiMetrics,\n ...(apiDimensions.length > 0 && { dimensions: apiDimensions }),\n ...(dimensionFilter && { dimensionFilter }),\n ...(orderBys.length > 0 && { orderBys }),\n ...(params.limit && { limit: params.limit }),\n keepEmptyRows: false,\n };\n\n return {\n request,\n propertyId: params.propertyId,\n warnings,\n errors,\n calculatedMetrics,\n apiMetrics: apiMetricKeys,\n };\n}\n\n/**\n * Build a GA4 report request for direct use (without copilot abstraction)\n */\nexport function buildGA4Report(params: {\n propertyId: string;\n metrics: string[];\n dimensions?: string[];\n filters?: Array<{ field: string; operator: string; value: unknown }>;\n dateRange?: { startDate: string; endDate: string };\n datePreset?: string;\n orderBy?: string;\n limit?: number;\n}): GA4QueryRequest {\n const dateRanges = params.dateRange\n ? [params.dateRange]\n : params.datePreset\n ? [resolveDatePreset(params.datePreset as GA4DatePreset)]\n : [{ startDate: \"28daysAgo\", endDate: \"today\" }];\n\n return {\n propertyId: params.propertyId,\n dateRanges,\n metrics: params.metrics,\n dimensions: params.dimensions,\n ...(params.filters && params.filters.length > 0 && {\n dimensionFilter: buildFilterFromParams(params.filters),\n }),\n ...(params.orderBy && {\n orderBys: [{\n metric: { metricName: params.orderBy },\n desc: true,\n }],\n }),\n ...(params.limit && { limit: params.limit }),\n };\n}\n","/**\n * google-analytics-mcp-server: an open-source MCP server for Google Analytics 4.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { formatMcpToolError } from \"../../core/errors.js\";\nimport {\n GA4_ADMIN_COLLECTIONS,\n type GA4AdminCollection,\n type GA4Client,\n type GA4PropertySetting,\n} from \"./client.js\";\nimport type {\n GA4FilterExpression,\n GA4OrderBy,\n GA4RunPivotReportRequest,\n GA4RunReportRequest,\n} from \"./types.js\";\nimport {\n redactGA4AudienceExportResponse,\n redactGA4PersonalIdentifiers,\n} from \"./privacy.js\";\n\nconst propertyIdSchema = z.string().min(1).describe(\"Numeric GA4 property ID or properties/{id}.\");\nconst dateRangeSchema = z.object({\n startDate: z.string().min(1).describe(\"YYYY-MM-DD or a GA4 relative date such as 28daysAgo.\"),\n endDate: z.string().min(1).describe(\"YYYY-MM-DD or a GA4 relative date such as today.\"),\n});\nconst rawFilterSchema = z.record(z.unknown()).describe(\"Native GA4 FilterExpression JSON.\");\nconst rawOrderBySchema = z.record(z.unknown()).describe(\"Native GA4 OrderBy JSON.\");\nconst aggregationSchema = z.enum([\"TOTAL\", \"MINIMUM\", \"MAXIMUM\", \"COUNT\"]);\n\nconst reportSchema = z.object({\n dateRanges: z.array(dateRangeSchema).min(1).max(4).optional(),\n dimensions: z.array(z.string().min(1)).max(9).optional().default([]),\n metrics: z.array(z.string().min(1)).min(1).max(10),\n dimensionFilter: rawFilterSchema.optional(),\n metricFilter: rawFilterSchema.optional(),\n orderBys: z.array(rawOrderBySchema).max(10).optional(),\n limit: z.number().int().min(1).max(250_000).optional(),\n offset: z.number().int().min(0).optional(),\n keepEmptyRows: z.boolean().optional(),\n metricAggregations: z.array(aggregationSchema).max(4).optional(),\n currencyCode: z.string().length(3).optional(),\n returnPropertyQuota: z.boolean().optional().default(true),\n cohortSpec: z.record(z.unknown()).optional().describe(\"Native GA4 CohortSpec JSON. Omit dateRanges for cohort requests.\"),\n comparisons: z.array(z.record(z.unknown())).max(4).optional().describe(\"Native GA4 Comparison objects.\"),\n}).refine((report) => Boolean(report.dateRanges?.length || report.cohortSpec), {\n message: \"Provide dateRanges or cohortSpec.\",\n});\n\nconst pivotSchema = z.object({\n fieldNames: z.array(z.string().min(1)).min(1).max(9),\n orderBys: z.array(rawOrderBySchema).max(10).optional(),\n offset: z.number().int().min(0).optional(),\n limit: z.number().int().min(1).max(250_000),\n metricAggregations: z.array(aggregationSchema).max(4).optional(),\n});\n\nconst pivotReportSchema = z.object({\n dateRanges: z.array(dateRangeSchema).min(1).max(4).optional(),\n dimensions: z.array(z.string().min(1)).min(1).max(9),\n metrics: z.array(z.string().min(1)).min(1).max(10),\n pivots: z.array(pivotSchema).min(1).max(9),\n dimensionFilter: rawFilterSchema.optional(),\n metricFilter: rawFilterSchema.optional(),\n currencyCode: z.string().length(3).optional(),\n keepEmptyRows: z.boolean().optional(),\n returnPropertyQuota: z.boolean().optional().default(true),\n cohortSpec: z.record(z.unknown()).optional().describe(\"Native GA4 CohortSpec JSON. Omit dateRanges for cohort requests.\"),\n comparisons: z.array(z.record(z.unknown())).max(4).optional().describe(\"Native GA4 Comparison objects.\"),\n}).refine((report) => Boolean(report.dateRanges?.length || report.cohortSpec), {\n message: \"Provide dateRanges or cohortSpec.\",\n});\n\nfunction ok(data: unknown) {\n const body = typeof data === \"object\" && data !== null && !Array.isArray(data)\n ? {\n ...data,\n warnings: \"warnings\" in data ? (data as Record<string, unknown>).warnings : [],\n limitations: \"limitations\" in data ? (data as Record<string, unknown>).limitations : [],\n nextActions: \"nextActions\" in data ? (data as Record<string, unknown>).nextActions : [],\n debug: {\n source: \"ga4\",\n apiVersion: \"data_api_v1beta/admin_api_v1beta_v1alpha\",\n requestCount: 1,\n ...((data as Record<string, unknown>).debug as Record<string, unknown> | undefined),\n },\n }\n : data;\n return { content: [{ type: \"text\" as const, text: JSON.stringify(body, null, 2) }] };\n}\n\nfunction reportRequest(input: z.infer<typeof reportSchema>): GA4RunReportRequest {\n return {\n dateRanges: input.dateRanges,\n dimensions: input.dimensions.map((name) => ({ name })),\n metrics: input.metrics.map((name) => ({ name })),\n dimensionFilter: input.dimensionFilter as GA4FilterExpression | undefined,\n metricFilter: input.metricFilter as GA4FilterExpression | undefined,\n orderBys: input.orderBys as GA4OrderBy[] | undefined,\n limit: input.limit,\n offset: input.offset,\n keepEmptyRows: input.keepEmptyRows,\n metricAggregations: input.metricAggregations,\n currencyCode: input.currencyCode,\n returnPropertyQuota: input.returnPropertyQuota,\n cohortSpec: input.cohortSpec,\n comparisons: input.comparisons,\n };\n}\n\nfunction pivotReportRequest(input: z.infer<typeof pivotReportSchema>): GA4RunPivotReportRequest {\n return {\n dateRanges: input.dateRanges,\n dimensions: input.dimensions.map((name) => ({ name })),\n metrics: input.metrics.map((name) => ({ name })),\n pivots: input.pivots.map((pivot) => ({\n ...pivot,\n orderBys: pivot.orderBys as GA4OrderBy[] | undefined,\n })),\n dimensionFilter: input.dimensionFilter as GA4FilterExpression | undefined,\n metricFilter: input.metricFilter as GA4FilterExpression | undefined,\n currencyCode: input.currencyCode,\n keepEmptyRows: input.keepEmptyRows,\n returnPropertyQuota: input.returnPropertyQuota,\n cohortSpec: input.cohortSpec,\n comparisons: input.comparisons,\n };\n}\n\nfunction validatePivotRequest(request: GA4RunPivotReportRequest): string[] {\n const errors: string[] = [];\n const declared = new Set(request.dimensions.map(({ name }) => name));\n const seen = new Set<string>();\n let cellBudget = 1;\n\n for (const pivot of request.pivots) {\n cellBudget *= pivot.limit;\n for (const field of pivot.fieldNames) {\n if (!declared.has(field) && field !== \"dateRange\" && field !== \"comparisons\") {\n errors.push(`Pivot field '${field}' is not declared in dimensions.`);\n }\n if (seen.has(field)) errors.push(`Pivot field '${field}' appears in more than one pivot.`);\n seen.add(field);\n }\n }\n if (cellBudget > 250_000) {\n errors.push(`The product of pivot limits is ${cellBudget}; GA4 allows at most 250,000.`);\n }\n return errors;\n}\n\nfunction stripProperty(request: GA4RunReportRequest | GA4RunPivotReportRequest) {\n const { property: _property, ...body } = request;\n return body;\n}\n\nexport function registerGA4SurfaceTools(server: McpServer, client: GA4Client): void {\n server.tool(\n \"ga4_run_pivot_report\",\n \"Run a read-only GA4 Data API pivot report with native filters, ordering, quota state, multiple date ranges, and up to 250,000 pivot cells. Every pivot requires an explicit limit.\",\n { propertyId: propertyIdSchema, report: pivotReportSchema },\n async ({ propertyId, report }) => {\n try {\n const request = pivotReportRequest(report);\n const errors = validatePivotRequest(request);\n if (errors.length) return ok({ error: \"Pivot validation failed\", errors });\n const response = await client.runPivotReport(propertyId, stripProperty(request) as GA4RunPivotReportRequest);\n return ok({\n report: response,\n data: client.flattenResponse(response),\n rowCount: response.rows?.length ?? 0,\n propertyQuota: response.propertyQuota,\n });\n } catch (error) { return formatMcpToolError(error); }\n },\n );\n\n server.tool(\n \"ga4_batch_run_reports\",\n \"Run 1-5 independent read-only GA4 Core reports for the same property in one official Data API batch request.\",\n { propertyId: propertyIdSchema, reports: z.array(reportSchema).min(1).max(5) },\n async ({ propertyId, reports }) => {\n try {\n const requests = reports.map((report) => stripProperty(reportRequest(report)) as GA4RunReportRequest);\n const response = await client.batchRunReports(propertyId, requests);\n const entries = (response.reports ?? []).map((report, index) => ({\n index,\n rowCount: report.rowCount ?? report.rows?.length ?? 0,\n data: client.flattenResponse(report),\n metadata: report.metadata,\n totals: report.totals,\n minimums: report.minimums,\n maximums: report.maximums,\n propertyQuota: report.propertyQuota,\n }));\n return ok({ reports: entries, reportCount: entries.length, kind: response.kind });\n } catch (error) { return formatMcpToolError(error); }\n },\n );\n\n server.tool(\n \"ga4_batch_run_pivot_reports\",\n \"Run 1-5 independent read-only GA4 pivot reports for the same property in one official Data API batch request.\",\n { propertyId: propertyIdSchema, reports: z.array(pivotReportSchema).min(1).max(5) },\n async ({ propertyId, reports }) => {\n try {\n const requests = reports.map(pivotReportRequest);\n const errors = requests.flatMap((request, index) => validatePivotRequest(request).map((error) => `Report ${index}: ${error}`));\n if (errors.length) return ok({ error: \"Pivot validation failed\", errors });\n const response = await client.batchRunPivotReports(\n propertyId,\n requests.map((request) => stripProperty(request) as GA4RunPivotReportRequest),\n );\n const entries = (response.pivotReports ?? []).map((report, index) => ({\n index,\n rowCount: report.rows?.length ?? 0,\n data: client.flattenResponse(report),\n report,\n }));\n return ok({ reports: entries, reportCount: entries.length, kind: response.kind });\n } catch (error) { return formatMcpToolError(error); }\n },\n );\n\n server.tool(\n \"ga4_check_compatibility\",\n \"Ask the official GA4 Data API which dimensions and metrics are compatible with a proposed Core report selection.\",\n {\n propertyId: propertyIdSchema,\n dimensions: z.array(z.string().min(1)).max(9).optional().default([]),\n metrics: z.array(z.string().min(1)).max(10).optional().default([]),\n dimensionFilter: rawFilterSchema.optional(),\n metricFilter: rawFilterSchema.optional(),\n compatibilityFilter: z.enum([\"COMPATIBILITY_UNSPECIFIED\", \"COMPATIBLE\", \"INCOMPATIBLE\"]).optional(),\n },\n async ({ propertyId, dimensions, metrics, dimensionFilter, metricFilter, compatibilityFilter }) => {\n try {\n const response = await client.checkCompatibility(propertyId, {\n dimensions: dimensions.map((name) => ({ name })),\n metrics: metrics.map((name) => ({ name })),\n ...(dimensionFilter && { dimensionFilter }),\n ...(metricFilter && { metricFilter }),\n ...(compatibilityFilter && { compatibilityFilter }),\n });\n return ok(response);\n } catch (error) { return formatMcpToolError(error); }\n },\n );\n\n server.tool(\n \"ga4_get_property_quotas_snapshot\",\n \"Read the current GA4 Data API property quota snapshot from the official v1alpha endpoint. This does not run a report, but Google still charges one property-quota token to the category with the most remaining quota.\",\n { propertyId: propertyIdSchema },\n async ({ propertyId }) => {\n try {\n const snapshot = await client.getPropertyQuotasSnapshot(propertyId);\n return ok({ propertyId, snapshot });\n } catch (error) { return formatMcpToolError(error); }\n },\n );\n\n server.tool(\n \"ga4_list_accounts\",\n \"List raw GA4 Analytics Admin accounts accessible to the authenticated user (read-only, auto-paginated).\",\n { pageSize: z.number().int().min(1).max(200).optional().default(200) },\n async ({ pageSize }) => {\n try {\n const accounts = await client.listAccounts(pageSize);\n return ok({ accounts, count: accounts.length });\n } catch (error) { return formatMcpToolError(error); }\n },\n );\n\n const adminCollectionNames = Object.keys(GA4_ADMIN_COLLECTIONS) as [GA4AdminCollection, ...GA4AdminCollection[]];\n server.tool(\n \"ga4_list_admin_resources\",\n \"List an allowlisted GA4 Admin collection in read-only mode: streams, custom definitions, key events, audiences, product links, annotations, channel groups, expanded datasets, subproperty/rollup configuration, and access bindings.\",\n {\n propertyId: propertyIdSchema,\n collection: z.enum(adminCollectionNames),\n pageSize: z.number().int().min(1).max(200).optional().default(200),\n includePersonalIdentifiers: z.boolean().optional().default(false)\n .describe(\"Access bindings only. Explicitly include user/email identifiers; false redacts them by default.\"),\n },\n async ({ propertyId, collection, pageSize, includePersonalIdentifiers }) => {\n try {\n const resources = await client.listAdminPropertyResources(propertyId, collection, pageSize);\n const exposesPersonalIdentifiers = collection === \"accessBindings\";\n const safeResources = exposesPersonalIdentifiers && !includePersonalIdentifiers\n ? redactGA4PersonalIdentifiers(resources)\n : resources;\n return ok({\n propertyId,\n collection,\n resources: safeResources,\n count: resources.length,\n warnings: exposesPersonalIdentifiers && includePersonalIdentifiers\n ? [\"SENSITIVE PERSONAL IDENTIFIERS INCLUDED BY EXPLICIT OPT-IN: access-binding user/email values are present. Restrict storage, sharing, and model output.\"]\n : exposesPersonalIdentifiers\n ? [\"Access-binding user/email identifiers were redacted by default. Set includePersonalIdentifiers=true only with explicit authorization.\"]\n : [],\n });\n } catch (error) { return formatMcpToolError(error); }\n },\n );\n\n const settingNames: [GA4PropertySetting, ...GA4PropertySetting[]] = [\n \"attributionSettings\",\n \"dataRetentionSettings\",\n \"googleSignalsSettings\",\n \"reportingIdentitySettings\",\n \"userProvidedDataSettings\",\n ];\n server.tool(\n \"ga4_get_property_configuration\",\n \"Read GA4 property details plus selected singleton Admin settings (attribution, retention, Google Signals, reporting identity, or user-provided-data settings).\",\n {\n propertyId: propertyIdSchema,\n settings: z.array(z.enum(settingNames)).max(settingNames.length).optional().default([]),\n },\n async ({ propertyId, settings }) => {\n try {\n const property = await client.getProperty(propertyId);\n const configuration: Record<string, unknown> = {};\n const warnings: string[] = [];\n for (const setting of [...new Set(settings)]) {\n try {\n configuration[setting] = await client.getPropertySetting(propertyId, setting);\n } catch (error) {\n warnings.push(`${setting}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n return ok({ property, configuration, warnings, debug: { requestCount: 1 + new Set(settings).size } });\n } catch (error) { return formatMcpToolError(error); }\n },\n );\n\n server.tool(\n \"ga4_list_audience_exports\",\n \"List existing GA4 Audience Export snapshots and Recurring Audience Lists without creating new exports.\",\n {\n propertyId: propertyIdSchema,\n includeRecurring: z.boolean().optional().default(true),\n pageSize: z.number().int().min(1).max(200).optional().default(100),\n },\n async ({ propertyId, includeRecurring, pageSize }) => {\n try {\n const audienceExports = await client.listAudienceExports(propertyId, pageSize);\n const recurringAudienceLists = includeRecurring\n ? await client.listRecurringAudienceLists(propertyId, Math.min(pageSize, 100))\n : [];\n return ok({\n audienceExports,\n recurringAudienceLists,\n counts: { audienceExports: audienceExports.length, recurringAudienceLists: recurringAudienceLists.length },\n debug: { requestCount: includeRecurring ? 2 : 1 },\n });\n } catch (error) { return formatMcpToolError(error); }\n },\n );\n\n server.tool(\n \"ga4_query_audience_export\",\n \"Query rows from an existing GA4 Audience Export. User/device identifiers are redacted by default; include them only through explicit opt-in.\",\n {\n audienceExportName: z.string().regex(/^properties\\/[^/]+\\/audienceExports\\/[^/]+$/),\n offset: z.number().int().min(0).optional().default(0),\n limit: z.number().int().min(1).max(10_000).optional().default(100),\n includePersonalIdentifiers: z.boolean().optional().default(false)\n .describe(\"Explicitly return user/device identifiers. False redacts identifier columns by default.\"),\n },\n async ({ audienceExportName, offset, limit, includePersonalIdentifiers }) => {\n try {\n const response = await client.queryAudienceExport(audienceExportName, {\n offset: String(offset),\n limit: String(limit),\n });\n return ok({\n audienceExportName,\n response: includePersonalIdentifiers ? response : redactGA4AudienceExportResponse(response),\n warnings: includePersonalIdentifiers\n ? [\"SENSITIVE PERSONAL IDENTIFIERS INCLUDED BY EXPLICIT OPT-IN: Audience Export user/device identifiers are present. Restrict storage, sharing, and model output.\"]\n : [\"Audience Export user/device identifiers were redacted by default. Set includePersonalIdentifiers=true only with explicit authorization.\"],\n });\n } catch (error) { return formatMcpToolError(error); }\n },\n );\n}\n","/**\n * google-analytics-mcp-server: an open-source MCP server for Google Analytics 4.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nconst REDACTED = \"[REDACTED]\";\n\nconst PERSONAL_IDENTIFIER_KEYS = new Set([\n \"user\",\n \"userid\",\n \"user_id\",\n \"email\",\n \"emailaddress\",\n \"email_address\",\n \"username\",\n \"deviceid\",\n \"device_id\",\n \"mobiledeviceid\",\n \"mobile_device_id\",\n \"googlesignalspseudonymousid\",\n \"google_signals_pseudonymous_id\",\n]);\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction normalizedKey(value: string): string {\n return value.replace(/[-\\s]/g, \"\").toLowerCase();\n}\n\nfunction isSensitiveAudienceDimension(name: string): boolean {\n const normalized = normalizedKey(name);\n return PERSONAL_IDENTIFIER_KEYS.has(normalized)\n || /(?:user|device|pseudonymous|email|username).*id$/.test(normalized)\n || /^(?:user|device).*identifier$/.test(normalized);\n}\n\n/** Redact direct user/device identity fields while retaining roles and resource metadata. */\nexport function redactGA4PersonalIdentifiers(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(redactGA4PersonalIdentifiers);\n if (!isRecord(value)) return value;\n\n return Object.fromEntries(Object.entries(value).map(([key, nested]) => [\n key,\n PERSONAL_IDENTIFIER_KEYS.has(normalizedKey(key))\n ? REDACTED\n : redactGA4PersonalIdentifiers(nested),\n ]));\n}\n\n/**\n * Audience Export rows are positional. Use the returned dimension metadata to\n * redact user/device identifier columns. If metadata is absent, redact every\n * row value so the default remains privacy-safe instead of guessing.\n */\nexport function redactGA4AudienceExportResponse(response: Record<string, unknown>): Record<string, unknown> {\n const redacted = redactGA4PersonalIdentifiers(response) as Record<string, unknown>;\n const audienceExport = isRecord(response.audienceExport) ? response.audienceExport : undefined;\n const dimensions = Array.isArray(audienceExport?.dimensions) ? audienceExport.dimensions : [];\n const names = dimensions.map((dimension) =>\n isRecord(dimension) && typeof dimension.dimensionName === \"string\"\n ? dimension.dimensionName\n : \"\"\n );\n const sensitiveIndexes = new Set<number>();\n if (names.length === 0) {\n sensitiveIndexes.add(-1);\n } else {\n names.forEach((name, index) => {\n if (!name || isSensitiveAudienceDimension(name)) sensitiveIndexes.add(index);\n });\n }\n\n if (!Array.isArray(redacted.audienceRows)) return redacted;\n return {\n ...redacted,\n audienceRows: redacted.audienceRows.map((row) => {\n if (!isRecord(row) || !Array.isArray(row.dimensionValues)) return row;\n return {\n ...row,\n dimensionValues: row.dimensionValues.map((dimensionValue, index) => {\n if (!sensitiveIndexes.has(-1) && !sensitiveIndexes.has(index)) return dimensionValue;\n return isRecord(dimensionValue)\n ? { ...dimensionValue, value: REDACTED }\n : REDACTED;\n }),\n };\n }),\n };\n}\n\nexport const GA4_REDACTION_MARKER = REDACTED;\n","/**\n * google-analytics-mcp-server: an open-source MCP server for Google Analytics 4.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { GA4_METRIC_CATALOG } from \"./metric-catalog.js\";\nimport { GA4_DIMENSION_CATALOG } from \"./dimension-catalog.js\";\n\nexport function registerGA4Resources(server: McpServer): void {\n server.resource(\"ga4-metrics\", \"ga4://metrics\", async () => ({\n contents: [{ uri: \"ga4://metrics\", mimeType: \"application/json\",\n text: JSON.stringify(GA4_METRIC_CATALOG.map(m => ({\n key: m.key, name: m.name, description: m.description,\n category: m.category, type: m.type, format: m.format, apiField: m.apiField,\n })), null, 2) }],\n }));\n\n server.resource(\"ga4-dimensions\", \"ga4://dimensions\", async () => ({\n contents: [{ uri: \"ga4://dimensions\", mimeType: \"application/json\",\n text: JSON.stringify(GA4_DIMENSION_CATALOG.map(d => ({\n key: d.key, name: d.name, description: d.description,\n category: d.category, apiField: d.apiField,\n })), null, 2) }],\n }));\n\n server.resource(\"ga4-compatibility\", \"ga4://compatibility\", async () => ({\n contents: [{ uri: \"ga4://compatibility\", mimeType: \"application/json\",\n text: JSON.stringify({\n description: \"GA4 constraints: max 9 dimensions + 10 metrics per request. Ecommerce dimensions require ecommerce metrics.\",\n maxDimensions: 9, maxMetrics: 10, maxRowsPerCoreRequest: 250000, maxReportsPerBatch: 5, maxPivotCells: 250000,\n dateFormats: [\"YYYY-MM-DD\", \"7daysAgo\", \"yesterday\", \"today\"],\n }, null, 2) }],\n }));\n\n server.resource(\"ga4-manifest\", \"ga4://manifest\", async () => ({\n contents: [{ uri: \"ga4://manifest\", mimeType: \"application/json\",\n text: JSON.stringify({\n name: \"GA4 MCP\",\n platform: \"google_analytics_4\",\n readOnly: true,\n authentication: {\n type: \"google_oauth_refresh_token\",\n tokensExposedByTools: false,\n },\n tools: [\n { name: \"ga4_health_check\", purpose: \"Verify credentials, list properties, and test property/metadata access\" },\n { name: \"ga4_list_properties\", purpose: \"List accessible GA4 properties\" },\n { name: \"ga4_run_report\", purpose: \"Run standard GA4 Data API reports\" },\n { name: \"ga4_run_pivot_report\", purpose: \"Run native GA4 Data API pivot reports\" },\n { name: \"ga4_batch_run_reports\", purpose: \"Run up to five Core reports in one official batch request\" },\n { name: \"ga4_batch_run_pivot_reports\", purpose: \"Run up to five pivot reports in one official batch request\" },\n { name: \"ga4_check_compatibility\", purpose: \"Check official Core dimension/metric compatibility\" },\n { name: \"ga4_get_property_quotas_snapshot\", purpose: \"Read the current Data API property quota snapshot\" },\n { name: \"ga4_run_realtime_report\", purpose: \"Run GA4 Data API realtime reports with limited realtime-compatible fields\" },\n { name: \"ga4_get_metadata\", purpose: \"Return property metadata dimensions and metrics\" },\n { name: \"ga4_get_custom_definitions\", purpose: \"Inventory reportable custom dimensions and metrics from metadata\" },\n { name: \"ga4_get_key_events\", purpose: \"Best-effort key event/conversion inventory from reports\" },\n { name: \"ga4_get_ecommerce_diagnostics\", purpose: \"Diagnose ecommerce event, revenue, and item coverage\" },\n { name: \"ga4_get_event_parameters\", purpose: \"Approximate event parameter inventory from registered custom definitions\" },\n { name: \"ga4_run_funnel_recipe\", purpose: \"Run simple read-only event/pagePath funnel step counts\" },\n { name: \"ga4_get_audience_export_diagnostics\", purpose: \"List existing Audience Exports and inspect state/readiness without creating exports\" },\n { name: \"ga4_get_audience_diagnostics\", purpose: \"List Admin audiences, recurring audience lists, and observed audienceName report fallback\" },\n { name: \"ga4_get_bigquery_export_diagnostics\", purpose: \"Detect BigQuery export links, export modes, stream coverage, and excluded events\" },\n { name: \"ga4_get_server_side_tagging_diagnostics\", purpose: \"Infer server-side/server-to-server tagging signals from data streams and Measurement Protocol secrets\" },\n { name: \"ga4_run_advanced_funnel_report\", purpose: \"Run Data API v1alpha funnel reports with breakdown/next-action and read-only fallback\" },\n { name: \"ga4_get_channel_groups\", purpose: \"List custom channel groups\" },\n { name: \"ga4_list_accounts\", purpose: \"List raw accessible Analytics Admin accounts\" },\n { name: \"ga4_list_admin_resources\", purpose: \"List allowlisted property Admin collections\" },\n { name: \"ga4_get_property_configuration\", purpose: \"Read property details and singleton Admin settings\" },\n { name: \"ga4_list_audience_exports\", purpose: \"List existing audience exports and recurring audience lists\" },\n { name: \"ga4_query_audience_export\", purpose: \"Query rows from an existing audience export\" },\n { name: \"ga4_validate_query\", purpose: \"Validate report metric/dimension combinations\" },\n ],\n resources: [\"ga4://metrics\", \"ga4://dimensions\", \"ga4://compatibility\", \"ga4://manifest\", \"ga4://recipes\", \"ga4://p2-diagnostics\"],\n safety: [\n \"No write or mutate endpoints are registered.\",\n \"OAuth access and refresh tokens are never returned in tool responses.\",\n \"Measurement Protocol secret values are redacted before tool output.\",\n \"Audience Export and access-binding personal identifiers are redacted unless explicitly opted in.\",\n \"Diagnostics are best-effort read-only views over Data API/Admin read endpoints.\",\n ],\n }, null, 2) }],\n }));\n\n server.resource(\"ga4-recipes\", \"ga4://recipes\", async () => ({\n contents: [{ uri: \"ga4://recipes\", mimeType: \"application/json\",\n text: JSON.stringify({\n recipes: [\n {\n name: \"Account readiness check\",\n tool: \"ga4_health_check\",\n input: { propertyId: \"123456789\" },\n useWhen: \"Before querying a new credential set or property.\",\n },\n {\n name: \"Realtime traffic snapshot\",\n tool: \"ga4_run_realtime_report\",\n input: { propertyId: \"123456789\", metrics: [\"activeUsers\"], dimensions: [\"eventName\"], limit: 25 },\n useWhen: \"To inspect live activity with realtime-compatible fields.\",\n },\n {\n name: \"Key event inventory\",\n tool: \"ga4_get_key_events\",\n input: { propertyId: \"123456789\", datePreset: \"last28days\", limit: 100 },\n useWhen: \"To identify key events/conversions currently receiving traffic.\",\n },\n {\n name: \"Ecommerce implementation diagnostics\",\n tool: \"ga4_get_ecommerce_diagnostics\",\n input: { propertyId: \"123456789\", datePreset: \"last28days\", limit: 100 },\n useWhen: \"To check view_item, add_to_cart, begin_checkout, purchase, revenue, and item coverage.\",\n },\n {\n name: \"Registered event parameters\",\n tool: \"ga4_get_event_parameters\",\n input: { propertyId: \"123456789\", eventNames: [\"purchase\", \"add_to_cart\"], datePreset: \"last28days\" },\n useWhen: \"To discover reportable custom event parameters and sample event volume.\",\n },\n {\n name: \"Simple ecommerce funnel\",\n tool: \"ga4_run_funnel_recipe\",\n input: {\n propertyId: \"123456789\",\n metric: \"activeUsers\",\n datePreset: \"last28days\",\n steps: [\n { name: \"Product views\", eventName: \"view_item\" },\n { name: \"Cart adds\", eventName: \"add_to_cart\" },\n { name: \"Checkout starts\", eventName: \"begin_checkout\" },\n { name: \"Purchases\", eventName: \"purchase\" },\n ],\n },\n useWhen: \"To get quick step counts without GA4 Explore funnel semantics.\",\n },\n {\n name: \"Advanced funnel with device breakdown\",\n tool: \"ga4_run_advanced_funnel_report\",\n input: {\n propertyId: \"123456789\",\n datePreset: \"last28days\",\n breakdownDimension: \"deviceCategory\",\n steps: [\n { name: \"Landing\", pagePath: \"/\", matchType: \"EXACT\" },\n { name: \"Signup start\", eventName: \"sign_up_start\" },\n { name: \"Signup complete\", eventName: \"sign_up\" },\n ],\n },\n useWhen: \"To use GA4 Data API funnel semantics with optional fallback to independent step counts.\",\n },\n {\n name: \"Pivoted country/device performance\",\n tool: \"ga4_run_pivot_report\",\n input: {\n propertyId: \"123456789\",\n report: {\n dateRanges: [{ startDate: \"28daysAgo\", endDate: \"today\" }],\n dimensions: [\"country\", \"deviceCategory\"],\n metrics: [\"sessions\", \"totalRevenue\"],\n pivots: [{ fieldNames: [\"country\"], limit: 20 }, { fieldNames: [\"deviceCategory\"], limit: 3 }],\n },\n },\n useWhen: \"To return a native multidimensional pivot and property quota state.\",\n },\n {\n name: \"Audience readiness\",\n tool: \"ga4_get_audience_diagnostics\",\n input: { propertyId: \"123456789\", datePreset: \"last28days\", limit: 100 },\n useWhen: \"To inspect Admin audiences, recurring audience lists, and observed audienceName traffic.\",\n },\n {\n name: \"Audience export readiness\",\n tool: \"ga4_get_audience_export_diagnostics\",\n input: { propertyId: \"123456789\", pageSize: 100 },\n useWhen: \"To find existing Audience Exports and their ACTIVE/FAILED/CREATING states without creating new exports.\",\n },\n {\n name: \"BigQuery export detection\",\n tool: \"ga4_get_bigquery_export_diagnostics\",\n input: { propertyId: \"123456789\", includeDataStreams: true },\n useWhen: \"To confirm whether GA4 BigQuery export is configured and which export modes are enabled.\",\n },\n {\n name: \"Server-side tagging inference\",\n tool: \"ga4_get_server_side_tagging_diagnostics\",\n input: { propertyId: \"123456789\", includeSettings: true, includeRules: true },\n useWhen: \"To infer server-side/server-to-server collection signals while keeping Measurement Protocol secret values redacted.\",\n },\n ],\n notes: [\n \"Use ga4://metrics and ga4://dimensions for standard report fields.\",\n \"Use ga4_get_metadata or ga4_get_custom_definitions for property-specific custom fields.\",\n \"Funnel recipes are independent step counts, not sequence-aware user path analysis.\",\n \"Advanced funnel reports use the GA4 Data API v1alpha runFunnelReport endpoint and can fall back to independent step counts.\",\n \"Audience Export and Recurring Audience List diagnostics are read-only and never create new lists.\",\n \"ga4_list_admin_resources is an allowlisted GET-only explorer over Admin API v1beta/v1alpha collections.\",\n \"Batch Data API methods accept at most five reports for one property, matching the official API contract.\",\n \"Core report and pivot-cell limits are 250,000; every pivot must include a limit.\",\n ],\n }, null, 2) }],\n }));\n\n server.resource(\"ga4-p2-diagnostics\", \"ga4://p2-diagnostics\", async () => ({\n contents: [{ uri: \"ga4://p2-diagnostics\", mimeType: \"application/json\",\n text: JSON.stringify({\n readOnlyP2Tools: [\n {\n name: \"ga4_get_audience_export_diagnostics\",\n endpoints: [\n \"GET analyticsdata.googleapis.com/v1beta/properties/{property}/audienceExports\",\n \"GET analyticsdata.googleapis.com/v1beta/properties/{property}/audienceExports/{audienceExport}\",\n \"POST analyticsdata.googleapis.com/v1beta/properties/{property}/audienceExports/{audienceExport}:query (optional read-only sample)\",\n ],\n noCreateEndpointCalled: true,\n },\n {\n name: \"ga4_get_audience_diagnostics\",\n endpoints: [\n \"GET analyticsadmin.googleapis.com/v1alpha/properties/{property}/audiences\",\n \"GET analyticsdata.googleapis.com/v1alpha/properties/{property}/recurringAudienceLists\",\n \"POST analyticsdata.googleapis.com/v1beta/properties/{property}:runReport for audienceName fallback\",\n ],\n noCreateEndpointCalled: true,\n },\n {\n name: \"ga4_get_bigquery_export_diagnostics\",\n endpoints: [\n \"GET analyticsadmin.googleapis.com/v1alpha/properties/{property}/bigQueryLinks\",\n \"GET analyticsadmin.googleapis.com/v1alpha/properties/{property}/dataStreams\",\n ],\n },\n {\n name: \"ga4_get_server_side_tagging_diagnostics\",\n endpoints: [\n \"GET analyticsadmin.googleapis.com/v1alpha/properties/{property}/dataStreams\",\n \"GET analyticsadmin.googleapis.com/v1alpha/properties/{property}/dataStreams/{stream}/measurementProtocolSecrets\",\n \"GET analyticsadmin.googleapis.com/v1alpha/properties/{property}/dataStreams/{stream}/enhancedMeasurementSettings\",\n \"GET analyticsadmin.googleapis.com/v1alpha/properties/{property}/dataStreams/{stream}/dataRedactionSettings\",\n \"GET analyticsadmin.googleapis.com/v1alpha/properties/{property}/dataStreams/{stream}/eventCreateRules\",\n \"GET analyticsadmin.googleapis.com/v1alpha/properties/{property}/dataStreams/{stream}/eventEditRules\",\n ],\n secretHandling: \"measurementProtocolSecrets.secretValue is redacted\",\n },\n {\n name: \"ga4_run_advanced_funnel_report\",\n endpoints: [\n \"POST analyticsdata.googleapis.com/v1alpha/properties/{property}:runFunnelReport\",\n \"POST analyticsdata.googleapis.com/v1beta/properties/{property}:runReport fallback when enabled\",\n ],\n },\n ],\n agentNotes: [\n \"Prefer diagnostics tools before asking to create GA4 assets; this server has no mutate tools.\",\n \"Treat alpha Admin/Data API fields as best-effort and inspect warnings in every response.\",\n \"Use includeRowSample on Audience Exports only when user-level export rows are explicitly needed.\",\n ],\n }, null, 2) }],\n }));\n}\n","/**\n * google-analytics-mcp-server: an open-source MCP server for Google Analytics 4.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { GA4Config } from \"../../config.js\";\nimport { registerGA4Tools } from \"./tools.js\";\nimport { registerGA4Resources } from \"./resources.js\";\nimport { logger } from \"../../core/logger.js\";\n\nexport function registerGA4(server: McpServer, config: GA4Config): void {\n registerGA4Tools(server, config);\n registerGA4Resources(server);\n logger.info(\"ga4\", \"Registered 27 read tools and 6 resources\");\n}\n","/**\n * google-analytics-mcp-server: an open-source MCP server for Google Analytics 4.\n * Copyright 2026 GetMCPAds. https://www.getmcpads.com\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { z } from \"zod\";\nimport { logger } from \"./core/logger.js\";\nconst configSchema = z.object({\n clientId: z.string().min(1, \"GA4_CLIENT_ID is required\"),\n clientSecret: z.string().min(1, \"GA4_CLIENT_SECRET is required\"),\n refreshToken: z.string().min(1, \"GA4_REFRESH_TOKEN is required\"),\n defaultPropertyId: z.string().optional(),\n logLevel: z.enum([\"debug\", \"info\", \"warn\", \"error\"]).default(\"info\"),\n});\nexport type GA4Config = z.infer<typeof configSchema>;\nexport function loadConfig(): GA4Config {\n const raw = {\n clientId: process.env[\"GA4_CLIENT_ID\"] ?? \"\",\n clientSecret: process.env[\"GA4_CLIENT_SECRET\"] ?? \"\",\n refreshToken: process.env[\"GA4_REFRESH_TOKEN\"] ?? \"\",\n defaultPropertyId: process.env[\"GA4_PROPERTY_ID\"] || process.env[\"GA4_DEFAULT_PROPERTY_ID\"] || undefined,\n logLevel: process.env[\"LOG_LEVEL\"] ?? \"info\",\n };\n const result = configSchema.safeParse(raw);\n if (!result.success) {\n const missing = result.error.issues.map(i => i.message).join(\", \");\n logger.error(\"config\", `Missing credentials: ${missing}`);\n throw new Error(`Missing GA4 credentials: ${missing}`);\n }\n logger.system(\"GA4 MCP Server configured\");\n return result.data;\n}\n"],"mappings":"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","names":["z","ECOMMERCE_METRICS","propertyIdSchema","z","isRecord","ok","buildStringFilter","buildInListFilter","z","z"]}
|