{"record":{"id":"49059761e4353fa8","repo":"nanocoai/nanoclaw","slug":"type-stdio-requires-command","errorCode":null,"errorMessage":"type \"stdio\" requires command","messagePattern":"type \"stdio\" requires command","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/container-config.ts","lineNumber":138,"sourceCode":"/**\n * Parse one CLI or approval payload into the persisted MCP config shape.\n * Duplicated in container/agent-runner/src/mcp-tools/self-mod.ts\n * (parseMcpServerInput) — no shared modules across the host/container\n * boundary; keep the two in sync.\n */\nexport function parseMcpServerConfig(input: Record<string, unknown>): McpServerConfig {\n  const command = typeof input.command === 'string' && input.command.trim() ? input.command : undefined;\n  const url = typeof input.url === 'string' && input.url.trim() ? input.url.trim() : undefined;\n\n  // A declared transport is honored; absence keeps the legacy CLI inference\n  // (url → http, command → stdio). \"streamable-http\" is the Agent Plugins\n  // spelling of the internal \"http\".\n  const type = input.type === 'streamable-http' ? 'http' : input.type;\n  if (type === 'sse') throw new Error('unsupported transport \"sse\"');\n  if (type !== undefined && type !== 'stdio' && type !== 'http') {\n    throw new Error('type must be \"stdio\", \"http\", or \"streamable-http\"');\n  }\n  if (type === 'stdio' && !command) throw new Error('type \"stdio\" requires command');\n  if (type === 'http' && !url) throw new Error('type \"http\" requires url');\n\n  const instructions = input.instructions;\n  if (instructions !== undefined && typeof instructions !== 'string') {\n    throw new Error('MCP instructions must be a string');\n  }\n\n  if (url !== undefined) {\n    if (command !== undefined) throw new Error('Provide exactly one of command or url');\n    if (input.args !== undefined || input.env !== undefined || input.cwd !== undefined) {\n      throw new Error('args, env, and cwd are only valid with command');\n    }\n    let parsed: URL;\n    try {\n      parsed = new URL(url);\n    } catch (err) {\n      throw new Error('url must be a valid HTTP(S) URL', { cause: err });\n    }","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/nanocoai/nanoclaw/blob/294ef2aee85218b23ad30eda9dfe10e590b54a8c/src/container-config.ts#L120-L156","documentation":"Thrown by parseMcpServerConfig when an MCP server entry declares type \"stdio\" but omits the required `command` field. A stdio MCP server is launched by executing a local binary, so the command is mandatory; the config is rejected before it can reach the container spec.","triggerScenarios":"Calling ncl groups config add-mcp-server (or the add_mcp_server self-mod MCP tool) with --type stdio and only a url, or with no command/url at all; also any programmatic call to parseMcpServerConfig with {type:'stdio'} lacking command.","commonSituations":"Copy-pasting an HTTP server config and changing type to stdio without replacing url with command; assuming a default command exists; omitting command because the server 'name' looks executable.","solutions":["Add a `command` field naming the executable, e.g. {\"type\":\"stdio\",\"command\":\"npx\",\"args\":[\"-y\",\"some-mcp-server\"]}","If you meant a remote server, use type \"http\"/\"streamable-http\" with a url instead","Verify the JSON you passed with ncl groups config get to see the parsed entry"],"exampleFix":"// before\n{\"type\":\"stdio\",\"args\":[\"-y\",\"server\"]}\n// after\n{\"type\":\"stdio\",\"command\":\"npx\",\"args\":[\"-y\",\"server\"]}","handlingStrategy":"validation","validationCode":"const srv = input; // before parseMcpServerConfig\nif (srv.type === 'stdio' && !srv.command) throw new UserError('stdio MCP entry needs command');","typeGuard":"function isValidStdioEntry(e: any): e is {type:'stdio'; command:string} {\n  return e?.type === 'stdio' && typeof e.command === 'string' && e.command.length > 0;\n}","tryCatchPattern":"catch (err) { if (err instanceof Error && err.message.includes('requires command')) showFieldError('command', err); else throw err; }","preventionTips":["Build MCP entries in a typed object (McpServerConfig) instead of raw JSON","Validate entries with ncl groups config get after every add-mcp-server"],"tags":["mcp","config-validation","container-config"],"backgroundTag":"required-field-missing","analyzedSha":"294ef2aee85218b23ad30eda9dfe10e590b54a8c","analyzedAt":"2026-08-28T13:59:10.357Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}