diff --git a/.gitignore b/.gitignore index a38dbc0..0004b92 100644 --- a/.gitignore +++ b/.gitignore @@ -62,6 +62,9 @@ local_settings.py db.sqlite3 db.sqlite3-journal +# Databases: +*.sqlite + # Flask stuff: instance/ .webassets-cache diff --git a/CHANGES.md b/CHANGES.md index 30fc043..37c38a8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,9 @@ - Packaging: Adjusted package dependencies for interoperability - Packaging: Added basic CLI entry point and server launcher `cratedb-mcp` - Documentation: Show a simple Claude Desktop configuration -- MCP documentation: Add reference to medium-sized llms.txt context file +- MCP docs: Add reference to medium-sized llms.txt context file - Boilerplate: Added software tests and CI configuration - Documentation: Added development sandbox section +- MCP docs: Used Hishel for transparently caching documentation resources, + by default for one hour. Use the `CRATEDB_MCP_DOCS_CACHE_TTL` environment + variable to adjust (default: 3600) diff --git a/README.md b/README.md index 66d2f72..31862f4 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,9 @@ export CRATEDB_MCP_HTTP_URL="http://localhost:4200/" export CRATEDB_MCP_HTTP_URL="https://example.aks1.westeurope.azure.cratedb.net:4200" ``` +The `CRATEDB_MCP_DOCS_CACHE_TTL` environment variable (default: 3600) defines +the cache lifetime for documentation resources. + # Usage Start MCP server with `stdio` transport (default). ```shell diff --git a/cratedb_mcp/__main__.py b/cratedb_mcp/__main__.py index 694f1d8..3dd235d 100644 --- a/cratedb_mcp/__main__.py +++ b/cratedb_mcp/__main__.py @@ -1,9 +1,17 @@ +import hishel import httpx from mcp.server.fastmcp import FastMCP from .knowledge import DOCUMENTATION_INDEX, Queries -from .settings import HTTP_URL +from .settings import DOCS_CACHE_TTL, HTTP_URL +# Configure Hishel, an httpx client with caching. +# Define one hour of caching time. +controller = hishel.Controller(allow_stale=True) +storage = hishel.SQLiteStorage(ttl=DOCS_CACHE_TTL) +client = hishel.CacheClient(controller=controller, storage=storage) + +# Create FastMCP application object. mcp = FastMCP("cratedb-mcp") @@ -29,7 +37,7 @@ def fetch_cratedb_docs(link: str): """Fetches a CrateDB documentation link from GitHub raw content.""" if 'https://raw.githubusercontent.com/crate/crate/' not in link: raise ValueError('Only github cratedb links can be fetched.') - return httpx.get(link).text + return client.get(link).text @mcp.tool(description="Returns an aggregation of all CrateDB's schema, tables and their metadata") def get_table_metadata() -> list[dict]: diff --git a/cratedb_mcp/settings.py b/cratedb_mcp/settings.py index 5cff8a6..6988f0d 100644 --- a/cratedb_mcp/settings.py +++ b/cratedb_mcp/settings.py @@ -1,3 +1,14 @@ import os +import warnings HTTP_URL: str = os.getenv("CRATEDB_MCP_HTTP_URL", "http://localhost:4200") + +# Configure cache lifetime for documentation resources. +DOCS_CACHE_TTL: int = 3600 +try: + DOCS_CACHE_TTL = int(os.getenv("CRATEDB_MCP_DOCS_CACHE_TTL", DOCS_CACHE_TTL)) +except ValueError as e: # pragma: no cover + # If the environment variable is not a valid integer, use the default value, but warn about it. + # TODO: Add software test after refactoring away from module scope. + warnings.warn(f"Environment variable `CRATEDB_MCP_DOCS_CACHE_TTL` invalid: {e}. " + f"Using default value: {DOCS_CACHE_TTL}.", category=UserWarning, stacklevel=2) diff --git a/pyproject.toml b/pyproject.toml index 9307d61..e3bb795 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,7 @@ dynamic = [ "version", ] dependencies = [ + "hishel<0.2", "mcp[cli]>=1.5.0", ] optional-dependencies.develop = [