DEV-205661: Add dq_get_job_run_profile and dq_get_job_run_monitors tools - #133
regmimridul wants to merge 5 commits into
Conversation
…nitors
Two read-only MCP tools over the public DQ job-run API, both keyed by run_id:
- dq_get_job_run_profile reads a run's column-level profiling statistics via a
new clients.GetDqJobRunProfile over GET /rest/dq/1.0/jobRuns/{id}/profile,
paginated with limit/offset and a derived hasMore.
- dq_get_job_run_monitors reads a run's adaptive and custom monitor results via
the existing clients.GetDqJobRunMonitors, adding each monitor's tolerance and
a summary counting monitors by state.
A run with no profile or no monitor results reports why instead of an empty
success, and HTTP 400/401/403/404/500 and transport failures map to
status/message/guidance as in dq_get_job_run.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
# Conflicts: # README.md # pkg/tools/register.go
docs/TOOL_CONTRIBUTION_STANDARDS.md section 4.1 requires the MCP tool name to spell out domain abbreviations, and section 4.2 requires qualifying nouns that collide across domains — "run" and "profile" mean different things to data quality, lineage and classification. dq_get_job_run_profile -> get_data_quality_job_run_profile dq_get_job_run_monitors -> get_data_quality_job_run_monitors Also aligns these two with their nearest siblings, which already use the long form: get_data_quality_rule, get_data_quality_rule_results, list_data_quality_rule_templates (section 6.5). Only the Name strings and LLM-facing prose change. Section 4.1 allows Go package directories to keep the short form, so pkg/tools/get_dq_job_run_* is unchanged, as are references to main's own dq_get_job_run tool. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
@EricWarnerCollibra I had connected with @aberkowCollibra on this PR. He had mentioned that there is going to be a new flag specifically for DQ related tools. Marking this as draft until we get a confirmation on that. Kindly review this PR and #134 once ready to go. |
Introduces the data-quality experimental feature and puts get_data_quality_job_run_profile and get_data_quality_job_run_monitors behind it, so the DQ surface is opt-in rather than on by default. The flag identifier lives next to ContextSpecificationsFeature and is registered in knownExperimentalFeatures, so --experimental=data-quality, COLLIBRA_MCP_EXPERIMENTAL and mcp.experimental all accept it and it shows up in --help. Its description is deliberately generic: the rule template write tools on feature/DEV-205663 join the same gate without having to touch that entry. The annotation test now enables the new feature, keeping its "every gate on" contract intact, and a hidden/visible pair proves the gate actually gates - matching the existing debug-tool tests. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
aberkowCollibra
left a comment
There was a problem hiding this comment.
Reviewed against docs/TOOL_CONTRIBUTION_STANDARDS.md as of this PR's head (4418835) — all 41 rules, in four passes over the section groups.
One blocking item: §2, the data-privacy hard rule. min/max on the profile tool are not scoped to numeric columns, so on a text column they return a real cell value to the LLM; observedValue and exception on the monitors tool are the same channel. Comments inline.
Also flagged: §6.6 (no 422 arm in either lookupError), §7 description gaps in both tools, and two mismatches around the new data-quality flag — the --help text says it gates DQ authoring tools when it gates only these two reads, and the README's known-features list doesn't mention it.
Passing, and worth saying: the flag mechanism itself is correctly wired end to end with both-direction tests. §3.1-3.4 all pass. §4.1 passes — this is the first DQ tool to actually spell out the abbreviation in the tool name. §5.3, §6.1, §6.2, §6.7, §8.1, §8.4, §10.3 pass. Validation precedes every network call and the URL is built with url.PathEscape + url.Values.
Three rules I couldn't reach a verdict on, rather than passing them silently: §1.2 (whether the monitors tool is inside DEV-205661's agreed surface — the PR body defers this itself); §8.2 (the structs cite dq-v1-public-oas-spec.yaml, which isn't in this repo and is referenced with no version or link, so five asserted behaviours — the includeTotal param, the 100/500 page limits, and two ordering guarantees the tool promises the model — can't be checked); §8.3 (a contract test on the DQ service side isn't visible from here).
§9 (personas and permissions) is held back deliberately — Permissions: []string{} on both tools needs the actual scope identifiers from the DQ team, and I'd rather leave it open than guess at values.
One housekeeping note: the PR description is stale against this head SHA. It says the tools "are registered ungated" and that the data-quality flag "no longer exists" — this SHA reintroduces the flag, gates both tools and marks both README entries Experimental. It also calls the tools dq_get_job_run_profile/dq_get_job_run_monitors, while the code registers get_data_quality_job_run_profile/get_data_quality_job_run_monitors. Anyone reviewing from the description will read §3 wrongly.
| Min string `json:"min,omitempty" jsonschema:"Minimum value observed. Absent when no values were observed."` | ||
| Max string `json:"max,omitempty" jsonschema:"Maximum value observed. Absent when no values were observed."` |
There was a problem hiding this comment.
§2 Data privacy — hard rule. min and max are declared as "Minimum value observed." / "Maximum value observed." with no restriction to numeric columns — note that mean, median, q1 and q3 on the following lines all carry "Numeric columns only." and these two do not.
So for a text column (customer_email, patient_name, account_number) these return one real customer's cell value, verbatim, to the LLM. §2 is unconditional:
No tool may return live customer data (actual rows, cell values, sample records, file contents) to the LLM.
Everything else in ColumnProfile is genuinely metadata and fine — the counts, the percentages, definedType/inferredType, and the masked topShapes patterns.
To satisfy the rule: drop min/max, or reduce them to a non-value signal (present/absent, in-range verdict). §2 also asks that this be raised with the CHIP maintainers before the code is written rather than caught in review, so worth a note on the ticket either way.
| MonitorType string `json:"monitorType,omitempty" jsonschema:"What the monitor watches, e.g. NULL, EMPTY, UNIQUENESS, MIN VALUE, ROW_COUNT, DATA_TYPE, SCHEMA_CHANGE."` | ||
| PrimaryColumn string `json:"primaryColumn,omitempty" jsonschema:"Column the monitor watches; absent for monitors that span the whole table."` | ||
| State string `json:"state,omitempty" jsonschema:"LEARNING | PASSING | BREAKING | SUPPRESSED | USER_PASSED | EXCEPTION | STALE | SKIPPED."` | ||
| ObservedValue string `json:"observedValue,omitempty" jsonschema:"The value this run actually observed."` |
There was a problem hiding this comment.
§2 Data privacy — hard rule. observedValue is "The value this run actually observed." For the monitor types listed three lines up — MIN VALUE, and MAX/MEAN variants — that observed value is a cell value from the customer's table, not a count. expectedMin/expectedMax carry the same exposure via the learned range.
No tool may return live customer data (actual rows, cell values, sample records, file contents) to the LLM.
A verdict plus a deviation ("above the learned range") would carry the same triage signal without the value.
For context, not a request to fix it here: pkg/tools/get_dq_job_run/tool.go:47-49 already ships these three fields ungated on main, so the existing exposure is wider than this PR. Flagging so maintainers can scope it.
| RowsBreaking int64 `json:"rowsBreaking,omitempty" jsonschema:"The observed value for a custom rule: how many rows failed the rule this run."` | ||
| RowsTotal int64 `json:"rowsTotal,omitempty"` | ||
| Tolerance int `json:"tolerance,omitempty" jsonschema:"The rule's threshold: count of breaking rows allowed before the rule is judged as failing."` | ||
| Exception string `json:"exception,omitempty" jsonschema:"Failure message, only set when the monitor errored (state EXCEPTION)."` |
There was a problem hiding this comment.
§2 Data privacy — hard rule. exception passes the DQ engine's failure message through verbatim. Engine and JDBC errors routinely echo the offending value (invalid input syntax for integer: '...'), which makes this an unbounded channel for customer data rather than metadata.
Same shape in both tools' error paths: the %v on the client error reaches the model, and pkg/clients/dgc_client.go:245 builds that error as fmt.Errorf("HTTP %d: %s", response.StatusCode, string(responseBody)) — the entire non-2xx body.
Redacting or truncating both would close it.
| case 0: | ||
| out.Message = fmt.Sprintf("Failed to read the profile for run %q: %v", runID, err) | ||
| out.Guidance = "A network/transport error occurred contacting the data-quality API. Retry." | ||
| default: |
There was a problem hiding this comment.
§6.6 Map downstream errors to structured statuses. The rule names four codes — "400 / 403 / 404 / 422 become typed outputs with readable messages." 422 has no arm here (the switch covers 404, 401, 403, 400), so it lands in default, whose guidance says "This is likely a server-side error. Retry shortly; if it persists, contact your Collibra administrator."
That is the opposite of true for an unprocessable entity, and it tells the agent to retry a request that will fail identically every time.
Second case reaching this branch: clients.GetDqJobRunProfile returns code=200 with a non-nil error when the JSON fails to parse (pkg/clients/dq_job_run_profile_client.go:88-90), which renders as (HTTP 200) — a status the agent can't act on.
case http.StatusUnprocessableEntity: with a "request was rejected as invalid, fix X" message, and routing the 2xx-parse-failure somewhere that doesn't print an HTTP code, would satisfy it.
| case 0: | ||
| out.Message = fmt.Sprintf("Failed to read the monitor results for run %q: %v", runID, err) | ||
| out.Guidance = "A network/transport error occurred contacting the data-quality API. Retry." | ||
| default: |
There was a problem hiding this comment.
§6.6 Map downstream errors to structured statuses. Same gap as the profile tool: no 422 arm, so it falls to default and the agent is told to retry a request that cannot succeed. §6.6 names 422 explicitly alongside 400/403/404.
Worth noting the sibling get_dq_job_run/tool.go has the identical gap, so §6.5 (be consistent with sibling tools) pulls against §6.6 here. §6.6 still applies to a new tool — consistency shouldn't propagate the gap.
| return &chip.Tool[Input, Output]{ | ||
| Name: "get_data_quality_job_run_profile", | ||
| Title: "Get Data Quality Job Run Profile", | ||
| Description: "Reads the column-level profiling statistics produced by a single Collibra data-quality job run, " + |
There was a problem hiding this comment.
§7 Tool descriptions. The paragraph bar (§7.1) is cleared, but three of the seven required coverage points are missing:
- Item 3, the tool it is most likely confused with — no neighbour is named. The two real confusions both take the same
run_id:dq_get_job_runand the sibling monitors tool. (The PR body says "Both tool descriptions cross-reference each other" — only the monitors one does.) - Item 4, prerequisites and ordering — nothing says where
run_idcomes from.dq_search_job_runsis the tool that supplies it and isn't named. It appears in the runtimeGuidancestrings, but the model doesn't see those when choosing a tool. - Item 7, side effects and permissions — read-only isn't stated, and the required permission isn't either, though the 403 path at line 275 knows it. Read-only is asserted in the package comment and the README; neither is LLM-facing.
§7.2 — "job", "job run" and "dataset" are used unglossed. The standard's own worked example glosses exactly these terms for a model with zero Collibra knowledge.
§7.4 — three example prompts are present, but the rule's bolded "including vague ones" isn't met: all three hand over the run id. Something like "is there anything odd about the data in this run?" is what that clause is asking for.
§7.5 — runDate (line 82) has no description, format or timezone, and the client flattens upstream DqPublicRunDate{Kind, Value} to Value alone, discarding the kind discriminator that tells the model how to read the string. min/max/mean/median/q1/q3 are typed string without saying so, and min/max on a non-numeric column don't say whether the ordering is lexicographic or by inferred type.
| return &chip.Tool[Input, Output]{ | ||
| Name: "get_data_quality_job_run_monitors", | ||
| Title: "Get Data Quality Job Run Monitors", | ||
| Description: "Reads the per-monitor results of a single Collibra data-quality job run by its run_id (jobRunId) — both the " + |
There was a problem hiding this comment.
§7 Tool descriptions. §7.3 is clean — this description stands alone and the dq_get_job_run reference is disambiguation, not a dependency. Gaps:
§7.2 — "monitor" is the subject of the tool and is never defined. The description separates adaptive from custom monitors but never says a monitor is a single data-quality check on a table's data — which is precisely the gloss the standard's own example gives ("Collibra calls it a 'monitor'"). dimension is likewise Collibra-coded, described only as "Data quality dimensions the monitor contributes to" with no gloss and no value list. DQ is used as a bare abbreviation in LLM-facing prose here and on lines 91, 101-102, 128.
§7.4 — all three example prompts spell out the artifact and supply the run id; none is vague, which the rule asks for explicitly.
§7.5, concrete items:
state(lines 61, 75) lists eight values with no meanings.USER_PASSED,STALE,SKIPPED,LEARNINGaren't self-explanatory, and the model has no basis to decide whetherLEARNINGorSUPPRESSEDcounts as a failure.monitorType(line 59) mixes separators —MIN VALUEwith a space, amongROW_COUNT,DATA_TYPE,SCHEMA_CHANGE. A model echoing one back can't tell which form is real.monitorSummary.total(line 88) is "Total monitor results for the run" whilecountStateonly countsPASSING/BREAKING/EXCEPTION. Nothing tells the model the remainder is a non-empty residue of other states, so "triage at a glance" invitestotal - (passing+breaking+exception)as a wrong inference.score(line 76) gives the range but not the direction — is 0 or 100 good? — nor what a "point" is.
Worth saying that tolerance at line 81 is the standard the rest should match: "count of breaking rows allowed before the rule is judged as failing" is exactly the disambiguation §7.5 asks for, and the two same-named tolerance fields are each distinguished.
| var knownExperimentalFeatures = map[string]string{ | ||
| skills.FeatureName: "Embedded skill catalog served via list_collibra_skills and load_collibra_skill.", | ||
| tools.ContextSpecificationsFeature: "Context specification tools: list_context_specifications, get_context_specification, and contextSpecificationId parameter on get_asset_details.", | ||
| tools.DataQualityFeature: "Data quality authoring and job-run inspection tools.", |
There was a problem hiding this comment.
The flag description doesn't match what the flag gates. This says "Data quality authoring and job-run inspection tools", and it's what --help prints (via formatExperimentalForHelp → cmd/chip/config.go:168).
But data-quality gates exactly two tools — the two read tools added here, at pkg/tools/register.go:134-137. Every DQ authoring tool registers unconditionally at register.go:110-128: create_dq_job, create_dq_rule, deploy_dq_rule_template, update_dq_job, delete_dq_job, delete_dq_job_run, cancel_dq_job_run.
So --help tells an operator that enabling data-quality switches on DQ authoring. It doesn't, and those tools are already on whether the flag is set or not. Something like "Data quality job-run profile and monitor inspection tools" would describe the actual gate.
The wiring itself is correct, for the record: const → knownExperimentalFeatures → gate → both-direction tests in register_test.go:32-53, and all three input channels (--experimental, COLLIBRA_MCP_EXPERIMENTAL, mcp.experimental) resolve through the unchanged IsExperimentalEnabled. The defect is only in what it claims to cover.
| - [`dq_get_job`](pkg/tools/get_dq_job/) - Read the full definition of a single Collibra data-quality job by `name` — type (PUSHDOWN/PULLUP), edge site, connection, schema/table, source SQL, run-date window, configured monitors (adaptive + custom DQ rules), notifications, and schedule. An exact name match is tried first; if none is found, jobs whose name contains the given text are offered as candidates (`needs_input`) to disambiguate. Read-only. **Experimental** (`data-quality` feature flag) | ||
| - [`dq_get_job_run`](pkg/tools/get_dq_job_run/) - Read the full details of a single Collibra data-quality job run by `run_id` — lifecycle status/activity/timing, and once the run reaches a terminal state (FINISHED/CANCELLED/FAILED), its overall score, row count, execution time, and the per-monitor breakdown (adaptive + custom DQ rules) behind that score. Fields that are only meaningful once a run has finished are absent while it is still in progress. Read-only. **Experimental** (`data-quality` feature flag) | ||
| - [`get_data_quality_job_run_profile`](pkg/tools/get_dq_job_run_profile/) - Read the column-level profiling statistics produced by a single data-quality job run by `run_id` — per column, the type declared by the source schema and the type inferred from the values, counts of values/nulls/empties/distinct values (nulls and empties also as percentages), min/max/mean, quartiles for numeric columns, and the top observed value shapes. Paginated (`limit`/`offset`, 100 columns per page, max 500). Read-only. **Experimental** (`data-quality` feature flag) | ||
| - [`get_data_quality_job_run_monitors`](pkg/tools/get_dq_job_run_monitors/) - Read the per-monitor results of a single data-quality job run by `run_id` — adaptive monitors with their observed value against the learned expected range and its sensitivity tier, and custom DQ rules with score, breaking/passing row counts and tolerance, plus a summary counting monitors by state. `dq_get_job_run` returns the same breakdown with the run's lifecycle details; prefer this tool for monitors alone or when tolerances are needed. Read-only. **Experimental** (`data-quality` feature flag) |
There was a problem hiding this comment.
data-quality is missing from "Known experimental features". That section (README:241-247) is hand-written prose, not generated from knownExperimentalFeatures — it documents only context-specifications and skills. This PR introduces a third feature name and doesn't add it, so the README now under-documents the flag these two entries reference.
Related and worth a decision on this PR, though it didn't create it: lines 48-55 already mark eight DQ tools Experimental (data-quality feature flag) — create_data_quality_rule, deploy_data_quality_rule_template, dq_cancel_job_run, dq_delete_job, dq_delete_job_run, dq_update_job, dq_get_job, dq_get_job_run — while register.go:110-128 registers all eight ungated.
Before this PR those markers pointed at a flag that no longer existed, so they read as obviously stale. DataQualityFeature is new here (it isn't on main), which makes the name real again and the markers newly plausible — a reader now has no way to tell which of the ten marked tools are actually gated. Either correct the eight stale markers or gate those tools.
§2 data privacy. min/max were declared as "Minimum/Maximum value observed" with
no type restriction, while mean/median/q1/q3 on the same struct are all scoped
"Numeric columns only". On a text column the extremes are one customer's cell
value verbatim - an email, a name, an account number - so they are now returned
only when the column is numeric, which is how the engine already scopes the
other statistics. A mixed inferredType ("String, Double") counts as non-numeric:
one member is text, so the extremes may be too.
Engine failure text is truncated before it reaches the model. The DQ client
wraps the entire non-2xx body into its error and JDBC failures routinely echo
the offending value ("invalid input syntax for integer: ..."), which made both
the error paths and the custom-monitor exception field unbounded channels for
customer data rather than metadata. An absent exception stays absent, so a
passing monitor cannot gain one.
§6.6 structured error statuses. Neither tool had a 422 arm, so an unprocessable
entity fell through to default and told the agent to "retry shortly" a request
that fails identically every time. Both now map 422, and both map the
client's code=200-with-parse-error case, which previously rendered as
"(HTTP 200)" - a status the agent cannot act on.
§7 descriptions. Both tools now name the neighbours they are confused with
(dq_get_job_run and each other, all three taking the same run_id), state that
run_id comes from dq_search_job_runs, and state read-only plus the required
permission. "job", "job run", "dataset" and "monitor" are glossed for a model
with no Collibra knowledge, and each gains vague example prompts alongside the
explicit ones. Field-level: the eight monitor states now say which mean failure
and which do not, monitorType records that its values are not uniformly
separated, summary.total warns that the state counts do not sum to it, score
states its direction, runDate documents its format, and the string-typed
statistics say they are strings.
The --help text for data-quality claimed it gated DQ authoring tools; at this
SHA it gates the two reads added here, and now says so. The README's known
experimental features list gains the data-quality entry it was missing.
Not changed here: observedValue/expectedMin/expectedMax carry the same §2
exposure, but get_dq_job_run already ships all three ungated on main, so the
contract is wider than this PR and narrowing it unilaterally would be a
breaking change to a shipped tool. Raised on the PR for maintainer scoping.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Thanks — worked through all of these. Every point was valid; nothing to push back on. Pushed as §2 — §2 — §2 — §6.6 — no §7 — descriptions. Rewrote both. They now name the confusable neighbours (all three taking the same Flag README known-features list. Added. Stale PR description. Rewritten against this head, with the correct tool names and the gating described accurately. §9 permissions — still open, agreed. I don't have the scope identifiers either and would rather leave On the flag scope generally: #134 previously extended |
The review on #133 raised two defects that apply verbatim to these three tools, though the reviewer only audited the job-run reads. §6.6: none of create/update/delete had a 422 arm, so an unprocessable entity fell through to the catch-all, which advises retrying a request that will fail identically every time. All three now map it. §2: the downstream error reached the model through a raw %v at eleven sites with no bound, and the DQ client wraps the entire non-2xx response body into that error. Capped via a local safeErr, as on the job-run tools. The cap here is 600 rather than their 200, deliberately. Those tools surface engine and JDBC failures from SQL executed against customer rows, where a tight bound is the whole point. These endpoints only validate author-supplied SQL and asset ids without running any of it, and §6.3 wants the error actionable enough for the agent to correct itself - "cannot translate sql for dialect snowflake" has to survive. The client prefixes about 180 characters of its own prose ahead of the body, so 200 discarded the detail entirely and left only the wrapper; TestCreateSurfacesValidationFailureFromAPI caught exactly that. Tests per tool for the 422 mapping and for bounding a pathological body, plus one on update asserting the actionable detail survives the cap. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
🎯 What does this PR do?
DEV-205661. Adds two read-only MCP tools over the public DQ job-run API, so an agent can inspect what a data-quality run actually observed without going through the job-details UI. Both are keyed by
run_id(jobRunId).get_data_quality_job_run_profile— column-level profiling statistics for a run. New client functionclients.GetDqJobRunProfileoverGET /rest/dq/1.0/jobRuns/{jobRunId}/profile. Per column: the type declared by the source schema vs. the type inferred from the values, counts of values/nulls/empties/distinct values, mean and quartiles for numeric columns, and the top observed value shapes. Paginated (limit/offset, 100 per page, API caps at 500); the tool always requestsincludeTotaland returns a derivedhasMoreso a caller can tell whether to page.get_data_quality_job_run_monitors— per-monitor results for a run, over the existingclients.GetDqJobRunMonitors. Adaptive monitors report the observed value against the learned expected range plus that range's sensitivity tier; custom rules report score, breaking/passing row counts and tolerance. Adds asummaryblock counting monitors by state so a failing run can be triaged without walking both lists.Both tools derive
nullPercent/emptyPercentand the state counts rather than leaving the model to compute them, and both map HTTP 400/401/403/404/422/500 and transport failures to astatus/message/guidancetriple rather than a Go error.A run that produced no profile or no monitor results returns
errorwith an explanation (the run did not complete, or profiling/monitors are not configured) instead of an empty success, and points atdq_get_job_runto check the run's status.Rollout gating
Both tools are gated behind the
data-qualityexperimental feature flag, off by default, per §3 ofdocs/TOOL_CONTRIBUTION_STANDARDS.md— §3.2's one-shared-feature-name-per-domain. The flag was previously removed in DEV-215225; this PR reintroduces it as the DQ domain name. Wiring:DataQualityFeatureconst →knownExperimentalFeatures→ a single gated block inRegisterAll→ both-direction tests inregister_test.go.Scope is deliberately narrow: the flag gates the tools added in this PR and its sibling #134, not the DQ tools already shipping ungated on
main. Bringing those behind the flag would remove them from existing deployments, which wants its own decision.Review fixes in this PR
min/maxwere declared without a type restriction, so on a text column they returned a customer's cell value verbatim. They are now returned for numeric columns only — matching howmean/median/q1/q3were already scoped — with a mixedinferredType("String, Double") treated as non-numeric. Engine failure text is truncated before it reaches the model, on both the error paths and the custom-monitorexceptionfield, since the DQ client wraps the entire non-2xx body and JDBC errors echo the offending value.422arms to both tools (previously fell todefault, which told the agent to retry a request that cannot succeed) and a200-with-parse-error arm (previously rendered as(HTTP 200)).run_idcomes fromdq_search_job_runs, state read-only plus the required permission, gloss "job"/"job run"/"dataset"/"monitor", and add vague example prompts. Field-level: monitorstatevalues now say which mean failure,monitorTyperecords that its values aren't uniformly separated,summary.totalwarns the state counts don't sum to it,scorestates its direction, andrunDatedocuments its format.--helptext and the README's known-experimental-features list now describe the flag accurately.Deferred, with reasoning:
observedValue/expectedMin/expectedMaxcarry the same §2 exposure for theMIN VALUE/MAX/MEANmonitor types, butget_dq_job_runalready ships all three ungated onmain. Narrowing them here would be a breaking change to a shipped contract while leaving the wider exposure in place, so this wants a maintainer decision on scope rather than a unilateral fix in this PR.Still open from review: §9 —
Permissions: []string{}on both tools needs the real scope identifiers from the DQ team.Impact Analysis
Low, and lower than before the gating. Purely additive: two new read-only tools, one new client function, and one gated block in
RegisterAll. No existing tool, client function or type is modified.Because both tools sit behind
data-quality(off by default), they are invisible unless an operator opts in — so there is no change at all to any existing deployment's tool list.chip-servicesetsCOLLIBRA_MCP_EXPERIMENTALfrom env and would needdata-qualityadded there to expose them.Both tools are
ReadOnlyHint: true/DestructiveHint: falseand perform a single GET each — no writes, no confirm checkpoint.The profile endpoint is paginated and defaults to 100 columns; the tool passes
includeTotal=true, which costs the DQ API one extra count query per call. Response size is bounded by thelimitcap of 500 columns.Verified with
gofmt -l,go build ./...,go vetandgo test ./pkg/tools/... ./cmd/chip/...— all passing, including new tests for the privacy scoping (text / mixed-type / numeric columns), the422mapping, and error truncation.✅ Checklist
🤖 Generated with Claude Code