TL;DR
- Google offers two MCP routes into Looker, and neither is GA with an SLA. The Looker-managed MCP server is in Preview. The open-source MCP Toolbox for Databases is provided as-is.
- Every path, including community servers and Scalekit, calls the same Looker API 4.0 and shares the instance's API quota. Identity and tool surface are what differ.
- On Looker (Google Cloud core), admins cannot impersonate regular users through
login_user. A multi-user agent needs per-user OAuth. - No path manages the credential lifecycle for you. API keys outlive SSO deprovisioning, and Looker OAuth refresh tokens last one month.
- Scalekit's Google Looker connector ships 31 tools over per-user OAuth. It vaults and refreshes tokens, logs every call, and can serve the tools over a scoped Virtual MCP server. ## Where your Looker agent starts
Your agent needs Looker. It has to find the right Explore, run a saved Look for the analyst who asked, and sometimes put the result on a weekly schedule. You have five realistic options: the Looker-managed MCP server, Google's MCP Toolbox, community MCP servers, the Looker API 4.0, and Scalekit's Looker connector used directly or over MCP. They differ on capability coverage, auth model, and who owns credentials in production. Here's how to pick.
What each Looker path actually is
Two of these options come from Google, one from the community, one is the raw API, and one is Scalekit's connector. Each one answers the identity question differently: whose Looker permissions does an agent call carry?
The Looker-managed MCP server (Preview)
The Looker-managed MCP server is built into Looker. It is served at LOOKER_INSTANCE_URL/mcp over OAuth 2.1 with PKCE. Google announced it at Next '26, and it is still a Preview feature under Pre-GA terms.
It runs on Looker-hosted Looker (Google Cloud core) and Looker (original) instances. Customer-hosted instances are not supported.
Setup has two admin steps. A Looker admin registers every AI agent as an OAuth client through the API Explorer. Every tool starts disabled until an admin enables it.
Connected agents inherit the authenticating user's Looker roles. Activity is logged in System Activity and, on Google Cloud core, in Cloud Audit Logs. Google's reference is the "Looker-managed MCP server" page in the Looker documentation.
The MCP Toolbox for Databases
The MCP Toolbox for Databases is Google's open-source MCP server. Its Looker tool set first shipped in August 2025. Google's Looker documentation says the Toolbox is provided as-is, is not a supported Google Cloud product, and has no SLA.
You run it yourself, in one of two ways:
- Local stdio process. It reads a Looker API client ID and secret from environment variables.
- Shared service. It runs behind an HTTPS reverse proxy, and MCP clients pass their OAuth tokens through to Looker. Google recommends the Toolbox for customer-hosted instances and for teams that prefer to run their own infrastructure.
Community Looker MCP servers
Several third-party MCP servers wrap the Looker SDK and add tools that the Google options lack. Most run locally over stdio and read a Looker client ID and secret from a config file. Individuals or vendors maintain them, not Google. Their security posture is whatever their authors chose.
The Looker API 4.0
The Looker API 4.0 is the complete application surface. It covers queries, Looks, dashboards, folders, scheduled plans, alerts, render tasks, users, roles, LookML projects, Git, embedding, and Conversational Analytics endpoints. The generated Python SDK exposes several hundred methods.
Authentication comes in three shapes:
-
API credentials. A client ID and secret exchanged at the
loginendpoint for a short-lived token. - OAuth with PKCE. Through an OAuth client application registered on the instance.
-
Admin impersonation.
login_usermints tokens that run as another user. Google's references are the "Looker API authentication" and "Looker API authentication using OAuth" pages.
Scalekit's Google Looker connector
The Google Looker connector is a Scalekit AgentKit connector with 31 prebuilt tools over Google OAuth 2.0. Each end user authorizes once. Scalekit stores and refreshes their tokens, and each connected account records the Looker instance hostname the user works in.
Your agent can reach the tools three ways:
- Call them with
execute_tool. - Load them as native framework tools.
- Connect through a Virtual MCP server. The documented setup uses an OAuth client from the Google Cloud project that hosts a Looker (Google Cloud core) instance.
Capability coverage: what your Looker agent can actually do
Google's two MCP options share one documented tool catalog. The managed server draws from it, filtered by the admin's allowlist. The Toolbox exposes it through its looker and looker-dev prebuilt sets. Scalekit's connector covers a different slice, with more weight on content and delivery.
Querying and content
| Capability | Google MCP catalog | Looker API 4.0 | Scalekit connector |
|---|---|---|---|
| Discover models and Explores | Yes, plus dimensions, measures, filters | Yes | Yes, models and Explores |
| Run an ad hoc Explore query | Yes (query) |
Yes | Yes (googlelooker_run_inline_query) |
| Return generated SQL | Yes (query_sql) |
Yes | Yes, via result_format: sql
|
| Search and run saved Looks | Yes | Yes | Yes |
| Save a query, rerun by ID | Not documented | Yes | Yes (googlelooker_create_query, googlelooker_run_query) |
| Create Looks and dashboards | Yes, including tiles | Yes | Partial: Looks and empty dashboards |
Delivery, administration, and authoring
| Capability | Google MCP catalog | Looker API 4.0 | Scalekit connector |
|---|---|---|---|
| Scheduled deliveries | Not documented | Yes | Yes: create, run once, list, delete |
| Folder management | Not documented | Yes | Yes |
| LookML authoring, Development Mode | Yes | Yes | No |
| Instance health checks | Yes (health_pulse, health_vacuum) |
Yes, logic is yours | No |
| Connection schema browsing | Yes | Yes | No |
| Image or PDF output | Not documented | Yes, render tasks | Partial: PNG and JPG results |
| Users, roles, alerts, embedding | Not documented | Yes | No |
Where each surface stops
Google's catalog is built for analysts and LookML developers working interactively. It stops short of delivery workflows. An agent that must schedule a Look, trigger a one-off send, or reorganize folders needs the API or Scalekit's connector.
Scalekit's connector stops short of LookML authoring, instance health, and admin operations. Those stay API-only unless you build them yourself.
The API has no ceiling. But every endpoint you use is a tool schema you write, test, and maintain. The hard part is writing the schema, not making the API call.
The auth path each option puts you on
Looker API credentials are always bound to a Looker user, and every call runs as that user. So the auth decision is an identity decision: whose permissions does each agent call carry?
Managed MCP: per-user OAuth, admin-registered clients
The managed server accepts only OAuth 2.1 with PKCE. Each user completes a browser consent flow, and the agent inherits that user's roles and content access.
The constraints are in registration and scoping:
- No Dynamic Client Registration during Preview. A Looker admin registers each agent as an OAuth client with a fixed redirect URI.
- No OAuth scopes yet. Access control is the instance-wide tool allowlist plus the user's base permissions. For a B2B product, every customer's Looker admin must register your agent before it can connect. And one allowlist governs every agent on that instance.
MCP Toolbox: one shared key or pass-through OAuth
By default, the Toolbox reads LOOKER_CLIENT_ID and LOOKER_CLIENT_SECRET from its environment. Every tool call then runs as the single Looker user who owns that key, no matter who prompted the agent.
Shared-service mode fixes the identity problem. You set LOOKER_USE_CLIENT_OAUTH=true and publish a Protected Resource Metadata file that points MCP clients at Looker as the authorization server. You then operate the proxy, TLS termination, and the Toolbox process. The same admin OAuth-client registration still applies.
Direct API: API keys, OAuth, or sudo
The API gives you three identity models:
- API keys belong to one user. Google advises against admin-bound keys in production. Create least-privilege service accounts instead.
- OAuth with PKCE issues a per-user access token plus a refresh token that lasts one month.
-
login_userlets an admin mint tokens that run as another user. By default, the resulting activity is attributed to the admin, not the target user. ### The impersonation limit on Looker (Google Cloud core)
The sudo shortcut does not work on Looker (Google Cloud core). There, login_user is denied unless two conditions hold:
- The caller is an API-only service account with the Admin role.
- The target is an embed user. Regular users cannot be impersonated, and Google's API reference tells you to use OAuth instead. If your agent serves real analysts on Google Cloud core, one admin credential cannot act as each of them. Per-user OAuth is the only model that carries each analyst's permissions into the call.
Per-user isolation is required on every path
A multi-tenant B2B agent needs per-user credential isolation on every path. The managed MCP server's OAuth flow gives you a token per user. The API's OAuth flow gives you a token per user. Neither path solves storage, rotation, or revocation.
Those are infrastructure problems regardless of which path you choose. Scalekit's connector keeps one connected account per user. What the user can't do in Looker, the agent can't do.
The risks of community and self-hosted Looker MCP servers
Local and community servers are the fastest way to see Looker data in Claude Desktop or Cursor. They also move a BI credential onto infrastructure that nobody reviewed. These risks show up in practice.
Credential exposure in plaintext config
The standard setup puts a Looker client ID and secret in an mcp.json file or environment block on a developer laptop. Any local process that can read that file can read the secret. Keys copied into repos, dotfiles, and screen shares spread further.
API credentials are bound to a user. So a leaked key exposes every Explore and folder that user can see. If the key belongs to an admin, it exposes the whole instance.
Unreviewed code holding account access
A stdio MCP server runs with your operating system user's permissions and your Looker key's permissions. Configurations that launch the latest package on every start pull in new code without review.
A community server that exposes LookML file edits, folder deletes, or schema reads grants that reach to its own code. It grants the same reach to any prompt injection that lands in your agent.
Maintenance and breakage
Looker changes API contracts on its own schedule. API 3.0 and 3.1 were removed in release 23.18, and query IDs moved to slug values. Google marks the Toolbox's prebuilt Looker tools as pre-1.0 and expects tool changes between versions.
A community server tracks those changes only if its maintainer does. When a tool breaks silently, your agent keeps answering with stale or failed data.
Missing audit trail
Looker's System Activity records API calls against the credential that made them. With a shared key, every call from every end user lands under one Looker user. With default login_user tokens, the activity is attributed to the admin.
Neither record tells an auditor which person asked the agent to run a query, or which agent run touched a dashboard.
Policy and liability exposure
Looker's API authentication is independent of user login. 2FA, SAML, and LDAP do not apply to API calls. Removing a user from a login protocol does not delete their API credentials.
So a key sitting in an agent config outlives the employee's SSO access until someone deletes the key or the Looker user. Add as-is licensing, no SLA, and Pre-GA terms, and your security questionnaire becomes hard to answer.
Recommended reading: MCP security risks for production agents.
What you own in production
For each path, the practical questions are the same: what breaks, what needs your attention, and who fixes it.
On the managed MCP path
Google hosts the server and maintains the tool schemas. You own:
- OAuth client registration on every instance.
- The global tool allowlist.
- Reconnecting clients after allowlist changes, because the tool manifest does not refresh on its own.
- Token storage inside whatever MCP client you run. During Preview, the server runs at fixed capacity, and Google warns of occasional timeouts at peak. There is no SLA.
On the Toolbox or community path
You own everything the managed path requires, plus the process itself:
- Deployment, TLS, and upgrades.
- Version pinning against pre-1.0 tool changes.
- Secret storage for API keys. In shared-service mode, you also own the reverse proxy and the Protected Resource Metadata file. Support is a GitHub issue queue.
On the direct API path
You own everything: schemas, error handling, retries, pagination, token acquisition, refresh, and revocation. You also decide when to adopt new Looker endpoints and contract changes. That control is the point of the API path. It is also the cost.
Quota is shared across every path
Google's documentation for the managed server says agent tool calls consume the instance's standard administrative and query-based API quotas. The same applies to any path that calls the Looker API, including Scalekit's connector.
Agent loops make several calls per question, so monitor API usage from the first pilot. On Scalekit, the RATE_LIMITED error code means Scalekit's own limit was hit. TOOL_ERROR carries Looker's rejection.
When to use each path
Each option wins in specific Looker scenarios. The lists below map each option to the agent it fits best.
Use the Looker-managed MCP server when
- Your analysts query a Looker-hosted instance from Claude Code, Cursor, or Gemini CLI, and they are present for the OAuth consent flow.
- A Looker admin on your side will register the client and curate the allowlist, which is typical for internal deployments.
-
You want LookML authoring and health tools with zero infrastructure, and Preview terms are acceptable.
Use the MCP Toolbox when
Your Looker instance is customer-hosted, where the managed server is not available.
A data team wants LookML development help in an IDE and will own the process that runs it.
-
You need Google's tool catalog in an environment you control, and as-is support is acceptable.
Use the Looker API directly when
You need endpoints that no MCP catalog covers, such as render tasks, alerts, embed URLs, or user and role administration.
You are building a deterministic pipeline, such as a nightly metrics export, and you want to control every request.
-
A background job runs as a dedicated least-privilege service account, and per-user identity is not required.
Use Scalekit's Looker connector when
Your product serves many analysts or many customers, and each call must run as the analyst who authorized it.
The agent runs Looks, queries Explores, and manages scheduled deliveries without a person present at execution time.
You want the same tools available through
execute_tool, native LangChain or Google ADK tools, and a Virtual MCP server.-
Looker is one of several connectors, and you need per-user isolation across all of them.
The credential problem that exists on every path
Whichever path you choose, the agent ends up holding someone's Looker authority. The token type changes from path to path. The infrastructure it needs does not.
N analysts, N credentials
Take a B2B analytics agent that serves 40 analysts across 8 customer instances. It holds 40 Looker credentials, and each one needs:
- Encrypted storage and isolation per tenant.
- Refresh before it expires.
- Re-consent when a refresh token lapses.
- Revocation when the analyst leaves. Refresh tokens expire on their own schedules. Looker OAuth refresh tokens last one month, so an agent that sits idle for five weeks needs re-authorization. An external Google OAuth app left in Testing status issues refresh tokens that expire after seven days.
None of this changes based on whether the token came from an MCP flow or an API flow.
Where Scalekit fits
Scalekit's Google Looker connector handles the OAuth flow, token storage, and refresh for every connected account. The MCP vs API decision does not change your auth infrastructure.
Credentials never touch the agent runtime or the LLM context. Tokens are encrypted with AES-256 and resolved at request time.
Building a Looker agent with Scalekit
The build has four steps:
- Configure the connection.
- Connect each analyst.
- Give the agent only the tools that analyst's connected account is authorized to call.
- Execute. The same connection can also back a Virtual MCP server.
Configure the Looker connection
In the Scalekit dashboard, go to AgentKit > Connections and create a Google Looker connection. Then:
- Paste the Google OAuth client ID and secret from the Google Cloud project that hosts your Looker (Google Cloud core) instance.
- Add Scalekit's redirect URI to that OAuth client.
- Keep Access Type set to Offline so Scalekit receives a refresh token.
The connection name (
googlelookerbelow) must matchconnection_namein your code exactly. A mismatch here is the most common integration error.
Every connected account also needs a Looker Instance Hostname, such as your-company.cloud.looker.com. During testing, you enter it under Connected Accounts. The connector docs cover each step.
Connect the analyst
Install scalekit-sdk-python and python-dotenv, then set SCALEKIT_ENVIRONMENT_URL, SCALEKIT_CLIENT_ID, and SCALEKIT_CLIENT_SECRET. Before the agent runs, confirm that the analyst's connected account is ACTIVE.
import os
from dotenv import load_dotenv
from scalekit import ScalekitClient
load_dotenv()
scalekit_client = ScalekitClient(
env_url=os.environ["SCALEKIT_ENVIRONMENT_URL"],
client_id=os.environ["SCALEKIT_CLIENT_ID"],
client_secret=os.environ["SCALEKIT_CLIENT_SECRET"],
)
actions = scalekit_client.actions
CONNECTION_NAME = "googlelooker" # must match AgentKit > Connections exactly
IDENTIFIER = "analyst_42" # your app's unique ID for this user
response = actions.get_or_create_connected_account(
connection_name=CONNECTION_NAME, identifier=IDENTIFIER
)
if response.connected_account.status != "ACTIVE":
link = actions.get_authorization_link(
connection_name=CONNECTION_NAME, identifier=IDENTIFIER
)
print("Authorize Google Looker:", link.link)
input("Press Enter after authorizing...")
response = actions.get_or_create_connected_account(
connection_name=CONNECTION_NAME, identifier=IDENTIFIER
)
if response.connected_account.status != "ACTIVE":
raise RuntimeError(
f"Looker account is {response.connected_account.status}; authorize and retry."
)
Retrieve the tools this analyst is authorized to call
The agent does not load a flat connector catalog. list_scoped_tools returns only the tools the current analyst's connected account is authorized to call. The tool_names filter narrows that list further, to a read-only analyst role.
Hard-delete tools such as googlelooker_delete_folder never reach the model's context. That tool removes a folder along with the Looks and dashboards it directly contains.
from google.protobuf.json_format import MessageToDict
READ_ONLY_TOOLS = [
"googlelooker_list_models",
"googlelooker_list_explores",
"googlelooker_search_looks",
"googlelooker_search_dashboards",
"googlelooker_run_look",
"googlelooker_run_inline_query",
]
scoped_response, _ = actions.tools.list_scoped_tools(
identifier=IDENTIFIER,
filter={"connection_names": [CONNECTION_NAME], "tool_names": READ_ONLY_TOOLS},
page_size=100,
)
llm_tools = []
for scoped in scoped_response.tools:
definition = MessageToDict(scoped.tool).get("definition", {})
llm_tools.append({
"name": definition.get("name"),
"description": definition.get("description", ""),
"input_schema": definition.get("input_schema", {}),
})
Run the agent loop with the Claude SDK
execute_tool runs each call as the analyst identified by IDENTIFIER. Scalekit resolves the token at request time, so the agent never sees it. Note that googlelooker_run_inline_query applies a 120-second timeout to complex queries.
import anthropic
client = anthropic.Anthropic()
messages = [{
"role": "user",
"content": "Run the Weekly Pipeline by Region Look and tell me which region dropped most.",
}]
while True:
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=2048,
tools=llm_tools,
messages=messages,
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason != "tool_use":
print("".join(b.text for b in response.content if b.type == "text"))
break
tool_results = []
for block in response.content:
if block.type == "tool_use":
result = actions.execute_tool(
tool_name=block.name,
tool_input=block.input,
identifier=IDENTIFIER,
connection_name=CONNECTION_NAME,
)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": str(result.data),
})
messages.append({"role": "user", "content": tool_results})
Use native LangChain tools instead
actions.langchain.get_tools() returns StructuredTool objects and accepts the same scoping filters. The loop needs no schema conversion. Install langchain-anthropic alongside the Scalekit SDK. The LangChain example shows the full setup.
from langchain_anthropic import ChatAnthropic
from langchain_core.messages import HumanMessage, ToolMessage
tools = actions.langchain.get_tools(
identifier=IDENTIFIER,
connection_names=[CONNECTION_NAME],
tool_names=READ_ONLY_TOOLS,
page_size=100,
)
tool_map = {t.name: t for t in tools}
llm = ChatAnthropic(model="claude-sonnet-5").bind_tools(tools)
messages = [HumanMessage("Which Explores in the sales model cover pipeline?")]
while True:
response = llm.invoke(messages)
messages.append(response)
if not response.tool_calls:
print(response.content)
break
for tc in response.tool_calls:
result = tool_map[tc["name"]].invoke(tc["args"])
messages.append(ToolMessage(content=str(result), tool_call_id=tc["id"]))
Serve Looker tools over a Virtual MCP server
For MCP-native frameworks, create a Virtual MCP server once per agent role. The server declares which connections and tools are exposed. Its endpoint is static, and the user's identity arrives with each run through a short-lived session token.
The managed Looker server has one instance-wide allowlist. Here, each agent role gets its own tool set.
from datetime import timedelta
from scalekit.actions.models.mcp_config import McpConfigConnectionToolMapping
# Run once per agent role
vmcp = actions.mcp.create_config(
name="looker-readonly-analyst",
connection_tool_mappings=[
McpConfigConnectionToolMapping(
connection_name=CONNECTION_NAME,
tools=READ_ONLY_TOOLS,
),
],
)
config_id = vmcp.config.id
mcp_server_url = vmcp.config.mcp_server_url
# Run before each agent session
accounts = actions.mcp.list_mcp_connected_accounts(
config_id=config_id, identifier=IDENTIFIER, include_auth_link=True
)
for account in accounts.connected_accounts:
if account.connected_account_status != "ACTIVE":
raise RuntimeError(f"{account.connection_name} needs auth: {account.authentication_link}")
token = actions.mcp.create_session_token(
mcp_config_id=config_id, identifier=IDENTIFIER, expiry=timedelta(minutes=30)
).token
print(f"SCALEKIT_MCP_SERVER_URL={mcp_server_url}")
print(f"SCALEKIT_MCP_SESSION_TOKEN={token}")
Connect a Mastra agent in TypeScript
Mastra connects through @mastra/mcp. The Node.js SDK does not mint session tokens yet, so mint them on your Python backend and pass the URL and token in. Install with npm install @mastra/core@1 @mastra/mcp@2 @ai-sdk/openai@4 dotenv.
Mastra prefixes tool names with the server key. For example, googlelooker_run_look appears as scalekit_googlelooker_run_look. The Mastra example has the full flow.
import { Agent } from '@mastra/core/agent';
import { MCPClient } from '@mastra/mcp';
import { openai } from '@ai-sdk/openai';
import 'dotenv/config';
const mcpServerUrl = process.env.SCALEKIT_MCP_SERVER_URL;
const mcpToken = process.env.SCALEKIT_MCP_SESSION_TOKEN; // minted per analyst, never shared
if (!mcpServerUrl || !mcpToken) {
throw new Error('Set SCALEKIT_MCP_SERVER_URL and SCALEKIT_MCP_SESSION_TOKEN');
}
const mcp = new MCPClient({
servers: {
scalekit: {
url: new URL(mcpServerUrl),
requestInit: { headers: { Authorization: `Bearer ${mcpToken}` } },
},
},
});
try {
const tools = await mcp.listTools();
const agent = new Agent({
id: 'looker-analyst',
name: 'Looker analyst',
instructions: 'Answer questions using saved Looks and Explore queries in Looker.',
model: openai('gpt-4o'),
tools,
});
const result = await agent.generate('Which region dropped most in the Weekly Pipeline Look?');
console.log(result.text);
} finally {
await mcp.disconnect();
}
Multi-tool and multi-tenant agents on one endpoint
A weekly metrics digest agent needs Looker to run Looks and Slack to post the summary. Add a second McpConfigConnectionToolMapping for your Slack connection to the same Virtual MCP server. List only the posting tool the role needs.
One server definition then serves every customer. Before each run, mint a session token for that analyst. The server calls Looker and Slack using only that analyst's connected accounts. No credential sharing between users, and no per-user server configuration.
See when to use a Virtual MCP server for design patterns.
Observe every downstream call
Every tool call is recorded against the connected account that made it. Under AgentKit > Connected Accounts, you can see account status, token refresh history, and tool execution logs. When a call fails, the tool call logs name the provider and tool responsible. Scalekit keeps a 90-day audit trail of connector calls.
Pair that with Looker's System Activity, and you can trace a query from the analyst who asked, through the agent run, to the Looker API call. Auth logs covers the logging surface.
Which one to build against
Match the path to the agent you are building:
- Interactive assistant for your own analysts on a Looker-hosted instance, with an admin to register it: use the managed MCP server, if Preview terms are acceptable.
- LookML development on a customer-hosted instance: run the MCP Toolbox and own it.
- Render tasks, alerts, embedding, or admin endpoints: call the Looker API directly.
- Many analysts or customers, background runs, or tools beyond Looker: per-user OAuth with managed credentials is a requirement. The credential problem is the same on every path. That is what needs production-grade infrastructure.
Get your Looker agent into production
Start with the Google Looker connector docs and the Google Looker connector overview. For a starting point, adapt the revenue forecast commentary agent or browse the GTM and RevOps agent templates. Compare plans on the pricing page.
Building a Looker agent and need help with multi-instance setups, Virtual MCP design, or onboarding your customers' Looker admins? Talk to us for immediate help from an engineer.
Top comments (0)