sha256:b5acd669e72d520dfdfb2ba9e23a09e1553bc42d34e6719c063c56cf1f0ad2cb
Last pushed
28 days by simonferquel
Type
Sandbox Kit
Manifest digest
sha256:b5acd669e72d520dfdfb2ba9e23a09e1553bc42d34e6719c063c56cf1f0ad2cb
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