Skip to content

fix(tools/looker): strip wrapping quotes from filter values for unquoted parameters#3273

Merged
drstrangelooker merged 2 commits into
googleapis:mainfrom
Automattic:fix/looker-unquoted-parameter-values
May 21, 2026
Merged

fix(tools/looker): strip wrapping quotes from filter values for unquoted parameters#3273
drstrangelooker merged 2 commits into
googleapis:mainfrom
Automattic:fix/looker-unquoted-parameter-values

Conversation

@anandnalya

Copy link
Copy Markdown
Contributor

Description

LookML parameter fields declared type: unquoted reject queries through the Looker tools because filter values arrive with a literal layer of wrapping quote characters and are substituted into SQL bare via {% parameter %} — producing invalid SQL like ... = "first_touch".

ProcessQueryArgs in internal/tools/looker/lookercommon/lookercommon.go already strips a wrapping layer of quotes from filter keys; this extends the same handling to string values, stripping a single layer of wrapping " or ' only when the first and last characters match. Bare values are correct for all parameter types over the WriteQuery wire format, so this is applied unconditionally — no need to branch on the field's declared LookML type. The filters parameter description is also tightened so the model is told explicitly not to wrap values in extra quotes, addressing the upstream cause while the normalization stays a backstop.

One fix covers every tool built on the shared helper: looker-query, looker-query-sql, looker-query-url, looker-make-look, and looker-add-dashboard-element.

Tests

  • Unit: added TestProcessQueryArgsStripsWrappingQuotes exercising ProcessQueryArgs directly — bare values (unchanged), double-quoted values, single-quoted values, quoted keys (regression), quoted-key+quoted-value, non-string values (untouched), single-character strings (no length-check footgun), and mismatched wrapping characters (left alone). go test -race ./internal/tools/looker/lookercommon/... passes.
  • Integration: updated the filters manifest assertions in tests/looker/looker_integration_test.go to match the tightened parameter description, keeping the live looker-query/looker-query-sql/looker-query-url integration suite green.

PR Checklist

  • Make sure you reviewed CONTRIBUTING.md
  • Make sure to open an issue as a bug/issue before writing your code!
  • Ensure the tests and linter pass
  • Code coverage does not decrease (if any source code was changed)
  • Appropriate docs were updated (if necessary)
  • Make sure to add ! if this involve a breaking change

🛠️ Fixes #3272

@anandnalya anandnalya requested review from a team as code owners May 21, 2026 10:28
@google-cla

google-cla Bot commented May 21, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request enhances the Looker tool by providing more detailed documentation for the filters parameter and improving the logic for stripping wrapping quotes from filter keys and string values. It also includes a new test suite to verify these changes across various edge cases. A review comment correctly identifies a potential runtime panic when comparing non-comparable types in the filter map and suggests an idiomatic Go approach of building a new map during iteration to avoid instability.

Comment thread internal/tools/looker/lookercommon/lookercommon.go
@anandnalya anandnalya force-pushed the fix/looker-unquoted-parameter-values branch from 42a9f3c to 00dba60 Compare May 21, 2026 10:31
…ted parameters

LookML parameters declared `type: unquoted` are substituted bare into SQL
via {% parameter %}. When the LLM emits a filter value with literal
wrapping quote characters (e.g. `"first_touch"`), those characters land
in the rendered SQL and Looker rejects the query.

Extend the existing key-quote-stripping logic in ProcessQueryArgs to
also strip a single layer of wrapping `"` or `'` from string values.
Bare values are correct for all parameter types over the WriteQuery
wire format, so this is safe to apply unconditionally.

Also tighten the `filters` parameter description so the LLM is told
explicitly not to wrap values in extra quotes, addressing the upstream
cause while the normalization remains as a backstop.

Affects every tool that runs or materializes a query via the shared
helper: looker-query, looker-query-sql, looker-query-url,
looker-make-look, and looker-add-dashboard-element.
@anandnalya anandnalya force-pushed the fix/looker-unquoted-parameter-values branch from 00dba60 to 8a8fc82 Compare May 21, 2026 10:49
@drstrangelooker

Copy link
Copy Markdown
Contributor

/gcbrun

@drstrangelooker

Copy link
Copy Markdown
Contributor

/gcbrun

@drstrangelooker drstrangelooker enabled auto-merge (squash) May 21, 2026 17:00
@drstrangelooker drstrangelooker merged commit 1e3de96 into googleapis:main May 21, 2026
19 checks passed
github-actions Bot pushed a commit to Jaleel-zhu/genai-toolbox that referenced this pull request May 21, 2026
…or unquoted parameters (googleapis#3273)

## Description

LookML `parameter` fields declared `type: unquoted` reject queries
through the Looker tools because filter values arrive with a literal
layer of wrapping quote characters and are substituted into SQL bare via
`{% parameter %}` — producing invalid SQL like `... = "first_touch"`.

`ProcessQueryArgs` in
`internal/tools/looker/lookercommon/lookercommon.go` already strips a
wrapping layer of quotes from filter *keys*; this extends the same
handling to string *values*, stripping a single layer of wrapping `"` or
`'` only when the first and last characters match. Bare values are
correct for all parameter types over the `WriteQuery` wire format, so
this is applied unconditionally — no need to branch on the field's
declared LookML type. The `filters` parameter description is also
tightened so the model is told explicitly not to wrap values in extra
quotes, addressing the upstream cause while the normalization stays a
backstop.

One fix covers every tool built on the shared helper: `looker-query`,
`looker-query-sql`, `looker-query-url`, `looker-make-look`, and
`looker-add-dashboard-element`.

## Tests

- **Unit:** added `TestProcessQueryArgsStripsWrappingQuotes` exercising
`ProcessQueryArgs` directly — bare values (unchanged), double-quoted
values, single-quoted values, quoted keys (regression),
quoted-key+quoted-value, non-string values (untouched), single-character
strings (no length-check footgun), and mismatched wrapping characters
(left alone). `go test -race ./internal/tools/looker/lookercommon/...`
passes.
- **Integration:** updated the `filters` manifest assertions in
`tests/looker/looker_integration_test.go` to match the tightened
parameter description, keeping the live
`looker-query`/`looker-query-sql`/`looker-query-url` integration suite
green.

## PR Checklist

- [x] Make sure you reviewed
[CONTRIBUTING.md](https://github.com/googleapis/mcp-toolbox/blob/main/CONTRIBUTING.md)
- [x] Make sure to open an issue as a bug/issue before writing your
code!
- [x] Ensure the tests and linter pass
- [x] Code coverage does not decrease (if any source code was changed)
- [ ] Appropriate docs were updated (if necessary)
- [ ] Make sure to add `!` if this involve a breaking change

🛠️ Fixes googleapis#3272

Co-authored-by: Dr. Strangelove <drstrangelove@google.com> 1e3de96
@NoClueMike

Copy link
Copy Markdown

If a h was a h of course of course

Yuan325 added a commit that referenced this pull request May 21, 2026
🤖 I have created a release *beep* *boop*
---


##
[1.3.0](v1.2.0...v1.3.0)
(2026-05-21)


### Features

* **auth:** Implement MCP auth tool-level scopes validation
([#3049](#3049))
([c528985](c528985))
* **looker:** Propagate client IP from incoming MCP requests to
downstream SDK calls
([#3253](#3253))
([75da6c2](75da6c2))
* Setup SQLCommenter and allow client metadata
([#3064](#3064))
([9f1f9b3](9f1f9b3))
* **tool/cloudsqladmin:** Add `cloud-sql-admin-execute-sql-many` and
`cloud-sql-admin-sql-many`
([#3083](#3083))
([ef300a8](ef300a8))


### Bug Fixes

* **auth/generic:** Fix generic auth expiration field and integration
with `authRequired`
([#3251](#3251))
([f4d16c0](f4d16c0))
* Enforce toolset/promptset boundary on tools/call and prompts/get
([#3036](#3036))
([c739b80](c739b80))
* **tools/http:** Prevent path traversal and base path scope escape
([#3218](#3218))
([80a6602](80a6602))
* **tools/looker:** Return a 401 error to MCP client when Looker returns
a 401 ([#3233](#3233))
([4f409a3](4f409a3))
* **tools/looker:** Strip wrapping quotes from filter values for
unquoted parameters
([#3273](#3273))
([1e3de96](1e3de96))
* **tools:** Initialize query result slices to empty array
([#3250](#3250))
([60ddf48](60ddf48))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
github-actions Bot pushed a commit that referenced this pull request May 21, 2026
🤖 I have created a release *beep* *boop*
---

##
[1.3.0](v1.2.0...v1.3.0)
(2026-05-21)

### Features

* **auth:** Implement MCP auth tool-level scopes validation
([#3049](#3049))
([c528985](c528985))
* **looker:** Propagate client IP from incoming MCP requests to
downstream SDK calls
([#3253](#3253))
([75da6c2](75da6c2))
* Setup SQLCommenter and allow client metadata
([#3064](#3064))
([9f1f9b3](9f1f9b3))
* **tool/cloudsqladmin:** Add `cloud-sql-admin-execute-sql-many` and
`cloud-sql-admin-sql-many`
([#3083](#3083))
([ef300a8](ef300a8))

### Bug Fixes

* **auth/generic:** Fix generic auth expiration field and integration
with `authRequired`
([#3251](#3251))
([f4d16c0](f4d16c0))
* Enforce toolset/promptset boundary on tools/call and prompts/get
([#3036](#3036))
([c739b80](c739b80))
* **tools/http:** Prevent path traversal and base path scope escape
([#3218](#3218))
([80a6602](80a6602))
* **tools/looker:** Return a 401 error to MCP client when Looker returns
a 401 ([#3233](#3233))
([4f409a3](4f409a3))
* **tools/looker:** Strip wrapping quotes from filter values for
unquoted parameters
([#3273](#3273))
([1e3de96](1e3de96))
* **tools:** Initialize query result slices to empty array
([#3250](#3250))
([60ddf48](60ddf48))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com> b001006
github-actions Bot pushed a commit to renovate-bot/googleapis-_-genai-toolbox that referenced this pull request May 21, 2026
🤖 I have created a release *beep* *boop*
---

##
[1.3.0](googleapis/mcp-toolbox@v1.2.0...v1.3.0)
(2026-05-21)

### Features

* **auth:** Implement MCP auth tool-level scopes validation
([googleapis#3049](googleapis#3049))
([c528985](googleapis@c528985))
* **looker:** Propagate client IP from incoming MCP requests to
downstream SDK calls
([googleapis#3253](googleapis#3253))
([75da6c2](googleapis@75da6c2))
* Setup SQLCommenter and allow client metadata
([googleapis#3064](googleapis#3064))
([9f1f9b3](googleapis@9f1f9b3))
* **tool/cloudsqladmin:** Add `cloud-sql-admin-execute-sql-many` and
`cloud-sql-admin-sql-many`
([googleapis#3083](googleapis#3083))
([ef300a8](googleapis@ef300a8))

### Bug Fixes

* **auth/generic:** Fix generic auth expiration field and integration
with `authRequired`
([googleapis#3251](googleapis#3251))
([f4d16c0](googleapis@f4d16c0))
* Enforce toolset/promptset boundary on tools/call and prompts/get
([googleapis#3036](googleapis#3036))
([c739b80](googleapis@c739b80))
* **tools/http:** Prevent path traversal and base path scope escape
([googleapis#3218](googleapis#3218))
([80a6602](googleapis@80a6602))
* **tools/looker:** Return a 401 error to MCP client when Looker returns
a 401 ([googleapis#3233](googleapis#3233))
([4f409a3](googleapis@4f409a3))
* **tools/looker:** Strip wrapping quotes from filter values for
unquoted parameters
([googleapis#3273](googleapis#3273))
([1e3de96](googleapis@1e3de96))
* **tools:** Initialize query result slices to empty array
([googleapis#3250](googleapis#3250))
([60ddf48](googleapis@60ddf48))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com> b001006
github-actions Bot pushed a commit to rodineyw/mcp-toolbox that referenced this pull request May 21, 2026
🤖 I have created a release *beep* *boop*
---

##
[1.3.0](googleapis/mcp-toolbox@v1.2.0...v1.3.0)
(2026-05-21)

### Features

* **auth:** Implement MCP auth tool-level scopes validation
([googleapis#3049](googleapis#3049))
([c528985](googleapis@c528985))
* **looker:** Propagate client IP from incoming MCP requests to
downstream SDK calls
([googleapis#3253](googleapis#3253))
([75da6c2](googleapis@75da6c2))
* Setup SQLCommenter and allow client metadata
([googleapis#3064](googleapis#3064))
([9f1f9b3](googleapis@9f1f9b3))
* **tool/cloudsqladmin:** Add `cloud-sql-admin-execute-sql-many` and
`cloud-sql-admin-sql-many`
([googleapis#3083](googleapis#3083))
([ef300a8](googleapis@ef300a8))

### Bug Fixes

* **auth/generic:** Fix generic auth expiration field and integration
with `authRequired`
([googleapis#3251](googleapis#3251))
([f4d16c0](googleapis@f4d16c0))
* Enforce toolset/promptset boundary on tools/call and prompts/get
([googleapis#3036](googleapis#3036))
([c739b80](googleapis@c739b80))
* **tools/http:** Prevent path traversal and base path scope escape
([googleapis#3218](googleapis#3218))
([80a6602](googleapis@80a6602))
* **tools/looker:** Return a 401 error to MCP client when Looker returns
a 401 ([googleapis#3233](googleapis#3233))
([4f409a3](googleapis@4f409a3))
* **tools/looker:** Strip wrapping quotes from filter values for
unquoted parameters
([googleapis#3273](googleapis#3273))
([1e3de96](googleapis@1e3de96))
* **tools:** Initialize query result slices to empty array
([googleapis#3250](googleapis#3250))
([60ddf48](googleapis@60ddf48))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com> b001006
github-actions Bot pushed a commit to Jaleel-zhu/genai-toolbox that referenced this pull request May 21, 2026
🤖 I have created a release *beep* *boop*
---

##
[1.3.0](googleapis/mcp-toolbox@v1.2.0...v1.3.0)
(2026-05-21)

### Features

* **auth:** Implement MCP auth tool-level scopes validation
([googleapis#3049](googleapis#3049))
([c528985](googleapis@c528985))
* **looker:** Propagate client IP from incoming MCP requests to
downstream SDK calls
([googleapis#3253](googleapis#3253))
([75da6c2](googleapis@75da6c2))
* Setup SQLCommenter and allow client metadata
([googleapis#3064](googleapis#3064))
([9f1f9b3](googleapis@9f1f9b3))
* **tool/cloudsqladmin:** Add `cloud-sql-admin-execute-sql-many` and
`cloud-sql-admin-sql-many`
([googleapis#3083](googleapis#3083))
([ef300a8](googleapis@ef300a8))

### Bug Fixes

* **auth/generic:** Fix generic auth expiration field and integration
with `authRequired`
([googleapis#3251](googleapis#3251))
([f4d16c0](googleapis@f4d16c0))
* Enforce toolset/promptset boundary on tools/call and prompts/get
([googleapis#3036](googleapis#3036))
([c739b80](googleapis@c739b80))
* **tools/http:** Prevent path traversal and base path scope escape
([googleapis#3218](googleapis#3218))
([80a6602](googleapis@80a6602))
* **tools/looker:** Return a 401 error to MCP client when Looker returns
a 401 ([googleapis#3233](googleapis#3233))
([4f409a3](googleapis@4f409a3))
* **tools/looker:** Strip wrapping quotes from filter values for
unquoted parameters
([googleapis#3273](googleapis#3273))
([1e3de96](googleapis@1e3de96))
* **tools:** Initialize query result slices to empty array
([googleapis#3250](googleapis#3250))
([60ddf48](googleapis@60ddf48))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com> b001006
@anandnalya anandnalya deleted the fix/looker-unquoted-parameter-values branch May 22, 2026 01:24
github-actions Bot pushed a commit to pepe57/genai-toolbox that referenced this pull request May 22, 2026
🤖 I have created a release *beep* *boop*
---

##
[1.3.0](googleapis/mcp-toolbox@v1.2.0...v1.3.0)
(2026-05-21)

### Features

* **auth:** Implement MCP auth tool-level scopes validation
([googleapis#3049](googleapis#3049))
([c528985](googleapis@c528985))
* **looker:** Propagate client IP from incoming MCP requests to
downstream SDK calls
([googleapis#3253](googleapis#3253))
([75da6c2](googleapis@75da6c2))
* Setup SQLCommenter and allow client metadata
([googleapis#3064](googleapis#3064))
([9f1f9b3](googleapis@9f1f9b3))
* **tool/cloudsqladmin:** Add `cloud-sql-admin-execute-sql-many` and
`cloud-sql-admin-sql-many`
([googleapis#3083](googleapis#3083))
([ef300a8](googleapis@ef300a8))

### Bug Fixes

* **auth/generic:** Fix generic auth expiration field and integration
with `authRequired`
([googleapis#3251](googleapis#3251))
([f4d16c0](googleapis@f4d16c0))
* Enforce toolset/promptset boundary on tools/call and prompts/get
([googleapis#3036](googleapis#3036))
([c739b80](googleapis@c739b80))
* **tools/http:** Prevent path traversal and base path scope escape
([googleapis#3218](googleapis#3218))
([80a6602](googleapis@80a6602))
* **tools/looker:** Return a 401 error to MCP client when Looker returns
a 401 ([googleapis#3233](googleapis#3233))
([4f409a3](googleapis@4f409a3))
* **tools/looker:** Strip wrapping quotes from filter values for
unquoted parameters
([googleapis#3273](googleapis#3273))
([1e3de96](googleapis@1e3de96))
* **tools:** Initialize query result slices to empty array
([googleapis#3250](googleapis#3250))
([60ddf48](googleapis@60ddf48))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com> b001006
github-actions Bot pushed a commit to CrazyForks/genai-toolbox that referenced this pull request May 22, 2026
🤖 I have created a release *beep* *boop*
---

##
[1.3.0](googleapis/mcp-toolbox@v1.2.0...v1.3.0)
(2026-05-21)

### Features

* **auth:** Implement MCP auth tool-level scopes validation
([googleapis#3049](googleapis#3049))
([c528985](googleapis@c528985))
* **looker:** Propagate client IP from incoming MCP requests to
downstream SDK calls
([googleapis#3253](googleapis#3253))
([75da6c2](googleapis@75da6c2))
* Setup SQLCommenter and allow client metadata
([googleapis#3064](googleapis#3064))
([9f1f9b3](googleapis@9f1f9b3))
* **tool/cloudsqladmin:** Add `cloud-sql-admin-execute-sql-many` and
`cloud-sql-admin-sql-many`
([googleapis#3083](googleapis#3083))
([ef300a8](googleapis@ef300a8))

### Bug Fixes

* **auth/generic:** Fix generic auth expiration field and integration
with `authRequired`
([googleapis#3251](googleapis#3251))
([f4d16c0](googleapis@f4d16c0))
* Enforce toolset/promptset boundary on tools/call and prompts/get
([googleapis#3036](googleapis#3036))
([c739b80](googleapis@c739b80))
* **tools/http:** Prevent path traversal and base path scope escape
([googleapis#3218](googleapis#3218))
([80a6602](googleapis@80a6602))
* **tools/looker:** Return a 401 error to MCP client when Looker returns
a 401 ([googleapis#3233](googleapis#3233))
([4f409a3](googleapis@4f409a3))
* **tools/looker:** Strip wrapping quotes from filter values for
unquoted parameters
([googleapis#3273](googleapis#3273))
([1e3de96](googleapis@1e3de96))
* **tools:** Initialize query result slices to empty array
([googleapis#3250](googleapis#3250))
([60ddf48](googleapis@60ddf48))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com> b001006
github-actions Bot pushed a commit to bhardwajRahul/genai-toolbox that referenced this pull request May 23, 2026
🤖 I have created a release *beep* *boop*
---

##
[1.3.0](googleapis/mcp-toolbox@v1.2.0...v1.3.0)
(2026-05-21)

### Features

* **auth:** Implement MCP auth tool-level scopes validation
([googleapis#3049](googleapis#3049))
([c528985](googleapis@c528985))
* **looker:** Propagate client IP from incoming MCP requests to
downstream SDK calls
([googleapis#3253](googleapis#3253))
([75da6c2](googleapis@75da6c2))
* Setup SQLCommenter and allow client metadata
([googleapis#3064](googleapis#3064))
([9f1f9b3](googleapis@9f1f9b3))
* **tool/cloudsqladmin:** Add `cloud-sql-admin-execute-sql-many` and
`cloud-sql-admin-sql-many`
([googleapis#3083](googleapis#3083))
([ef300a8](googleapis@ef300a8))

### Bug Fixes

* **auth/generic:** Fix generic auth expiration field and integration
with `authRequired`
([googleapis#3251](googleapis#3251))
([f4d16c0](googleapis@f4d16c0))
* Enforce toolset/promptset boundary on tools/call and prompts/get
([googleapis#3036](googleapis#3036))
([c739b80](googleapis@c739b80))
* **tools/http:** Prevent path traversal and base path scope escape
([googleapis#3218](googleapis#3218))
([80a6602](googleapis@80a6602))
* **tools/looker:** Return a 401 error to MCP client when Looker returns
a 401 ([googleapis#3233](googleapis#3233))
([4f409a3](googleapis@4f409a3))
* **tools/looker:** Strip wrapping quotes from filter values for
unquoted parameters
([googleapis#3273](googleapis#3273))
([1e3de96](googleapis@1e3de96))
* **tools:** Initialize query result slices to empty array
([googleapis#3250](googleapis#3250))
([60ddf48](googleapis@60ddf48))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com> b001006
drstrangelooker added a commit that referenced this pull request May 28, 2026
## Description

PR #3273 fixed the wrapping-quote case for `type: unquoted` Looker
parameter filters, but the value still reaches Looker un-escaped, so
identifiers containing `_`, `%`, or `,` are still rejected by Looker's
filter-expression parser. The parser treats `_` as a single-character
wildcard and `%` as a multi-character wildcard; for `type: unquoted`
parameters the substituted SQL must match `[A-Za-z0-9_.$]` literally, so
`first_touch` is expanded into a wildcard pattern and 400s with `The
filter "first_touch" is not allowed.` Looker's own
`default_filter_value` is stored already escaped (`first^_touch`),
confirming that filter-expression escape is the correct wire format for
`/queries/run`.

This adds a new helper `EscapeUnquotedParameterFilters` in
`internal/tools/looker/lookercommon/lookercommon.go` that fetches the
explore's parameter metadata via `sdk.LookmlModelExplore` and escapes
`^`/`_`/`%`/`,` with a `^` prefix in any string filter value keyed to a
`type: unquoted` parameter. `ProcessQueryArgs` stays pure and keeps the
wrapping-quote stripping from #3273. The new helper is idempotent —
values that already contain `^` are assumed pre-escaped and pass through
unchanged — so callers can keep using the raw `default_filter_value`
form. Metadata-lookup failures degrade to a no-op so users without
explore-read permission still get their non-parameter queries through.

Wired into every WriteQuery-building tool: `looker-query`,
`looker-query-sql`, `looker-query-url`, `looker-make-look`, and
`looker-add-dashboard-element`.

## Tests

- **Unit:** added `TestEscapeFiltersForUnquotedParameters` and
`TestEscapeFiltersForUnquotedParameters_NoopGuards` in
`internal/tools/looker/lookercommon/lookercommon_test.go` covering:
single-metacharacter escape, multi-metacharacter escape in one value,
pre-escaped pass-through, no-metacharacter pass-through, non-parameter
filters untouched, non-string values untouched, mixed filters, empty
unquoted set is a no-op, and nil-guard panics. `go test -race
./internal/tools/looker/...` passes.
- **End-to-end:** verified against the live Looker API — posting
`{"…attribution_model_selector": "first_touch"}` to `/queries/run/json`
400s with the documented error; the same body after the escape
(`first^_touch` on the wire) returns rows.

## PR Checklist

- [x] Make sure you reviewed
[CONTRIBUTING.md](https://github.com/googleapis/mcp-toolbox/blob/main/CONTRIBUTING.md)
- [x] Make sure to open an issue as a bug/issue before writing your
code!
- [x] Ensure the tests and linter pass
- [x] Code coverage does not decrease (if any source code was changed)
- [ ] Appropriate docs were updated (if necessary)
- [ ] Make sure to add `!` if this involve a breaking change

🛠️ Fixes #3288

---------

Co-authored-by: Dr. Strangelove <drstrangelove@google.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: Looker type:unquoted parameter queries fail due to wrapping quotes in filter values

4 participants