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
5 changes: 5 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
## Unreleased
- Parameters: Added CLI option `--host` and environment variable `CRATEDB_MCP_HOST`
- Parameters: Added CLI option `--path` and environment variable `CRATEDB_MCP_PATH`
- Dependencies: Allowed updating to [FastMCP 2.10], which includes a specification
update to [MCP spec 2025-06-18].

[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

## v0.0.3 - 2025-06-18
- Dependencies: Downgraded to `fastmcp<2.7` to fix a breaking change
Expand Down
12 changes: 6 additions & 6 deletions cratedb_mcp/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@
# ------------------------------------------
# Health / Status
# ------------------------------------------
def get_cluster_health() -> list[dict]:
def get_cluster_health() -> dict:
"""Query sys.health ordered by severity."""
return query_cratedb(Queries.HEALTH)


# ------------------------------------------
# Text-to-SQL
# ------------------------------------------
def query_cratedb(query: str) -> list[dict]:
def query_cratedb(query: str) -> dict:
"""Sends a `query` to the set `$CRATEDB_CLUSTER_URL`"""
url = HTTP_URL
if url.endswith("/"):
Expand All @@ -25,13 +25,13 @@ def query_cratedb(query: str) -> list[dict]:
return httpx.post(f"{url}/_sql", json={"stmt": query}, timeout=Settings.http_timeout()).json()


def query_sql(query: str):
def query_sql(query: str) -> dict:
if not sql_is_permitted(query):
raise PermissionError("Only queries that have a SELECT statement are allowed.")
return query_cratedb(query)


def get_table_metadata() -> list[dict]:
def get_table_metadata() -> dict:
"""
Return an aggregation of schema:tables, e.g.: {'doc': [{name:'mytable', ...}, ...]}

Expand All @@ -48,12 +48,12 @@ def get_table_metadata() -> list[dict]:
documentation_index = DocumentationIndex()


def get_cratedb_documentation_index():
def get_cratedb_documentation_index() -> dict:
"""Get curated CrateDB documentation index."""
return documentation_index.items()


def fetch_cratedb_docs(link: str):
def fetch_cratedb_docs(link: str) -> str:
"""Fetch a CrateDB documentation link."""
if not documentation_index.url_permitted(link):
raise ValueError(f"Link is not permitted: {link}")
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ dependencies = [
"cachetools<7",
"click<9",
"cratedb-about==0.0.5",
"fastmcp>=2.7,<2.10",
"fastmcp>=2.7,<2.11",
"hishel<0.2",
"pueblo==0.0.11",
"sqlparse<0.6",
Expand Down
10 changes: 8 additions & 2 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@ def test_cli_valid_default(mocker, capsys):

# Verify the outcome.
assert run_mock.call_count == 1
assert run_mock.call_args == mock.call("stdio")
assert run_mock.call_args in [
mock.call("stdio"),
mock.call("stdio", show_banner=True),
]


def test_cli_valid_custom(mocker, capsys):
Expand All @@ -99,7 +102,10 @@ def test_cli_valid_custom(mocker, capsys):

# Verify the outcome.
assert run_mock.call_count == 1
assert run_mock.call_args == mock.call("http", host="127.0.0.1", port=65535, path=None)
assert run_mock.call_args in [
mock.call("http", host="127.0.0.1", port=65535, path=None),
mock.call("http", show_banner=True, host="127.0.0.1", port=65535, path=None),
]


def test_cli_invalid_transport_option(mocker, capsys):
Expand Down