Problem
altinity-mcp with --openapi already looks more like a ClickHouse frontend than a pure MCP server. To make it a drop-in plug-in replacement in front of ClickHouse, it should also speak the native ClickHouse HTTP API documented at https://clickhouse.com/docs/interfaces/http, so existing tools, dashboards, and clients can point at altinity-mcp without changes:
curl 'http://mcp.example.com/?query=SELECT+1'
curl -u alice:pass 'http://mcp.example.com/?query=SELECT+1&database=analytics&default_format=JSONCompact'
curl -X POST 'http://mcp.example.com/' --data-binary 'SELECT 1'
Today any of these hit the MCP streamable-HTTP endpoint and fail.
Feasibility — hook points already exist
The HTTP router is centralized:
cmd/altinity-mcp/main.go — mux setup around mcpRoutePatterns (MCP transport) and openAPIRoutePatterns (lines 407-433); a new chHTTPRoutePatterns can be added to the same mux or, better, registered as a catch-all that sniffs ?query= or a POST body before handing off to MCP.
pkg/server/server.go:2519 — the OpenAPI dispatcher already routes by path suffix in a switch. Adding a case that matches ?query= is a one-line addition.
handleExecuteQueryOpenAPI (pkg/server/server.go) — the core 'parse request → run query → serialize' pipeline already exists. The CH-compat handler can reuse it; the work is input parsing (CH params/headers/body) and output formatting (TSV/JSON-variants), not query execution.
GetClickHouseClientFromCtx / GetClickHouseClientWithOAuth — already resolves the right backend connection based on JWE/OAuth/cluster-secret context. The CH-compat handler should funnel through the same path so all three auth modes work identically to MCP/OpenAPI.
Proposed dispatch
In the HTTP mux, before the MCP handler, route to a new handleClickHouseHTTP when any of these is true on the request:
- Method is
GET and ?query= is non-empty
- Method is
POST and Content-Type is text/plain; charset=UTF-8 (CH's native format) or the body starts with SQL keywords
- Request has
X-ClickHouse-User / X-ClickHouse-Key / X-ClickHouse-Database headers (unambiguous CH-client signal)
Otherwise fall through to existing MCP/OpenAPI handlers. This keeps MCP and OpenAPI clients unaffected.
Scope — phase 1 (minimum viable plug-in)
Request parsing
Response formats (pick the most useful subset first)
Errors
Preserves
Scope — phase 2 (nice-to-have, file separate issues as needed)
Out of scope
- Binary native-protocol HTTP upgrades
- Replica / cluster discovery endpoints (
/replicas_status, /ping etc. — these are trivial to add if asked, but not part of 'query execution' plug-in)
- TLS termination differences — inherit from existing server TLS
Why this is cleaner than it sounds
The CH HTTP API is substantial but altinity-mcp already has:
- Query execution with auth context —
handleExecuteQueryOpenAPI path
- Per-request ClickHouse client resolution with JWE/OAuth/cluster-secret context
QueryResult { Columns, Types, Rows, Count } internal type — straightforward to serialize to TSV/JSON variants
Phase 1 is mostly a format writer + a request parser + one route case. The heavy lifting (auth, execution, OpenAPI parity) stays untouched.
Acceptance criteria
curl 'http://localhost:8080/?query=SELECT+1' returns 1\n with Content-Type: text/tab-separated-values
curl -u user:pass 'http://localhost:8080/?query=SELECT+currentUser()' runs as user (cluster-secret / JWE / OAuth mode, whichever is configured)
curl 'http://localhost:8080/?query=SELECT+1&default_format=JSONCompact' returns valid JSONCompact
- Invalid SQL returns HTTP 500 with
Code: prefix body
- Existing MCP clients and
/openapi endpoints continue to work unchanged
- Integration test: point a
clickhouse-client --url at altinity-mcp and execute a simple SELECT
Problem
altinity-mcp with
--openapialready looks more like a ClickHouse frontend than a pure MCP server. To make it a drop-in plug-in replacement in front of ClickHouse, it should also speak the native ClickHouse HTTP API documented at https://clickhouse.com/docs/interfaces/http, so existing tools, dashboards, and clients can point at altinity-mcp without changes:Today any of these hit the MCP streamable-HTTP endpoint and fail.
Feasibility — hook points already exist
The HTTP router is centralized:
cmd/altinity-mcp/main.go— mux setup aroundmcpRoutePatterns(MCP transport) andopenAPIRoutePatterns(lines 407-433); a newchHTTPRoutePatternscan be added to the same mux or, better, registered as a catch-all that sniffs?query=or a POST body before handing off to MCP.pkg/server/server.go:2519— the OpenAPI dispatcher already routes by path suffix in aswitch. Adding acasethat matches?query=is a one-line addition.handleExecuteQueryOpenAPI(pkg/server/server.go) — the core 'parse request → run query → serialize' pipeline already exists. The CH-compat handler can reuse it; the work is input parsing (CH params/headers/body) and output formatting (TSV/JSON-variants), not query execution.GetClickHouseClientFromCtx/GetClickHouseClientWithOAuth— already resolves the right backend connection based on JWE/OAuth/cluster-secret context. The CH-compat handler should funnel through the same path so all three auth modes work identically to MCP/OpenAPI.Proposed dispatch
In the HTTP mux, before the MCP handler, route to a new
handleClickHouseHTTPwhen any of these is true on the request:GETand?query=is non-emptyPOSTandContent-Typeistext/plain; charset=UTF-8(CH's native format) or the body starts with SQL keywordsX-ClickHouse-User/X-ClickHouse-Key/X-ClickHouse-Databaseheaders (unambiguous CH-client signal)Otherwise fall through to existing MCP/OpenAPI handlers. This keeps MCP and OpenAPI clients unaffected.
Scope — phase 1 (minimum viable plug-in)
Request parsing
?query=(URL-encoded SQL)?database=→ overrides default database?default_format=→ picks response format?query=)?query=SELECT+1+FROM++ POST body = concatenation (matches CH semantics)X-ClickHouse-User/X-ClickHouse-Key/X-ClickHouse-Databaseheaders (higher priority than basic auth, per CH docs)Response formats (pick the most useful subset first)
TabSeparated(CH default — required)TabSeparatedWithNamesJSONCompactJSONEachRowFORMATclause detection in the SQL (CH-compat: trailingFORMAT JSONCompactwins overdefault_format)Errors
Code: 62. DB::Exception: ... (SYNTAX_ERROR))WWW-Authenticate: BasicheaderPreserves
clickhouse-limitstill applied (or: bypass since a real CH client expects unbounded results?)oauthClaims.Emailif the request arrives authenticated via OAuthScope — phase 2 (nice-to-have, file separate issues as needed)
executeSelect)INSERT ... FORMATwith body data (POST to upload TSV/CSV)RowBinary,Native,Values,Pretty*,CSV,CSVWithNames?param_<name>=query parameters (CH's{name:Type}substitution)Accept-Encoding: gzip,lz4)X-ClickHouse-Progress,X-ClickHouse-Summaryresponse headersreadonly,max_result_rows,max_execution_timeand other settings as query paramsOut of scope
/replicas_status,/pingetc. — these are trivial to add if asked, but not part of 'query execution' plug-in)Why this is cleaner than it sounds
The CH HTTP API is substantial but altinity-mcp already has:
handleExecuteQueryOpenAPIpathQueryResult { Columns, Types, Rows, Count }internal type — straightforward to serialize to TSV/JSON variantsPhase 1 is mostly a format writer + a request parser + one route case. The heavy lifting (auth, execution, OpenAPI parity) stays untouched.
Acceptance criteria
curl 'http://localhost:8080/?query=SELECT+1'returns1\nwithContent-Type: text/tab-separated-valuescurl -u user:pass 'http://localhost:8080/?query=SELECT+currentUser()'runs asuser(cluster-secret / JWE / OAuth mode, whichever is configured)curl 'http://localhost:8080/?query=SELECT+1&default_format=JSONCompact'returns valid JSONCompactCode:prefix body/openapiendpoints continue to work unchangedclickhouse-client --urlat altinity-mcp and execute a simple SELECT