Skip to content

Commit 3526db7

Browse files
committed
CrateDB: Hello, World!
1 parent 2c34659 commit 3526db7

File tree

3 files changed

+48
-12
lines changed

3 files changed

+48
-12
lines changed

README-CRATEDB.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# CrateDB Model Context Protocol Server
2+
3+
## About
4+
5+
This CrateDB MCP Server is based on the PostgreSQL Model Context Protocol (PG-MCP) Server.
6+
[pg-mcp] uses [asyncpg], so it can also be used with CrateDB.
7+
8+
`server/resources/schema.py` received a few adjustments to compensate for
9+
missing metadata features of CrateDB, nothing serious.
10+
11+
## Usage
12+
13+
Start CrateDB.
14+
```shell
15+
docker run --rm \
16+
--name=cratedb --publish=4200:4200 --publish=5432:5432 \
17+
--env=CRATE_HEAP_SIZE=2g crate/crate:nightly \
18+
-Cdiscovery.type=single-node
19+
```
20+
21+
Initialize Python environment.
22+
```shell
23+
git clone https://github.com/crate-workbench/pg-mcp --branch=cratedb
24+
cd pg-mcp
25+
uv venv --python 3.13 --seed .venv
26+
uv sync --frozen
27+
```
28+
29+
Run MCP server and test program.
30+
```shell
31+
uv run -m server.app
32+
uv run test.py "postgresql://crate@localhost/doc"
33+
```
34+
35+
Run example Claude session (untested).
36+
```shell
37+
export DATABASE_URL=postgresql://crate@localhost
38+
export ANTHROPIC_API_KEY=...
39+
uv run -m client.claude_cli "Give me 5 Austria mountains (querying specific tables, like sys.summits)"
40+
```
41+
42+
43+
[asyncpg]: https://pypi.org/project/asyncpg
44+
[pg-mcp]: https://github.com/stuzero/pg-mcp

server/resources/schema.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ async def list_schemas(conn_id: str):
1414
"""List all non-system schemas in the database."""
1515
query = """
1616
SELECT
17-
schema_name,
18-
obj_description(pg_namespace.oid) as description
17+
schema_name
1918
FROM information_schema.schemata
2019
JOIN pg_namespace ON pg_namespace.nspname = schema_name
2120
WHERE
@@ -30,9 +29,7 @@ async def list_schema_tables(conn_id: str, schema: str):
3029
"""List all tables in a specific schema with their descriptions."""
3130
query = """
3231
SELECT
33-
t.table_name,
34-
obj_description(format('"%s"."%s"', t.table_schema, t.table_name)::regclass::oid) as description,
35-
pg_stat_get_tuples_inserted(format('"%s"."%s"', t.table_schema, t.table_name)::regclass::oid) as total_rows
32+
t.table_name
3633
FROM information_schema.tables t
3734
WHERE
3835
t.table_schema = $1
@@ -49,8 +46,7 @@ async def get_table_columns(conn_id: str, schema: str, table: str):
4946
c.column_name,
5047
c.data_type,
5148
c.is_nullable,
52-
c.column_default,
53-
col_description(format('%s.%s', c.table_schema, c.table_name)::regclass::oid, c.ordinal_position) as description
49+
c.column_default
5450
FROM information_schema.columns c
5551
WHERE
5652
c.table_schema = $1 AND
@@ -66,7 +62,6 @@ async def get_table_indexes(conn_id: str, schema: str, table: str):
6662
SELECT
6763
i.relname as index_name,
6864
pg_get_indexdef(i.oid) as index_definition,
69-
obj_description(i.oid) as description,
7065
am.amname as index_type,
7166
ARRAY_AGG(a.attname ORDER BY k.i) as column_names,
7267
ix.indisunique as is_unique,
@@ -112,7 +107,6 @@ async def get_table_constraints(conn_id: str, schema: str, table: str):
112107
WHEN c.contype = 'x' THEN 'EXCLUSION'
113108
ELSE 'OTHER'
114109
END as constraint_type_desc,
115-
obj_description(c.oid) as description,
116110
pg_get_constraintdef(c.oid) as definition,
117111
CASE
118112
WHEN c.contype = 'f' THEN
@@ -149,7 +143,6 @@ async def get_index_details(conn_id: str, schema: str, table: str, index: str):
149143
SELECT
150144
i.relname as index_name,
151145
pg_get_indexdef(i.oid) as index_definition,
152-
obj_description(i.oid) as description,
153146
am.amname as index_type,
154147
ix.indisunique as is_unique,
155148
ix.indisprimary as is_primary,
@@ -202,7 +195,6 @@ async def get_constraint_details(conn_id: str, schema: str, table: str, constrai
202195
WHEN c.contype = 'x' THEN 'EXCLUSION'
203196
ELSE 'OTHER'
204197
END as constraint_type_desc,
205-
obj_description(c.oid) as description,
206198
pg_get_constraintdef(c.oid) as definition,
207199
CASE
208200
WHEN c.contype = 'f' THEN

server/tools/query.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ async def pg_explain(query: str, conn_id: str, params=None):
7878
Complete JSON-formatted execution plan
7979
"""
8080
# Prepend EXPLAIN to the query
81-
explain_query = f"EXPLAIN (FORMAT JSON) {query}"
81+
explain_query = f"EXPLAIN {query}"
8282

8383
# Execute the explain query
8484
result = await execute_query(explain_query, conn_id, params)

0 commit comments

Comments
 (0)