diff --git a/README.md b/README.md index 8108e26..098c54c 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,82 @@ Doris MCP (Model Context Protocol) Server is a backend service built with Python * Python 3.12+ * Database connection details (e.g., Doris Host, Port, User, Password, Database) -## Quick Start +## 🚀 Quick Start + +### Installation from PyPI + +```bash +# Install the latest version +pip install mcp-doris-server + +# Install specific version +pip install mcp-doris-server==0.3 +``` + +> **💡 Command Compatibility**: After installation, both `doris-mcp-server` and `mcp-doris-server` commands are available for backward compatibility. You can use either command interchangeably. + +### Start Streamable HTTP Mode (Web Service) + +```bash +# Full configuration with database connection +doris-mcp-server \ + --transport http \ + --host 0.0.0.0 \ + --port 3000 \ + --db-host 127.0.0.1 \ + --db-port 9030 \ + --db-user root \ + --db-password your_password +``` + +### Start Stdio Mode (for Cursor and other MCP clients) + +```bash +# For direct integration with MCP clients like Cursor +doris-mcp-server --transport stdio +``` + +### Verify Installation + +```bash +# Check installation +doris-mcp-server --help + +# Test HTTP mode (in another terminal) +curl http://localhost:3000/health +``` + +### Environment Variables (Optional) + +Instead of command-line arguments, you can use environment variables: + +```bash +export DORIS_HOST="127.0.0.1" +export DORIS_PORT="9030" +export DORIS_USER="root" +export DORIS_PASSWORD="your_password" + +# Then start with simplified command +doris-mcp-server --transport http --host 0.0.0.0 --port 3000 +``` + +### Command Line Arguments + +The `doris-mcp-server` command supports the following arguments: + +| Argument | Description | Default | Required | +|:---------|:------------|:--------|:---------| +| `--transport` | Transport mode: `http` or `stdio` | `http` | No | +| `--host` | HTTP server host (HTTP mode only) | `0.0.0.0` | No | +| `--port` | HTTP server port (HTTP mode only) | `3000` | No | +| `--db-host` | Doris database host | `localhost` | No | +| `--db-port` | Doris database port | `9030` | No | +| `--db-user` | Doris database username | `root` | No | +| `--db-password` | Doris database password | - | Yes (unless in env) | + +## Development Setup + +For developers who want to build from source: ### 1. Clone the Repository @@ -481,9 +556,36 @@ You can connect Cursor to this MCP server using Stdio mode (recommended) or Stre Stdio mode allows Cursor to manage the server process directly. Configuration is done within Cursor's MCP Server settings file (typically `~/.cursor/mcp.json` or similar). -### Using uv (Recommended) +### Method 1: Using PyPI Installation (Recommended) -If you have `uv` installed, you can run the server directly: +Install the package from PyPI and configure Cursor to use it: + +```bash +pip install mcp-doris-server +``` + +**Configure Cursor:** Add an entry like the following to your Cursor MCP configuration: + +```json +{ + "mcpServers": { + "doris-stdio": { + "command": "doris-mcp-server", + "args": ["--transport", "stdio"], + "env": { + "DORIS_HOST": "127.0.0.1", + "DORIS_PORT": "9030", + "DORIS_USER": "root", + "DORIS_PASSWORD": "your_db_password" + } + } + } +} +``` + +### Method 2: Using uv (Development) + +If you have `uv` installed and want to run from source: ```bash uv run --project /path/to/doris-mcp-server doris-mcp-server @@ -491,32 +593,24 @@ uv run --project /path/to/doris-mcp-server doris-mcp-server **Note:** Replace `/path/to/doris-mcp-server` with the actual absolute path to your project directory. -1. **Configure Cursor:** Add an entry like the following to your Cursor MCP configuration: +**Configure Cursor:** Add an entry like the following to your Cursor MCP configuration: - ```json - { - "mcpServers": { - "doris-stdio": { - "command": "uv", - "args": ["run", "--project", "/path/to/your/doris-mcp-server", "doris-mcp-server"], - "env": { - "DORIS_HOST": "127.0.0.1", - "DORIS_PORT": "9030", - "DORIS_USER": "root", - "DORIS_PASSWORD": "your_db_password", - "DORIS_DATABASE": "your_default_db", - "LOG_LEVEL": "INFO" - } - } +```json +{ + "mcpServers": { + "doris-stdio": { + "command": "uv", + "args": ["run", "--project", "/path/to/your/doris-mcp-server", "doris-mcp-server"], + "env": { + "DORIS_HOST": "127.0.0.1", + "DORIS_PORT": "9030", + "DORIS_USER": "root", + "DORIS_PASSWORD": "your_db_password" } } - ``` - -2. **Key Points:** - * Replace `/path/to/your/doris-mcp-server` with the actual absolute path to the project's root directory on your system. - * The `--project` argument is crucial for `uv` to find the `pyproject.toml` and run the correct command. - * Database connection details are set directly in the `env` block. Cursor will pass these to the server process. - * No `.env` file is needed for this mode when configured via Cursor. + } +} +``` ### Streamable HTTP Mode (v0.3.0+) diff --git a/pyproject.toml b/pyproject.toml index 26c74e5..9a95f56 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,7 @@ classifiers = [ dependencies = [ # Core MCP dependencies - "mcp>=1.0.0", + "mcp>=1.8.0", # Database drivers "aiomysql>=0.2.0", "PyMySQL>=1.1.0", @@ -147,8 +147,10 @@ Issues = "https://github.com/apache/doris-mcp-server/issues" Changelog = "https://github.com/apache/doris-mcp-server/blob/main/CHANGELOG.md" [project.scripts] +mcp-doris-server = "doris_mcp_server.main:main_sync" doris-mcp-server = "doris_mcp_server.main:main_sync" doris-mcp-client = "doris_mcp_server.client:main" +mcp-doris-client = "doris_mcp_server.client:main" [tool.hatch.build.targets.wheel] packages = ["doris_mcp_server"] @@ -163,7 +165,7 @@ include = [ # Black configuration [tool.black] line-length = 88 -target-version = ['py310', 'py311', 'py312'] +target-version = ['py312'] include = '\.pyi?$' extend-exclude = ''' /( diff --git a/uv.lock b/uv.lock index 4b1b05d..bd7c8d1 100644 --- a/uv.lock +++ b/uv.lock @@ -1,19 +1,3 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. version = 1 revision = 1 requires-python = ">=3.12" @@ -534,170 +518,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408 }, ] -[[package]] -name = "doris-mcp-server" -version = "0.3.0" -source = { editable = "." } -dependencies = [ - { name = "aiofiles" }, - { name = "aiohttp" }, - { name = "aiomysql" }, - { name = "aioredis" }, - { name = "asyncio-mqtt" }, - { name = "bcrypt" }, - { name = "click" }, - { name = "cryptography" }, - { name = "fastapi" }, - { name = "httpx" }, - { name = "mcp" }, - { name = "numpy" }, - { name = "orjson" }, - { name = "pandas" }, - { name = "passlib", extra = ["bcrypt"] }, - { name = "prometheus-client" }, - { name = "pydantic" }, - { name = "pydantic-settings" }, - { name = "pyjwt" }, - { name = "pymysql" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "pytest-cov" }, - { name = "python-dateutil" }, - { name = "python-dotenv" }, - { name = "python-jose", extra = ["cryptography"] }, - { name = "python-multipart" }, - { name = "pyyaml" }, - { name = "requests" }, - { name = "rich" }, - { name = "sqlparse" }, - { name = "starlette" }, - { name = "structlog" }, - { name = "toml" }, - { name = "tqdm" }, - { name = "typer" }, - { name = "uvicorn", extra = ["standard"] }, - { name = "websockets" }, -] - -[package.optional-dependencies] -dev = [ - { name = "bandit" }, - { name = "black" }, - { name = "flake8" }, - { name = "isort" }, - { name = "mypy" }, - { name = "myst-parser" }, - { name = "pre-commit" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "pytest-cov" }, - { name = "pytest-mock" }, - { name = "pytest-xdist" }, - { name = "ruff" }, - { name = "safety" }, - { name = "sphinx" }, - { name = "sphinx-rtd-theme" }, - { name = "tox" }, -] -docs = [ - { name = "myst-parser" }, - { name = "sphinx" }, - { name = "sphinx-autoapi" }, - { name = "sphinx-rtd-theme" }, -] -monitoring = [ - { name = "grafana-client" }, - { name = "jaeger-client" }, - { name = "opentelemetry-api" }, - { name = "opentelemetry-sdk" }, - { name = "prometheus-client" }, -] -performance = [ - { name = "cchardet" }, - { name = "orjson" }, - { name = "uvloop" }, -] - -[package.dev-dependencies] -dev = [ - { name = "ruff" }, -] - -[package.metadata] -requires-dist = [ - { name = "aiofiles", specifier = ">=23.0.0" }, - { name = "aiohttp", specifier = ">=3.9.0" }, - { name = "aiomysql", specifier = ">=0.2.0" }, - { name = "aioredis", specifier = ">=2.0.0" }, - { name = "asyncio-mqtt", specifier = ">=0.16.0" }, - { name = "bandit", marker = "extra == 'dev'", specifier = ">=1.7.0" }, - { name = "bcrypt", specifier = ">=4.1.0" }, - { name = "black", marker = "extra == 'dev'", specifier = ">=23.12.0" }, - { name = "cchardet", marker = "extra == 'performance'", specifier = ">=2.1.0" }, - { name = "click", specifier = ">=8.1.0" }, - { name = "cryptography", specifier = ">=41.0.0" }, - { name = "fastapi", specifier = ">=0.108.0" }, - { name = "flake8", marker = "extra == 'dev'", specifier = ">=7.0.0" }, - { name = "grafana-client", marker = "extra == 'monitoring'", specifier = ">=3.5.0" }, - { name = "httpx", specifier = ">=0.26.0" }, - { name = "isort", marker = "extra == 'dev'", specifier = ">=5.13.0" }, - { name = "jaeger-client", marker = "extra == 'monitoring'", specifier = ">=4.8.0" }, - { name = "mcp", specifier = ">=1.0.0" }, - { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.8.0" }, - { name = "myst-parser", marker = "extra == 'dev'", specifier = ">=2.0.0" }, - { name = "myst-parser", marker = "extra == 'docs'", specifier = ">=2.0.0" }, - { name = "numpy", specifier = ">=1.24.0" }, - { name = "opentelemetry-api", marker = "extra == 'monitoring'", specifier = ">=1.21.0" }, - { name = "opentelemetry-sdk", marker = "extra == 'monitoring'", specifier = ">=1.21.0" }, - { name = "orjson", specifier = ">=3.9.0" }, - { name = "orjson", marker = "extra == 'performance'", specifier = ">=3.9.0" }, - { name = "pandas", specifier = ">=2.0.0" }, - { name = "passlib", extras = ["bcrypt"], specifier = ">=1.7.0" }, - { name = "pre-commit", marker = "extra == 'dev'", specifier = ">=3.6.0" }, - { name = "prometheus-client", specifier = ">=0.19.0" }, - { name = "prometheus-client", marker = "extra == 'monitoring'", specifier = ">=0.19.0" }, - { name = "pydantic", specifier = ">=2.5.0" }, - { name = "pydantic-settings", specifier = ">=2.1.0" }, - { name = "pyjwt", specifier = ">=2.8.0" }, - { name = "pymysql", specifier = ">=1.1.0" }, - { name = "pytest", specifier = ">=8.4.0" }, - { name = "pytest", marker = "extra == 'dev'", specifier = ">=7.4.0" }, - { name = "pytest-asyncio", specifier = ">=1.0.0" }, - { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23.0" }, - { name = "pytest-cov", specifier = ">=6.1.1" }, - { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=4.1.0" }, - { name = "pytest-mock", marker = "extra == 'dev'", specifier = ">=3.12.0" }, - { name = "pytest-xdist", marker = "extra == 'dev'", specifier = ">=3.5.0" }, - { name = "python-dateutil", specifier = ">=2.8.0" }, - { name = "python-dotenv", specifier = ">=1.0.0" }, - { name = "python-jose", extras = ["cryptography"], specifier = ">=3.3.0" }, - { name = "python-multipart", specifier = ">=0.0.6" }, - { name = "pyyaml", specifier = ">=6.0.0" }, - { name = "requests", specifier = ">=2.31.0" }, - { name = "rich", specifier = ">=13.7.0" }, - { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.1.0" }, - { name = "safety", marker = "extra == 'dev'", specifier = ">=2.3.0" }, - { name = "sphinx", marker = "extra == 'dev'", specifier = ">=7.2.0" }, - { name = "sphinx", marker = "extra == 'docs'", specifier = ">=7.2.0" }, - { name = "sphinx-autoapi", marker = "extra == 'docs'", specifier = ">=3.0.0" }, - { name = "sphinx-rtd-theme", marker = "extra == 'dev'", specifier = ">=2.0.0" }, - { name = "sphinx-rtd-theme", marker = "extra == 'docs'", specifier = ">=2.0.0" }, - { name = "sqlparse", specifier = ">=0.4.4" }, - { name = "starlette", specifier = ">=0.27.0" }, - { name = "structlog", specifier = ">=23.2.0" }, - { name = "toml", specifier = ">=0.10.0" }, - { name = "tox", marker = "extra == 'dev'", specifier = ">=4.11.0" }, - { name = "tqdm", specifier = ">=4.66.0" }, - { name = "typer", specifier = ">=0.9.0" }, - { name = "uvicorn", extras = ["standard"], specifier = ">=0.25.0" }, - { name = "uvloop", marker = "extra == 'performance'", specifier = ">=0.19.0" }, - { name = "websockets", specifier = ">=12.0" }, -] -provides-extras = ["dev", "docs", "performance", "monitoring"] - -[package.metadata.requires-dev] -dev = [{ name = "ruff", specifier = ">=0.11.13" }] - [[package]] name = "dparse" version = "0.6.4" @@ -1126,6 +946,170 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/79/45/823ad05504bea55cb0feb7470387f151252127ad5c72f8882e8fe6cf5c0e/mcp-1.9.3-py3-none-any.whl", hash = "sha256:69b0136d1ac9927402ed4cf221d4b8ff875e7132b0b06edd446448766f34f9b9", size = 131063 }, ] +[[package]] +name = "mcp-doris-server" +version = "0.3.0" +source = { editable = "." } +dependencies = [ + { name = "aiofiles" }, + { name = "aiohttp" }, + { name = "aiomysql" }, + { name = "aioredis" }, + { name = "asyncio-mqtt" }, + { name = "bcrypt" }, + { name = "click" }, + { name = "cryptography" }, + { name = "fastapi" }, + { name = "httpx" }, + { name = "mcp" }, + { name = "numpy" }, + { name = "orjson" }, + { name = "pandas" }, + { name = "passlib", extra = ["bcrypt"] }, + { name = "prometheus-client" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "pyjwt" }, + { name = "pymysql" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "pytest-cov" }, + { name = "python-dateutil" }, + { name = "python-dotenv" }, + { name = "python-jose", extra = ["cryptography"] }, + { name = "python-multipart" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "rich" }, + { name = "sqlparse" }, + { name = "starlette" }, + { name = "structlog" }, + { name = "toml" }, + { name = "tqdm" }, + { name = "typer" }, + { name = "uvicorn", extra = ["standard"] }, + { name = "websockets" }, +] + +[package.optional-dependencies] +dev = [ + { name = "bandit" }, + { name = "black" }, + { name = "flake8" }, + { name = "isort" }, + { name = "mypy" }, + { name = "myst-parser" }, + { name = "pre-commit" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "pytest-cov" }, + { name = "pytest-mock" }, + { name = "pytest-xdist" }, + { name = "ruff" }, + { name = "safety" }, + { name = "sphinx" }, + { name = "sphinx-rtd-theme" }, + { name = "tox" }, +] +docs = [ + { name = "myst-parser" }, + { name = "sphinx" }, + { name = "sphinx-autoapi" }, + { name = "sphinx-rtd-theme" }, +] +monitoring = [ + { name = "grafana-client" }, + { name = "jaeger-client" }, + { name = "opentelemetry-api" }, + { name = "opentelemetry-sdk" }, + { name = "prometheus-client" }, +] +performance = [ + { name = "cchardet" }, + { name = "orjson" }, + { name = "uvloop" }, +] + +[package.dev-dependencies] +dev = [ + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "aiofiles", specifier = ">=23.0.0" }, + { name = "aiohttp", specifier = ">=3.9.0" }, + { name = "aiomysql", specifier = ">=0.2.0" }, + { name = "aioredis", specifier = ">=2.0.0" }, + { name = "asyncio-mqtt", specifier = ">=0.16.0" }, + { name = "bandit", marker = "extra == 'dev'", specifier = ">=1.7.0" }, + { name = "bcrypt", specifier = ">=4.1.0" }, + { name = "black", marker = "extra == 'dev'", specifier = ">=23.12.0" }, + { name = "cchardet", marker = "extra == 'performance'", specifier = ">=2.1.0" }, + { name = "click", specifier = ">=8.1.0" }, + { name = "cryptography", specifier = ">=41.0.0" }, + { name = "fastapi", specifier = ">=0.108.0" }, + { name = "flake8", marker = "extra == 'dev'", specifier = ">=7.0.0" }, + { name = "grafana-client", marker = "extra == 'monitoring'", specifier = ">=3.5.0" }, + { name = "httpx", specifier = ">=0.26.0" }, + { name = "isort", marker = "extra == 'dev'", specifier = ">=5.13.0" }, + { name = "jaeger-client", marker = "extra == 'monitoring'", specifier = ">=4.8.0" }, + { name = "mcp", specifier = ">=1.0.0" }, + { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.8.0" }, + { name = "myst-parser", marker = "extra == 'dev'", specifier = ">=2.0.0" }, + { name = "myst-parser", marker = "extra == 'docs'", specifier = ">=2.0.0" }, + { name = "numpy", specifier = ">=1.24.0" }, + { name = "opentelemetry-api", marker = "extra == 'monitoring'", specifier = ">=1.21.0" }, + { name = "opentelemetry-sdk", marker = "extra == 'monitoring'", specifier = ">=1.21.0" }, + { name = "orjson", specifier = ">=3.9.0" }, + { name = "orjson", marker = "extra == 'performance'", specifier = ">=3.9.0" }, + { name = "pandas", specifier = ">=2.0.0" }, + { name = "passlib", extras = ["bcrypt"], specifier = ">=1.7.0" }, + { name = "pre-commit", marker = "extra == 'dev'", specifier = ">=3.6.0" }, + { name = "prometheus-client", specifier = ">=0.19.0" }, + { name = "prometheus-client", marker = "extra == 'monitoring'", specifier = ">=0.19.0" }, + { name = "pydantic", specifier = ">=2.5.0" }, + { name = "pydantic-settings", specifier = ">=2.1.0" }, + { name = "pyjwt", specifier = ">=2.8.0" }, + { name = "pymysql", specifier = ">=1.1.0" }, + { name = "pytest", specifier = ">=8.4.0" }, + { name = "pytest", marker = "extra == 'dev'", specifier = ">=7.4.0" }, + { name = "pytest-asyncio", specifier = ">=1.0.0" }, + { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23.0" }, + { name = "pytest-cov", specifier = ">=6.1.1" }, + { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=4.1.0" }, + { name = "pytest-mock", marker = "extra == 'dev'", specifier = ">=3.12.0" }, + { name = "pytest-xdist", marker = "extra == 'dev'", specifier = ">=3.5.0" }, + { name = "python-dateutil", specifier = ">=2.8.0" }, + { name = "python-dotenv", specifier = ">=1.0.0" }, + { name = "python-jose", extras = ["cryptography"], specifier = ">=3.3.0" }, + { name = "python-multipart", specifier = ">=0.0.6" }, + { name = "pyyaml", specifier = ">=6.0.0" }, + { name = "requests", specifier = ">=2.31.0" }, + { name = "rich", specifier = ">=13.7.0" }, + { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.1.0" }, + { name = "safety", marker = "extra == 'dev'", specifier = ">=2.3.0" }, + { name = "sphinx", marker = "extra == 'dev'", specifier = ">=7.2.0" }, + { name = "sphinx", marker = "extra == 'docs'", specifier = ">=7.2.0" }, + { name = "sphinx-autoapi", marker = "extra == 'docs'", specifier = ">=3.0.0" }, + { name = "sphinx-rtd-theme", marker = "extra == 'dev'", specifier = ">=2.0.0" }, + { name = "sphinx-rtd-theme", marker = "extra == 'docs'", specifier = ">=2.0.0" }, + { name = "sqlparse", specifier = ">=0.4.4" }, + { name = "starlette", specifier = ">=0.27.0" }, + { name = "structlog", specifier = ">=23.2.0" }, + { name = "toml", specifier = ">=0.10.0" }, + { name = "tox", marker = "extra == 'dev'", specifier = ">=4.11.0" }, + { name = "tqdm", specifier = ">=4.66.0" }, + { name = "typer", specifier = ">=0.9.0" }, + { name = "uvicorn", extras = ["standard"], specifier = ">=0.25.0" }, + { name = "uvloop", marker = "extra == 'performance'", specifier = ">=0.19.0" }, + { name = "websockets", specifier = ">=12.0" }, +] +provides-extras = ["dev", "docs", "performance", "monitoring"] + +[package.metadata.requires-dev] +dev = [{ name = "ruff", specifier = ">=0.11.13" }] + [[package]] name = "mdit-py-plugins" version = "0.4.2"