Skip to content

Build a connector

Build connector distributions with the versioned Umbod connector-builder image. The image provides Python, uv, and the Connector SDK that matches the corresponding Umbod release.

Requirements

  • Docker
  • Access to an OCI registry where you can push a plugin image
  • Helm 3 and kubectl
  • An Umbod 0.0.1-beta.2 installation

Set the Umbod release and the destination for your plugin image:

export UMBOD_RELEASE=0.0.1-beta.2
export UMBOD_CONNECTOR_BUILDER=ghcr.io/computerlovetech/umbod-connector-builder:$UMBOD_RELEASE
export PLUGIN_IMAGE=registry.example.com/your-organization/my-umbod-connector:0.1.0

Use the same release for the connector-builder image, core image, and Helm chart. Use immutable image tags rather than floating tags.

Create the Python distribution

A connector is a standard Python distribution. Its pyproject.toml must:

  • require Python 3.14 or newer;
  • depend on a compatible umbod version;
  • map that SDK dependency to /opt/umbod-connector-sdk for builds in the base image;
  • declare each connector in the umbod.connectors entry-point group;
  • configure the chosen Python build backend to include the connector package.

The builder image contains the SDK source at /opt/umbod-connector-sdk, so dependency resolution does not require a separately published Python package.

Declare the SDK source and connector entry point in pyproject.toml:

[project]
name = "my-umbod-connector"
version = "0.1.0"
requires-python = ">=3.14"
dependencies = [
  "umbod>=0.1.0,<0.2.0",
]

[project.entry-points."umbod.connectors"]
my-connector = "my_umbod_connector:plugin"

[tool.uv.sources]
umbod = { path = "/opt/umbod-connector-sdk" }

[build-system]
requires = ["hatchling>=1.27.0,<2"]
build-backend = "hatchling.build"

[tool.hatch.build.targets.wheel]
packages = ["src/my_umbod_connector"]

Generate or refresh the lock file inside the builder image:

docker run --rm \
  --user "$(id -u):$(id -g)" \
  --env HOME=/tmp \
  --volume "$PWD:/workspace" \
  "$UMBOD_CONNECTOR_BUILDER" \
  uv lock

Connector code imports the public contracts from umbod_sdk.connectors.plugin_api. The object named by an entry point must implement the Connector SDK plugin contract. Keep the entry-point name, connector definition ID, and Helm availability ID identical.

Create the plugin image

Use the connector-builder image as the build stage. Export third-party runtime dependencies without the SDK, install them into /plugin-bundle, then install the connector wheel without dependency resolution.

ARG UMBOD_RELEASE=0.0.1-beta.2
FROM ghcr.io/computerlovetech/umbod-connector-builder:${UMBOD_RELEASE} AS builder

WORKDIR /workspace

COPY pyproject.toml uv.lock ./
RUN uv export \
      --frozen \
      --no-dev \
      --no-emit-project \
      --no-emit-package umbod \
      --output-file /tmp/runtime-requirements.txt \
    && uv pip install \
      --target /plugin-bundle \
      --requirements /tmp/runtime-requirements.txt

COPY . .
RUN uv build --wheel --out-dir /tmp/plugin-wheel \
    && uv pip install \
      --target /plugin-bundle \
      --no-deps \
      /tmp/plugin-wheel/*.whl \
    && test ! -e /plugin-bundle/umbod_sdk

FROM alpine:3.23

COPY --from=builder /plugin-bundle /plugin-bundle

ENTRYPOINT ["sh", "-c", "cp -R /plugin-bundle/. /plugins/"]

The resulting bundle contains connector code, Python distribution metadata, and third-party runtime dependencies. It does not contain the Connector SDK; Umbod core supplies the authoritative SDK at runtime.

Build and push the image:

docker build \
  --build-arg UMBOD_RELEASE="$UMBOD_RELEASE" \
  --tag "$PLUGIN_IMAGE" \
  .

docker push "$PLUGIN_IMAGE"

The registry must be reachable from the Kubernetes cluster. Configure Kubernetes image-pull credentials when using a private registry.

Validate locally

Materialize the bundle and validate it with the matching core image:

mkdir -p .umbod-plugin-bundle

docker run --rm \
  --volume "$PWD/.umbod-plugin-bundle:/plugins" \
  "$PLUGIN_IMAGE"

docker run --rm \
  --volume "$PWD/.umbod-plugin-bundle:/plugins:ro" \
  "ghcr.io/computerlovetech/umbod:$UMBOD_RELEASE" \
  umbod connectors validate --plugin-path /plugins

Validation imports connector code. Only validate plugin images you trust. Structural validation does not call external providers or verify live credentials.

Install with Helm

Create values.plugins.yaml:

plugins:
  image:
    repository: registry.example.com/your-organization/my-umbod-connector
    tag: 0.1.0
    pullPolicy: IfNotPresent
  availableConnectorIds:
    - my-connector

Upgrade the existing release:

helm upgrade --install umbod \
  oci://ghcr.io/computerlovetech/charts/umbod \
  --version "$UMBOD_RELEASE" \
  --values values.plugins.yaml \
  --reuse-values \
  --set-string core.image.tag="$UMBOD_RELEASE" \
  --set-string frontend.image.tag="$UMBOD_RELEASE" \
  --wait --timeout 5m

For a release installed in another namespace, add the same --namespace option used during installation.

Run the chart tests:

helm test umbod --logs --timeout 2m

Diagnose startup failures

The core Pod starts only after plugin materialization and validation succeed:

kubectl get pods
kubectl describe pod -l app.kubernetes.io/component=core
kubectl logs -l app.kubernetes.io/component=core -c connector-plugins
kubectl logs -l app.kubernetes.io/component=core -c validate-connector-plugins

Typical failures include an unavailable image, an SDK version mismatch, missing distribution metadata, duplicate connector IDs, or an unavailable ID in plugins.availableConnectorIds.

Configure and expose the connector

After the workloads become ready:

  1. Open Umbod and go to Connectors.
  2. Select Add connector, then select the new connector.
  3. Supply its settings and check the configuration.
  4. Save and publish the connector.
  5. Open the connector, activate its required tools, prompts, and resources, then save the capability selection.
  6. Grant the connector and required capabilities to the calling agent's group under Group permissions.

The connector is agent-facing only after it is installed, deployment-available, configured, published, activated, and authorized.