diff --git a/README.md b/README.md index 6717d68e..3322e532 100644 --- a/README.md +++ b/README.md @@ -24,41 +24,33 @@ This MCP is **not** just documentation or static code analysis. It is a **semant 3. **Minimal MCP Surface** - Exposes only what an LLM needs: Discovery, Description, Instantiation, Execution, and model persistence. -## 🛠️ Prerequisites - -- **Python 3.10+** -- **pip** package manager - ## 🛠️ Installation -### Virtual Environment Setup +### Zero-install via uvx (recommended) -It is recommended to use a virtual environment: +If you have [uv](https://github.com/astral-sh/uv) installed, no separate installation step is needed. Just update your MCP client config (see [Connecting from an LLM Client](#connecting-from-an-llm-client) below) and `uvx` will handle the rest automatically. ```bash -python3 -m venv .venv -source .venv/bin/activate # On Windows: .venv\Scripts\activate +# Verify uv is available +uvx sktime-mcp --help ``` -### Package Installation - -Install from PyPI (recommended for users): +### pip ```bash pip install sktime-mcp -# With all optional dependencies +# With optional extras (SQL, forecasting models, file formats) pip install "sktime-mcp[all]" ``` -For development (editable install from source): +### Development installation ```bash -pip install -e ".[dev]" +git clone https://github.com/sktime/sktime-mcp +cd sktime-mcp +python3 -m pip install -e ".[dev]" ``` - -For a more detailed first-time setup flow, including MCP server verification and troubleshooting, see [Beginner Setup](#-beginner-setup-firsttime-users). - ## 🧭 Beginner Setup (First‑Time Users) If you are new to sktime‑mcp or to MCP‑based workflows, this section provides a minimal starting point to help you verify that your setup is working correctly. @@ -140,24 +132,36 @@ The server uses stdio transport by default, compatible with Claude Desktop, Clau | Linux | `~/.config/claude/claude_desktop_config.json` | | Windows | `%APPDATA%\Claude\claude_desktop_config.json` | +**With uvx (recommended — no prior install needed):** ```json { "mcpServers": { "sktime": { - "command": "sktime-mcp" + "command": "uvx", + "args": ["sktime-mcp"] } } } ``` -If you are using a virtual environment, or if `sktime-mcp` is not on your `PATH`, point the client directly at the environment's Python executable — this ensures the correct packages are used: +**With optional extras:** +```json +{ + "mcpServers": { + "sktime": { + "command": "uvx", + "args": ["sktime-mcp[forecasting,sql]"] + } + } +} +``` +**With pip-installed package:** ```json { "mcpServers": { "sktime": { - "command": "/.venv/bin/python", - "args": ["-m", "sktime_mcp.server"] + "command": "sktime-mcp" } } } diff --git a/docs/source/intro.md b/docs/source/intro.md index 64b4408c..c86662a5 100644 --- a/docs/source/intro.md +++ b/docs/source/intro.md @@ -46,12 +46,14 @@ Get up and running in seconds. Use with **Claude Desktop**, **Cursor**, or any M ### 1. Install +**Zero-install via uvx (recommended):** if you have [uv](https://github.com/astral-sh/uv) installed, skip this step — uvx fetches and runs the package automatically when your MCP client starts. + ```bash -# Install directly from PyPI (with all optional dependencies recommended) -pip install "sktime-mcp[all]" +# Or install explicitly with pip +pip install sktime-mcp ``` -Alternatively, when contributing, use GitHub to install from source: +When contributing, install from source: ```bash git clone https://github.com/sktime/sktime-mcp.git @@ -61,13 +63,22 @@ source .venv/bin/activate pip install -e ".[dev]" ``` -### 2. Run -```bash -sktime-mcp +### 2. Connect (Claude Desktop / Claude Code Config) +Add this to your `claude_desktop_config.json`: + +**With uvx (no prior install needed):** +```json +{ + "mcpServers": { + "sktime": { + "command": "uvx", + "args": ["sktime-mcp"] + } + } +} ``` -### 3. Connect (Claude Desktop Config) -Add this to your `claude_desktop_config.json`: +**With pip-installed package:** ```json { "mcpServers": { diff --git a/docs/source/user-guide.md b/docs/source/user-guide.md index 36a0eb04..80a5be36 100644 --- a/docs/source/user-guide.md +++ b/docs/source/user-guide.md @@ -11,31 +11,63 @@ Welcome to the **sktime-mcp** User Guide. This guide walks you through installin Before you begin, ensure you have: - **Python 3.10+** installed. -- **pip** package manager. -- A compatible MCP client (like **Claude Desktop**, **Cursor**, or compatible **VS Code extensions** like Cline). +- A compatible MCP client (like **Claude Desktop**, **Cursor**, or **VS Code with Copilot**). ### Installation -Install the package directly from the source. We recommend installing with all dependencies to unlock full functionality. +**Zero-install via uvx (recommended):** if you have [uv](https://github.com/astral-sh/uv) installed, no install step is needed. Just configure your MCP client (see below) and `uvx` handles everything automatically. ```bash -# Standard installation -pip install -e . +# Or install with pip +pip install sktime-mcp -# Recommended: Install with all optional extras (SQL, Forecasting, Files) -pip install -e ".[all]" +# With all optional extras (SQL, forecasting models, file formats) +pip install "sktime-mcp[all]" ``` -### Running the Server +### MCP Client Configuration -Start the MCP server to begin listening for connections: +**With uvx (recommended — no prior install needed):** +```json +{ + "mcpServers": { + "sktime": { + "command": "uvx", + "args": ["sktime-mcp"] + } + } +} +``` -```bash -sktime-mcp +**With optional extras:** +```json +{ + "mcpServers": { + "sktime": { + "command": "uvx", + "args": ["sktime-mcp[forecasting,sql]"] + } + } +} ``` -*Or manually via Python:* +**With pip-installed package:** +```json +{ + "mcpServers": { + "sktime": { + "command": "sktime-mcp" + } + } +} +``` + +### Running the Server manually + ```bash +sktime-mcp + +# Or via Python python -m sktime_mcp.server ``` diff --git a/src/sktime_mcp/__main__.py b/src/sktime_mcp/__main__.py new file mode 100644 index 00000000..b82e74f0 --- /dev/null +++ b/src/sktime_mcp/__main__.py @@ -0,0 +1,5 @@ +"""Allow `python -m sktime_mcp` as an alternative entry point.""" + +from sktime_mcp.server import main + +main() diff --git a/src/sktime_mcp/runtime/executor.py b/src/sktime_mcp/runtime/executor.py index 0dc2673f..094c63b9 100644 --- a/src/sktime_mcp/runtime/executor.py +++ b/src/sktime_mcp/runtime/executor.py @@ -21,7 +21,7 @@ logger = logging.getLogger(__name__) -# Dynamically discover all available sktime demo datasets at import time. +# Dynamically discover all available sktime demo datasets on first access. # This replaces the old hardcoded dictionary and automatically exposes every # load_* function in sktime.datasets to the MCP server. def _discover_demo_datasets() -> dict: @@ -39,7 +39,15 @@ def _discover_demo_datasets() -> dict: return {} # fallback: empty dict if sktime not installed -DEMO_DATASETS = _discover_demo_datasets() +_DEMO_DATASETS: dict | None = None + + +def _get_demo_datasets() -> dict: + """Lazy singleton — discovers datasets only on first call.""" + global _DEMO_DATASETS + if _DEMO_DATASETS is None: + _DEMO_DATASETS = _discover_demo_datasets() + return _DEMO_DATASETS class Executor: @@ -87,15 +95,16 @@ def instantiate( # L-7: We can also add custom load_dataset functions here def load_dataset(self, name: str) -> dict[str, Any]: """Load a demo dataset.""" - if name not in DEMO_DATASETS: + demo_datasets = _get_demo_datasets() + if name not in demo_datasets: return { "success": False, "error": f"Unknown dataset: {name}", - "available": list(DEMO_DATASETS.keys()), + "available": list(demo_datasets.keys()), } try: - module_path = DEMO_DATASETS[name] + module_path = demo_datasets[name] parts = module_path.rsplit(".", 1) module = __import__(parts[0], fromlist=[parts[1]]) loader = getattr(module, parts[1]) @@ -529,7 +538,7 @@ def instantiate_pipeline( def list_datasets(self) -> list[str]: """List available demo datasets.""" - return list(DEMO_DATASETS.keys()) + return list(_get_demo_datasets().keys()) def load_data_source(self, config: dict[str, Any]) -> dict[str, Any]: """ diff --git a/src/sktime_mcp/tools/codegen.py b/src/sktime_mcp/tools/codegen.py index e68a6df6..a833903e 100644 --- a/src/sktime_mcp/tools/codegen.py +++ b/src/sktime_mcp/tools/codegen.py @@ -8,7 +8,7 @@ from typing import Any from sktime_mcp.registry.interface import get_registry -from sktime_mcp.runtime.executor import DEMO_DATASETS +from sktime_mcp.runtime.executor import _get_demo_datasets from sktime_mcp.runtime.handles import get_handle_manager @@ -253,9 +253,10 @@ def export_code_tool( # Optionally add fit/predict example if include_fit_example: - # Resolve the dataset loader from DEMO_DATASETS - if dataset and dataset in DEMO_DATASETS: - module_path = DEMO_DATASETS[dataset] + # Resolve the dataset loader from demo datasets + _demo_datasets = _get_demo_datasets() + if dataset and dataset in _demo_datasets: + module_path = _demo_datasets[dataset] module_parts = module_path.rsplit(".", 1) loader_module = module_parts[0] loader_func = module_parts[1]