Skip to content

Commit 34988ab

Browse files
committed
MCP: Add tool get_table_columns
1 parent f8f5ad3 commit 34988ab

File tree

10 files changed

+83
-6
lines changed

10 files changed

+83
-6
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
- OCI: Added `curl` to image
55
- MCP: Started using MCP tool descriptions from Python docstrings
66
- Instructions: Copy-editing. Wording.
7+
- MCP: Added tool `get_table_columns`
78

89
## v0.0.5 - 2025-07-22
910
- MCP: Fixed defunct `get_cratedb_documentation_index` tool

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ The CrateDB MCP Server provides two families of tools.
196196
The **Text-to-SQL tools** talk to a CrateDB database cluster to inquire database
197197
and table metadata, and table content.
198198
<br>
199-
Tool names are: `get_cluster_health`, `get_table_metadata`, `query_sql`
199+
Tool names are: `query_sql`, `get_table_columns`, `get_table_metadata`
200200

201201
The **documentation server tools** looks up guidelines specific to CrateDB topics,
202202
to provide the most accurate information possible.
@@ -205,6 +205,8 @@ Relevant information is pulled from <https://cratedb.com/docs>, curated per
205205
<br>
206206
Tool names are: `get_cratedb_documentation_index`, `fetch_cratedb_docs`
207207

208+
Health inquiry tool: `get_cluster_health`
209+
208210
### Install package
209211

210212
The configuration snippets for AI assistants are using the `uvx` launcher

cratedb_mcp/core.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
fetch_cratedb_docs,
1111
get_cluster_health,
1212
get_cratedb_documentation_index,
13+
get_table_columns,
1314
get_table_metadata,
1415
query_sql,
1516
)
@@ -46,6 +47,13 @@ def add_tools(self):
4647
tags={"text-to-sql"},
4748
)
4849
)
50+
self.mcp.add_tool(
51+
Tool.from_function(
52+
fn=get_table_columns,
53+
description=get_table_columns.__doc__,
54+
tags={"text-to-sql"},
55+
)
56+
)
4957
self.mcp.add_tool(
5058
Tool.from_function(
5159
fn=get_table_metadata,

cratedb_mcp/knowledge.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,21 @@
99

1010

1111
class Queries:
12+
TABLES_COLUMNS = """
13+
SELECT
14+
c.table_schema,
15+
c.table_name,
16+
c.column_name,
17+
c.data_type,
18+
c.is_nullable,
19+
c.column_default
20+
FROM information_schema.columns c
21+
WHERE {where}
22+
ORDER BY
23+
c.table_schema,
24+
c.table_name,
25+
c.ordinal_position
26+
"""
1227
TABLES_METADATA = """
1328
WITH partitions_health AS (SELECT table_name,
1429
table_schema,

cratedb_mcp/prompt/instructions.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
Use all available tools for gathering accurate information.
44

55
You have the following tools available:
6-
1. `get_table_metadata`: Return table metadata for all tables stored in CrateDB.
7-
2. `query_sql`: Execute an SQL query on CrateDB and return the results. Select only the columns you need (avoid `SELECT *`) and, where appropriate, add a `LIMIT` clause to keep result sets concise.
8-
3. `get_cratedb_documentation_index`: Return the table of contents for the curated CrateDB documentation index. Use it whenever you need to verify CrateDB-specific syntax.
9-
4. `fetch_cratedb_docs`: Given a `link` returned by `get_cratedb_documentation_index`, fetch the full content of that documentation page. Content can be quoted verbatim when answering questions about CrateDB.
6+
- `get_table_columns`: Return table column information for all tables stored in CrateDB.
7+
- `get_table_metadata`: Return table metadata for all tables stored in CrateDB.
8+
- `query_sql`: Execute an SQL query on CrateDB and return the results. Select only the columns you need (avoid `SELECT *`) and, where appropriate, add a `LIMIT` clause to keep result sets concise.
9+
- `get_cratedb_documentation_index`: Return the table of contents for the curated CrateDB documentation index. Use it whenever you need to verify CrateDB-specific syntax.
10+
- `fetch_cratedb_docs`: Given a `link` returned by `get_cratedb_documentation_index`, fetch the full content of that documentation page. Content can be quoted verbatim when answering questions about CrateDB.
1011

1112
Please follow those rules when using the available tools:
13+
- First use `get_table_columns` to find out about tables stored in the database and their column names and types. Next, use `query_sql` to execute the SQL query.
1214
- First use `get_table_metadata` to find out about tables stored in the database and their metadata. Next, use `query_sql` to execute the SQL query.
1315
- First use `get_cratedb_documentation_index` to get an overview about curated documentation resources about CrateDB. Then, use `fetch_cratedb_docs` to retrieve individual pages by `link`.
1416

cratedb_mcp/tool.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,40 @@ def query_sql(query: str) -> dict:
2828
return query_cratedb(query)
2929

3030

31+
def get_table_columns() -> dict:
32+
"""
33+
Return schema and column information for all tables stored in CrateDB from
34+
its `information_schema` table. Use it to discover database entities you are
35+
unfamiliar with, the column names are crucial for correctly formulating SQL
36+
queries.
37+
38+
The returned fields are table_schema, table_name, column_name,
39+
data_type, is_nullable, and column_default.
40+
41+
The returned sections are:
42+
- user tables: includes all user-defined tables without system tables
43+
- system tables: `information_schema`, `pg_catalog`, and `sys`.
44+
45+
"""
46+
47+
variants = {
48+
"user": """
49+
table_schema != 'information_schema' AND
50+
table_schema != 'pg_catalog' AND
51+
table_schema != 'sys'
52+
""",
53+
"information_schema": "table_schema = 'information_schema'",
54+
"pg_catalog": "table_schema = 'pg_catalog'",
55+
"sys": "table_schema = 'sys'",
56+
}
57+
58+
response = {}
59+
for variant, where in variants.items():
60+
query = Queries.TABLES_COLUMNS.format(where=where)
61+
response[variant] = query_sql(query)
62+
return response
63+
64+
3165
def get_table_metadata() -> dict:
3266
"""
3367
Return table metadata for all tables stored in CrateDB.

examples/mcptools.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ mcpt tools cratedb-mcp serve
2828

2929
# Explore the Text-to-SQL tools.
3030
mcpt call query_sql --params '{"query":"SELECT * FROM sys.summits LIMIT 3"}' cratedb-mcp serve
31+
mcpt call get_table_columns cratedb-mcp serve
3132
mcpt call get_table_metadata cratedb-mcp serve
3233
mcpt call get_cluster_health cratedb-mcp serve
3334

tests/test_examples.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66

77
def test_mcptools():
8-
proc = subprocess.run(["examples/mcptools.sh"], capture_output=True, timeout=15, check=True)
8+
proc = subprocess.run(["examples/mcptools.sh"], capture_output=True, timeout=20, check=True)
99
assert proc.returncode == 0
1010
if b"Skipped." in proc.stdout:
1111
raise pytest.skip("mcptools not installed")

tests/test_knowledge.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ def test_documentation_index():
1212

1313

1414
def test_queries():
15+
assert "information_schema.columns" in Queries.TABLES_COLUMNS
1516
assert "information_schema.tables" in Queries.TABLES_METADATA
1617
assert "partitions_health" in Queries.TABLES_METADATA
1718
assert "sys.health" in Queries.HEALTH

tests/test_mcp.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
fetch_cratedb_docs,
55
get_cluster_health,
66
get_cratedb_documentation_index,
7+
get_table_columns,
78
get_table_metadata,
89
query_sql,
910
)
@@ -57,6 +58,18 @@ def test_query_sql_forbidden_sneak_value():
5758
assert ex.match("Only queries that have a SELECT statement are allowed")
5859

5960

61+
def test_get_table_columns():
62+
response = str(get_table_columns())
63+
assert "information_schema" in response
64+
assert "pg_catalog" in response
65+
66+
# Verify the returned structure.
67+
result = get_table_columns()
68+
assert isinstance(result, dict)
69+
expected_keys = {"user", "information_schema", "pg_catalog", "sys"}
70+
assert set(result.keys()) == expected_keys
71+
72+
6073
def test_get_table_metadata():
6174
assert "partitions_health" in str(get_table_metadata())
6275

0 commit comments

Comments
 (0)