
Your agent needs to read GA4: channel trends before a board update, a realtime spike during a launch, conversion breakdowns for a Monday digest. Google publishes an official MCP server and two REST APIs, and the instinct is to treat that as a routine MCP versus API choice. It is not. Google's MCP server is local, experimental, and read-only, and that changes the decision the moment a second person uses your agent.
Two of these surfaces belong to Google. The third is how most B2B teams end up shipping. Separate them before comparing them.
Google announced its Model Context Protocol (MCP) server for Google Analytics in July 2025 and still labels it Experimental in the repository. Your MCP client launches it locally as pipx run analytics-mcp, and it wraps the Google Analytics Admin API and Data API. Google does not publish a hosted remote endpoint for it.
Seven tools ship across three groups: get_account_summaries, get_property_details, and list_google_ads_links for configuration lookups; run_report, run_funnel_report, and get_custom_dimensions_and_metrics for core reporting; and run_realtime_report for live traffic. Google's own note on the MCP server is the one that matters: it serves read requests only.
Auth runs through Application Default Credentials with the read-only Analytics scope, analytics.readonly. You establish it either with a gcloud auth application-default login against a desktop or web OAuth client, or through service account impersonation.
The resulting credential lands in a JSON file on disk and is referenced by GOOGLE_APPLICATION_CREDENTIALS in your client config. Note what is absent: no authorization handshake at the protocol layer, and no token bound to a session.
Two APIs sit underneath everything. The Data API at analyticsdata.googleapis.com handles reporting: runReport, runPivotReport, runRealtimeReport, the batch variants, checkCompatibility, getMetadata, and audience exports. The Admin API at analyticsadmin.googleapis.com handles configuration: accounts, properties, data streams, custom dimensions and metrics, key events, Measurement Protocol secrets, Google Ads and Firebase links, change history, and runAccessReport.
Both accept a user account or a service account. There is no API key path and no personal access token, and either credential type has to be granted access to the property inside Google Analytics before it returns a single row.
When a vendor publishes a remote MCP endpoint, Scalekit often carries two connectors for it: an API connector, and an MCP connector that proxies the vendor's own server with auth attached. Google Analytics has no remote endpoint to wrap, so there is one connector.
The Scalekit Google Analytics connector is built on the Admin API and Data API, uses OAuth 2.0 per user, and exposes 66 prebuilt tools with LLM-ready schemas. If you want an MCP interface on top of it, you generate one with a Virtual MCP server rather than deploying anything.
The capability gap here is not a matter of degree. One surface reads, the other surface reads and writes, and the third lets you choose which subset your agent sees.
Every write is out of scope by design. An agent that tags a new key event after a launch, provisions a data stream for a new subdomain, or archives a stale custom dimension cannot do any of it through the MCP server.
Read gaps exist too. Pivot reports, batch reports, checkCompatibility, audience exports, and change history are all absent. checkCompatibility matters more than its name suggests: paired with getMetadata, it is the guardrail that catches an invalid dimension and metric pairing before the agent commits to a runReport that fails and still costs tokens.
Raw event-level data is not in any of the three columns. That path is the BigQuery export, which is why analytics agents that need session stitching or custom attribution usually pair a GA4 connector with a warehouse connector. Event ingestion is also separate. The Measurement Protocol authenticates with a measurement ID and an api_secret, not OAuth, so a write agent that sends server-side events is a different auth problem from a read agent that pulls reports.
This is where the Google Analytics comparison stops resembling the Slack or Notion version of this article. The two paths do not offer different flavors of OAuth. Only one of them has a per-user model at all.
ADC is a machine-level construct. It resolves to a credential file, an impersonated service account, or an attached compute identity, and every request the server makes uses it. There is no MCP authorization handshake, no token per session, and no way for the protocol to distinguish one caller from another.
For a solo analyst wiring the server into Gemini CLI or Claude Code, that is exactly right. The credential is theirs, the machine is theirs, and Google Analytics enforces their own property permissions on every call.
The direct API path gives you a real choice. Authorization Code with a refresh token per user produces one credential per analyst and inherits that analyst's property permissions. A service account produces one shared credential for the whole workspace and inherits whatever access you granted it.
The service account is operationally simpler and strategically worse. Everyone gets identical reach, usually because someone granted the service account broad access for convenience. What the user cannot do, the agent should not be able to do, and a shared service account throws that property away.
Picture forty marketers across eight customer workspaces asking your agent for channel performance. On the MCP path, there is one ADC identity, so either every marketer reads every property that identity can reach, or you run one server process per user and manage forty credential files.
Neither is a deployment model. Per-user credential isolation is the requirement, and the direct API path is the only one of the two that can express it. That framing is expanded in single-tenant versus multi-tenant tool calling.
Both Google surfaces hand you the same operational bill. The difference is how early you notice.
Standard Google Analytics properties allow 200,000 Data API tokens per day, 40,000 per hour, and 10 concurrent requests. Analytics 360 properties get 2,000,000 per day, 400,000 per hour, and 50 concurrent. Most requests cost fewer than 10 tokens, and complex requests cost more.
One number deserves attention before you scale: a single Cloud project can consume at most 35% of a property's hourly tokens, which is 14,000 on a standard property. Your agent does not get the whole bucket even when nothing else is querying.
A single analyst question becomes a metadata lookup, a compatibility check, a core report, and a comparison window. Four calls where a dashboard would have made one, times every user, against a bucket none of them own individually.
Ask for quota back on every call and log it from day one: returnPropertyQuota on the raw API, return_property_quota in the connector's tool input. Exhaustion surfaces as a resource-exhausted error mid-conversation, not as a clean failure at the edge.
The Admin API splits across a stable v1beta channel and a v1alpha channel where Google explicitly expects breaking changes. Audience definitions, access bindings, BigQuery links, expanded data sets, and event create and edit rules all live in alpha. Funnel reporting is alpha on the Data API side.
Building deterministic pipelines on alpha methods is a decision, not an accident. Make it deliberately, and pin the surfaces you depend on.
The Data Access report records every read of Google Analytics data, retains records for up to two years, and is available to users with the Administrator role. Its dimensions include userEmail, userIP, and accessMechanism, which carries a distinct value for reads that arrive through the Google Analytics API.
This is the part teams discover during a security review. If your agent reads through a shared service account, every row of your customer's access report shows the same robot email against thousands of accessCount values. Per-user OAuth keeps that report readable, and it keeps the answer to "which analyst pulled revenue data on the 14th" available inside the customer's own tooling. The broader failure modes are documented in audit trails for agent auth in B2B SaaS.
The split is unusually clean for this tool, because the MCP server was built for a person at a terminal and the APIs were built for software.
Naming the auth model is the easy half. The half that costs engineering time starts after the token is issued.
Per-user OAuth solves scoping and creates inventory. Forty analysts across eight workspaces means forty refresh tokens to encrypt at rest, isolate per tenant, refresh proactively, and revoke the day someone leaves.
Waiting for a 401 before refreshing is where this breaks in production. Multiple agent threads hit expiry at once, each fires its own refresh, and you get a retry storm on top of a property-level quota you were already sharing. The failure modes are catalogued in how to handle token refresh for AI agents.
Neither Google surface gives you a vault, rotation logic, or a revocation flow. The token type differs between the two paths; the infrastructure you need is identical.
The Scalekit Google Analytics connector runs the OAuth flow, stores the credential in a per-tenant vault, refreshes it before expiry, and resolves it at request time so it never enters the model's context. Your agent code passes an identifier, not a token.
Two paths follow, and both start from the same connected account. The Python path calls tools directly. The TypeScript path goes through a Virtual MCP server for frameworks that speak MCP natively.
Configure the Google Analytics connection once in the Scalekit dashboard under AgentKit and Connections. The connection_name you pass in code must match that dashboard entry exactly, because connection names are workspace-specific and this is the single most common integration error.
The agent never holds a Google credential. It holds an identifier, and Scalekit resolves the connected account behind it.
This is not the Google Analytics catalog. list_scoped_tools returns the tools this user's connected account is authorized to call, which is the whole difference between a per-user agent and a shared-credential one.
actions.langchain.get_tools() returns native StructuredTool objects, so there is no schema reshaping between Scalekit and LangChain. For more on how LangChain tool calling works end-to-end, see LangChain tool calling: How it works, where it stops, and how Scalekit completes it.
A reporting agent does not need 66 tools. Define the server once per agent role, listing only the tools that role is allowed to call, and you get a static mcp_server_url back.
Mastra has native MCP support through @mastra/mcp, so it discovers the five scoped tools and their schemas from the URL directly.
Three things change once the connector sits between your agent and GA4, and none of them are about saving OAuth boilerplate.
Every tool call resolves the credential of the user who triggered the agent, so userEmail in your customer's Data Access report stays meaningful. On the Scalekit side, downstream tool calls are logged with the connected account they ran under, giving you the agent-side half of the same trail.
That pairing is what a security questionnaire is actually asking for: attribution in the third-party system and attribution in your own. More on the failure modes in agent tool observability.
Handing a model the full Google Analytics connector means 66 tool definitions in context. At roughly 200 tokens per definition, that is north of 13,000 tokens spent before the agent reads a single row, and a decision space no model handles well.
Scoping to the five tools a reporting agent needs cuts the overhead by most of that and produces more reliable selection. The fix is not better prompting. It is surface reduction, argued at length in token-efficient tool calling.
Virtual MCP servers are created once per agent role, not once per customer. Every run resolves a short-lived, user-scoped credential against the same definition, so a marketing analytics agent spanning Google Analytics, Google Ads, and Search Console is one configuration rather than three OAuth implementations multiplied by your tenant count.
That is also the shape most production analytics agents take. The full catalog is on the connectors page. Understanding the credential ownership patterns across agent tool-calling architectures is worth reviewing before you finalize your approach.
Two honest gaps. Funnel reports and audience definitions are not in the connector's 66 tools, so an agent that needs runFunnelReport or audience management still calls the v1alpha surfaces itself. And Scalekit is a managed dependency in your request path, which is a real architectural decision even when the vault and the audit chain are what you wanted.
If you are one analyst with your own GA4 access and read-only questions, install Google's MCP server and stop reading. It is free, official, and correct for that job.
If your agent serves customers, writes configuration, runs on a schedule, or needs pivot reports and compatibility checks, build against the Data API and Admin API. The MCP server's Application Default Credentials model has no per-user concept, and no amount of deployment engineering will give it one.
The single question that settles it: does more than one identity need to read Google Analytics through your agent? If yes, you are on the API path, and the credential lifecycle for those identities is the infrastructure that decides whether the thing survives its first enterprise security review. For a deeper look at the hidden costs of building this yourself, see the hidden cost of building OAuth internally for AI agents.
Talk to other agent developers solving the same problems in the Scalekit Slack community, or bring an architecture question straight to an engineer through the Talk to us page.
Start here: the Google Analytics connector docs and the Google Analytics connector page.