looker-mcp-server
A Model Context Protocol (MCP) server for the Looker API with full tool coverage, OAuth pass-through, and user impersonation support.
Ask AI about looker-mcp-server
Powered by Claude Β· Grounded in docs
I know everything about looker-mcp-server. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
looker-mcp-server
A full-featured Model Context Protocol (MCP) server for the Looker API. Gives AI assistants direct access to your Looker instance β querying the semantic model, managing content, editing LookML, and administering users β all through a standard MCP interface.
Features
- 160 tools across 15 groups covering the full Looker API surface
- Semantic layer queries β query through LookML models, not raw SQL
- OAuth pass-through β forward user tokens from an upstream gateway or MCP OAuth flow
- User impersonation β admin sudo on self-hosted Looker, OAuth on Google Cloud core
- Dual transport β stdio for local/CLI use, streamable-http for production deployment
- Selective tool loading β enable only the tool groups you need via
--groups - Pluggable identity β swap in custom authentication via the
IdentityProviderprotocol - Health endpoints β
/healthzand/readyzfor container orchestration
Quick Start
Installation
pip install looker-mcp-server
# or
uv add looker-mcp-server
Environment Variables
At minimum, set your Looker instance URL and API3 credentials:
export LOOKER_BASE_URL="https://mycompany.looker.com"
export LOOKER_CLIENT_ID="your-api3-client-id"
export LOOKER_CLIENT_SECRET="your-api3-client-secret"
Run with stdio (for Claude Code, Claude Desktop, etc.)
looker-mcp-server --groups explore,query,schema
Run with HTTP (for production deployment)
LOOKER_TRANSPORT=streamable-http looker-mcp-server --groups all --port 8080
MCP Client Configuration
Claude Code
Add to your Claude Code MCP settings:
{
"mcpServers": {
"looker": {
"command": "looker-mcp-server",
"args": ["--groups", "explore,query,schema,content"],
"env": {
"LOOKER_BASE_URL": "https://mycompany.looker.com",
"LOOKER_CLIENT_ID": "your-client-id",
"LOOKER_CLIENT_SECRET": "your-client-secret"
}
}
}
}
Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"looker": {
"command": "looker-mcp-server",
"args": ["--groups", "explore,query,schema,content"],
"env": {
"LOOKER_BASE_URL": "https://mycompany.looker.com",
"LOOKER_CLIENT_ID": "your-client-id",
"LOOKER_CLIENT_SECRET": "your-client-secret"
}
}
}
}
Tool Groups
Tools are organized into groups that can be selectively enabled. Default groups are marked with *.
| Group | Tools | Description |
|---|---|---|
| explore* | list_models, get_model, get_explore, list_dimensions, list_measures, list_connections | Browse LookML models, explores, and fields |
| query* | query, query_sql, run_look, run_dashboard, query_url, search_content | Run queries through the semantic layer |
| schema* | list_databases, list_schemas, list_tables, list_columns | Inspect underlying database schema |
| content* | list_looks, create_look, update_look, delete_look, list_dashboards, create_dashboard, update_dashboard, delete_dashboard, add_dashboard_element, add_dashboard_filter, generate_embed_url, validate_content | Manage Looks and dashboards |
| board | list_boards, get_board, create_board, update_board, delete_board, get_board_section, create_board_section, update_board_section, delete_board_section, get_board_item, create_board_item, update_board_item, delete_board_item | Curate content with boards, sections, and items |
| folder | list_folders, get_folder, create_folder, update_folder, delete_folder, get_folder_children, get_folder_ancestors, get_folder_looks, get_folder_dashboards | Navigate and manage the folder hierarchy |
| health* | health_pulse, health_analyze, health_vacuum | Instance health checks and usage analysis |
| modeling | list_projects, get_project, create_project, update_project, delete_project, get_project_manifest, get_project_deploy_key, create_project_deploy_key, list_project_files, get_file, create_file, update_file, delete_file, validate_project, list_datagroups, reset_datagroup | LookML project lifecycle, file edits, syntax validation, and datagroup cache management |
| git | get_git_branch, list_git_branches, get_git_branch_by_name, create_git_branch, switch_git_branch, delete_git_branch, deploy_to_production, reset_to_production, get_git_deploy_key, create_git_deploy_key, list_git_connection_tests, run_git_connection_test | Git branch lifecycle, production deploy, SSH deploy-key rotation, and git-connection diagnostics |
| admin | list_users, get_user, create_user, update_user, delete_user, create_credentials_email, send_password_reset, list_roles, get_role, create_role, update_role, delete_role, get_role_groups, get_role_users, list_permissions, list_permission_sets, create_permission_set, update_permission_set, delete_permission_set, list_model_sets, create_model_set, update_model_set, delete_model_set, list_groups, create_group, delete_group, add_group_user, remove_group_user, set_role_groups, set_role_users, set_user_roles, get_user_roles, list_schedules, create_schedule, update_schedule, delete_schedule, run_schedule_once | User, role, RBAC, group, and schedule management |
| connection | get_connection, list_connection_dialects, create_connection, update_connection, delete_connection, test_connection | Database connection CRUD and health checks |
| user_attributes | list_user_attributes, get_user_attribute, create_user_attribute, update_user_attribute, delete_user_attribute, list_user_attribute_group_values, set_user_attribute_group_values, delete_user_attribute_group_value, list_user_attribute_values_for_user, set_user_attribute_user_value, delete_user_attribute_user_value | User attribute definitions plus per-group and per-user value overrides (row-level security, per-developer credentials, filter defaults) |
| credentials | list_credentials_api3, create_credentials_api3, get_credentials_api3, delete_credentials_api3, get_credentials_ldap, delete_credentials_ldap, get_credentials_saml, delete_credentials_saml, get_credentials_oidc, delete_credentials_oidc, get_credentials_google, delete_credentials_google | Non-email credentials β API3 key-pair rotation plus get/delete for LDAP, SAML, OIDC, and Google SSO links |
| audit | get_query_history, get_content_usage, get_pdt_build_log, get_schedule_history, get_user_activity_log, list_running_queries, kill_query, list_active_sessions, get_session, terminate_session, list_project_ci_runs, get_project_ci_run, trigger_project_ci_run | Query history, content usage, PDT build + schedule + event logs via system__activity, plus live-ops (running queries, sessions, CI runs) |
| workflows | provision_connection, bootstrap_lookml_project, deploy_lookml_changes, rollback_to_production, provision_user, grant_access, offboard_user, rotate_api_credentials, audit_query_activity, audit_instance_health, investigate_runaway_queries, find_stale_content, disable_stale_sessions | Task-oriented Layer 2 compositions β provisioning workflows (bootstrap, deploy, provision users) plus ops/audit workflows (offboard, rotate credentials, audit, cleanup) |
Selecting Groups
# Default groups only (explore, query, schema, content, health)
looker-mcp-server
# Specific groups
looker-mcp-server --groups explore,query
# All groups (including board, folder, modeling, git, admin, connection, user_attributes, credentials, audit, workflows)
looker-mcp-server --groups all
Configuration Reference
All settings are configured via environment variables with the LOOKER_ prefix, or via a .env file.
| Variable | Default | Description |
|---|---|---|
LOOKER_BASE_URL | (required) | Base URL of the Looker instance |
LOOKER_CLIENT_ID | API3 client ID for service account | |
LOOKER_CLIENT_SECRET | API3 client secret for service account | |
LOOKER_API_VERSION | 4.0 | Looker API version |
LOOKER_DEPLOYMENT_TYPE | self_hosted | self_hosted or google_cloud_core |
LOOKER_TRANSPORT | stdio | stdio or streamable-http |
LOOKER_HOST | 0.0.0.0 | HTTP bind address |
LOOKER_PORT | 8080 | HTTP port |
LOOKER_SUDO_AS_USER | true | Enable user impersonation when identity headers are present |
LOOKER_SUDO_ASSOCIATIVE | false | Attribute sudo activity to admin (true) or impersonated user (false) |
LOOKER_USER_EMAIL_HEADER | X-User-Email | HTTP header carrying user email for sudo impersonation |
LOOKER_USER_TOKEN_HEADER | X-User-Token | HTTP header carrying pre-exchanged OAuth token |
LOOKER_TIMEOUT | 60.0 | HTTP request timeout in seconds |
LOOKER_MAX_ROWS | 5000 | Default maximum rows for query tools |
LOOKER_VERIFY_SSL | true | Verify TLS certificates |
LOOKER_LOG_LEVEL | INFO | Logging level |
LOOKER_MCP_MODE | dev | dev (permissive) or public (OAuth 2.1 resource-server, MCP 2025-11-25). See MCP-Level Authentication. |
LOOKER_MCP_JWKS_URI | Authorization server JWK Set URL (RFC 7517). Required when LOOKER_MCP_MODE=public. Must be an https:// URL. | |
LOOKER_MCP_ISSUER_URL | Expected iss claim (RFC 8414). Required when LOOKER_MCP_MODE=public. Must be an https:// URL. | |
LOOKER_MCP_RESOURCE_URI | This server's canonical URI for RFC 8707 audience binding and the RFC 9728 PRM resource field. Required when LOOKER_MCP_MODE=public. Must be an https:// URL without fragment. | |
LOOKER_MCP_AUTH_TOKEN | Static bearer token for MCP-level authentication. Deprecated β emits a warning in dev mode, rejected outright in public mode (RFC 9068 Β§2.1 forbids symmetric static bearers for OAuth 2.1 access tokens). Scheduled for removal in a future major release; migrate to LOOKER_MCP_MODE=public. |
Authentication & Impersonation
The server supports three authentication modes, selected automatically based on configuration and request headers.
Mode 1: Service Account (API Key)
The simplest mode β all API calls use the configured service-account credentials.
export LOOKER_CLIENT_ID="your-api3-client-id"
export LOOKER_CLIENT_SECRET="your-api3-client-secret"
export LOOKER_SUDO_AS_USER=false
Mode 2: Admin Sudo (Self-Hosted Looker)
An admin service account impersonates individual users via Looker's login_user API. The user is identified by an email address in the request headers (typically set by an upstream gateway).
export LOOKER_CLIENT_ID="admin-api3-client-id"
export LOOKER_CLIENT_SECRET="admin-api3-client-secret"
export LOOKER_DEPLOYMENT_TYPE=self_hosted
export LOOKER_SUDO_AS_USER=true
When a request arrives with X-User-Email: alice@company.com, the server:
- Logs in with admin credentials
- Looks up Alice's Looker user ID by email
- Creates a sudo session as Alice via
login_user - Executes the tool call as Alice
- Logs out both sessions
Note: On Looker (Google Cloud core),
login_useronly works for Embed-type users. Regular users require OAuth mode.
Mode 3: OAuth Pass-Through (Google Cloud Core)
For Looker (Google Cloud core) deployments where regular users cannot be impersonated via sudo. An upstream gateway performs OAuth token exchange and passes the user's token in a header.
export LOOKER_CLIENT_ID="fallback-api3-client-id"
export LOOKER_CLIENT_SECRET="fallback-api3-client-secret"
export LOOKER_DEPLOYMENT_TYPE=google_cloud_core
export LOOKER_SUDO_AS_USER=true
When a request arrives with X-User-Token: <oauth-access-token>, the server uses that token directly β no login/logout cycle needed.
If no token header is present, the server falls back to service-account mode.
Automatic Mode Selection
When LOOKER_SUDO_AS_USER=true (the default), the server uses a DualModeIdentityProvider that automatically routes:
- Self-hosted β sudo (via
X-User-Emailheader) - Google Cloud core β OAuth (via
X-User-Tokenheader) - No identity headers β service account fallback
Per-Call Admin Impersonation (act_as_user)
Looker dev mode (workspace_id=dev) is per-user-isolated by design. Each user has their own dev workspace; uncommitted LookML changes, the active branch, and dev-mode local branches all live in the calling user's workspace. That means an admin running delete_git_branch against the admin's dev workspace does nothing about a stuck branch in another user's dev workspace.
The git tools accept an optional act_as_user argument so an admin can perform the call as a different user β typically to clean up someone else's stuck dev-workspace state without leaving the MCP for raw HTTP. Accepts either a numeric user ID or an email address (resolved to an ID via Looker's user-search API).
// Example: admin sweeping a stale CI branch out of ci-bot's dev workspace
{
"tool": "delete_git_branch",
"arguments": {
"project_id": "acme_analytics",
"branch_name": "tmp_ci_5bd8888773",
"act_as_user": "ci-bot@example.com"
}
}
Configuration. Per-call admin impersonation is gated by LOOKER_SUDO_AS_USER β that flag is the single kill switch for sudo-capable behavior in the OSS server, and act_as_user respects it. Set LOOKER_SUDO_AS_USER=true (the default when admin credentials are configured) to enable. With LOOKER_SUDO_AS_USER=false, passing act_as_user raises a clear validation error rather than silently running the call under the configured identity β surfacing the misconfiguration at the call site instead of letting it route to the wrong user.
Security model. The MCP forwards capability β it does not gate it. Sudo permission is enforced by Looker server-side: if the configured LOOKER_CLIENT_ID does not have sudo capability, login_user returns HTTP 403 and the tool fails. There is no MCP-side "who may impersonate whom" policy in the open-source server; layer one in via a wrapping IdentityProvider if you need it (see the next section).
Tool coverage (v1). All eight git/workspace-scoped tools accept act_as_user: get_git_branch, list_git_branches, get_git_branch_by_name, create_git_branch, switch_git_branch, delete_git_branch, deploy_to_production, reset_to_production. Project-level tools (deploy keys, connection diagnostics) deliberately do not β they don't depend on per-user dev workspace state.
Audit log. Every argument-driven sudo emits an INFO-level structlog line:
{
"event": "looker.audit.act_as_user",
"tool": "delete_git_branch",
"target_user_id": "77",
"target_user_email": "ci-bot@example.com",
"triggered_by": "argument",
"configured_user": "admin-api3-client-id"
}
This is independent of the trace-level looker.session.sudo debug line and is the right hook for downstream audit pipelines. Header-driven sudo (gateway pattern) is tagged triggered_by="header" on the debug line β looker.audit.act_as_user fires only for explicit per-call admin impersonation.
Mode interaction. act_as_user overrides the inner identity, including OAuth and header-based sudo. This is intentional β an explicit admin override should win over implicit gateway routing β but the underlying credentials must still have sudo capability, which Looker enforces. On Google Cloud core only Embed-type users can be impersonated; for regular GCC users use Mode 3 (OAuth pass-through) instead.
Failure modes.
act_as_useris neither all-digits nor an email (no@) β validation error rejected up front, before any Looker call. Avoids forwarding garbage to/login/{value}where it would surface as an opaque HTTP 400.- Email does not match any Looker user β validation error. Fail-loud is deliberate; silently falling back to the configured identity would let a typo'd email run the action under the wrong user.
LOOKER_SUDO_AS_USER=falseandact_as_useris passed β validation error explaining how to fix (enable sudo or remove the argument).- Configured credentials lack sudo capability β Looker returns 403 on
login_user, surfaced asPermission denied β the current user lacks access.
Extending with Custom Identity Providers
The IdentityProvider protocol is the primary extension point for integrating with custom authentication systems.
from looker_mcp_server.identity import IdentityProvider, LookerIdentity, RequestContext
from looker_mcp_server.server import create_server
from looker_mcp_server.config import LookerConfig
class MyIdentityProvider:
"""Custom identity provider that integrates with your auth system."""
async def resolve(self, context: RequestContext) -> LookerIdentity:
# Extract identity from headers, tokens, etc.
token = context.headers.get("authorization", "").removeprefix("Bearer ")
if token:
# Exchange for a Looker-scoped token via your auth system
looker_token = await my_token_exchange(token)
return LookerIdentity(mode="oauth", access_token=looker_token)
# Fall back to service account
return LookerIdentity(
mode="api_key",
client_id="your-client-id",
client_secret="your-client-secret",
)
# Wire it up
config = LookerConfig()
mcp, client = create_server(config, identity_provider=MyIdentityProvider())
The RequestContext provides:
headersβ HTTP request headers (empty in stdio mode)tool_nameβ name of the MCP tool being invokedtool_groupβ which group the tool belongs toargumentsβ arguments passed to the tool
MCP-Level Authentication
MCP-level authentication (who can connect to the server) has two modes, selected by LOOKER_MCP_MODE.
LOOKER_MCP_MODE=dev (default) β permissive
Intended for local development, stdio deployments, and trust-network scenarios behind an upstream gateway. Two sub-options:
- No MCP-level auth (default) β any client that can reach the transport can connect.
- Static bearer token (deprecated) β set
LOOKER_MCP_AUTH_TOKENand clients must present it. Emits aDeprecationWarningat startup because RFC 9068 Β§2.1 forbids symmetric static bearers for OAuth 2.1 access tokens, and because static bearers don't carry per-user identity or expiry. Scheduled for removal in a future major release β migrate toLOOKER_MCP_MODE=public.
LOOKER_MCP_MODE=public β OAuth 2.1 resource-server (MCP 2025-11-25)
Internet-exposed / compliance-gated deployments. The server:
- Validates every request's
Authorization: Bearer <JWT>header as an OAuth 2.1 access token. - Accepts only
RS256andES256signatures (RFC 9068 Β§2.1). HS256 is hard-rejected at header inspection to close the algorithm-confusion attack vector (CVE-2015-9235). - Caches the authorization server's JWKS (RFC 7517) with a 1-hour TTL and throttled kid-miss refresh (β€1 forced refresh per 5 minutes).
- Enforces
iss(RFC 8414) andaud(RFC 8707) claim binding. - Serves an RFC 9728 Protected Resource Metadata document for client auto-discovery. The spec-canonical URL follows RFC 9728 Β§3 construction:
/.well-known/oauth-protected-resourcewhenLOOKER_MCP_RESOURCE_URIis an origin-only identifier, or/.well-known/oauth-protected-resource<resource-path>when it carries a path. The origin-rooted path is also served as a defensive fallback. - Emits realm-bearing
WWW-Authenticatechallenges on 401 (RFC 7235 Β§4.1 + RFC 9728 Β§5.1) pointing clients at the PRM URL. - Rejects URL-query bearer tokens (
?access_token=,?authorization=) with a 400invalid_requestper OAuth 2.1 Β§5.1.1 β URL-bound tokens leak into referrer headers, proxy logs, and browser history regardless of destination. - Rejects
LOOKER_MCP_AUTH_TOKENoutright β if the static bearer env var is set alongsideLOOKER_MCP_MODE=public, the server fails to start.
Required configuration:
export LOOKER_MCP_MODE=public
export LOOKER_MCP_JWKS_URI="https://auth.example.com/.well-known/jwks.json"
export LOOKER_MCP_ISSUER_URL="https://auth.example.com"
export LOOKER_MCP_RESOURCE_URI="https://looker-mcp.example.com/mcp"
All three URIs must be absolute https:// URLs; the server fails closed at startup with a typed DeploymentPostureError if any are missing, malformed, or use http://. The LOOKER_MCP_RESOURCE_URI must not carry a fragment (RFC 9728 Β§3).
Deprecation timeline for LOOKER_MCP_AUTH_TOKEN
- This release (0.13.0) β deprecated in
devmode (warning emitted), rejected inpublicmode (startup failure). - Future major release β removed entirely.
If you currently rely on LOOKER_MCP_AUTH_TOKEN for gateway-level MCP protection, plan the migration now: either stand up an authorization server that issues OAuth 2.1 access tokens bound to aud=<LOOKER_MCP_RESOURCE_URI>, or keep the server in dev mode behind a trusted network perimeter.
Health Endpoints
When running in HTTP mode, the server exposes:
GET /healthzβ liveness probe (always returns 200 if server is running)GET /readyzβ readiness probe (verifies Looker connectivity with a login/logout cycle)GET /.well-known/oauth-protected-resourceβ RFC 9728 Protected Resource Metadata (only whenLOOKER_MCP_MODE=public). WhenLOOKER_MCP_RESOURCE_URIhas a path, the same document is also served at/.well-known/oauth-protected-resource<resource-path>β that is the spec-canonical URL per RFC 9728 Β§3, and the one referenced byresource_metadata=...in 401WWW-Authenticatechallenges.
Development
# Clone
git clone https://github.com/ultrathink-solutions/looker-mcp-server.git
cd looker-mcp-server
# Install dependencies
uv sync --locked --dev
# Run quality checks
uv run ruff check . # lint
uv run ruff format . # format
uv run pyright # type check
uv run pytest tests/ -v # tests
See CONTRIBUTING.md for contribution guidelines.
