Skip to content

Local package -> Docker image workflow

This guide walks through building Node Wire packages locally (as wheels) and using those wheels to build the Docker images in docker/.

The Dockerfiles in this repo install local wheel artifacts from packages/**/dist/*.whl, so you must build wheels first.


Prerequisites

  • Python 3.11, 3.12 or 3.13 available in your shell
  • Docker installed and running
  • Build tooling installed:
python -m pip install --upgrade build   # or install uv; the script uses `uv build` when present

Run all commands from the repository root. The build context must be the repository root (.) so the Dockerfiles' COPY src/ and COPY config/ resolve.


Build wheel packages locally

Build the wheels for every package (runtime, bindings, ToolHive and the connectors):

bash scripts/build-packages.sh

Build only specific packages (faster when iterating):

bash scripts/build-packages.sh \
  packages/runtime \
  packages/connectors/smtp \
  packages/connectors/stripe

The script (scripts/build-packages.sh):

  • builds one pure-Python (py3-none-any) wheel per package, which installs in the images and on your machine alike,
  • writes artifacts under each package's dist/ folder,
  • fails if a wheel is not py3-none-any or contains compiled .so/.pyd files.

Confirm wheel artifacts exist

Quick check (example for SMTP):

ls packages/runtime/dist/*.whl
ls packages/connectors/smtp/dist/*.whl
ls packages/connectors/stripe/dist/*.whl

If ls fails, rebuild that package before continuing.


Build Docker images from local wheels

Build all MCP connector images

Run the build script:

./scripts/build-mcp-images.sh

Each image is tagged latest and the version, which defaults to the one in pyproject.toml. Pass --version to override it:

./scripts/build-mcp-images.sh --version 1.0.0

The script builds the following images:

Image Dockerfile
nw-google-drive docker/google-drive/Dockerfile
nw-smartonfhir-epic docker/fhir-epic/Dockerfile
nw-smartonfhir-cerner docker/fhir-cerner/Dockerfile
nw-smtp docker/smtp/Dockerfile
nw-stripe docker/stripe/Dockerfile
nw-salesforce docker/salesforce/Dockerfile
nw-slack docker/slack/Dockerfile

Build one image manually

Build a single image with docker build:

docker build -f docker/smtp/Dockerfile -t nw-smtp:local .

Run them with Docker Compose

docker-compose.mcp.yml starts every MCP server as a stdio container, which is useful for local validation before configuring ToolHive. It needs the images built and your .env populated with the credentials of the connectors you run. Each service pins NW_ALLOWED_CONNECTORS to its own connector, so a broad value in .env does not make a per-connector image import optional dependencies it does not contain. To start every server, or a single one, run:

docker compose -f docker-compose.mcp.yml up --build
docker compose -f docker-compose.mcp.yml up --build nw-smartonfhir-epic   # one server only

To register these images in ToolHive, see the ToolHive agent scenario.


Wheel requirements by image

Each Dockerfile expects specific wheel files to exist in dist/. Keep this table in sync with the Dockerfiles in docker/ — add a row here whenever you add a Tier 3 standalone MCP image (see the ship checklist).

Image Required wheels
docker/smtp/Dockerfile packages/runtime/dist/*.whl, packages/connectors/smtp/dist/*.whl
docker/google-drive/Dockerfile packages/runtime/dist/*.whl, packages/connectors/google_drive/dist/*.whl
docker/fhir-epic/Dockerfile packages/runtime/dist/*.whl, packages/connectors/fhir_epic/dist/*.whl
docker/fhir-cerner/Dockerfile packages/runtime/dist/*.whl, packages/connectors/fhir_cerner/dist/*.whl
docker/stripe/Dockerfile packages/runtime/dist/*.whl, packages/connectors/stripe/dist/*.whl
docker/salesforce/Dockerfile packages/runtime/dist/*.whl, packages/connectors/salesforce/dist/*.whl
docker/slack/Dockerfile packages/runtime/dist/*.whl, packages/connectors/slack/dist/*.whl
Dockerfile (unified MCP server) runtime + connector wheels (http_generic, stripe, smtp, slack, google_drive, fhir_epic, fhir_cerner, salesforce)

Common failures and fixes

COPY ... dist/*.whl failed: no source files were specified

A required wheel is missing. Re-run scripts/build-packages.sh for the missing package(s), then rebuild the image.

Docker build cannot find src/ or config/

Use repo root as build context (.):

docker build -f docker/smtp/Dockerfile -t nw-smtp:local .

Do not run docker build from inside docker/<name>/.

Docker daemon not running

Start Docker Desktop (or daemon) and retry package/image builds.


A typical iteration rebuilds the changed packages, then the image, then checks it:

# 1) Rebuild changed packages
bash scripts/build-packages.sh packages/runtime packages/connectors/smtp

# 2) Build image(s)
docker build -f docker/smtp/Dockerfile -t nw-smtp:local .

# 3) Verify image exists
docker images --filter reference=nw-smtp