feat(query): share completion metadata across API and MCP (#1844) - #2038
Conversation
Move query completion candidate generation out of the CLI shell helper and into the archive query contract layer so shell, CLI JSON, Python API, and MCP consumers all expose the same payload shape. Relocate public action contracts under operations while keeping the CLI import as a compatibility adapter. Add facade, MCP, discovery, envelope, and CLI adapter tests around the shared completion payload. Refresh generated topology for the new query contract module. Verification: nix develop --command devtools verify --quick; focused CLI/API/MCP tests for query completions; MCP read-tool discovery smoke; action-contract compatibility tests. Default devtools verify was attempted but the testmon pytest phase was interrupted after roughly 20 minutes without completing.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: aa6361f48e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| class QueryCompletionError(ValueError): | ||
| """Raised when a completion request lacks required context.""" |
There was a problem hiding this comment.
Surface completion usage errors as client errors
When an MCP client requests an invalid completion shape, such as kind="structural-field" without unit or an unsupported kind, the code below raises QueryCompletionError, but this class is only a ValueError. server_support._exception_to_error_json classifies non-PolylogueError exceptions as redacted internal_errors, so the new public MCP tool hides the actionable usage message from clients instead of returning a domain/client error; make this error a PolylogueError subclass or handle it before the safe-call wrapper.
Useful? React with 👍 / 👎.
## Summary Adds a read-only daemon/web API endpoint for shared query-completion metadata. `GET /api/query-completions` now returns the same `query_completions` payload already used by CLI JSON, Python API, and MCP. ## Problem After #2038, the completion metadata contract was shared across CLI/API/MCP, but the local web/workbench surface still had no route to consume it. That left future query-builder work one step away from either reusing the contract or reimplementing completion metadata in the browser layer. ## Solution - Route `GET /api/query-completions` through `polylogue.archive.query.completions.query_completion_payload`. - Default omitted `kind` to `field`, matching the most useful discovery case. - Return a typed 400 JSON error for invalid completion context, such as `kind=structural-field` without `unit`. - Add live HTTP tests for success and invalid-context behavior. ## Verification - `nix develop --command devtools test tests/unit/daemon/test_web_reader.py -k query_completions` — 2 passed. - `nix develop --command devtools verify --quick` — exit_code 0. - Push hook reran `devtools verify --quick` at `b884da43` — exit_code 0. Ref #1844 Ref #1846
Summary
Moves query-completion candidate generation into a shared query contract module and exposes the same structured completion payload through the CLI JSON command, the Python API, and MCP. Public action contracts now live under
polylogue.operations.action_contracts, with the existing CLI import kept as an adapter so current CLI/tests keep working.Problem
polylogue query-completionsmade the metadata visible, but the candidate model and providers still lived inpolylogue.cli.shell_completion_values. That made MCP, Python, and future web/query-builder consumers either depend on CLI internals or reimplement the same registry reads. It also left the MCP discovery matrix incomplete for a couple of recently-added read tools.Solution
polylogue.archive.query.completionsas the shared payload/candidate source for query fields, structural units/fields, readable count/date operators, and action candidates.polylogue.operations.action_contracts;polylogue.cli.action_contractsnow re-exports that registry for compatibility.CompletionItems locally.Polylogue.query_completions(...)and MCPquery_completionsso non-shell consumers get the same payload shape.Verification
nix develop --command devtools test tests/unit/cli/test_command_aux_runtime.py -k 'query_field_candidates or query_action_candidates'— 3 passed.nix develop --command devtools test tests/unit/cli/test_click_app.py -k query_completions— 2 passed.nix develop --command devtools test tests/unit/api/test_facade_contracts.py -k 'query_completions or no_undiscovered'— 3 passed.nix develop --command devtools test tests/unit/mcp/test_tool_contracts.py -k QueryCompletions— 1 passed.nix develop --command devtools test tests/unit/mcp/test_tool_discovery.py -k 'query_completions or known_minimal'— 2 passed.nix develop --command devtools test tests/unit/mcp/test_tool_discovery.py -k tool_returns_valid_response_envelope— 51 passed.nix develop --command devtools test tests/unit/cli/test_cli_action_contracts.py tests/unit/cli/test_completion_matrix.py tests/unit/devtools/test_action_contract_report.py— 110 passed.nix develop --command devtools verify --quick— exit_code 0.devtools verify --quickataa6361f4— exit_code 0.nix develop --command devtools verifywas also attempted. Static/generated checks passed, but the testmon pytest phase was interrupted after roughly 20 minutes without completing; I did not leave the pytest process running.Ref #1844
Ref #1825