Sign inSign up

simonferquel/hermes-agent-mcp-gateway-kit:latest

Manifest digest

sha256:b5acd669e72d520dfdfb2ba9e23a09e1553bc42d34e6719c063c56cf1f0ad2cb

Last pushed

28 days by simonferquel

Type

Sandbox Kit

Manifest digest

sha256:b5acd669e72d520dfdfb2ba9e23a09e1553bc42d34e6719c063c56cf1f0ad2cb

yaml
schemaVersion: "2"
kind: sandbox
name: hermes-agent-mcp
displayName: Hermes Agent (MCP Gateway)
description: 'The self-improving AI agent by Nous Research — creates skills from experience and runs anywhere. A personal-preferences kit derived from docker/sbx-kits-contrib''s hermes-agent: bakes in direct Anthropic (claude-opus-5) as the default model/provider on every start, and self-registers the sandbox''s hosted MCP gateway (when one is reserved) into Hermes''s own ~/.hermes/config.yaml so Hermes discovers gateway-provided tools automatically with no manual `hermes mcp add` step.'
sandbox:
    image: docker.io/simonferquel/hermes-agent-mcp-base:latest
    entrypoint:
        - /home/agent/.local/bin/hermes
agentInstructions:
    filename: AGENTS.md
permissions:
    network:
        allow:
            - github.com
            - raw.githubusercontent.com
            - objects.githubusercontent.com
            - nodejs.org
            - registry.npmjs.org
            - pypi.org
            - files.pythonhosted.org
            - astral.sh
            - openrouter.ai
            - '*.openrouter.ai'
            - api.openai.com
            - api.anthropic.com
            - portal.nousresearch.com
            - duckduckgo.com
            - archive.ubuntu.com
            - security.ubuntu.com
            - ports.ubuntu.com
            - download.docker.com
credentials:
    - service: anthropic
      apiKey:
        name: ANTHROPIC_API_KEY
        proxyManaged: true
        inject:
            - domain: api.anthropic.com
              header: x-api-key
              format: '%s'
    - service: openai
      apiKey:
        name: OPENAI_API_KEY
        proxyManaged: true
        inject:
            - domain: api.openai.com
              header: Authorization
              format: Bearer %s
    - service: openrouter
      apiKey:
        name: OPENROUTER_API_KEY
        proxyManaged: true
        inject:
            - domain: openrouter.ai
              header: Authorization
              format: Bearer %s
environment:
    variables:
        HERMES_HOME: /home/agent/.hermes
        REQUESTS_CA_BUNDLE: /etc/ssl/certs/ca-certificates.crt
        SSL_CERT_FILE: /etc/ssl/certs/ca-certificates.crt
        UV_NATIVE_TLS: "true"
        UV_SYSTEM_CERTS: "true"
setup:
    startup:
        - command:
            - sh
            - -c
            - |
              set -e
              mkdir -p /usr/local/share/ca-certificates
              if [ -f /etc/ssl/certs/credentials-proxy-ca.crt ]; then
                cp /etc/ssl/certs/credentials-proxy-ca.crt \
                   /usr/local/share/ca-certificates/credentials-proxy-ca.crt
                update-ca-certificates
              elif [ -n "$PROXY_CA_CERT_B64" ]; then
                printf '%s' "$PROXY_CA_CERT_B64" | base64 -d \
                  > /usr/local/share/ca-certificates/credentials-proxy-ca.crt
                update-ca-certificates
              fi
          user: "0"
          description: Merge the sandbox proxy CA into the system CA bundle so Python/httpx TLS verification succeeds through the credential-injecting MITM proxy
        - command:
            - sh
            - -c
            - |
              set -e

              HERMES_LAUNCHER="$(command -v hermes 2>/dev/null || true)"
              [ -n "$HERMES_LAUNCHER" ] || HERMES_LAUNCHER="$HOME/.local/bin/hermes"
              # Defensive only: install now runs synchronously before startup,
              # so the launcher should always exist by this point.
              [ -x "$HERMES_LAUNCHER" ] || exit 0

              # $HERMES_LAUNCHER is the bash shim hermes-agent's own installer
              # generates: `exec "<venv>/bin/python" "<install_dir>/hermes" "$@"`.
              # Pull out that interpreter so this reuses Hermes's own Python +
              # ruamel.yaml rather than depending on a system python3.
              PYTHON_BIN="$(awk -F'"' '/^exec /{print $2; exit}' "$HERMES_LAUNCHER" 2>/dev/null || true)"
              [ -n "$PYTHON_BIN" ] && [ -x "$PYTHON_BIN" ] || exit 0
              "$PYTHON_BIN" -c "import ruamel.yaml" >/dev/null 2>&1 || exit 0

              CONFIG_PATH="${HERMES_HOME:-$HOME/.hermes}/config.yaml"
              GATEWAY_URL="$MCP_GATEWAY_URL" GATEWAY_TOKEN_NAME="${MCP_SENTINEL_TOKEN_NAME:-mcp-gateway}" \
              MODEL_PROVIDER="anthropic" MODEL_DEFAULT="claude-opus-5" \
              CONFIG_PATH="$CONFIG_PATH" "$PYTHON_BIN" <<'PYEOF'
              import os

              from ruamel.yaml import YAML

              yaml = YAML()
              yaml.preserve_quotes = True
              yaml.indent(mapping=2, sequence=4, offset=2)
              yaml.width = 4096

              config_path = os.environ["CONFIG_PATH"]
              gateway_url = os.environ.get("GATEWAY_URL", "")
              token_name = os.environ.get("GATEWAY_TOKEN_NAME", "mcp-gateway")
              model_provider = os.environ["MODEL_PROVIDER"]
              model_default = os.environ["MODEL_DEFAULT"]

              config = {}
              if os.path.exists(config_path):
                  with open(config_path, "r") as f:
                      loaded = yaml.load(f)
                  if isinstance(loaded, dict):
                      config = loaded

              # Baked-in favourite: always set provider/model so the kit's
              # preferred inference endpoint takes effect on every start.
              model = config.get("model")
              if not isinstance(model, dict):
                  model = {}
                  config["model"] = model
              model["provider"] = model_provider
              model["default"] = model_default

              # Gateway registration — only when a gateway is reserved.
              if gateway_url:
                  servers = config.get("mcp_servers")
                  if not isinstance(servers, dict):
                      servers = {}
                      config["mcp_servers"] = servers
                  servers["mcp-gateway"] = {
                      "url": gateway_url,
                      "headers": {"Authorization": f"Bearer {token_name}"},
                  }

              os.makedirs(os.path.dirname(config_path), exist_ok=True)
              tmp_path = config_path + ".tmp"
              with open(tmp_path, "w") as f:
                  yaml.dump(config, f)
              os.replace(tmp_path, config_path)
              PYEOF
          user: "1000"
          description: Bake in direct Anthropic (claude-opus-5) as the default model/provider, and register the MCP gateway when one is reserved, into ~/.hermes/config.yaml