Skip to main content
Glama

deploy

Builds and runs a local app folder as a running service with a URL, and rolls that service back to its previous image when a release needs undoing.

Instructions

Use when: the source is a local folder. Asynchronous — follow with wait_for_service.

    Deploys that folder (build + run + URL), or rolls back with
    ``rollback=True``. The response carries ``next_step`` — a machine-shaped
    ``{tool, args, why}`` naming the very call to make next — because a 201
    here means "the build was accepted", never "the app is up".

    ``path`` is read by the process serving this tool, so it works only on
    the local ``nerdit mcp`` process; on a remote/HTTP connection it is
    refused with ``mcp.local_path_unavailable``. Use ``write_app_files`` +
    ``deploy_app`` there, or ``deploy_git`` / ``deploy_template``.
    ``rollback=True`` needs no path and works over either transport.

    If the app's ``nerdit.toml`` declares ``[ai.*]`` bindings, each is
    injected at launch as ``NERDIT_AI_<NAME>_URL/_KEY/_MODEL``, and the
    ``default`` binding also sets ``OPENAI_BASE_URL/OPENAI_API_KEY/
    OPENAI_MODEL`` — point the app's OpenAI SDK at these; no code change
    needed. ``dry_run=True``
    returns the build/config plan diff (env key names only, values never).

    ``nerdit.toml`` ``[deploy]`` keys — anything else is IGNORED and
    reported back in the response ``hints``: ``name``, ``port``, ``gpus``,
    ``start``, ``health``, ``health_type`` (``http``|``tcp``),
    ``memory_limit``, ``cpu_limit``, ``volumes``, ``release``, ``cutover``,
    ``auto_deploy``, ``edge_auth``, ``build_settings``. Generated Node builds run
    ``scripts.build`` automatically;
    override it through ``[deploy.build_settings]`` or use a Dockerfile.

    ``release`` runs once inside the new image before traffic moves; if it
    fails the deploy settles ``failed`` and the image reverts, never the data —
    whatever the migration already committed stays, so write idempotent
    migrations. ``cutover`` null = zero-downtime swap
    when eligible, false = same-port swap.
    On a successful non-dry-run deploy the
    response carries ``summary`` (app/status/version/public_url), ``hints``
    (ordered one-liners, never empty) and ``next_step`` (the structured
    follow-up call); a ``dry_run`` plan and a ``rollback`` response carry
    none of the three — read a plan's advisories from its ``warnings``. On a
    node in ``path`` proxy mode the app is served at ``/<name>/``, so a
    frontend must be built with that base
    path; in ``subdomain`` mode it is served at the root of its own
    hostname and needs none — read ``capabilities.proxy.mode``.

    ``build_settings`` overrides
    preset/install/build/start/node_version/package_manager/subdir/public_env.
    ``preset`` selects node/nextjs/python/dockerfile; null resets to
    repository/default detection.
    An existing Dockerfile retains precedence.
    Omit it to preserve saved overrides; a null field resets to repository/default,
    and ``build: false`` skips compilation.
    ``public_env`` passes public build-time variables (shown in clear in a dry
    run; secret references are refused). Secret mounts are unsupported;
    runtime env is unchanged.

    SANDBOX: by DEFAULT containers run with all Linux capabilities dropped
    (``cap_drop=["ALL"]``) and no-new-privileges — both are operator settings
    (``[containers].drop_all_caps`` / ``[containers].no_new_privileges``), so
    read the live profile in ``capabilities.sandbox`` rather than assuming: an
    operator may have relaxed it. Under that default, an image whose entrypoint
    does root work — ``chown``/``chmod`` of its own runtime dirs, dropping
    privileges via ``setuid``/``setgid``, writing ``/var/run`` — dies before
    your app ever starts. Stock ``nginx`` is the classic trap. Use a non-root
    image and a non-root ``USER`` that owns the files it writes: for a static
    site, ``nginxinc/nginx-unprivileged`` (listens on **8080**, so set
    ``port=8080``) or any tiny static server. A crash from this reports
    remediation code ``image_needs_privileges`` in ``diagnose_service``. PORT
    CONTRACT: the container MUST listen on the declared ``port`` (the tool
    argument, else ``nerdit.toml`` ``[deploy].port``, else **8000**) — that is
    the only port the daemon publishes and health-checks, so listening anywhere
    else fails the health probe with a healthy-looking container. A port below
    1024 is NOT the reason a root entrypoint is needed: every container runs on
    the docker bridge in its own network namespace (never host networking),
    where Docker sets ``net.ipv4.ip_unprivileged_port_start=0``, so binding 80
    needs no capability and no root — the chown/setuid work does.
    

Input Schema

NameRequiredDescriptionDefault
envNoRuntime env for the container: values are literal strings, never resolved (a ``${secrets.*}`` reference is passed through as text); a null value deletes that key on redeploy. Secrets go through ``set_secret``, which overrides an ``env`` key of the same name at launch.
gpusNoGPUs to reserve for the container (0 = none). Omit to use ``[deploy].gpus``, else the count already recorded on a redeploy, else 0.
nameNoApp name (lowercase DNS label); an existing name redeploys it in place. Omit to use the folder's ``nerdit.toml`` ``[deploy].name``, else the folder's own name. Required with ``rollback=True``.
pathNoPath to the app folder ON THE DAEMON HOST (``~`` expanded); its contents are zipped and uploaded. Required unless ``rollback=True``. Usable ONLY from the local ``nerdit mcp`` process: over a remote/HTTP connection to a daemon it is refused with ``mcp.local_path_unavailable``, since it would read that host's disk and not yours — send the source with ``write_app_files`` + ``deploy_app`` instead.
portNoPort the container listens on: the only port published and health-checked. Omit to use ``nerdit.toml`` ``[deploy].port``, else the port already recorded on a redeploy, else 8000.
startNoStart command run inside the built image (shell syntax). Omit to use ``[deploy].start`` or the buildpack default.
healthNoHTTP health path probed on ``port`` (e.g. ``/health``); the app is ``healthy`` once it answers 2xx. Omit to keep ``[deploy].health`` (or the existing app's probe on redeploy).
vendorNoGPU vendor to reserve when ``gpus`` > 0 (``nvidia`` or ``amd``); omit to let the daemon choose on a fresh deploy — a redeploy keeps the vendor already recorded.
dry_runNotrue = validate and return the plan (what would change) with zero writes; nothing is built, deployed or audited as a change.
rollbackNotrue = re-point the service at its previous image instead of building: only ``name`` and ``idempotency_key`` are read, no folder is zipped and every other argument is ignored. Refused together with ``dry_run``.
build_settingsNoBuild overrides, same keys as ``[deploy.build_settings]`` in ``nerdit.toml``: ``preset`` (node/nextjs/python/dockerfile), ``install``, ``build`` (``false`` skips compilation), ``start``, ``node_version``, ``package_manager``, ``subdir``, ``public_env`` (build-time variables baked into the bundle, never secrets). Commands run inside the build container.
idempotency_keyNoCaller-chosen key: a retry with the same key replays the first result instead of acting twice. Omit and a fresh key is minted per call, so each call acts.

Schema Changelog

Changes observed during successful MCP inspections.

  1. First observedv0.1.0

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden and does so extensively: async acceptance vs. app readiness, local-path transport restrictions, [deploy] key handling and ignored keys, release/cutover failure semantics, dry_run no-write behavior, sandbox capability defaults, and port publishing/health-check rules. These are substantive behavioral disclosures, not restatements of schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

It is front-loaded with the usage condition and async contract, and structured into clear operational concerns. It is unusually long and packs deployment manual detail into the description, but nearly all of it is actionable for a complex deploy tool, so it avoids fluff even if it is not terse.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 12-parameter deploy tool with no annotations and no output schema, the description is complete: it explains what is returned on success, what next_step is, what dry_run and rollback responses omit, and how to follow up. Nothing critical to correct invocation appears missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already defines each parameter. The description still adds cross-parameter meaning: rollback reads only name and idempotency_key, path is local-process-only, build_settings nulls reset to repository/default, and public_env refuses secrets. This goes beyond the schema, though some of it duplicates schema text.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening states a specific verb and resource: deploying a local folder (build + run + URL), with rollback as a secondary mode. It explicitly distinguishes this tool from deploy_git, deploy_template, deploy_app, and the write_app_files path, so an agent can route correctly without opening another schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives an explicit selection rule ('Use when: the source is a local folder'), states the asynchronous follow-up (wait_for_service), and names alternatives for remote/HTTP connections (write_app_files + deploy_app, deploy_git, deploy_template). It also clarifies rollback usage and dry_run boundaries.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.