diff --git a/.env.example b/.env.example index 3d9ffdb..65bca59 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,12 @@ # Dolibarr Configuration -DOLIBARR_URL=https://your-dolibarr.example.com +DOLIBARR_URL=https://your-dolibarr.example.com/api/index.php DOLIBARR_API_KEY=YOUR_API_KEY_HERE # Logging LOG_LEVEL=INFO + +# MCP HTTP listener (recommended for Docker Compose) +MCP_TRANSPORT=http +MCP_HTTP_HOST=0.0.0.0 +MCP_HTTP_PORT=8080 +MCP_HOST_PORT=18004 diff --git a/README.md b/README.md index 434fac1..ec36544 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,10 @@ inside the virtual environment, for example ### Docker (optional) ```bash +# Copy the template and update credentials (file is gitignored) +cp .env.example .env + +# Start the HTTP listener for MCP clients (Open WebUI, MCPO, etc.) docker compose up -d # or docker build -t dolibarr-mcp . @@ -84,6 +88,8 @@ docker run -d \ The server reads configuration from the environment or a `.env` file. Both `DOLIBARR_URL` and `DOLIBARR_SHOP_URL` are accepted for the base API address. +The repository `.gitignore` excludes `.env`, so a local `.env` file will not be +overwritten by `git pull` on your server. | Variable | Description | | --- | --- | @@ -93,6 +99,7 @@ The server reads configuration from the environment or a `.env` file. Both | `MCP_TRANSPORT` | Transport to use: `stdio` (default) or `http` for streamable HTTP. | | `MCP_HTTP_HOST` | Host/interface to bind when using HTTP transport (default `0.0.0.0`). | | `MCP_HTTP_PORT` | Port to bind when using HTTP transport (default `8080`). | +| `MCP_HOST_PORT` | Optional host port to publish in Docker Compose (default `18004`). | Example `.env`: @@ -154,6 +161,12 @@ Then point Open WebUI’s MCP configuration at `http://:8080/`. The MCP protocol headers (including `mcp-protocol-version`) are handled automatically by Open WebUI’s MCP client. +### Docker Compose listener (recommended for MCP clients) + +The bundled `docker-compose.yml` starts the HTTP listener by default so MCP +clients such as Open WebUI or MCPO can connect to +`http://:${MCP_HOST_PORT:-18004}/` right away. + ### Test the Dolibarr credentials Use the standalone connectivity check before wiring the server into an MCP host: diff --git a/docker/docker-compose.yml b/docker-compose.yml similarity index 50% rename from docker/docker-compose.yml rename to docker-compose.yml index fcc24f1..4479ef0 100644 --- a/docker/docker-compose.yml +++ b/docker-compose.yml @@ -1,39 +1,31 @@ services: dolibarr-mcp: build: - context: .. + context: . dockerfile: docker/Dockerfile image: dolibarr-mcp:latest container_name: dolibarr-mcp-server restart: unless-stopped environment: # Dolibarr API Configuration - - DOLIBARR_URL=${DOLIBARR_URL:-https://your-dolibarr-instance.com/api/index.php} - - DOLIBARR_API_KEY=${DOLIBARR_API_KEY:-your_api_key_here} - + DOLIBARR_URL: ${DOLIBARR_URL:-https://your-dolibarr-instance.com/api/index.php} + DOLIBARR_API_KEY: ${DOLIBARR_API_KEY:-your_api_key_here} + # Logging Configuration - - LOG_LEVEL=${LOG_LEVEL:-INFO} - - PYTHONUNBUFFERED=1 - - # MCP Server Configuration - - MCP_TRANSPORT=${MCP_TRANSPORT:-http} - - MCP_HTTP_PORT=${MCP_HTTP_PORT:-8080} - - MCP_HTTP_HOST=${MCP_HTTP_HOST:-0.0.0.0} - - MCP_SERVER_NAME=dolibarr-mcp - - MCP_SERVER_VERSION=1.0.0 - - volumes: - # Mount configuration if needed - - ../.env:/app/.env:ro - - # Optional: Mount for custom configurations or plugins - # - ./config:/app/config:ro - # - ./plugins:/app/plugins:ro - - # Expose port for future HTTP interface + LOG_LEVEL: ${LOG_LEVEL:-INFO} + PYTHONUNBUFFERED: 1 + + # MCP Server Configuration (HTTP listener by default) + MCP_TRANSPORT: ${MCP_TRANSPORT:-http} + MCP_HTTP_PORT: ${MCP_HTTP_PORT:-8080} + MCP_HTTP_HOST: ${MCP_HTTP_HOST:-0.0.0.0} + MCP_SERVER_NAME: dolibarr-mcp + MCP_SERVER_VERSION: 1.0.0 + + # Expose port for HTTP MCP clients ports: - - "18004:8080" - + - "${MCP_HOST_PORT:-18004}:8080" + # Health check healthcheck: test: ["CMD", "python", "-c", "import sys; from src.dolibarr_mcp.config import Config; sys.exit(0 if Config().api_key else 1)"] @@ -41,42 +33,39 @@ services: timeout: 10s retries: 3 start_period: 10s - + # Resource limits deploy: resources: limits: - cpus: '1.0' + cpus: "1.0" memory: 512M reservations: - cpus: '0.25' + cpus: "0.25" memory: 128M - + # Logging logging: driver: "json-file" options: max-size: "10m" max-file: "3" - + networks: - dolibarr-mcp-network - # Optional: Include a test service + # Optional: Connection check (run on demand) dolibarr-mcp-test: build: - context: .. + context: . dockerfile: docker/Dockerfile image: dolibarr-mcp:latest container_name: dolibarr-mcp-test environment: - - DOLIBARR_URL=${DOLIBARR_URL} - - DOLIBARR_API_KEY=${DOLIBARR_API_KEY} - - LOG_LEVEL=DEBUG - volumes: - - ../.env:/app/.env:ro - - ../test_dolibarr_mcp.py:/app/test_dolibarr_mcp.py:ro - command: python /app/test_dolibarr_mcp.py + DOLIBARR_URL: ${DOLIBARR_URL} + DOLIBARR_API_KEY: ${DOLIBARR_API_KEY} + LOG_LEVEL: DEBUG + command: python -m dolibarr_mcp.test_connection profiles: - test networks: @@ -88,7 +77,7 @@ networks: name: dolibarr-mcp-net # To run the main server: -# docker-compose up - +# docker compose up -d +# # To run tests: -# docker-compose --profile test up dolibarr-mcp-test +# docker compose --profile test up dolibarr-mcp-test diff --git a/docs/development.md b/docs/development.md index 49fea86..bd8a9cd 100644 --- a/docs/development.md +++ b/docs/development.md @@ -80,10 +80,10 @@ Include this value when filing issues or investigating user reports. Container assets live in `docker/`: - `Dockerfile` – production-ready image for the MCP server -- `docker-compose.yml` – local stack that spins up Dolibarr together with the MCP server +- `../docker-compose.yml` – compose file for the MCP server and optional checks Build and run the container locally with: ```bash -docker compose -f docker/docker-compose.yml up --build +docker compose up --build ``` diff --git a/src/dolibarr_mcp/cli.py b/src/dolibarr_mcp/cli.py index dbf8909..342e0f5 100644 --- a/src/dolibarr_mcp/cli.py +++ b/src/dolibarr_mcp/cli.py @@ -1,6 +1,7 @@ """Command line interface for Dolibarr MCP Server.""" import asyncio +import os import sys from typing import Optional @@ -28,14 +29,18 @@ def test(url: Optional[str], api_key: Optional[str]): @cli.command() -@click.option("--host", default="localhost", help="Host to bind to") -@click.option("--port", default=8080, help="Port to bind to") +@click.option("--host", default="0.0.0.0", help="Host to bind to") +@click.option("--port", default=8080, type=int, help="Port to bind to") def serve(host: str, port: int): """Start the Dolibarr MCP server.""" click.echo(f"🚀 Starting Dolibarr MCP server on {host}:{port}") click.echo("📝 Use this server with MCP-compatible clients") click.echo("🔧 Configure your environment variables in .env file") - + + os.environ.setdefault("MCP_TRANSPORT", "http") + os.environ["MCP_HTTP_HOST"] = host + os.environ["MCP_HTTP_PORT"] = str(port) + # Run the MCP server asyncio.run(server_main())