HypeQuery is a TypeScript semantic layer that sits between AI agents and ClickHouse, translating natural-language queries into type-safe SQL without prompt injection risks. With MCP (Model Context Protocol) integration and schema-as-code definitions, it turns a columnar OLAP database into queryable infrastructure that agents can call like any other tool. The project positions itself as the missing link between agent orchestration and production analytical workloads.
The core problem: agents need to query structured data, but raw SQL access creates security holes and brittle dependencies on column names. HypeQuery solves this by defining a semantic model in TypeScript that agents interact with through stable, versioned abstractions.
Why ClickHouse and Semantic Layers Matter for Agents
ClickHouse is a columnar database optimized for analytical queries (aggregations, time-series analysis, large scans). Unlike transactional databases (Postgres, MySQL), ClickHouse excels at reading millions of rows to compute metrics, which is exactly what agents need when answering questions like “What were our top-selling products last quarter?” or “Show me anomalies in API latency over the past week.”
The semantic layer adds three critical properties:
- Type safety: Agents call
query.getTopProducts({ limit: 10, timeRange: 'Q1-2026' })instead of constructing SQL strings. The TypeScript compiler catches errors before runtime. - Access control: The semantic layer defines which tables, columns, and aggregations are exposed. Agents cannot SELECT from arbitrary tables or run UPDATE/DELETE statements.
- Versioning: When you rename a column or change an aggregation formula, the semantic model absorbs the change. Agents continue calling the same function; the underlying SQL adapts.
How Natural-Language Queries Become Type-Safe SQL
HypeQuery’s flow looks like this:
Agent prompt: "Show top 5 products by revenue in Q1 2026"
↓
LLM generates tool call: getTopProducts({ limit: 5, timeRange: 'Q1-2026' })
↓
HypeQuery semantic model translates to:
SELECT product_name, SUM(revenue) as total_revenue
FROM sales
WHERE date >= '2026-01-01' AND date < '2026-04-01'
GROUP BY product_name
ORDER BY total_revenue DESC
LIMIT 5
↓
ClickHouse executes query, returns rows
↓
HypeQuery serializes result as JSON
↓
Agent receives structured data: [{ product_name: "Widget A", total_revenue: 50000 }, ...]
Key security boundary: The agent never sees SQL. It calls a TypeScript function with validated parameters. HypeQuery’s semantic model defines allowed filters, aggregations, and joins. If the agent tries to query a table not in the model, the call fails at compile time (TypeScript) or runtime (schema validation).
Example semantic model definition:
import { defineModel, metric, dimension } from '@hypequery/core';
export const salesModel = defineModel({
name: 'sales',
table: 'sales_events',
dimensions: {
productName: dimension({ column: 'product_name', type: 'string' }),
date: dimension({ column: 'event_date', type: 'date' }),
region: dimension({ column: 'region', type: 'string' }),
},
metrics: {
revenue: metric({
sql: 'SUM(price * quantity)',
type: 'number',
}),
orderCount: metric({
sql: 'COUNT(DISTINCT order_id)',
type: 'number',
}),
},
filters: {
timeRange: (range: string) => {
// Parse range like 'Q1-2026' into date bounds
const [start, end] = parseQuarter(range);
return `event_date >= '${start}' AND event_date < '${end}'`;
},
},
});
Prompt injection mitigation: Because the agent calls salesModel.query({ metrics: ['revenue'], filters: { timeRange: 'Q1-2026' } }), there is no string concatenation. The filter logic is defined in code, not assembled from user input. Even if an LLM hallucinates a malicious parameter, TypeScript’s type system rejects it.
MCP Integration: Schema-Aware Tools vs REST Endpoints
HypeQuery supports MCP, which means it can expose semantic models as tools that MCP-compatible agents (Claude Desktop, custom LangChain setups) can discover and call.
Two integration patterns:
Pattern 1: Schema-Aware MCP Tool
HypeQuery generates an MCP tool definition from the semantic model:
{
"name": "query_sales_data",
"description": "Query sales metrics by product, region, and time range",
"input_schema": {
"type": "object",
"properties": {
"metrics": {
"type": "array",
"items": { "enum": ["revenue", "orderCount"] }
},
"dimensions": {
"type": "array",
"items": { "enum": ["productName", "region", "date"] }
},
"filters": {
"type": "object",
"properties": {
"timeRange": { "type": "string" }
}
},
"limit": { "type": "number", "default": 100 }
},
"required": ["metrics"]
}
}
The MCP server (running HypeQuery) receives the tool call, validates it against the schema, executes the ClickHouse query, and returns JSON. The agent gets structured data without knowing SQL exists.
Advantages:
- Agent sees only the semantic model’s API (metrics, dimensions, filters).
- Schema changes (adding a new metric) automatically update the tool definition.
- No custom API routes or REST boilerplate.
Pattern 2: REST Endpoint with Validation
For non-MCP agents, HypeQuery can expose a REST API:
POST /api/query
{
"model": "sales",
"metrics": ["revenue"],
"dimensions": ["productName"],
"filters": { "timeRange": "Q1-2026" },
"limit": 10
}
The endpoint validates the request against the semantic model’s schema (using Zod or similar), translates it to SQL, and returns results. This works for agents that call HTTP APIs directly (AutoGPT, custom Python scripts).
Trade-off table:
| Approach | Pros | Cons |
|---|---|---|
| MCP Tool | Auto-discovery, type-safe, no API design | Requires MCP-compatible agent runtime |
| REST Endpoint | Works with any HTTP client, easy to test | Manual API versioning, no auto-discovery |
| Direct SDK | Lowest latency, full TypeScript types | Agent must run in Node.js/Deno/Bun |
Versioning and Migrating Semantic Models
Agents depend on stable column names and aggregation logic. If you rename product_name to product_title in ClickHouse, every agent query breaks unless the semantic model absorbs the change.
HypeQuery’s versioning strategy:
-
Semantic model as abstraction: The model defines
productNameas a dimension. Internally, it maps toproduct_namein ClickHouse. If you rename the column, update the mapping in the model. Agents continue callingproductName.dimensions: { productName: dimension({ column: 'product_title', type: 'string' }), // Changed from 'product_name' } -
Model versioning: HypeQuery supports multiple versions of a model. Agents can specify which version to query:
POST /api/query { "model": "sales", "version": "v2", // Explicitly request v2 schema "metrics": ["revenue"] }Old agents use
v1, new agents usev2. You deprecatev1after a migration window. -
Schema migration scripts: When you change aggregation logic (e.g., revenue now includes tax), you version the metric:
metrics: { revenue: metric({ sql: 'SUM(price * quantity)', // v1 deprecated: true, }), revenueWithTax: metric({ sql: 'SUM((price * quantity) * 1.1)', // v2 }), }Agents migrate from
revenuetorevenueWithTaxover time. The semantic model logs warnings when deprecated metrics are used.
Failure mode: If you delete a metric without deprecating it first, agents calling that metric will fail at runtime. Mitigation: enforce a deprecation policy (mark deprecated, wait 30 days, then remove).
Deployment Shape and Observability
Typical architecture:
Agent Runtime (LangChain, Claude Desktop)
|
| MCP tool call: query_sales_data({ metrics: ['revenue'] })
v
HypeQuery MCP Server (Node.js)
|
| SELECT SUM(price * quantity) FROM sales_events WHERE ...
v
ClickHouse (self-hosted or ClickHouse Cloud)
Deployment options:
- Self-hosted: Run HypeQuery as a Node.js service (Express or Fastify). Deploy on ECS, Kubernetes, or a VM. ClickHouse runs separately (on-prem or ClickHouse Cloud).
- Serverless: Deploy HypeQuery on Vercel/Netlify Functions. Each query is a cold start, so this works for low-frequency agent queries (not real-time dashboards).
- Embedded: Import HypeQuery as a library in your agent runtime. No separate service, but you lose multi-agent isolation.
Observability:
- Query logs: HypeQuery logs every query with model name, parameters, execution time, and result row count. Ship logs to Datadog or CloudWatch.
- ClickHouse metrics: Monitor query duration, memory usage, and rows scanned in ClickHouse’s system tables (
system.query_log). - Error tracking: If a query fails (syntax error, timeout, permission denied), HypeQuery returns a structured error. Agents can retry or escalate to a human.
Common failure modes:
- ClickHouse timeout: Agents request a query that scans billions of rows. ClickHouse kills it after 30 seconds. Mitigation: Set row limits in the semantic model (
maxRows: 100000). - Schema drift: Someone alters the ClickHouse table without updating the semantic model. Queries fail with “column not found.” Mitigation: Run schema validation tests in CI.
- MCP server downtime: If the HypeQuery MCP server crashes, agents cannot query data. Mitigation: Deploy multiple replicas behind a load balancer.
Code Example: Defining and Querying a Semantic Model
// semantic-models/sales.ts
import { defineModel, metric, dimension } from '@hypequery/core';
export const salesModel = defineModel({
name: 'sales',
table: 'sales_events',
dimensions: {
productName: dimension({ column: 'product_name', type: 'string' }),
region: dimension({ column: 'region', type: 'string' }),
date: dimension({ column: 'event_date', type: 'date' }),
},
metrics: {
revenue: metric({
sql: 'SUM(price * quantity)',
type: 'number',
}),
orderCount: metric({
sql: 'COUNT(DISTINCT order_id)',
type: 'number',
}),
},
});
// agent-tool.ts
import { HypeQuery } from '@hypequery/core';
import { salesModel } from './semantic-models/sales';
const hq = new HypeQuery({
clickhouse: {
host: 'localhost',
port: 8123,
database: 'analytics',
},
models: [salesModel],
});
// Agent calls this function via MCP or direct SDK
async function getTopProducts(
limit: number,
timeRange: string
): Promise<Array<{ productName: string; revenue: number }>> {
const result = await hq.query({
model: 'sales',
metrics: ['revenue'],
dimensions: ['productName'],
filters: { timeRange },
orderBy: [{ metric: 'revenue', direction: 'desc' }],
limit,
});
return result.rows;
}
When to Use HypeQuery vs Alternatives
Use HypeQuery when:
- Your agents need to query structured analytical data (sales, logs, metrics) stored in ClickHouse.
- You want type-safe abstractions over SQL to prevent prompt injection and schema coupling.
- You need versioned semantic models that agents can depend on across schema migrations.
- You are already using ClickHouse and want to expose it to agents without writing custom API layers.
Avoid HypeQuery when:
- Your agents only need unstructured document retrieval (use a vector database like Pinecone or Weaviate with RAG).
- You need transactional writes (ClickHouse is append-only; use Postgres or MySQL for CRUD operations).
- Your data is small enough to fit in memory (use DuckDB or Pandas; no need for a distributed database).
- You need sub-10ms query latency (ClickHouse is fast for analytics but not for key-value lookups; use Redis or DynamoDB).
Complementary Tools in the Agent Stack
HypeQuery solves a specific problem (type-safe analytical queries), but agents need other infrastructure components:
| Tool | Purpose | How It Works With HypeQuery |
|---|---|---|
| Langfuse | LLM observability (traces, costs) | Logs HypeQuery tool calls and latency |
| Agent-Reach | Web scraping and data extraction | Agents scrape external data, then query internal analytics via HypeQuery |
| Hasura | GraphQL over Postgres | Use Hasura for transactional data, HypeQuery for analytics |
| Cube.js | Semantic layer for BI dashboards | Cube.js serves humans, HypeQuery serves agents |
HypeQuery is not a replacement for vector databases, observability platforms, or web scrapers. It provides a type-safe bridge between agent orchestration frameworks (LangChain, LlamaIndex) and production analytical databases.
Technical Verdict
HypeQuery works when you need agents to query structured analytical data without writing SQL or exposing raw database access. The TypeScript semantic layer provides type safety, versioning, and access control that raw ClickHouse connections lack. MCP integration makes it plug-and-play for compatible agents, while the REST API supports custom runtimes.
The biggest risk is schema drift. If your ClickHouse schema changes frequently, you must keep the semantic model in sync or agents will fail. Mitigation: treat the semantic model as infrastructure code with version control, automated CI tests that validate schema mappings against live ClickHouse tables, and a formal deprecation policy that requires 30-day warnings before removing metrics. The second risk is query performance. Agents can request expensive queries (full table scans, complex joins). Mitigation: set row limits, timeouts, and monitor ClickHouse’s query log.
For teams already using ClickHouse, HypeQuery is a low-friction way to expose data to agents. For greenfield projects, evaluate whether you need ClickHouse’s columnar performance (billions of rows, complex aggregations) or if a simpler database (Postgres with Hasura, DuckDB) suffices. If your agents only need simple lookups, HypeQuery is overkill. If they need to compute metrics across large datasets, it is the right tool.
Source Links
- Primary source: HypeQuery GitHub Repository
- Discussion: Show HN: HypeQuery