Install Opencode Plugin Dependencies

This recipe applies to opencode and opencode2 when a local plugin or custom tool in ~/.config/opencode imports an npm package, or an MCP server runs from ~/.config/opencode/node_modules. Without it, those plugins load on your machine but not in the sandbox, and the only trace is a failed to load plugin ... Cannot find package line in the opencode log inside the VM.

The launcher copies your ~/.config/opencode into the VM, but not its node_modules/ or package*.json (see Default drop-in provisioning). Copying them would not help: your machine and the Linux VM are different platforms, so a package with native code would be built for the wrong one, and a copy goes stale silently after you change the packages on your machine. opencode does not install the packages of a local plugin either. Instead, you install them inside the VM, where they are built for Linux and kept in the sandbox’s home volume.

While the packages are missing, agents-sandbox run warns:

/Users/you/.config/opencode/package.json declares dependencies, but node_modules is not copied into the VM ...

Declare every import

The VM install only knows the packages listed in ~/.config/opencode/package.json. A package your plugin imports but the file does not list is missing in the VM, even when it happens to be installed on your machine. The warning cannot detect that case, so check it yourself: every package named in an import ... from "<package>" line of your plugins and tools must appear under dependencies.

{
  "dependencies": {
    "@opencode-ai/plugin": "1.0.2",
    "zod": "4.1.8"
  }
}

Then create or update the lockfile on your machine, so the VM installs exactly the versions you tested:

npm install --prefix ~/.config/opencode

This writes ~/.config/opencode/package-lock.json. Keep it next to package.json; the install in Install the dependencies in the VM refuses to run without it. Run the command again after every change to package.json. A lockfile that no longer matches package.json fails the install with npm ci can only install packages when your package.json and package-lock.json ... are in sync. This happens when another tool, such as bun, manages the directory and updates only its own lockfile.

Add node to the image

The opencode images contain no node or npm. Add them in your project’s .agents-sandbox/Dockerfile:

# .agents-sandbox/Dockerfile
FROM agents-sandbox/runner-base:latest
RUN apt-get update && apt-get install -y --no-install-recommends nodejs npm && rm -rf /var/lib/apt/lists/*

Every project whose sandbox should load the plugins needs this. See Runner Image for how the project Dockerfile is built.

Install the dependencies in the VM

A startup hook runs a script in the VM each time the sandbox starts (see Home provisioning & startup hooks). Save this script as ~/.config/agents-sandbox/install-opencode-deps.sh:

#!/bin/sh
# Installs the npm packages of ~/.config/opencode inside the VM.
set -eu
cd "$HOME/.config/opencode"
if ! command -v npm >/dev/null 2>&1; then
  echo "install-opencode-deps: npm is not installed in this image, skipping" >&2
  exit 0
fi
# Reinstall only when the lockfile or the node version changed.
installed_from="node_modules/.installed-from"
wanted="$(node --version) $(sha256sum package-lock.json | cut -d ' ' -f 1)"
if [ -f "$installed_from" ] && [ "$(cat "$installed_from")" = "$wanted" ]; then
  exit 0
fi
npm ci --no-audit --no-fund
echo "$wanted" > "$installed_from"

Then add the package files and the hook to ~/.config/agents-sandbox/config.yaml:

home:
  .config/opencode/package.json: ~/.config/opencode/package.json
  .config/opencode/package-lock.json: ~/.config/opencode/package-lock.json
  .local/share/agents-sandbox/install-opencode-deps.sh:
    source: install-opencode-deps.sh
    hook: startup

The home: entries copy the package files into the VM, and the hook runs npm ci from them. The first start installs the packages; later starts skip the install until you change the lockfile or the image’s node version. A failed install, for example while the npm registry is unreachable, prints a warning and the sandbox starts anyway.

Once package.json reaches the VM this way, the warning from agents-sandbox run no longer appears.

The install needs to reach registry.npmjs.org. The default public network profile allows it; with profile: none, add the host to network.egress-allow (see Networking). Without it, npm ci fails with 403 Forbidden.

MCP servers from the config directory

An MCP server whose command points into ~/.config/opencode/node_modules needs two more things in the VM: node on the PATH, which Add node to the image installs, and a path that exists in the VM. An absolute path from your machine, such as /Users/you/.config/opencode/node_modules/..., does not. Write the path with opencode’s {env:HOME} variable instead, so it resolves to your home directory on your machine and to /home/dev in the VM:

{
  "mcp": {
    "mobile": {
      "type": "local",
      "command": ["node", "{env:HOME}/.config/opencode/node_modules/@mobilenext/mobile-mcp/lib/index.js"]
    }
  }
}

List the server’s package under dependencies in package.json so the hook installs it.

Check the result

Restart the sandbox so the hook runs:

agents-sandbox stop
agents-sandbox run

The startup output shows the hook running npm ci on the first start. opencode loads plugins when a session opens the project, so start a session before you check its log. Then open a shell in the sandbox from another terminal:

agents-sandbox shell

In that shell, ls ~/.config/opencode/node_modules lists the installed packages. If a plugin still does not load, search the opencode log for the package it cannot find, and add that package to package.json:

grep -h 'failed to load plugin' ~/.local/share/opencode/log/*.log

Mounting the packages from your machine instead

A mount can make your machine’s node_modules visible in the VM, and it also silences the warning:

mounts:
  /home/dev/node_modules:
    source: ~/.config/opencode/node_modules
    readonly: true

This only works when none of the packages contains native code, because the files are built for your machine, not for the Linux VM. Prefer Install the dependencies in the VM.


Docs built from release v0.7.0