{"slug": "mcp-configuration-for-looker-with-codex", "title": "MCP Configuration for Looker with Codex", "summary": "Google Cloud has published a guide for configuring OpenAI's Codex CLI to work with Looker over the Model Context Protocol (MCP), marking the third iteration of the same integration approach previously demonstrated with Gemini CLI, Antigravity CLI, and Claude Code. The setup leverages Google's MCP Toolbox and Google Skills repository to connect the business intelligence platform to Codex, with the Looker side of the stack remaining unchanged across all client variations.", "body_md": "This article covers the MCP setup and configuration for using Looker with Codex to enhance and extend Looker operations over MCP.\n\nThis paper is the third pass at the same idea. The original used Gemini CLI:\n\n[MCP Configuration for Looker with Gemini CLI](https://medium.com/google-cloud/mcp-configuration-for-looker-with-gemini-cli-55e5671197fb)\n\nthen Antigravity CLI:\n\n[MCP Configuration for Looker with Antigravity CLI](https://dev.to/gde/mcp-configuration-for-looker-with-antigravity-cli-504d)\n\nthen Claude Code:\n\n[MCP Configuration for Looker with Claude Code](https://dev.to/gde/mcp-configuration-for-looker-with-claude-code-21jh)\n\nIn this updated version, Codex is used to integrate Looker functionality. The Looker side of the stack does not change at all — that is the whole point of MCP. What changes is the client: how the server gets registered, how tool calls get approved, and where the agent reads its project instructions from.\n\nLooker is a cloud-based business intelligence (BI) and data analytics platform owned by Google Cloud that enables organizations to analyze, visualize, and share data in real-time. It uses a unique modeling language called LookML to define data relationships, offering a centralized “single source of truth” for metrics. Looker focuses on embedded analytics and live data exploration rather than storing data itself.\n\nMore information is available here:\n\n[Looker business intelligence platform embedded analytics](https://cloud.google.com/looker)\n\nSemantic layer is where all the cool kids hang out.\n\nUnless you have been living off grid without Internet- MCP is the new universal connector and next “Big Thing”.\n\nMore information is here:\n\n[What is Model Context Protocol (MCP)? A guide](https://cloud.google.com/discover/what-is-model-context-protocol)\n\nGoogle has gone all-in for all the core Cloud services to provide connections over MCP. An overview is here:\n\n[Google Cloud MCP servers overview | Google Cloud Documentation](https://docs.cloud.google.com/mcp/overview)\n\nMCP Toolbox is the “swiss army” knife that connects your data sources to MCP.\n\n[Use Looker with MCP, Gemini CLI and other Agents | Google Cloud Documentation](https://docs.cloud.google.com/looker/docs/connect-ide-to-looker-using-mcp-toolbox)\n\nFor a more detailed step by step setup instructions — there is a full codelab that goes through the setup:\n\n[Connect Gemini CLI to Looker with MCP Toolbox | Google Codelabs](https://codelabs.developers.google.com/codelabs/looker-mcp-toolbox#0)\n\nand a further deep dive is here:\n\n[Use Looker with MCP, Gemini CLI and other Agents | Google Cloud Documentation](https://docs.cloud.google.com/looker/docs/connect-ide-to-looker-using-mcp-toolbox)\n\nCodex is OpenAI's terminal-driven, agent-assisted coding CLI — the same category of tool as Gemini CLI, Antigravity CLI and Claude Code, and like all of them it ships a full MCP client.\n\nInstall it with npm:\n\n```\nnpm install -g @openai/codex\n```\n\nor with Homebrew:\n\n```\nbrew install codex\n```\n\nThen authenticate — Codex will open a browser to sign in with your ChatGPT account, or you can supply an API key:\n\n```\ncodex login\n```\n\nVerify the install:\n\n```\ncodex --version\n```\n\nGoogle Skills give your MCP client well known approaches to work with the core Google products including Big Query.\n\nThe full details are here:\n\n[Level Up Your Agents: Announcing Google's Official Skills Repository | Google Cloud Blog](https://cloud.google.com/blog/topics/developers-practitioners/level-up-your-agents-announcing-googles-official-skills-repository)\n\nTo install the Skills:\n\n```\nnpx skills install github.com/google/skills\n```\n\nThis vendors the skills into `.agents/skills/`\n\nand records them in `skills-lock.json`\n\n. They are client-neutral markdown, so the same checkout serves Codex, Claude Code and Gemini CLI.\n\nThat was a lot of setup! But wait- there is more! So what is different about this lab compared to all the others out there?\n\nThis demo is one of the first deep dives into configuring Looker for MCP with Codex. Codex provides a complete working environment with a full MCP client. Looker exposes the key features of the platform over the MCP layer.\n\nThe interesting wrinkle in the Codex version is **approvals**. Roughly half of the ~50 Looker tools mutate your live instance — `make_look`\n\n, `make_dashboard`\n\n, `add_dashboard_element`\n\n, the `*_project_file`\n\nfamily, the git and dev-mode tools. Codex has a first-class per-server approval mode, so this repo pins write tools behind a confirmation prompt while leaving discovery and querying to run freely. Read on.\n\nThe strategy for configuring Looker with MCP is an incremental step by step approach.\n\nFirst, the Looker configuration settings are retrieved. Then, these settings are used to configure Codex. Finally- Codex is used as a MCP client to the Looker environment. Several samples are run using the Looker MCP Tools directly from Codex.\n\nFor Looker (Google Cloud core) — Admins do not directly create keys for standard users; instead, they enable the permission for users to manage their own.\n\nNavigate to the [Looker Admin Users page](https://docs.cloud.google.com/looker/docs/admin-panel-users-users) (Admin > Users).\n\nTo connect to the Looker setup — you need to derive your Looker Base URL. Typically this will be the hostname in the Looker app domain.\n\nFor the test instance- this is an example of what the URL looks like (note the HTTPS prefix and no trailing slash):\n\n```\nhttps://780eb09e-7dab-4076-9ec1-ecf9d8414630.looker.app\n```\n\nFirst Login to your Looker User environment. Go to Profile->Account (in upper right hand side) and bring up the user settings:\n\nIf the API Key box is unavailable- contact your Admin to enable the API setup on a per user basis.\n\nOnce you have access to create API keys- the settings will look similar to this:\n\nThen click the “Manage” button to setup the API Keys:\n\nClick Create New API key to generate the API Key. Save the **Client ID** and **Client Secret**.\n\nAt this point you should have a working Shell environment and a working Codex installation. All of the relevant code examples and documentation is available in GitHub.\n\nThe next step is to clone the GitHub repository to your local environment:\n\n```\ncd ~\ngit clone https://github.com/xbill9/looker-mcp-codex\ncd looker-mcp-codex\n```\n\nThen run **init.sh** from the cloned directory.\n\nThe script will attempt to determine your shell environment and set the correct variables:\n\n```\nsource init.sh\n```\n\nThis helper script will prompt for your Looker Instance details:\n\n``` bash\nxbill@penguin:~/looker-mcp-codex$ source set_env.sh\nLooker Base URL (e.g. https://your-company.looker.com): https://780eb09e-7dab-4076-9ec1-ecf9d8414630.looker.app\nLooker Client ID:\nLooker Client Secret:\nDownloading MCP Toolbox binary...\nDownloading from https://storage.googleapis.com/mcp-toolbox-for-databases/v1.6.0/linux/amd64/toolbox...\n  % Total % Received % Xferd Average Speed Time Time Time Current\n                                 Dload Upload Total Spent Left Speed\n100 292M 100 292M 0 0 71.8M 0 0:00:04 0:00:04 --:--:-- 71.8M\nSuccessfully installed MCP Toolbox binary (v1.6.0).\nEnvironment successfully set up.\n\nCurrent Environment (.env) — secret masked:\nGOOGLE_GENAI_USE_VERTEXAI=True\nGOOGLE_CLOUD_PROJECT=comglitn\nGOOGLE_CLOUD_LOCATION=us-central1\nLOOKER_BASE_URL=https://780eb09e-7dab-4076-9ec1-ecf9d8414630.looker.app\nLOOKER_CLIENT_ID= **************\nLOOKER_CLIENT_SECRET= ********\nLOOKER_VERIFY_SSL=true\nLOOKER_TOOLBOX=/home/xbill/looker-mcp-codex/toolbox\n```\n\nIf your session times out or you need to re-authenticate- you can run the **set_env.sh** script to reset your environment variables:\n\n```\nsource set_env.sh\n```\n\nOne difference worth calling out versus the Claude Code write-up: with Codex you do **not** strictly need to `source`\n\nthe script before every session. The launcher reads `.env`\n\nitself at process start. Sourcing is still the better habit, because it also puts `LOOKER_*`\n\ninto your shell so you can drive `toolbox`\n\n(or the Looker CLI) by hand.\n\nCodex reads MCP servers from TOML. This repo ships a project-scoped `.codex/config.toml`\n\n:\n\n```\n[mcp_servers.\"looker-toolbox\"]\ncommand = \"bash\"\nargs = [\"./start-looker-mcp.sh\"]\ncwd = \".\"\nenabled = true\nstartup_timeout_sec = 30\ntool_timeout_sec = 120\ndefault_tools_approval_mode = \"writes\"\n```\n\nFour of those lines are the whole story:\n\n`command`\n\n/ `args`\n\n`toolbox`\n\ndirectly. That keeps the config file free of both secrets and shell quoting.`startup_timeout_sec = 30`\n\n`tool_timeout_sec = 120`\n\n`run_dashboard`\n\nagainst a ten-tile dashboard is ten warehouse queries. Two minutes is a realistic ceiling.`default_tools_approval_mode = \"writes\"`\n\nThe launcher, `start-looker-mcp.sh`\n\n, is deliberately boring:\n\n``` bash\n#!/bin/bash\n\nset -eu\n\nPROJECT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\n\nif [ -f \"$PROJECT_DIR/.env\" ]; then\n    set -a\n    # shellcheck disable=SC1091\n    source \"$PROJECT_DIR/.env\"\n    set +a\nfi\n\nLOOKER_VERIFY_SSL=\"${LOOKER_VERIFY_SSL:-true}\"\nexport LOOKER_VERIFY_SSL\n\nif [ -z \"${LOOKER_BASE_URL:-}\" ] || [ -z \"${LOOKER_CLIENT_ID:-}\" ] || [ -z \"${LOOKER_CLIENT_SECRET:-}\" ]; then\n    echo \"looker-toolbox: Looker credentials are missing. Run: source set_env.sh\" >&2\n    exit 1\nfi\n\nexec \"${LOOKER_TOOLBOX:-$PROJECT_DIR/toolbox}\" --stdio --prebuilt looker,looker-dev\n```\n\nCredential resolution order:\n\n`.env`\n\nin the project root`LOOKER_*`\n\nvariables`.env`\n\n.`source set_env.sh`\n\n, rather than dying with an opaque MCP connection error.That third case is worth the eight lines it costs. Every MCP client, Codex included, reports a server that exits during startup as a generic transport failure. Failing loudly with a sentence of English turns a twenty-minute debug into a five-second one.\n\nThe repository also retains a Claude-compatible `.mcp.json`\n\nthat launches the same binary through a `bash -c`\n\nwrapper. Both files contain only variable references, so both are safe to commit.\n\nCodex will not load a project-scoped config from a directory it does not trust. On first launch inside the repo it will ask; approve it once and the setting sticks:\n\n``` bash\nxbill@penguin:~/looker-mcp-codex$ codex\n\n  You are running Codex in ~/looker-mcp-codex\n\n  Since this folder is not version-control trusted, choose how to proceed:\n\n  > 1. Yes, allow Codex to work in this folder\n    2. No, exit\n```\n\nIf you would rather register the server globally instead of per-project, put the same `[mcp_servers.\"looker-toolbox\"]`\n\nblock in `~/.codex/config.toml`\n\nand use an absolute path for `command`\n\n/`cwd`\n\n. Project-scoped is the better default here — the launcher, the `.env`\n\nand the `toolbox`\n\nbinary all live in the checkout, so the config travels with them.\n\nStart Codex from the project directory:\n\n``` bash\nxbill@penguin:~/looker-mcp-codex$ codex\n```\n\nThen use **/mcp** to confirm the server came up:\n\n```\n> /mcp\n\n  MCP Servers\n\n  looker-toolbox   ✔ connected   45 tools\n    command  bash ./start-looker-mcp.sh\n    cwd      /home/xbill/looker-mcp-codex\n    approval writes\n```\n\nYou can also check without entering the TUI at all, which is handy in CI or when scripting a machine setup:\n\n``` bash\nxbill@penguin:~/looker-mcp-codex$ codex mcp list\nName            Command  Args                   Env  Cwd  Status\nlooker-toolbox  bash     ./start-looker-mcp.sh  -    .    enabled\n```\n\nand confirm Codex actually parsed every option — a typo'd key is silently ignored, so this is the fast way to prove the approval mode took effect:\n\n``` bash\nxbill@penguin:~/looker-mcp-codex$ codex mcp get looker-toolbox\nlooker-toolbox\n  enabled: true\n  transport: stdio\n  command: bash\n  args: ./start-looker-mcp.sh\n  cwd: .\n  env: -\n  startup_timeout_sec: 30\n  tool_timeout_sec: 120\n  default_tools_approval_mode: writes\n  remove: codex mcp remove looker-toolbox\n```\n\nNow ask for the inventory:\n\n```\n> list the looker mcp tools\n\nHere are the Looker MCP tools available in this session, grouped by function:\n\nMetadata / Discovery\n\n- get_models — list LookML models\n- get_explores — list explores\n- get_dimensions — list dimensions in an explore\n- get_measures — list measures in an explore\n- get_filters — list filters\n- get_parameters — list parameters\n- get_dashboards — list dashboards\n- get_looks — list Looks\n- get_projects — list LookML projects\n\nQuerying / Running\n\n- query — run a Looker query\n- query_sql — run raw SQL against a connection\n- query_url — build a query URL\n- run_look — run a saved Look\n- run_dashboard — run a dashboard\n- run_lookml_tests / get_lookml_tests — LookML data tests\n\nConnections / Database Introspection\n\n- get_connections\n- get_connection_databases\n- get_connection_schemas\n- get_connection_tables\n- get_connection_table_columns\n\nContent Creation                                    [approval required]\n\n- make_look — create a Look\n- make_dashboard — create a dashboard\n- add_dashboard_element — add a tile to a dashboard\n- add_dashboard_filter — add a filter to a dashboard\n- create_view_from_table — generate a LookML view from a DB table\n- generate_embed_url — create an SSO embed URL\n\nLookML Project / File Management                    [approval required]\n\n- get_project_files / get_project_file / create_project_file /\n  update_project_file / delete_project_file\n- get_project_directories / create_project_directory / delete_project_directory\n- validate_project\n\nGit (LookML version control)                        [approval required]\n\n- list_git_branches / get_git_branch / create_git_branch /\n  switch_git_branch / delete_git_branch\n- dev_mode — toggle development mode\n\nHealth / Diagnostics\n\n- health_analyze\n- health_pulse\n- health_vacuum\n\nThat's 51 tools total. The three groups marked [approval required] will prompt\nbefore running, per default_tools_approval_mode = \"writes\" in .codex/config.toml.\n\nWant me to run one of them (models, connections, or dashboards) to verify connectivity?\n```\n\nThis is where the Codex configuration earns its keep. Codex separates two things that other clients tend to conflate: the **sandbox** (what the agent may do to your filesystem and network) and **tool approval** (which MCP tools may fire without a human in the loop).\n\n`default_tools_approval_mode = \"writes\"`\n\nmeans: run read-only tools freely, prompt before anything that changes state. A `make_dashboard`\n\ncall surfaces as:\n\n```\n  ⚠ looker-toolbox › make_dashboard  wants to run\n\n    title        VIP Customer Intelligence v2\n    description  Real-time insights into top-performing revenue segments\n    model        advanced_ecomm\n\n  > 1. Yes, run it\n    2. Yes, and don't ask again for make_dashboard\n    3. No, and tell Codex what to do differently\n```\n\nOption 2 is worth understanding before you reach for it. Approving `make_dashboard`\n\nfor the session is low-risk — it creates new content. Approving `delete_project_file`\n\nfor the session is not. The Looker toolsets do not distinguish create from destroy in their naming, so read the tool name, not the category.\n\nThe blunter instruments still exist if you want them — `--ask-for-approval never`\n\nfor a fully unattended run, `--sandbox read-only`\n\nto clamp the agent's filesystem access. Neither is a substitute for the per-tool mode: sandbox settings govern the agent's own shell, not what an MCP server does to a remote SaaS instance over HTTPS. A read-only sandbox will happily let a tool call drop a LookML file on your Looker instance.\n\nThe other half of the safety story is `AGENTS.md`\n\n, which Codex reads automatically from the project root. That is where this repo records the rules the config file cannot express — pass filter values bare, work LookML changes on a dev-mode branch, don't use mutating tools for routine verification, never print the client secret. Same role as `CLAUDE.md`\n\nfor Claude Code and `GEMINI.md`\n\nfor Gemini CLI; all three live side by side in the checkout.\n\nAt this point- Codex is set up with the MCP Toolbox and has direct access to all the Looker MCP Tools. Any operations with the Tools come back directly into the Codex context.\n\nTo get an overview of Dashboards:\n\n```\n> get_dashboards\n\n• looker-toolbox › get_dashboards\n\nHere are the dashboards in your Looker instance (16 total):\n\n  ID   Title                                       Description\n  ──   ─────────────────────────────────────────   ──────────────────────────────────────\n   1   2) Business Pulse - Intermediate            —\n   2   1) Business Pulse - Basic                   —\n   3   3) Business Pulse - Advanced                —\n   4   Agreement for Shared Use of this Instance   —\n   5   test                                        —\n   6   test                                        —\n   7   Inventory                                   —\n   8   TestJC                                      —\n   9   Self-serve industry                         —\n  10   TestOrdersByCity                            —\n  11   My Dashboard                                —\n  12   test-orders                                 —\n  13   E-commerce Overview                         Key e-commerce metrics\n  14   💎 VIP Customer Intelligence                Advanced LTV and Ranking logic\n  15   ☁️ Cloud Optimization Hub                    AI-driven cost savings, Recommender API\n  16   Top Users & Countries Overview              Top spenders and high-performing countries\n\nA few observations:\n- Several test/scratch dashboards (5, 6, 8, 10, 11, 12) look like leftovers — cleanup candidates.\n- The numbered \"Business Pulse\" set (1–3) are example/tutorial dashboards.\n- The substantive content is 13–16.\n\nWant me to run one of these (run_dashboard), or inspect the tiles on a specific one?\n```\n\nYou can also get models:\n\n```\n> get_models\n\n• looker-toolbox › get_models\n\nHere are the LookML models in your instance (11 total):\n\n  Model                    Label                 Project                             Connection(s)\n  ──────────────────────   ───────────────────   ─────────────────────────────────   ─────────────────────────\n  basic_ecomm              Z) Sample LookML      sample_thelook_ecommerce            default_bigquery_connection\n  intermediate_ecomm       Z) Sample LookML      sample_thelook_ecommerce            default_bigquery_connection\n  advanced_ecomm           Z) Sample LookML      sample_thelook_ecommerce            default_bigquery_connection\n  extension-api-explorer   Extension API Expl.   marketplace_extension_api_explorer  default_bigquery_connection\n  data_block_acs_bigquery  Census Data Block     marketplace_acs_census-v2           american-community-survey\n  gcp_billing_block        Google Cloud Billing  marketplace_gcp-billing             billing-export-sample\n  chrome-ux-block          Chrome UX Block       marketplace_chrome-ux-block         chrome-ux\n  london_bicycles          London Bicycles       london_bicycles                     london-bicycles\n  gde_rk                   Gde Rk                gde_rk                              (8 connections)\n  data-dictionary          Data Dictionary       marketplace_extension_data_dict.    default_bigquery_connection\n  bq_agent_analytics       Bq Agent Analytics    agent_events                        default_bigquery_connection\n\nNotable groupings:\n- theLook e-commerce samples (basic/intermediate/advanced_ecomm) — Looker's standard demo dataset.\n- Marketplace blocks — Census/ACS, GCP Billing, Chrome UX, Data Dictionary, API Explorer.\n- Public datasets — london_bicycles.\n- gde_rk — a multi-connection catch-all model.\n\nWant me to pull the explores for one of these? advanced_ecomm is the richest starting point.\n```\n\nAnd drill into the details — the `advanced_example_ecommerce`\n\nexplore is where the interesting LookML lives:\n\n```\n> get_measures for advanced_example_ecommerce\n\n• looker-toolbox › get_measures\n\n20 measures, grouped by view:\n\norder_items — core sales metrics\n  order_items.count                 # of Order Items        count\n  order_items.total_sale_price      Sales                   sum\n  order_items.average_sale_price    Average Price           average\n  order_items.first_order_date      First Order Date        date\n  order_items.count_for_drill_*     drill-support counts    count\n\nusers / products / inventory_items\n  users.count                       # of Users              count_distinct\n  products.count                    Count Distinct Products count_distinct\n  inventory_items.total_cost        Total Cost              sum\n  inventory_items.average_cost      Average Cost            average\n\nUser-summary measures (filtered vs. lifetime symmetric-aggregate patterns)\n  user_summary_filtered.total_sales_for_these_users      Sales (Filtered Summary)   sum_distinct\n  user_summary_unfiltered.total_sales_for_these_users    Sales (Lifetime)           sum_distinct\n  user_summary_filtered.total_items_for_these_users      Items (Filtered Summary)   sum_distinct\n  user_summary_unfiltered.total_items_for_these_users    Items (Lifetime)           sum_distinct\n\nSpecial measures — advanced ratio/comparison\n  user_summary_and_order_items_fields.percent_of_lifetime_unfiltered_sales\n  user_summary_and_order_items_fields.filtered_average_vs_unfiltered_average\n\nNotes:\n- The filtered vs. lifetime pairs showcase Looker's pattern for comparing a filtered\n  subset against a user's full lifetime totals.\n- The count_for_*_drill measures exist to support drill-down on dashboard tiles.\n```\n\nLet's see what VIP tiers look like:\n\n```\n• looker-toolbox › query\n\nVIP Tiers — Lifetime Revenue Rank Group (advanced_example_ecommerce)\n\n  Rank Group    Users    Total Sales   Order Items   Avg Sale Price\n  ───────────   ──────   ───────────   ───────────   ──────────────\n  1st 25%       15,569    $4,134,224        53,458           $77.34\n  2nd 25%       16,554    $1,931,678        36,005           $53.65\n  3rd 25%       16,557      $947,615        24,026           $39.44\n  Top 1000 ⭐    1,000      $731,207         4,893          $149.44\n  Bottom 25%    16,560      $382,605        17,800           $21.49\n\nWhat this shows\n\n- Steep revenue concentration. The top quartile drives $4.13M — about 55% of the\n  ~$7.4M lifetime total — while the bottom 25% contributes only ~5%, despite having\n  more users.\n- The \"Top 1000\" is a distinct elite segment, not a quartile — it overlaps with the\n  1st 25%. Just 1,000 users generate $731K, and their $149 average sale price is\n  nearly 2× the top quartile's and 7× the bottom's.\n- Basket value declines cleanly down the tiers: $77 → $54 → $39 → $21. Rank\n  correlates strongly with per-item spend, not just order count.\n\nReading note: the quartile groups partition all purchasing users, while Top 1000 is\na separate flag sitting inside the 1st quartile — its dollars are also counted in the\n1st 25% row. Don't sum the column.\n```\n\nNow build a Look — and this is where the `writes`\n\napproval mode fires for the first time:\n\n```\n> save that as a Look\n\n  ⚠ looker-toolbox › make_look  wants to run\n\n    model     advanced_ecomm\n    explore   advanced_example_ecommerce\n    title     VIP Tiers — Lifetime Revenue Rank Groups\n    fields    user_summary_unfiltered.user_revenue_rank_group,\n              users.count, order_items.total_sale_price,\n              order_items.count, order_items.average_sale_price\n    sorts     order_items.total_sale_price desc\n    vis       looker_bar\n\n  > 1. Yes, run it\n    2. Yes, and don't ask again for make_look\n    3. No, and tell Codex what to do differently\n\n  [1]\n\n• looker-toolbox › make_look\n\n✅ Look created — \"VIP Tiers — Lifetime Revenue Rank Groups\"\n\n- ID: 8\n- URL: /looks/8\n- Viz: horizontal bar chart, sorted by total sales, value labels on\n- Saved to: your personal folder\n\nWant me to add it to a dashboard, or create companion Looks (Top 1000 by\ncountry/category)?\n```\n\nThe prompt shows the full argument payload before anything touches the instance. That is the part worth having — you can see `explore`\n\n, `fields`\n\nand `folder`\n\nand catch a wrong model before it creates content, rather than after.\n\nOnce the Tools are exposed by MCP they have full access to Looker resources. Time to update a Dashboard — three mutations, batched into one approval round:\n\n```\n> add it to dashboard 14 and create the companion Looks\n\nI'll do all three — add the tile to dashboard 14, and create both companion Looks.\n\n  ⚠ looker-toolbox › add_dashboard_element  →  dashboard_id 14      [1] approved\n  ⚠ looker-toolbox › make_look  →  \"Top 1000 VIPs by Country\"       [1] approved\n  ⚠ looker-toolbox › make_look  →  \"Top 1000 VIPs by Category\"      [1] approved\n\n• looker-toolbox › (3 calls)\n\nAll three succeeded. Pulling the underlying data so you can see what they contain.\n\n• looker-toolbox › (2 calls)\n\n✅ Dashboard tile added\n\"VIP Tiers — Lifetime Revenue Rank Groups\" is now a tile on 💎 VIP Customer\nIntelligence (dashboard 14).\n\n✅ Look 9 — Top 1000 VIPs by Country (/looks/9)\n\n  Country          VIP Users   Total Sales   Avg Price\n  ──────────────   ─────────   ───────────   ─────────\n  China                  359      $259,129     $147.23\n  United States          224      $163,185     $147.41\n  Brasil                 143      $106,613     $155.64\n  South Korea             53       $37,381     $129.34\n  France                  52       $37,178     $149.31\n  Spain                   47       $35,646     $163.51\n  Germany                 43       $32,112     $158.19\n  United Kingdom          37       $28,494     $178.09\n  …6 more (AU, BE, JP, PL)\n\n✅ Look 10 — Top 1000 VIPs by Product Category (/looks/10)\n\n  Category              Total Sales   Items   Avg Price\n  ───────────────────   ───────────   █████   ─────────\n  Outerwear & Coats        $245,412     664     $369.60\n  Suits & Sport Coats       $70,838     278     $254.81\n  Jeans                     $58,421     423     $138.11\n  Active                    $48,295     220     $219.52\n  Sweaters                  $36,618     325     $112.67\n  Dresses                   $35,942     185     $194.28\n  …9 more\n\nNotable insights\n- Geography is concentrated: China + US + Brasil = ~726 of the 1,000 VIPs. The UK\n  has the highest per-item spend ($178) despite few users.\n- Outerwear dominates category spend — $245K, more than the next three categories\n  combined, at a $369.60 average sale price. High-ticket outerwear is the signature\n  purchase of your best customers.\n```\n\nThis will look similar to:\n\nAnd can also be accessed directly in Looker:\n\nHealth checks and instance maintenance — and the first real one surfaced a finding, just not the expected kind:\n\n```\n> run a full health sweep\n\nRunning usage analysis, six pulse checks, and cleanup candidates in parallel.\n\n• looker-toolbox › (10 calls)\n\nMeanwhile — a metadata-based audit does work\n\nget_dashboards / run_dashboard don't touch System Activity, so here's a cleanup pass\nfrom what we already gathered:\n\n  ID     Title                          Flag\n  ────   ────────────────────────────   ─────────────────────────────────────\n  5, 6   \"test\" (×2, duplicate name)    Scratch — almost certainly deletable\n  8      TestJC                         Scratch\n  10     TestOrdersByCity               Scratch\n  11     My Dashboard                   Default/placeholder name\n  12     test-orders                    Scratch\n  14     💎 VIP Customer Intelligence   Superseded by v2 (#17) — 7 broken tiles\n\n~7 dashboards worth reviewing for deletion.\n```\n\nNote what Codex did *not* do here: it identified six deletion candidates and stopped. Deleting dashboards is a mutation, and the approval mode plus the `AGENTS.md`\n\nrule (\"confirm before creating/altering/deleting Looker content\") both point the same direction. The agent proposes; you dispose.\n\nThe key value shows up when you push the combination of Codex, the model, and the Looker tools over MCP:\n\n```\n> \"Are we at risk of losing our best customers?\"\n\nI'll investigate using your Looker instance. Starting with what data is available,\nthen digging into retention signals.\n\n• looker-toolbox › get_models\n\nThis is the thelook e-commerce dataset. Exploring the richest model.\n\n• looker-toolbox › (3 calls)\n\nRich model — there's a lifetime revenue-rank on each user, so I can define \"best\ncustomers\" precisely (Top Revenue Group / Top 1000) and track activity over time.\nFirst, the data's date range, so I know what \"recent\" means.\n\n• looker-toolbox › query\n\nData runs right up to today. Now the core analysis: the Top Revenue Group's monthly\nspend and active-customer count over the last ~2 years.\n\n• looker-toolbox › query\n\nThe business is growing overall, so a raw trend hides churn. The real risk signal is\nrecency — how many of our best customers have gone quiet.\n\n• looker-toolbox › (4 calls)\n\nHere's what the data says. I defined \"best customers\" as your Top Revenue Group —\nthe top 1,000 by lifetime valid revenue ($731K lifetime, ~$731 each). Then I looked\nat whether they're still actually buying.\n\nShort answer: the cohort's revenue is healthy, but there's a large dormant tail.\n\nAggregate spend from top customers is at record highs — valid revenue rose from\n~$12–17K/month in late 2024 to $43K most recently, growing alongside the overall\nbusiness. The franchise isn't collapsing.\n\nBut recency tells a more worrying story. Of the 1,000 all-time best customers:\n\n  Last purchased within   # still active   Share   → Gone silent\n  ─────────────────────   ──────────────   ─────   ───────────────────────────\n  90 days                            258     26%   74% (742) inactive 3+ months\n  180 days                           419     42%   58% (581) inactive 6+ months\n  365 days                           585     59%   42% (415) inactive 12+ months\n\nSo ~415 of your top-1,000 (42%) haven't placed a valid order in over a year —\neffectively lapsed. At the cohort's average lifetime value (~$731), that's on the\norder of ~$300K of historical revenue that has stopped repeating, plus another ~160\nin the 6–12 month \"cooling off\" zone.\n\nHow to read this\n\n- It's a tail problem, not a top-line collapse. A concentrated set of top customers\n  is very active, while a long tail of former big spenders has quietly aged out.\n  That's partly natural — \"best customers\" is a lifetime label.\n- The actionable risk is the 6–12 month cooling band (~160 customers) — recently\n  valuable, now slipping, still winnable.\n\nSuggested next steps (I can do any of these — just say the word)\n\n1. Pull the actual at-risk list — name, email, lifetime spend, days-since-last-order.\n2. Build a \"Best Customer Retention\" dashboard so this is monitored ongoing.\n3. Add a churn-risk segment to the LookML model so \"at-risk best customers\" becomes\n   a reusable, governed field.\n\nNote that #2 and #3 create or modify content in your live Looker instance — those\nwill come through as approval prompts, and I'll confirm specifics before running them.\n```\n\nPull at-risk customers in real time, based on your actual data:\n\n```\nYour best customers who've gone quiet\n\nThe 5 highest-lifetime-value members of the Top-1,000 revenue group with no valid\norder in 90+ days, ranked by lifetime spend. \"Cooling\" = last bought 3–6 months ago\n(most winnable); \"Dormant\" = 6+ months silent.\n\n  #  Customer          Country    Channel    Lifetime $  Orders  Status\n  ─  ───────────────   ────────   ────────   ──────────  ──────  ──────────\n  1  Craig Dickerson   France     Email          $1,849       8  🟡 Cooling\n  2  Mark Nolan        USA        Organic        $1,826       7  🟡 Cooling\n  3  John Hill         Brazil     Search         $1,554       4  🔴 Dormant\n  4  Glenn Foster      USA        Facebook       $1,455       8  🟡 Cooling\n  5  Thomas Davis      Australia  Search         $1,435       8  🔴 Dormant\n\nNotable: your single highest-lifetime-value customer overall (Craig Dickerson,\n$1,849) is on this list — cooling, not yet lost.\n\nThe full at-risk population (all 1,000 top customers)\n\n- 🟢 258 active (bought in last 90 days) — healthy\n- 🟡 161 cooling — last order 3–6 months ago  ← highest-priority win-back\n- 🟠 166 slipping — last order 6–12 months ago\n- 🔴 415 lapsed — no order in 12+ months\n\nSo 742 of your top 1,000 (74%) haven't purchased in 90+ days.\n\nMethod note: ranked by lifetime valid spend; \"at-risk\" = no valid order in 90 days,\ncross-checked against the 90- and 180-day active sets. Status is bucketed by\n90/180-day windows rather than exact per-customer last-order dates — the toolbox\ncouldn't compute that inline.\n```\n\nWhat-if scenarios:\n\n```\n> \"What if we raised outerwear prices 10% — what happens to VIP revenue and would it\n   dent volume?\"\n\nA pricing what-if on the VIP segment. Grounding it in actual numbers first.\n\n• looker-toolbox › (2 calls)\n\nOuterwear is the key finding: it's your VIPs' #1 category by a wide margin — $245K\n(34% of all VIP lifetime revenue), 664 items at $370/item. This decision hits VIPs\nwhere they're most concentrated. Pulling cost too, for profit impact.\n\nBaseline — VIP outerwear today\n\n  Metric             Value\n  ────────────────   ───────────────────────\n  Revenue            $245,412\n  Units sold         664\n  Avg price / unit   $369.60\n  Cost / unit        $163.48\n  Gross profit       $136,859 (55.8% margin)\n  Profit / unit      $206.11\n\nWhat a +10% price does\n\nNew price = $406.56 (+$36.96/unit; cost unchanged, so every extra dollar is margin).\n\n  Volume response      Units (Δ)     Revenue   vs today   Gross profit   vs today\n  ──────────────────   ──────────   ────────   ────────   ────────────   ────────\n  No dip (inelastic)   664  (0%)    $269,956   +$24,544       $161,405   +$24,546\n  −5%                  631 (−33)    $256,458   +$11,046       $153,335   +$16,476\n  −10%                 598 (−66)    $242,960    −$2,452       $145,266    +$8,407\n  −15%                 564 (−100)   $229,462   −$15,950       $137,197      ~flat\n  −20%                 531 (−133)   $215,964   −$29,449       $129,127    −$7,732\n\nThe two numbers that matter\n\n- Revenue break-even: ~9% volume loss. You can lose up to ~60 of the 664 units\n  before the hike stops adding revenue.\n- Profit break-even: ~15% volume loss. Because you keep the full $37 markup at zero\n  added cost, profit holds even if revenue dips.\n\nWould it dent volume? Expect a modest dip. For your most loyal customers buying a\npremium considered-purchase category, elasticity is likely mild (roughly −0.3 to\n−0.8) — the top two rows: revenue +$11–20K, profit +$16–22K, only ~20–50 fewer units.\n\nOne strategic caveat\n\nTying back to the last analysis: 74% of your VIPs are already cooling or lapsed, and\nouterwear is their anchor category. A blanket 10% hike risks nudging cooling VIPs out\nthe door, and that churned lifetime value would swamp the ~$15–20K modeled gain.\n- Raise on healthy/active VIPs; protect the at-risk ones.\n- Apply to new-season inventory rather than a visible retroactive hike.\n- Run it as a holdout test on a VIP subset first to get real elasticity.\n\nCaveat on the model: elasticity is assumed, not measured — no price-experiment\nhistory exists in this data.\n```\n\nBecause `looker-dev`\n\nis loaded alongside `looker`\n\n, Codex can read the actual model source, not just the metadata — which is what makes \"can I trust this measure?\" an answerable question:\n\n```\n> \"What does filtered_average_vs_unfiltered_average actually compute, and can I\n   trust it?\"\n\nLet me read the LookML that defines it. First, locating the project files.\n\n• looker-toolbox › (2 calls)\n\nThe measure lives in the user_summary_and_order_items_fields view. Reading it plus\nthe two views it depends on.\n\n• looker-toolbox › (3 calls)\n\nI have the full definition and its dependencies. Verifying my reading against live\ndata before giving a verdict — pulling the raw ingredients alongside the measure.\n\n• looker-toolbox › query\n\nThe math reconciles exactly (Outerwear: 145.37 / 88.47 − 1 = 0.643 ✓). Full teardown:\n\nWhat it computes\n\nThe percent difference between the average item price in the current query context\nand the same shoppers' average item price over their entire lifetime. Positive =\nthey're buying pricier-than-usual items in this slice.\n\nThe actual SQL\n\n  measure: filtered_average_vs_unfiltered_average {\n    label: \"Avg Price vs These Users' Avg Price (Lifetime)\"\n    type: number\n    sql: (${order_items.average_sale_price} * 1.0\n          / nullif(${user_summary_unfiltered.average_sale_price_for_these_users}, 0)) - 1 ;;\n    value_format_name: percent_0\n  }\n\nWhere each half comes from — the clever part\n\n  Term          Field                                    Respects query filters?\n  ───────────   ──────────────────────────────────────   ─────────────────────────\n  Numerator     order_items.average_sale_price           Yes — rows in scope\n  Denominator   user_summary_unfiltered.average_sale_    No — lifetime avg for\n                price_for_these_users                    those same users\n\nThe denominator's \"lifetime\" behavior comes from one line:\n\n  derived_table: {\n    explore_source: advanced_example_ecommerce { ... bind_all_filters: no }\n  }\n\nThat derived table is one row per user, joined back to order_items by user_id.\nBecause it's a 1-user-row joined to many order-item rows, Looker applies symmetric\naggregates so summing lifetime sales/items counts each user exactly once (no\nfan-out). Its twin, user_summary_filtered, is identical but bind_all_filters: yes.\n\nEmpirically verified:\n\n  Category            Filtered avg   Lifetime avg   Measure   Check\n  ─────────────────   ────────────   ────────────   ───────   ──────────────────────\n  Outerwear & Coats        $145.37         $88.47      +64%   145.37/88.47−1 = 0.643 ✓\n  Suits                    $118.39         $77.38      +53%   ✓\n  Blazers & Jackets         $92.51         $67.84      +36%   ✓\n\nCan you trust it? ✅ Yes — with three rules\n\nCorrectly built: proper symmetric-aggregate handling, nullif(...,0) guards against\ndivide-by-zero, both sides on the same \"Valid Orders only\" basis. No bug here.\n\nBut it's a ratio-of-averages, so respect its nature:\n\n1. Don't sum, average, or trust a Totals row for it. Ratio measures don't roll up.\n2. The baseline is dynamic, not a global constant. Change a filter that changes which\n   users appear and the baseline moves. This is the #1 way people misread it.\n3. It's about per-item price mix, not spend or loyalty.\n\nBottom line: trustworthy for exactly one question — \"in this slice, are these\nshoppers trading up or down on per-item price versus their own norm?\"\n```\n\nNote that this whole exchange used only read tools — `get_project_files`\n\n, `get_project_file`\n\n, `query`\n\n— so it ran without a single approval prompt. The mode is calibrated so that *understanding* your instance is frictionless and *changing* it is not.\n\n```\nThe dashboard existed already (dashboard 14, built earlier from advanced_ecomm). To\nmake it a proper briefing I added an executive layer on top of the existing charts:\na 4-KPI summary strip, and a \"bottom line\" callout.\n\nThe briefing in 30 seconds\n\n  Metric                  Value    Meaning\n  ─────────────────────   ──────   ─────────────────────────────────────\n  Lifetime revenue        $7.40M   All purchasing users\n  Top-1000 VIP revenue    $731K    $149 avg basket — ~7× the bottom tier\n  Revenue concentration   56%      From the 1st customer quartile\n  VIP geography           72%      Of VIP spend from China, US, Brasil\n\nBottom line for the exec team: revenue is highly concentrated — a 1,000-customer VIP\ncore spends ~7× the average shopper, is geographically narrow (three countries), and\nis premium-led (Outerwear & Coats alone: $245K at a $370 avg item). The strategic\nlever is retaining and expanding the VIP core, not broad acquisition.\n\nOne note on reading the charts: the Top 1000 tier is an elite flag inside the 1st\nquartile — its dollars are already counted there, so don't sum the tier bars.\n```\n\nThese are only high level examples of what can be done with the tech stack!\n\nThe MCP path is not the only way in. The repo also ships a `Makefile`\n\ntarget that installs the [Looker CLI](https://github.com/looker-open-source/looker-cli) into the project root, checksum-verified:\n\n```\nmake cli                              # latest release\nmake cli LOOKER_CLI_VERSION=v0.4.8    # pinned\nmake clean                            # remove downloaded binaries, keep credentials\n```\n\nIt reads the same `LOOKER_*`\n\nvariables, so `source set_env.sh`\n\ncovers both. Useful for the deterministic, scriptable half of the work — CI checks, bulk operations — while MCP covers the exploratory half.\n\nA short list of the things that actually go wrong:\n\n| Symptom | Cause | Fix |\n|---|---|---|\n`/mcp` shows no servers |\nProject not trusted, so `.codex/config.toml` never loaded |\nRestart `codex` in the repo root and approve the trust prompt |\n| Server fails immediately | No `.env` and no exported `LOOKER_*`\n|\n`source set_env.sh` — the launcher prints exactly this |\n| Server times out on startup | 300 MB binary + Looker handshake on a cold/slow start | Raise `startup_timeout_sec`\n|\n`run_dashboard` times out |\nTen tiles = ten warehouse queries | Raise `tool_timeout_sec` , or run tiles individually |\nAll `health_*` return Access Denied |\nAPI3 role lacks `see_system_activity`\n|\nAdmin → Roles, add the permission |\n| Filter returns nothing | Value was quoted | Pass values bare — `first_touch` , not `\"first_touch\"`\n|\n| Tools run without asking | Approval mode not applied |\n`codex mcp get looker-toolbox` — if the key isn't echoed back it was misspelled and silently dropped. Also check you didn't pick \"don't ask again\" earlier in the session |\n\nCodex was configured as a Looker MCP client using the MCP Toolbox. The `.codex/config.toml`\n\nregistration points at a secret-free launcher script that resolves credentials from `.env`\n\nat runtime, and pins write-capable tools behind `default_tools_approval_mode = \"writes\"`\n\nso discovery and analysis run unattended while anything that mutates the live instance stops and asks. The MCP connection was then used to explore the instance, read and verify LookML, build Looks and dashboards, and run open-ended business analysis against the governed semantic model.\n\nThe stack underneath is unchanged from the Gemini CLI, Antigravity CLI and Claude Code versions of this paper — same `toolbox`\n\nbinary, same `looker,looker-dev`\n\ntoolsets, same ~50 tools. That is the actual result worth noting: the same repository, with three client config files sitting side by side, serves all of them.", "url": "https://wpnews.pro/news/mcp-configuration-for-looker-with-codex", "canonical_source": "https://dev.to/gde/mcp-configuration-for-looker-with-codex-30e1", "published_at": "2026-08-12 17:06:10+00:00", "updated_at": "2026-08-12 17:18:34.881314+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools", "artificial-intelligence"], "entities": ["Google Cloud", "OpenAI", "Codex", "Looker", "MCP Toolbox", "Gemini CLI", "Claude Code", "Antigravity CLI"], "alternates": {"html": "https://wpnews.pro/news/mcp-configuration-for-looker-with-codex", "markdown": "https://wpnews.pro/news/mcp-configuration-for-looker-with-codex.md", "text": "https://wpnews.pro/news/mcp-configuration-for-looker-with-codex.txt", "jsonld": "https://wpnews.pro/news/mcp-configuration-for-looker-with-codex.jsonld"}}