Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
update to [MCP spec 2025-06-18].
- OCI: Added building OCI standard image `cratedb-mcp`
- OCI: Added building OCI image `cratedb-mcpo` for Open WebUI
- Prompt: Added instructions (system prompt) to MCP server.
Thanks, @hammerhead and @WalBeh.

[FastMCP 2.10]: https://github.com/jlowin/fastmcp/releases/tag/v2.10.0
[MCP spec 2025-06-18]: https://modelcontextprotocol.io/specification/2025-06-18/changelog
Expand Down
43 changes: 27 additions & 16 deletions cratedb_mcp/__main__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import importlib.resources

from cratedb_about.instruction import GeneralInstructions
from fastmcp import FastMCP
from fastmcp.tools import Tool

Expand All @@ -10,19 +13,13 @@
query_sql,
)

# Create FastMCP application object.
mcp: FastMCP = FastMCP(__appname__)

instructions_general = GeneralInstructions().render()
instructions_mcp = (importlib.resources.files("cratedb_mcp") / "instructions.md").read_text()

# ------------------------------------------
# Health / Status
# ------------------------------------------
mcp.add_tool(
Tool.from_function(
fn=get_cluster_health,
description="Return the health of the CrateDB cluster.",
tags={"health", "monitoring", "status"},
)
# Create FastMCP application object.
mcp: FastMCP = FastMCP(
name=__appname__,
instructions=instructions_mcp + instructions_general,
)


Expand All @@ -31,15 +28,17 @@
# ------------------------------------------
mcp.add_tool(
Tool.from_function(
fn=query_sql,
description="Send an SQL query to CrateDB. Only 'SELECT' queries are allowed.",
fn=get_table_metadata,
description="Return column schema and metadata for all tables stored in CrateDB. "
"Use it to inquire entities you don't know about.",
tags={"text-to-sql"},
)
)
mcp.add_tool(
Tool.from_function(
fn=get_table_metadata,
description="Return an aggregation of all CrateDB's schema, tables and their metadata.",
fn=query_sql,
description="Send an SQL query to CrateDB and return results. "
"Only 'SELECT' queries are allowed.",
tags={"text-to-sql"},
)
)
Expand All @@ -64,3 +63,15 @@
tags={"documentation"},
)
)


# ------------------------------------------
# Health / Status
# ------------------------------------------
mcp.add_tool(
Tool.from_function(
fn=get_cluster_health,
description="Return the health of the CrateDB cluster.",
tags={"health", "monitoring", "status"},
)
)
25 changes: 25 additions & 0 deletions cratedb_mcp/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
## Tool instructions

Use all available tools from the CrateDB MCP server `cratedb-mcp` or derived
applications for gathering accurate information.

You have the following tools available:

1. `get_table_metadata`: Return table and column schema information for all tables stored in CrateDB. Use it when you need to discover entities and database metadata you are unfamiliar with.
2. `query_sql`: Execute an SQL query on CrateDB and return the results.
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.
4. `fetch_cratedb_docs`: Given a link returned by `get_cratedb_documentation_index`, fetch the full content of that documentation page.

Those rules are applicable when using the available tools:

- First use `get_table_metadata` to find out about tables stored in the database and their
column names and types. Next, use `query_sql` to execute a parameterised SQL query that
returns only the columns you need (avoid `SELECT *`) and, where appropriate, add a
`LIMIT` clause to keep result sets concise.

- First use `get_cratedb_documentation_index` to find out about curated documentation resources
about CrateDB. Then, use `fetch_cratedb_docs` to retrieve individual pages that
can be quoted verbatim when answering questions about CrateDB.

After fetching data, reason about the output and provide a concise interpretation before
formulating the final answer.
16 changes: 8 additions & 8 deletions cratedb_mcp/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,6 @@
from cratedb_mcp.util.sql import sql_is_permitted


# ------------------------------------------
# Health / Status
# ------------------------------------------
def get_cluster_health() -> dict:
"""Query sys.health ordered by severity."""
return query_cratedb(Queries.HEALTH)


# ------------------------------------------
# Text-to-SQL
# ------------------------------------------
Expand Down Expand Up @@ -58,3 +50,11 @@ def fetch_cratedb_docs(link: str) -> str:
if not documentation_index.url_permitted(link):
raise ValueError(f"Link is not permitted: {link}")
return documentation_index.client.get(link, timeout=Settings.http_timeout()).text


# ------------------------------------------
# Health / Status
# ------------------------------------------
def get_cluster_health() -> dict:
"""Query sys.health ordered by severity."""
return query_cratedb(Queries.HEALTH)
14 changes: 10 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ dependencies = [
"attrs",
"cachetools<7",
"click<9",
"cratedb-about==0.0.5",
"cratedb-about==0.0.6",
"fastmcp>=2.7,<2.11",
"hishel<0.2",
"pueblo==0.0.11",
Expand All @@ -94,6 +94,15 @@ optional-dependencies.test = [

scripts.cratedb-mcp = "cratedb_mcp.cli:cli"

[tool.setuptools]
include-package-data = true

[tool.setuptools.package-data]
cratedb_mcp = [ "*.md" ]

[tool.setuptools.packages.find]
namespaces = false

[tool.ruff]
line-length = 100

Expand Down Expand Up @@ -132,9 +141,6 @@ lint.per-file-ignores."tests/*" = [
"S101", # Allow use of `assert`.
]

[tool.setuptools.packages.find]
namespaces = false

[tool.pytest.ini_options]
addopts = """
-rfEXs -p pytester --strict-markers --verbosity=3
Expand Down
13 changes: 13 additions & 0 deletions tests/test_instructions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from cratedb_mcp.__main__ import mcp


def test_instructions():
instructions_text = mcp.instructions

# MCP instructions.
assert "Tool instructions" in instructions_text

# General instructions.
assert "Things to remember when working with CrateDB" in instructions_text
assert "Rules for writing SQL queries" in instructions_text
assert "Core writing principles" in instructions_text