Skip to content

feat: add row-cap on tool outputs (#249)#305

Closed
dtehan-td wants to merge 2 commits into
Teradata:mainfrom
dtehan-td:feature-249
Closed

feat: add row-cap on tool outputs (#249)#305
dtehan-td wants to merge 2 commits into
Teradata:mainfrom
dtehan-td:feature-249

Conversation

@dtehan-td

Copy link
Copy Markdown
Collaborator

Summary

  • Adds a configurable row-cap on tool outputs to prevent context blow-ups when tools return large result sets. Defaults to 1000 rows (DEFAULT_ROW_LIMIT) with a 50000 hard ceiling (MAX_ROW_LIMIT); both are env-configurable.
  • Per-tool overrides via YAML (row_limit, max_row_limit, bypass_row_cap, narrowing_parameters) and per-profile overrides via tool_row_limits: in profiles.yml. A universal get_all=true parameter is injected on every capped tool as an escape hatch up to the ceiling.
  • handle_base_readQuery wraps injectable SELECTs with TOP N+1, with a Python-side trim fallback for CTEs and registry/cube tools. Truncation metadata (rows_returned, row_limit, hard_ceiling, narrowing-parameter hints) is attached to the response so clients can prompt the user to narrow.

Test plan

  • uv run ruff check src/ passes
  • uv run mypy src/ passes
  • uv run python -m pytest tests/unit/test_row_cap.py passes
  • uv run python tests/run_mcp_tests.py "uv run teradata-mcp-server" passes against a live Teradata instance, including the new tests/cases/limits_test_cases.json
  • Manual: confirm get_all=true raises the cap to MAX_ROW_LIMIT and the truncation metadata appears when results exceed the cap
  • Manual: verify a YAML tool with bypass_row_cap: true is not capped and does not expose get_all

Caps tool result sets before they reach the LLM to prevent context blow-ups.
Defaults to 1000 rows (DEFAULT_ROW_LIMIT) with a 50000 hard ceiling
(MAX_ROW_LIMIT), both env-configurable. Per-tool overrides via YAML
(`row_limit`, `max_row_limit`, `bypass_row_cap`, `narrowing_parameters`)
or per-profile via `tool_row_limits:` in profiles.yml. Adds a universal
`get_all=true` parameter on capped tools as an escape hatch up to the
ceiling.

Wraps injectable SELECTs with TOP N+1 in handle_base_readQuery and falls
back to a Python-side trim for CTEs and registry/cube tools. Truncation
metadata (rows_returned, row_limit, hard_ceiling, narrowing_params hint)
is attached to the response so clients can prompt the user to narrow.

Also adds limits_test_cases.json with `expect:` assertion support in the
test runner, unit tests for row_cap helpers, and a README entry for the
Graph tools module.
@dtehan

dtehan commented May 7, 2026

Copy link
Copy Markdown
Contributor

@remi-td take a look at the code, it turned out to be more complicated that I expected to limit the number of rows. I added a bunch of testing. But if you think the approach is too complicated I will understand.

Document the row-cap on tool outputs across the three audiences that
need it: operators (CONFIGURATION.md — env vars, per-profile
tool_row_limits), tool authors (HOW_TO_ADD_YOUR_FUNCTION.md — YAML
keys, reserved kwargs, build_truncation_metadata), and client
integrators (CLIENT_GUIDE.md — get_all escape hatch and example
metadata.truncation payload).
@dtehan-td dtehan-td closed this May 13, 2026
@dtehan-td dtehan-td deleted the feature-249 branch May 13, 2026 21:46
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.

2 participants