Skip to main content
Glama

apply_project

Deploys every service declared in a project's nerdit.toml; use dry-run to validate and preview changes before applying.

Instructions

Apply a project's declaration: deploy every [services.<name>] of its nerdit.toml.

    The loop: ``get_project`` (read) → ``apply_project(dry_run=true)`` →
    if ``status`` is ``waiting_for_variables``, do NOT invent or request
    the values in chat: ask the human to run ``nerdit vars set <project>
    --secret --prompt KEY`` for each name in ``missing``, then dry-run
    again → ``apply_project`` → ``wait_for_service`` once per ``label`` in
    ``services`` → ``diagnose_service`` on any that failed.

    The response lists ``services`` in declaration order, each with its
    ``label`` (the name every other tool takes: ``web`` is the project
    name, any other service ``<service>--<project>``), ``action``
    (``fresh``/``redeploy``) and, on a dry run, its ``plan`` (env key names
    only, values never); ``public_urls`` carries every advertised address.
    ``waiting_for_variables`` deploys nothing and returns names only.
    Asynchronous like every deploy: ``applied`` means accepted, not healthy.

    Every refusal lands before the first build, and a dry run creates
    nothing and mints no idempotency key. A project another token owns is
    409 ``project.owned``. There is NO rollback across services: a failure
    on one is ``project.apply_incomplete``, whose ``services`` lists the
    ones already applied and ``failed`` the one that was not — fix it and
    apply again (done services redeploy, the rest are retried). A
    ``[deploy]``-only ``nerdit.toml`` is not a declaration: use
    ``deploy_app`` / ``deploy_git`` for it (they answer 422
    ``deploy.use_apply`` on a declaration).

    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
refNoGit branch or tag to clone; only with ``repo_url``.
dry_runNotrue = validate and return the plan (what would change) with zero writes; nothing is built, deployed or audited as a change.
projectYesImmutable prj_ ID or name of the project to apply. The resolved name must equal ``[project].name`` in ``nerdit.toml``. An unknown name may be created; an unknown or retired ID is refused.
repo_urlNohttps Git URL whose root ``nerdit.toml`` is the declaration. Omit to apply the project's workspace (what ``write_project_files`` wrote).
token_refNoPrivate-repo credential as a REFERENCE, never a token: ``${vars.KEY}``, ``${secrets.shared.KEY}`` or ``${github.installation}``; only with ``repo_url``.
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 burden and does so: asynchronous semantics (``applied`` means accepted, not healthy), refusals before first build, dry run creating nothing and minting no idempotency key, 409 ``project.owned``, and no rollback across services via ``project.apply_incomplete``. It also documents sandbox defaults, the remediation code ``image_needs_privileges``, and the port/health-probe contract.

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?

Front-loaded: purpose first, then the operating loop, then response shape, then failure semantics. It is long, and the SANDBOX/PORT CONTRACT block is verbose, but nearly every sentence encodes a concrete failure mode or default an agent would otherwise get wrong; trimming would risk losing actionable detail.

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?

No output schema exists, so the description compensates by naming the response fields (``services``, ``label``, ``action``, ``plan``, ``public_urls``, ``missing``, ``failed``) and their semantics. For a complex, multi-stage deploy tool it covers prerequisites, ordering, identity conflicts, partial-failure recovery, and environment defaults.

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 baseline is 3, but the description adds meaning the schema does not: ``dry_run`` creates nothing and mints no idempotency key, and the PORT CONTRACT clarifies how the ``port`` argument interacts with ``nerdit.toml`` ``[deploy].port`` and the default 8000. It does not re-explain ``repo_url``/``token_ref``/``ref`` beyond the schema, which is appropriate.

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?

States a specific verb and resource: deploy every ``[services.<name>]`` of a project's ``nerdit.toml``. It explicitly contrasts itself with ``deploy_app``/``deploy_git`` for ``[deploy]``-only manifests, so an agent can tell it apart from the sibling deploy tools without inspecting 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?

Gives an explicit ordered loop (get_project → dry_run → vars set → apply → wait_for_service → diagnose_service) and the branch condition ``waiting_for_variables``. It names the alternatives for manifests that are not declarations, and explicitly forbids inventing secret values in chat.

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