Add ClickHouse chat agent example#123
Conversation
A chat.agent() with listTables/describeTable/runQuery tools backed by the ClickHouse Node.js client. Read-only query guards enforced in code and via ClickHouse settings (readonly=2, row cap, execution timeout).
WalkthroughThis PR adds a new example project, clickhouse-chat-agent, implementing a Trigger.dev-based chat agent that answers questions using ClickHouse. It includes project configuration files (package.json, tsconfig.json, trigger.config.ts, .gitignore, .env.example), an agent implementation providing listTables, describeTable, and runQuery tools with read-only query enforcement and output truncation, a streaming run handler using Claude Opus, and a README with setup and usage instructions. Estimated code review effort: 3 (Moderate) | ~20 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@clickhouse-chat-agent/trigger/clickhouse-agent.ts`:
- Around line 80-96: The current read-only guard in runQuery only checks
statement prefixes, so it still allows queries to override limits with SETTINGS
and call outbound table functions. Tighten the ClickHouse agent’s query path by
enforcing immutable user/profile-level caps for max rows and execution time, and
add a locked-down allowlist/blocklist for table functions such as url, s3,
remote, mysql, and postgres within runQuery (or the underlying ClickHouse
user/config used by it). Keep the existing READ_ONLY_STATEMENTS check, but make
the real restriction come from the ClickHouse profile/user configuration and
explicit table-function filtering.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 345dd6d8-ad46-4e2c-b0d1-4184a968f2f8
⛔ Files ignored due to path filters (1)
clickhouse-chat-agent/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (7)
clickhouse-chat-agent/.env.exampleclickhouse-chat-agent/.gitignoreclickhouse-chat-agent/README.mdclickhouse-chat-agent/package.jsonclickhouse-chat-agent/trigger.config.tsclickhouse-chat-agent/trigger/clickhouse-agent.tsclickhouse-chat-agent/tsconfig.json
| const READ_ONLY_STATEMENTS = /^\s*(select|with|show|describe|desc|explain|exists)\b/i; | ||
|
|
||
| const runQuery = tool({ | ||
| description: | ||
| "Run a read-only SQL query against ClickHouse and get the results as JSON rows. " + | ||
| "Only SELECT-style statements are allowed. Always include a LIMIT (at most 100 rows) " + | ||
| "unless the query is an aggregation.", | ||
| inputSchema: z.object({ | ||
| query: z.string().describe("The ClickHouse SQL query to run"), | ||
| }), | ||
| execute: async ({ query }) => { | ||
| if (!READ_ONLY_STATEMENTS.test(query)) { | ||
| return { | ||
| error: | ||
| "Only read-only statements (SELECT, WITH, SHOW, DESCRIBE, EXPLAIN, EXISTS) are allowed.", | ||
| }; | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
Does ClickHouse readonly=2 allow a query-level SETTINGS clause to override max_result_rows and max_execution_time that were supplied as client clickhouse_settings, and which table functions (url, s3, remote, mysql, postgres) are permitted under readonly=2 on ClickHouse Cloud?
💡 Result:
Yes, in ClickHouse, a query-level SETTINGS clause can be used to override settings such as max_result_rows and max_execution_time, provided the user has the necessary permissions [1]. Under the readonly=2 setting, users are permitted to change settings (excluding the readonly setting itself), which enables them to modify query-level constraints [2][3][4]. Regarding table functions (url, s3, remote, mysql, postgres) under readonly=2: 1. Permission Requirement: Many table functions that access external data—such as url, s3, remote, mysql, and postgres—require the CREATE TEMPORARY TABLE permission to execute because they may involve temporary metadata or connection handling [5][6]. 2. readonly=2 Functionality: The readonly=2 setting explicitly allows users to execute SET and CREATE TEMPORARY TABLE queries [2]. Consequently, users with readonly=2 are generally permitted to use these table functions, whereas users with readonly=1 (which restricts these actions) are typically blocked from using them [5][6][7]. 3. ClickHouse Cloud: While ClickHouse Cloud follows standard ClickHouse permission models, specific cloud-managed security policies or constraints (often enforced via user profiles or settings constraints) may further restrict the use of certain table functions regardless of the readonly setting to maintain platform security. Users should verify their specific service-level configuration or documentation if encountering access issues, as external data access might be governed by additional cloud-side controls.
Citations:
- 1: https://clickhouse.com/docs/operations/settings/query-level
- 2: https://clickhouse.com/docs/operations/settings/permissions-for-queries
- 3: https://github.com/ClickHouse/ClickHouse/blob/master/docs/en/operations/settings/constraints-on-settings.md
- 4: https://clickhouse.com/docs/operations/settings/constraints-on-settings
- 5: Cannot execute SELECT from a tablefunction with READONLY mode ClickHouse/ClickHouse#74248
- 6: https://clickhouse.com/docs/sql-reference/table-functions/url
- 7: https://groups.google.com/g/clickhouse/c/xDjdJR4bPAk
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the target file and related references.
git ls-files | rg '^clickhouse-chat-agent/trigger/clickhouse-agent\.ts$|clickhouse_settings|readonly|max_result_rows|max_execution_time|allowed_table_functions|table function|runQuery|tool\('
# Show the relevant section of the target file with line numbers.
sed -n '1,220p' clickhouse-chat-agent/trigger/clickhouse-agent.ts
# Search for other query-execution/settings code that may affect this behavior.
rg -n "clickhouse_settings|readonly|max_result_rows|max_execution_time|allowed_table_functions|SETTINGS|url\(|s3\(|remote\(|mysql\(|postgres\(" clickhouse-chat-agent -SRepository: triggerdotdev/examples
Length of output: 7083
readonly=2 still leaves the query limits and network surface overrideable.
SELECT-only parsing doesn’t stop a prompt from adding SETTINGS max_result_rows=0, max_execution_time=0 or using outbound table functions like url()/s3()/remote()/mysql()/postgres(). Use an immutable profile/user-level restriction for the caps and a locked-down table-function allowlist instead of relying on per-query settings.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@clickhouse-chat-agent/trigger/clickhouse-agent.ts` around lines 80 - 96, The
current read-only guard in runQuery only checks statement prefixes, so it still
allows queries to override limits with SETTINGS and call outbound table
functions. Tighten the ClickHouse agent’s query path by enforcing immutable
user/profile-level caps for max rows and execution time, and add a locked-down
allowlist/blocklist for table functions such as url, s3, remote, mysql, and
postgres within runQuery (or the underlying ClickHouse user/config used by it).
Keep the existing READ_ONLY_STATEMENTS check, but make the real restriction come
from the ClickHouse profile/user configuration and explicit table-function
filtering.
What
A new example:
clickhouse-chat-agent— a chat agent that answers questions about your data by writing and running SQL against ClickHouse Cloud, using the official Node.js ClickHouse client.listTables/describeTable/runQuerytools (AI SDKtool()+ Zod)readonly=2, a 1,000-row cap and 30s timeout via ClickHouse settingsdescribeTableuses a boundIdentifierquery param (no SQL string interpolation)Testing
Typechecked against
@trigger.dev/sdk4.5.1 and run end-to-end against a ClickHouse Cloud service loaded with the NYC Taxi example dataset — schema discovery, aggregations and markdown-table answers all work in the agent playground.Companion docs PR: triggerdotdev/trigger.dev#4195 (links to this folder — merge this one first).
🤖 Generated with Claude Code
Summary by CodeRabbit