nanocoai/nanoclaw · error · Error

server name must be 1-64 characters of letters, digits, "_"

Error message

server name must be 1-64 characters of letters, digits, "_" or "-"

What it means

validateMcpServerName enforces MCP server names of 1-64 chars from [A-Za-z0-9_-] and explicitly rejects "__proto__" (which would set the record's prototype instead of an own key). Called on every intake path: sanitizeStoredMcpServers, add-mcp-server, and config reads.

Source

Thrown at src/container-config.ts:113

 * container/agent-runner/src/mcp-tools/self-mod.ts; keep the two in sync.
 */
const MCP_SERVER_NAME_RE = /^[A-Za-z0-9_-]{1,64}$/;
const ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;

/** The owning plugin's name when a stored MCP server entry was stamped from a plugin. */
export function mcpServerPluginOwner(entry: unknown): string | undefined {
  if (typeof entry !== 'object' || entry === null) return undefined;
  const plugin = (entry as Record<string, unknown>).plugin;
  return typeof plugin === 'string' && plugin !== '' ? plugin : undefined;
}

/** Throws unless `name` is a safe MCP server name (1-64 chars of [A-Za-z0-9_-]). */
export function validateMcpServerName(name: string): void {
  // "__proto__" passes the regex but assigning servers["__proto__"] sets the
  // record's prototype instead of an own key — the server would be silently
  // dropped (or worse) on every intake path, so reject it by name.
  if (!MCP_SERVER_NAME_RE.test(name) || name === '__proto__') {
    throw new Error('server name must be 1-64 characters of letters, digits, "_" or "-"');
  }
}

// The Agent Plugins fixed cwd shapes: ./p, ${PLUGIN_ROOT}[/p], ${PLUGIN_DATA}[/p].
const CWD_FORM_RE = /^(?:\.\/|\$\{PLUGIN_ROOT\}(?:\/|$)|\$\{PLUGIN_DATA\}(?:\/|$))/;

/**
 * Parse one CLI or approval payload into the persisted MCP config shape.
 * Duplicated in container/agent-runner/src/mcp-tools/self-mod.ts
 * (parseMcpServerInput) — no shared modules across the host/container
 * boundary; keep the two in sync.
 */
export function parseMcpServerConfig(input: Record<string, unknown>): McpServerConfig {
  const command = typeof input.command === 'string' && input.command.trim() ? input.command : undefined;
  const url = typeof input.url === 'string' && input.url.trim() ? input.url.trim() : undefined;

  // A declared transport is honored; absence keeps the legacy CLI inference
  // (url → http, command → stdio). "streamable-http" is the Agent Plugins

View on GitHub (pinned to 294ef2aee8)

Solutions

  1. Rename the server to letters/digits/_/- only, max 64 chars (e.g. my-server)
  2. Never use __proto__ as a name
  3. Fix the stored container_configs row if a bad name got into the DB via ncl groups config update

Example fix

// before
{"mcpServers": {"my server": {"command": "npx", "args": ["-y", "x"]}}}
// after
{"mcpServers": {"my-server": {"command": "npx", "args": ["-y", "x"]}}}
Defensive patterns

Strategy: validation

Validate before calling

if (!/^[A-Za-z0-9_-]{1,64}$/.test(name) || name === '__proto__') throw new Error('bad MCP server name');

Type guard

function isValidMcpServerName(n: string): boolean {
  return /^[A-Za-z0-9_-]{1,64}$/.test(n) && n !== '__proto__';
}

Prevention

When it happens

Trigger: `ncl groups config add-mcp-server --name "my server!"` or a container config containing a server named with spaces/unicode/dots, or the literal name __proto__.

Common situations: Using a human-readable server name with spaces, or importing an MCP config from another tool whose names use other characters.

Related errors


AI-assisted analysis of nanocoai/nanoclaw@294ef2aee8 (2026-08-28). Data as JSON: /api/errors/c53ecbb5b2862b5f. Report an issue: GitHub.