All images, scripts and skills

Codex: connect to DevGrail (MCP + login)

sh1 included script

Sets up Codex inside this workspace so it can drive DevGrail itself — containers, projects, scripts, files, and terminal sessions — scoped to the DevGrail API key supplied at run time. Registers the `devgrail` streamable HTTP MCP server in `${CODEX_HOME:-~/.codex}/config.toml` using the container's `DEVGRAIL_MCP_URL`. Via the included **Copy of Codex: sign in without a browser (auth)** script it also installs a Codex login — pasted, or a secret backed by `/home/dev/.codex/auth.json` in a workspace that is already signed in. The combined form asks for both sets of parameters. An existing registration is left untouched unless **Replace existing registration** is `yes`. Configuration is backed up before changes. **Security:** the DevGrail API key is stored in cleartext as an Authorization header in Codex config. Prefer a key limited to what this workspace needs. After it runs: start `codex` and ask it to list your DevGrail containers.

What it asks for

DevGrail API keyrequired
The key Codex will send to the DevGrail MCP server. Its grants decide what the agent can see and do; prefer a narrowly scoped key.
MCP URL (override)
Leave empty on current workspace images; the URL comes from DEVGRAIL_MCP_URL. For older images, enter the full DevGrail MCP URL.
Replace existing registration
Choose 'yes' to replace an existing devgrail MCP entry after rotating the API key or changing the URL. With 'no', an existing entry is left untouched.

What it runs

The whole script, exactly as it will execute in your workspace. Nothing is hidden — read it before you run it.

#!/usr/bin/env sh
set -eu
umask 077

log() { printf '[devgrail-mcp] %s\n' "$*"; }
die() { printf '[devgrail-mcp] ERROR: %s\n' "$*" >&2; exit 1; }

MCP_NAME="devgrail"
API_KEY="${DEVGRAIL_API_KEY:-}"
MCP_URL="${DEVGRAIL_MCP_URL_OVERRIDE:-${DEVGRAIL_MCP_URL:-}}"
REPLACE="${REPLACE_EXISTING:-no}"
CODEX_DIR="${CODEX_HOME:-$HOME/.codex}"
CONFIG_FILE="$CODEX_DIR/config.toml"
BACKUP_FILE="$CONFIG_FILE.bak"
HAD_CONFIG="no"
EXISTING="no"

command -v codex >/dev/null 2>&1 \
    || die "the 'codex' CLI is not on PATH — run this in a workspace that has Codex installed"
[ -n "$API_KEY" ] || die "no DevGrail API key was supplied"
[ -n "$MCP_URL" ] || die "DEVGRAIL_MCP_URL is not set in this container — fill in the 'MCP URL (override)' parameter"

# Static TOML strings require a single-line token. DevGrail API keys use this
# conservative URL-safe character set.
case "$API_KEY" in
    *[!A-Za-z0-9._~-]*) die "the DevGrail API key contains characters that cannot be stored safely in config.toml" ;;
esac

mkdir -p "$CODEX_DIR"

if codex mcp get "$MCP_NAME" >/dev/null 2>&1; then
    EXISTING="yes"
    if [ "$REPLACE" != "yes" ]; then
        log "MCP server '$MCP_NAME' is already registered — nothing to do."
        log "To point it at a new key or URL, re-run with 'Replace existing registration' = yes."
        exit 0
    fi
fi

if [ -f "$CONFIG_FILE" ]; then
    HAD_CONFIG="yes"
    cp -p "$CONFIG_FILE" "$BACKUP_FILE"
    log "backed up existing Codex config to $BACKUP_FILE"
fi

restore_config() {
    if [ "$HAD_CONFIG" = "yes" ]; then
        cp -p "$BACKUP_FILE" "$CONFIG_FILE"
    else
        rm -f "$CONFIG_FILE"
    fi
}

if [ "$EXISTING" = "yes" ]; then
    codex mcp remove "$MCP_NAME" >/dev/null 2>&1 \
        || { restore_config; die "could not remove the existing '$MCP_NAME' entry"; }
    log "removed the existing '$MCP_NAME' entry so it can be re-added"
fi

codex mcp add "$MCP_NAME" --url "$MCP_URL" </dev/null >/dev/null \
    || { restore_config; die "codex mcp add failed"; }

HEADER_LINE="http_headers = { Authorization = \"Bearer $API_KEY\" }"
tmp="$CONFIG_FILE.devgrail.tmp"
trap 'rm -f "$tmp"' 0
if awk -v header="$HEADER_LINE" '
    BEGIN { in_target = 0; inserted = 0 }
    $0 == "[mcp_servers.devgrail]" {
        in_target = 1
        print
        next
    }
    /^\[/ {
        if (in_target && !inserted) {
            print header
            inserted = 1
        }
        in_target = 0
    }
    {
        if (in_target && $0 ~ /^[[:space:]]*http_headers[[:space:]]*=/) {
            if (!inserted) {
                print header
                inserted = 1
            }
            next
        }
        print
    }
    END {
        if (in_target && !inserted) {
            print header
            inserted = 1
        }
        if (!inserted) exit 42
    }
' "$CONFIG_FILE" > "$tmp"; then
    mv "$tmp" "$CONFIG_FILE"
    trap - 0
else
    restore_config
    die "could not add the authorization header to $CONFIG_FILE"
fi
chmod 600 "$CONFIG_FILE"

codex mcp get "$MCP_NAME" >/dev/null 2>&1 \
    || { restore_config; die "'$MCP_NAME' was added but cannot be read back — restored the previous config"; }

log "registered MCP server '$MCP_NAME' -> $MCP_URL"
log "Security note: the bearer token is stored in cleartext in $CONFIG_FILE."
log "Next: run 'codex' and ask it to list your DevGrail containers."

Included scripts

These come along automatically when you add this script.

Codex: sign in without a browser (auth)
#!/usr/bin/env sh
set -eu
umask 077

log()  { printf '[codex-setup] %s\n' "$*"; }
warn() { printf '[codex-setup] WARNING: %s\n' "$*" >&2; }
die()  { printf '[codex-setup] ERROR: %s\n' "$*" >&2; exit 1; }

AUTH_JSON="${CODEX_AUTH_JSON:-}"
OVERWRITE="${OVERWRITE_EXISTING_AUTH:-yes}"
CODEX_DIR="${CODEX_HOME:-$HOME/.codex}"
AUTH_FILE="$CODEX_DIR/auth.json"
CONFIG_FILE="$CODEX_DIR/config.toml"

[ -n "$AUTH_JSON" ] || die "no Codex auth JSON was supplied"
command -v codex >/dev/null 2>&1 \
    || die "the 'codex' CLI is not on PATH — run this in a workspace that has Codex installed"

if [ "$(id -u)" -eq 0 ]; then SUDO=""
elif command -v sudo >/dev/null 2>&1; then SUDO="sudo -n"
else SUDO=""
fi

# jq is optional: it validates the copied auth file. Never fail merely because
# jq cannot be installed; the login copy is still useful.
# This script body is piped to sh on stdin, so every installer gets </dev/null.
if ! command -v jq >/dev/null 2>&1; then
    log "jq not found; attempting install (optional)"
    export DEBIAN_FRONTEND=noninteractive
    if   command -v apt-get >/dev/null 2>&1; then $SUDO apt-get update -qq </dev/null && $SUDO apt-get install -y -qq jq </dev/null || true
    elif command -v dnf     >/dev/null 2>&1; then $SUDO dnf install -y jq </dev/null || true
    elif command -v yum     >/dev/null 2>&1; then $SUDO yum install -y jq </dev/null || true
    elif command -v apk     >/dev/null 2>&1; then $SUDO apk add --no-cache jq </dev/null || true
    elif command -v pacman  >/dev/null 2>&1; then $SUDO pacman -Sy --noconfirm jq </dev/null || true
    elif command -v zypper  >/dev/null 2>&1; then $SUDO zypper install -y jq </dev/null || true
    elif command -v brew    >/dev/null 2>&1; then brew install jq </dev/null || true
    fi
    command -v jq >/dev/null 2>&1 || warn "continuing without jq"
fi

mkdir -p "$CODEX_DIR"

if command -v jq >/dev/null 2>&1; then
    printf '%s' "$AUTH_JSON" | jq empty >/dev/null 2>&1 \
        || die "the supplied value is not valid JSON — paste the whole auth.json contents, from '{' to '}'"
fi

if [ -f "$AUTH_FILE" ] && [ "$OVERWRITE" != "yes" ]; then
    log "$AUTH_FILE already exists and overwrite is off — leaving it alone"
else
    if [ -f "$AUTH_FILE" ]; then
        cp -p "$AUTH_FILE" "$AUTH_FILE.bak"
        log "backed up previous credentials to $AUTH_FILE.bak"
    fi
    printf '%s\n' "$AUTH_JSON" > "$AUTH_FILE"
    chmod 600 "$AUTH_FILE"
    log "wrote $AUTH_FILE (mode 600)"
fi

# A copied auth.json must be selected explicitly when a credential store is
# available. Keep all unrelated Codex configuration intact.
if [ -f "$CONFIG_FILE" ]; then
    tmp="$CONFIG_FILE.devgrail.tmp"
    trap 'rm -f "$tmp"' 0
    if grep -Eq '^[[:space:]]*cli_auth_credentials_store[[:space:]]*=' "$CONFIG_FILE"; then
        sed 's/^[[:space:]]*cli_auth_credentials_store[[:space:]]*=.*$/cli_auth_credentials_store = "file"/' "$CONFIG_FILE" > "$tmp"
    else
        {
            printf '%s\n\n' 'cli_auth_credentials_store = "file"'
            cat "$CONFIG_FILE"
        } > "$tmp"
    fi
    if cmp -s "$CONFIG_FILE" "$tmp"; then
        rm -f "$tmp"
        trap - 0
        log "$CONFIG_FILE already selects file-based credential storage"
    else
        cp -p "$CONFIG_FILE" "$CONFIG_FILE.bak"
        mv "$tmp" "$CONFIG_FILE"
        trap - 0
        log "updated $CONFIG_FILE (backup: $CONFIG_FILE.bak)"
    fi
else
    printf '%s\n' 'cli_auth_credentials_store = "file"' > "$CONFIG_FILE"
    log "created $CONFIG_FILE"
fi
chmod 600 "$CONFIG_FILE"

log "done. Run 'codex' — it should start without asking you to log in."

How to get it

Open your DevGrail workspace, go to Scripts → Official scripts, and choose Download. It is copied into your own library, where you can edit it, run it, or set it as a workspace default — the copy is yours, and later changes here do not touch it.