Skip to main content
Glama

deploy_app

Deploys an app workspace written in this conversation and ships edits made with write_app_files; follow with wait_for_service because deployment is asynchronous.

Instructions

Use when: you used write_app_files. Asynchronous — follow with wait_for_service.

    Distinct from ``deploy`` (which uploads a folder from the machine the MCP
    server runs on) and from ``deploy_git`` (which clones a repository): this
    one deploys the workspace of the same name, so it is the deploy verb for
    an app you wrote in this conversation. Call it again after each
    ``write_app_files`` to ship an edit — ``redeploy_service`` refuses
    workspace apps by design and points back here.

    If the workspace'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``. A dry run returns the build/config plan diff with env key
    names only, values never, and mints no idempotency key.

    ``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``. Use ``[deploy.build_settings]``
    for build overrides, or a Dockerfile for custom builds.

    ``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. ``env`` values are literal and
    never resolve ``${secrets.*}``: a secret reaches the app through
    ``set_secret`` / ``nerdit secrets set`` (injected at launch, overriding
    an ``env`` key of the same name).
    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 carries 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.

    ``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. Commands run inside the build container.
    ``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.
nameYesName of an existing workspace (also the app name it deploys under). No workspace by that name is ``404 workspace.not_found``.
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.
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.9/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 behavior, dry-run side effects, release failure semantics, env/secret handling, response fields, sandbox constraints, and port contract. No contradiction with annotations is possible because none are provided.

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?

The description is front-loaded with the key usage rule and sibling distinctions, and it is organized into useful paragraphs. However, it is unusually long and dense for a tool description; nearly every detail is relevant, but the length reduces scannability.

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 complex mutation tool with 10 parameters, no annotations, and no output schema, the description is complete enough to invoke and interpret correctly. It covers usage, behavior, response shape, failure semantics, sandbox requirements, and build/deploy configuration.

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

Parameters5/5

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

Schema description coverage is 100%, so the baseline is 3, but the description adds substantial parameter meaning beyond the schema. It explains dry-run plan behavior, port and sandbox implications, build_settings reset/precedence rules, env secret overrides, and idempotency-key minting behavior.

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 description states a specific verb and resource: deploying the workspace app of the same name. It explicitly distinguishes this tool from sibling tools `deploy` and `deploy_git`, so an agent can select it correctly without opening other schemas.

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?

It gives explicit triggers and conditions: use after `write_app_files`, call again after each edit, and follow asynchronously with `wait_for_service`. It also names alternatives and exclusions, including that `redeploy_service` refuses workspace apps by design.

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