Skip to content

Latest commit

 

History

History
342 lines (262 loc) · 14.5 KB

docker.md

File metadata and controls

342 lines (262 loc) · 14.5 KB

Docker Deployment Guide

Use this guide to deploy and manage ToolPlane with Docker and Docker Compose.

1. Quick Start

Make sure that you have Docker and Docker Compose installed.

# Clone the repository
git clone https://github.com/nmdra/ERPBridge.git
cd ERPBridge

# Start the local development stack safely
make dev-up

# Direct Compose use requires MOCK_ERP_CREDENTIALS_JSON or
# MOCK_ERP_CREDENTIALS_FILE to be set first.
docker compose up --build --force-recreate -d

The stack includes:

  • ToolPlane Server (:8080): The core MCP middleware. This development bootstrap enables the two system.*_test demonstration tools. Direct Compose use defaults to false. Production deployments must leave MCP_ENABLE_TEST_TOOLS unset or false.
  • Mock ERP (:8081): Simulates legacy ERP endpoints.
  • Redis (:6379): Provides the exact-match cache.

RedisInsight binds to 127.0.0.1 by default. Keep this setting unless you explicitly need local-network access. Never expose RedisInsight publicly.

For a disposable end-to-end onboarding check, run:

./scripts/test-onboarding.sh

The check uses an isolated Compose project, temporary CLI home, dynamic loopback ports, synthetic fixture data, and ephemeral credentials. On exit, it removes its containers and temporary files. It does not use caller credentials or print ERP response bodies.

2. Configuration

Set the server environment variables in docker-compose.yml.

Variable Description Default (compose)
BASE_URL Public URL of the MCP server. http://localhost:8080
ERPBRIDGE_HOST_PORT Host port for the ToolPlane container. 8080
ERP_BASE_URL Base URL of the underlying ERP system. http://mock-erp:8081
MOCK_ERP_HOST_PORT Host port for the MockERP container. 8081
MOCK_ERP_IMAGE MockERP image used by Compose. ghcr.io/nmdra/mockerp:0.2.1
MOCK_ERP_VERSION MockERP release used for OpenAPI generation. 0.2.1
MOCK_ERP_OPENAPI_URL Versioned MockERP OpenAPI URL. https://raw.githubusercontent.com/nmdra/mockerp/v0.2.1/openapi.yaml
MOCK_ERP_DB_PATH SQLite database path inside MockERP. /data/mockerp.db
MOCK_ERP_CREDENTIALS_JSON JSON credential configuration for local/container use. (required)
MOCK_ERP_CREDENTIALS_FILE Mounted JSON credential file path. (required alternative)
REDIS_URL URL for the Redis cache. redis://redis:6379
REDIS_HOST_PORT Host port for Redis. 6379
REDIS_INSIGHT_BIND_ADDRESS Address bound by RedisInsight. 127.0.0.1
REDIS_INSIGHT_HOST_PORT Host port for RedisInsight. 8001
DATABASE_PATH Path of the SQLite tool registry inside the container. /app/data/erpbridge.db
RATE_LIMIT_RPS Tool-execution requests per second, keyed by token principal, MCP session, or process fallback. 10
RATE_LIMIT_BURST Token bucket burst size. 20
INSECURE_AUTH_ALLOWED_HOSTS Development-only exact host:port exceptions for credentialed HTTP calls. mock-erp:8081
MCP_ENABLE_TEST_TOOLS Development-only registration of system.*_test demo tools. true with make dev-up; false with direct Compose
MCP_ADVERTISE_REVISION_ALIASES Lists exact-revision names in MCP discovery. The default hides them so clients see one entry per tool. unset
API_AUTH_TOKEN Bearer token for protected admin, MCP, and direct-invoke routes. unset
ERPBRIDGE_CREDENTIALS_DIR Optional mounted directory used only by resources with credentialSource: file. unset
PLUGIN_ENDPOINT_ALLOWLIST Exact host:port values allowed for credentialed plugin resources. unset
PLUGIN_MOCK_API_KEY Environment-backed API key reference used by the plugin integration fixture. unset
MOCK_PLUGIN_API_KEY API key accepted by the separately deployed mock plugin fixture. unset

For the full list of server environment variables, see the Environment Variables Reference.

MockERP fails closed when neither MOCK_ERP_CREDENTIALS_JSON nor MOCK_ERP_CREDENTIALS_FILE is configured. make dev-up generates an ephemeral JSON credential pair only when neither source is set. It makes sure that the rendered Compose configuration is valid. It then force recreates the stack and polls both service health endpoints. It does not write or print the generated values. Use the JSON variable for local development, or mount a Docker secret and set the file path. Do not commit credential values to this repository.

The bootstrap does not source .env; Compose reads that file and preserves its quoted values. Direct docker compose commands do not run the bootstrap preflight. Set a credential source first. Then run docker compose config --quiet. Run docker compose up --build --force-recreate -d. RedisInsight binds to 127.0.0.1 by default. Change REDIS_INSIGHT_BIND_ADDRESS only when you explicitly need another interface.

Optional mounted credentials

Environment variables remain the default credential source. For a credential that must rotate without recreating the ToolPlane container, mount a directory and set ERPBRIDGE_CREDENTIALS_DIR in the server container. Add credentialSource: file beside the logical credentialRef for only the tools, APIs, resources, or plugins that use that mount:

services:
  erpbridge-server:
    volumes:
      - ./operator-secrets:/run/secrets/erpbridge:ro
    environment:
      ERPBRIDGE_CREDENTIALS_DIR: /run/secrets/erpbridge

The mounted file name is the validated credentialRef. ToolPlane reads it on each request. It accepts complete projected-file generations. Missing, empty, control-character, invalid UTF-8, non-regular, or oversized (over 64 KiB) content causes the request to fail closed. It does not retain the previous value or fall back to an environment value. For a writable local directory, replace files by writing a complete temporary file and atomically renaming it over the live name.

For Kubernetes, use a normal directory volume rather than subPath. Projected Secret updates do not update subPath mounts. AWS EKS ASCP, AKS Key Vault CSI, and GKE Secret Manager CSI can populate this directory. Use workload identity. Apply least privilege. Configure provider rotation. Provider updates are eventual and replicas can observe them at different times. Adding a new cloud secret may require changing the provider's workload mapping and rolling the workload before the new filename is mounted. ToolPlane does not access AWS, Azure, or Google Cloud APIs directly. Environment-variable changes still require a container recreation.

Credentialed ERP and plugin endpoints must use HTTPS. The bundled MockERP fixture is HTTP-only, so Compose explicitly sets INSECURE_AUTH_ALLOWED_HOSTS=mock-erp:8081. The opt-in plugin integration fixture adds mock-plugin:8080 to this exact host-and-port list and sets PLUGIN_ENDPOINT_ALLOWLIST=mock-plugin:8080. These exceptions are for local development only. Do not set a broad or production allowlist.

3. Tool Registry

The server stores tool definitions in a SQLite database (DATABASE_PATH). The schemas/ directory is not mounted into the container. Git does not track schemas/. The server has no file-system watcher.

To load tools into the registry, generate and apply the manifests from your host:

  1. For manual generation, register the ERP API. If you use the default generation task in the next step, skip this registration command:

    ./bridgectl api register --name erp --url http://localhost:8081 --module erp --description "Mock ERP"
  2. If erp is not already registered in the selected context, fetch the pinned OpenAPI contract and generate and apply the tool schemas. This task registers erp itself. If erp already exists, use the manual generation workflow in the Onboarding Guide instead:

    make generate-tools

Within 10 seconds, the server detects new registry entries and exposes them over MCP. You do not need to restart it.

4. Using bridgectl with Docker

Use the local bridgectl binary to manage the server running in Docker.

  1. Build bridgectl:

    go build -o bridgectl tools/bridgectl/main.go
  2. Verify Connection:

    ./bridgectl tool get
  3. Generate a new tool:

    MOCK_ERP_VERSION=0.2.1 make generate-tools

    The target fetches the matching tagged OpenAPI contract. It applies one temporary YAML stream and removes the temporary files on exit. It does not write generated files to schemas/.

  4. Test an API through the server:

    ./bridgectl api test erp

    This is the default mode. ToolPlane resolves the API credentialRef from its environment and returns only a status summary. Use ./bridgectl api test erp --local only for a legacy host-side diagnostic.

    CLI control-plane requests use the configured MCP host root. The exact /mcp or /mcp/ suffix is removed automatically. Other non-empty paths fail with CONTROL_PLANE_URL_INVALID. /mcp/ remains the MCP client transport endpoint and must not be used as a REST path.

5. Logs & Monitoring

  • View Container Logs:

    docker compose logs -f erpbridge-server
  • Live Stream Logs via CLI:

    ./bridgectl log tail

The REST /api/logs/stream endpoint flushes authenticated SSE headers before its first event and sends data: <JSON>\n\n frames. Closing the client stops its subscription. Each subscriber has a bounded 100-event channel. If a slow subscriber's channel is full, the server drops new events. This raw stream is not the redacted Console BFF projection.

  • Metrics: Prometheus metrics are available at http://localhost:8080/metrics.

When the container receives SIGTERM or SIGINT, ToolPlane stops accepting new HTTP connections. It shuts down the listener before the process exits.

6. External plugins

ToolPlane does not install, start, update, or schedule plugin code. Deploy each plugin process separately, then apply a Plugin resource with its reachable endpoint and an exact-version PluginBinding. See the External Plugin Resource Schema for the manifest and /v1/process JSON contract.

For a deployment that uses a published plugin image, pin the image tag in the operator-owned deployment configuration:

services:
  response-transformer:
    image: ghcr.io/nmdra/erpbridge-plugins/mock-plugin:0.1.0

The image is not part of the ToolPlane server image. An after-response plugin receives a normalized result and binding configuration. A raw-response plugin can process ERP response bytes before normalization. Plugins do not receive ERP credentials, inbound request headers, or caller identity. A credentialed plugin resource stores an environment-variable reference, not the key value:

metadata:
  type: docker
spec:
  endpoint: http://mock-plugin:8080
  auth:
    type: api-key
    credentialRef: PLUGIN_MOCK_API_KEY
    header: X-API-Key

The server resolves the reference at invocation time and sends one API-key header. The control plane must have API_AUTH_TOKEN enabled, and the endpoint must match PLUGIN_ENDPOINT_ALLOWLIST exactly.

The repository includes an opt-in black-box test. It builds the deterministic fixture from the sibling ../ERPBridge-Plugins polyrepo, starts it with the pinned MockERP image, and removes only its isolated containers and volumes:

make test-plugin-integration

The test uses the Compose project name erpbridge-plugin-test and ports 18080, 18081, 18090, and 16379 so it does not reuse the normal local stack. It generates separate admin, ERP, and plugin credentials at runtime, passes them only to the services that need them, and checks missing, wrong, and correct plugin API keys at /v1/process. The plugin /health endpoint remains unprotected for readiness checks. Do not run it when those ports are already in use.

7. Connecting MCP Clients

ToolPlane supports the Stdio and Streamable HTTP transports.

Claude Desktop (Stdio)

Claude Desktop connects to MCP servers via standard input and output. The server binary supports the --stdio flag.

  1. Locate Configuration:

    • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
    • Windows: %APPDATA%\Claude\claude_desktop_config.json
  2. Add ToolPlane Server: Add the following to the mcpServers section. This runs the server in stdio mode inside a container.

    {
      "mcpServers": {
        "erpbridge": {
          "command": "docker",
          "args": [
            "run",
            "-i",
            "--rm",
            "ghcr.io/nmdra/erpbridge-server:latest",
            "--stdio"
          ]
        }
      }
    }

    Note: The tool registry uses the SQLite database. In this disposable container, tools persist only while the container runs. To keep tools across restarts, run the full docker compose stack. Then connect via Streamable HTTP.

  3. Restart Claude: Fully quit and restart Claude Desktop. Look for the tool icon in the chat input.

Cursor (Streamable HTTP)

Cursor connects to remote MCP servers via HTTP. Use this method when the ToolPlane stack is already running via docker compose up.

  1. Make Sure the Server Is Running: Make sure that the stack is running and the server is reachable at http://localhost:8080.

  2. Configure Cursor:

    • Open Cursor Settings (Cmd+, or Ctrl+,).
    • Navigate to Features > MCP.
    • Click + Add New MCP Server.
    • Name: ERPBridge
    • Type: streamable-http (or http, depending on your Cursor version)
    • URL: http://localhost:8080/mcp/
  3. Verify: You see a green status indicator. You can now use the ERP tools in Cursor Chat or Composer.

8. Troubleshooting

  • Connection Refused: Make sure that ERP_BASE_URL in docker-compose.yml uses the service name http://mock-erp:8081 instead of localhost. Compose pulls ghcr.io/nmdra/mockerp:0.2.1; override MOCK_ERP_IMAGE only with a compatible image.

  • Claude Stdio Timeout: If Claude fails to connect, build the server binary first and run it directly. This shows any startup errors.

  • Schema Errors: Before you apply the tool definition, make sure that it passes local validation:

    ./bridgectl tool validate -f schemas/erp/list_employees.json