Every AI assistant story in BI ends at the same wall: the model can write SQL, but it does not know your joins, your definitions of active customer or net revenue, or which rows a given user is allowed to see. The Model Context Protocol (MCP) is the emerging standard for closing that gap — a small server exposes tools and resources, and any MCP-capable client (Gemini CLI, Claude Desktop, Cursor, Agentspace, your own agent) can call them. Putting Looker behind an MCP server means the agent asks questions through your LookML semantic layer instead of guessing at raw tables.
This tutorial sets up a Looker MCP server, connects a client, walks through the tools it exposes, and covers the parts most teams get wrong: credentials, permissions, and shaping the model so an agent can actually use it.
What an MCP server for Looker gives you
MCP is a client/server protocol. The server advertises a list of tools with JSON schemas; the client (the thing hosting the LLM) decides when to call them and feeds results back into the conversation. A Looker MCP server is a thin wrapper over the Looker API 4.0, so its tools map to things you already know:
| Tool (typical name) | What it does | Looker API behind it |
|---|---|---|
get_models | List LookML models the credential can see | all_lookml_models |
get_explores | List explores in a model | lookml_model |
get_dimensions / get_measures | Field names, types, labels, descriptions for an explore | lookml_model_explore |
query | Run an inline query (fields, filters, sorts, limit) and return rows | run_inline_query |
get_looks / run_look | Find and execute saved Looks | search_looks, run_look |
get_dashboards | Find dashboards and their elements | search_dashboards |
The important part is what is not there: no arbitrary SQL. The agent must name a model, an explore, and fields that exist. If it hallucinates a field, the API returns an error and the agent retries — a much better failure mode than a confidently wrong SELECT.
Step 1: create a dedicated service user and API key
Do not use your own admin credentials. Create a Looker user specifically for the agent:
- Admin > Users > Add Users, e.g.
mcp-agent@yourcompany.com. - Assign a group with a narrow role. A read-only agent needs
access_data,see_lookml_dashboards,see_looks,see_user_dashboards, andexplore. It does not needdevelop,manage_models,see_sqlor any admin permission. - Scope that role with a model set listing only the models the agent may touch. Everything else becomes invisible, not just discouraged.
- Give the user the same user attributes you would give a restricted human, so
access_filterrules apply to it. - Admin > Users > Edit > API Keys > New API Key, and store the client ID and secret in a secret manager.
Whatever this user cannot see, the agent cannot see. That is your security boundary — not the prompt.
Step 2: run the server
MCP servers are usually launched over stdio (the client spawns the process) or exposed over HTTP/SSE for shared use. A stdio setup with the Looker MCP server package looks like this:
# credentials the server reads at startup
export LOOKERSDK_BASE_URL="https://yourcompany.cloud.looker.com"
export LOOKERSDK_CLIENT_ID="xxxxxxxxxxxxxxxx"
export LOOKERSDK_CLIENT_SECRET="yyyyyyyyyyyyyyyy"
export LOOKERSDK_VERIFY_SSL=true
npx -y looker-mcp-server # or: pipx run looker-mcp, or docker run ...
If the process exits immediately, run it by hand and read stderr: nine times out of ten it is a wrong LOOKERSDK_BASE_URL (it must include the scheme and, for legacy instances, the :19999 API port) or an API key that belongs to a disabled user.
Then register it with a client. Gemini CLI, Claude Desktop and Cursor all use the same JSON shape, in their respective settings files:
{
"mcpServers": {
"looker": {
"command": "npx",
"args": ["-y", "looker-mcp-server"],
"env": {
"LOOKERSDK_BASE_URL": "https://yourcompany.cloud.looker.com",
"LOOKERSDK_CLIENT_ID": "xxxxxxxxxxxxxxxx",
"LOOKERSDK_CLIENT_SECRET": "yyyyyyyyyyyyyyyy"
}
}
}
}
Restart the client and confirm the tools appear in its tool list. Do this before you try any prompt — half of all "the agent cannot see my data" reports are a server that never started.
Step 3: a first conversation
Ask something concrete and watch the tool calls:
"Using the
ecommercemodel, what was net revenue by month for the last 6 months, and which brand grew fastest?"
A healthy trace looks like: get_explores(model="ecommerce") → get_measures(explore="orders") → query(...). The query the agent builds is an ordinary inline query:
{
"model": "ecommerce",
"view": "orders",
"fields": ["orders.created_month", "orders.total_revenue"],
"filters": { "orders.created_date": "6 months" },
"sorts": ["orders.created_month desc"],
"limit": 500
}
Every governed thing still applies: your measure definition, your joins, access_filter, always_filter, caching policy. If the number differs from the dashboard, the model is inconsistent — the agent just found it for you.
Step 4: shape LookML so the agent chooses well
An agent reads names, labels and descriptions and nothing else. Most "bad AI answers" are actually undocumented models.
explore: orders {
label: "Orders"
description: "One row per order. Use for revenue, order volume and customer questions. Revenue is net of refunds."
always_filter: { filters: [orders.created_date: "24 months"] }
access_filter: { field: users.region user_attribute: region }
}
view: orders {
dimension: id { primary_key: yes hidden: yes }
dimension: user_id { hidden: yes }
measure: total_revenue {
type: sum
sql: ${TABLE}.net_amount ;;
label: "Net Revenue"
value_format_name: usd_0
description: "Net revenue in USD after refunds and cancellations. Synonyms: revenue, sales, top line. Do not use Gross Amount for reporting."
}
measure: gross_amount {
type: sum
sql: ${TABLE}.gross_amount ;;
hidden: yes
}
}
Four rules that pay for themselves immediately:
- Hide everything that is not an answer. Keys, foreign keys, staging fields, deprecated duplicates. Fewer fields means fewer wrong choices.
- One measure per business concept, with the losing alternative hidden. Two similar measures is the single biggest source of wrong agent answers.
- Write descriptions for the agent, not for yourself. Include synonyms and the definition of ambiguous words ("last month" = previous calendar month).
- Put a bound on open questions with
always_filterso a vague prompt does not scan five years in BigQuery.
Step 5: guardrails before anyone else gets it
- Read-only by default. If your server implements write tools (create Look, update dashboard, run a schedule), disable them or point them at a dev instance until you have an approval flow.
- Row-level security must be in the model. Never rely on the prompt. Test by giving the service user a restricted
regionattribute and asking for another region's numbers; the answer should come back empty, not filtered by politeness. - Cost. Every tool call is a real query. Cap
limit, keepalways_filter, and watch System Activity formcp-agent@—history.sourceand query counts by user will show you exactly what the agent costs. - Auditing. Because everything is API 4.0 traffic from one user,
historyandapi_usagein System Activity give you a complete log of what the agent asked. Review it weekly for the first month. - Per-user identity. A single service credential means every end user sees what that credential sees. If your use case needs per-person security, run the server with user-level OAuth tokens instead of a shared API key, or restrict the deployment to a group whose members share the same access profile.
Where this fits next to Conversational Analytics and Gemini in Looker
Three overlapping options, different jobs:
- Gemini in Looker — assistive features inside the Looker UI, for Looker users.
- Conversational Analytics API — Google-managed data agents you embed in your product, backed by a Looker explore.
- MCP server — an open door for any agent or developer tool to reach Looker, including engineering assistants that also touch your repo, dbt project and ticket system.
MCP is the one that makes Looker a participant in a wider agent ecosystem rather than a destination. It is also the one that most rewards a clean, well-described, tightly permissioned model — which is exactly the work that was worth doing anyway.
Checklist
- Dedicated service user, narrow role, model set, user attributes set.
- API key in a secret manager, not a config file in Git.
- Server starts by hand before you wire it to a client.
- One explore curated for the agent: hidden keys, single measures, real descriptions,
always_filter. - Security tested by attempting a query the credential must not answer.
- System Activity monitoring for the agent user from day one.
Wiring an agent to Looker is quick; making the semantic layer good enough that its answers are trustworthy is the actual project. That is the work our senior Looker developers do every day — see our AI & Gemini in Looker service and our tutorial on the Conversational Analytics API. If you want a second pair of eyes on your model before you point an agent at it, contact us.