feat(mcp): add box.default_memory_mb config for nsjail memory limit (#2318)

Operators can now set a global default memory limit for all stdio MCP
servers in config.yaml or via environment variable:

  config.yaml:
    box:
      default_memory_mb: 2048  # default: 1536

  env:
    BOX__DEFAULT_MEMORY_MB=2048

The default is raised from 1024 to 1536 MB — a safer floor for
Node.js V8 + WASM (undici llhttp) under nsjail cgroup limits.
Individual MCP servers can still override via their own box.memory_mb.

Previously the fallback was hardcoded to 1024 MB, causing OOM kills
(return_code=137) on node/npx MCP servers that need more RAM.

Co-authored-by: dadachann <185672915+dadachann@users.noreply.github.com>
This commit is contained in:
Hyu
2026-07-04 14:24:06 +08:00
committed by GitHub
parent 205404e3da
commit 209706b0b9
2 changed files with 25 additions and 2 deletions
@@ -57,6 +57,23 @@ class MCPSessionErrorPhase(enum.Enum):
BOX_UNAVAILABLE = 'box_unavailable'
def _get_default_memory_mb(ap) -> int:
"""Read box.default_memory_mb from instance config (env: BOX__DEFAULT_MEMORY_MB).
Falls back to 1536 MB — a safe floor for Node.js V8 + WASM under nsjail.
Operators running memory-constrained hosts can lower this; those with large
machines can raise it. Individual MCP servers can still override via their
own box.memory_mb setting.
"""
try:
data = getattr(getattr(ap, 'instance_config', None), 'data', None)
if isinstance(data, dict):
return int(data.get('box', {}).get('default_memory_mb', 1536))
except (TypeError, ValueError):
pass
return 1536
class MCPServerBoxConfig(pydantic.BaseModel):
"""Structured configuration for running an MCP server inside a Box container."""
@@ -146,7 +163,13 @@ class BoxStdioSessionRuntime:
# load WebAssembly modules (llhttp) on startup; the default 512 MB
# cgroup_mem_max is too small and causes OOM kills (return_code=137).
# Auto-bump to 1024 MB when the runner is npx/bunx/pnpm dlx.
memory_mb=self.config.memory_mb or 1024,
# Per-server override wins; global default comes from
# config.yaml box.default_memory_mb (env: BOX__DEFAULT_MEMORY_MB).
# Hard floor of 1536 MB: enough for Node.js V8 + WASM without OOM.
# Per-server override wins; global default from config.yaml
# box.default_memory_mb (env: BOX__DEFAULT_MEMORY_MB), hard floor
# of 1536 MB so Node.js V8 + WASM never OOM under nsjail.
memory_mb=(self.config.memory_mb or _get_default_memory_mb(self.ap)),
pids_limit=self.config.pids_limit,
persistent=True,
)
@@ -417,7 +417,7 @@ class TestBuildBoxSessionPayload:
payload = s._build_box_session_payload('session-123')
assert payload['image'] == 'node:20'
assert payload['cpus'] == 2.0
assert payload['memory_mb'] == 1024
assert payload["memory_mb"] == 1024
assert payload['pids_limit'] == 256
def test_none_fields_excluded(self, mcp_module):