Read this before substantial code changes. For test commands and validation, see .claude/skills/testing/SKILL.md. For review work, see .claude/skills/review/SKILL.md. For library or server claims, see .claude/skills/upstream-verify/SKILL.md.
mcp-clickhouse is an MCP server that exposes ClickHouse and chDB to MCP clients (Claude Desktop, Cursor, etc.). It is built on FastMCP and uses clickhouse-connect as the HTTP driver. The package ships to PyPI as mcp-clickhouse and as a container image.
mcp_clickhouse/
mcp_server.py FastMCP server, tool registration, query execution, pagination, health check
mcp_env.py Environment-driven config (ClickHouseConfig, ChDBConfig, MCPServerConfig) with singletons
main.py Entry point. Resolves transport and starts the server
mcp_middleware_hook.py Loads user middleware from MCP_MIDDLEWARE_MODULE
chdb_prompt.py Prompt text returned by the chdb_initial_prompt prompt
tests/ pytest suite. Most tests expect a live ClickHouse on localhost
test-services/ docker-compose for local ClickHouse for test and development
example_middleware.py Reference middleware module for MCP_MIDDLEWARE_MODULE
fastmcp.json FastMCP project manifest pointing at mcp_clickhouse/mcp_server.py
Dockerfile Container image build
Function-level invariants (singleton config caching, context-override handling, truststore inject, thread-pool timeout semantics) live in docstrings on the relevant code. The items below are the cross-cutting ones that do not belong in any single function.
Three defaults ship at the safer setting on purpose:
- Queries run with
readonly=1unlessCLICKHOUSE_ALLOW_WRITE_ACCESS=true. - Even with writes enabled, destructive statements (
DROP TABLE,DROP DATABASE,DROP VIEW,DROP DICTIONARY,TRUNCATE TABLE) are rejected unlessCLICKHOUSE_ALLOW_DROP=true. The check is a regex scan in_validate_query_for_destructive_ops. - HTTP and SSE transports require an auth token via
CLICKHOUSE_MCP_AUTH_TOKENunlessCLICKHOUSE_MCP_AUTH_DISABLED=trueis set explicitly.
When touching get_readonly_setting, _validate_query_for_destructive_ops, or the auth wiring in mcp_server.py, keep the behavior matrix intact and update tests.
ClickHouse and chDB can each be enabled or disabled. All four combinations must keep working:
CLICKHOUSE_ENABLED(defaulttrue) gateslist_databases,list_tables,run_query.CHDB_ENABLED(defaultfalse) gatesrun_chdb_select_queryandchdb_initial_prompt.- chDB requires the
chdboptional extra. If the package is missing, the server warns and skips chDB tool registration. Do not makechdba hard import at module load. /healthaccounts for all combinations. It returns503if both backends are effectively disabled or unreachable.
These are the contract. Treat all changes here as breaking and update README.md alongside any intentional change. AGENTS.md lists the surface explicitly.
table_pagination_cache is a cachetools.TTLCache(maxsize=100, ttl=3600). Tokens are UUIDs. Tokens are invalidated across filter changes (database, like, not_like, include_detailed_columns); the server logs a warning and restarts from the beginning. Pagination state does not survive process restarts and is not safe across replicas.
MCP_MIDDLEWARE_MODULE lets users inject arbitrary middleware by module name. The loader (mcp_middleware_hook.py) calls module.setup_middleware(mcp) and reraises import errors. Documented FastMCP hooks (on_call_tool, on_request, on_read_resource, etc.) are part of the user contract. example_middleware.py is the reference and must stay runnable.
The server is not a hot path, but a few areas matter:
list_tablesmakes one query per table for detailed column metadata.include_detailed_columns=Falseis the escape hatch for large schemas. Preserve the batching inget_paginated_table_data.- The query thread pool caps concurrency at 10. Tool behavior under timeouts must stay predictable.
CLICKHOUSE_MCP_QUERY_TIMEOUTis enforced at the Python level viaFuture.result(timeout=...); the query is not canceled server-side. - Large result sets are returned raw from
clickhouse-connectwithout rebuilding. Avoid per-row Python overhead.
- Python
3.10+. CI exercises Python3.13. - ClickHouse server: CI uses
clickhouse/clickhouse-server:24.10. Behavior should stay reasonable across recent versions. fastmcp>=2.0.0,<3.0.0.clickhouse-connect>=0.8.16.- Transports:
stdio,http,sse. - Optional extras: bare install (no chDB) must keep working. The
chdbextra is exercised in CI. - Container image: built in CI; must at least import and start under the default command.
The code wins. If you find a divergence while working, fix the doc in the same PR rather than letting it rot.