{"record":{"id":"45cc87fd6cca257d","repo":"nanocoai/nanoclaw","slug":"mcp-instructions-must-be-a-string","errorCode":null,"errorMessage":"MCP instructions must be a string","messagePattern":"MCP instructions must be a string","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/container-config.ts","lineNumber":143,"sourceCode":" */\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    }\n    const loopback = ['localhost', '127.0.0.1', '[::1]', 'host.docker.internal'].includes(parsed.hostname);\n    if (parsed.protocol !== 'https:' && !(parsed.protocol === 'http:' && loopback)) {\n      throw new Error('url must use HTTPS (plain HTTP is allowed only for localhost and host.docker.internal)');\n    }\n    if (parsed.username || parsed.password || parsed.hash) {","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/nanocoai/nanoclaw/blob/294ef2aee85218b23ad30eda9dfe10e590b54a8c/src/container-config.ts#L125-L161","documentation":"parseMcpServerConfig rejects an `instructions` field that is present but not a string. `instructions` is optional free-form guidance passed to the agent; any object/array/number value indicates malformed config JSON and is rejected.","triggerScenarios":"Passing instructions as an array of strings, an object, or a number in an MCP server entry via ncl or the add_mcp_server tool.","commonSituations":"Quoting mistakes in shell JSON (instructions ending up parsed as an object); hand-editing container_configs rows directly in SQLite with wrong JSON shapes.","solutions":["Make instructions a single string: {\"instructions\":\"Use this server for web search\"}","Omit the field entirely if you don't need it","If hand-editing the DB, validate the JSON with a linter before restarting"],"exampleFix":"// before\n{\"instructions\":[\"search\",\"fetch\"]}\n// after\n{\"instructions\":\"search and fetch pages\"}","handlingStrategy":"type-guard","validationCode":"if (srv.instructions !== undefined && typeof srv.instructions !== 'string') { srv.instructions = String(srv.instructions); }","typeGuard":"const hasStringInstructions = (e: any) => e.instructions === undefined || typeof e.instructions === 'string';","tryCatchPattern":"catch (err) { if (err.message.includes('instructions must be a string')) coerceOrStrip('instructions'); else throw err; }","preventionTips":["Keep instructions as one prose string","Run JSON payloads through a schema check (zod) at the UI edge"],"tags":["mcp","config-validation","type-mismatch"],"backgroundTag":"schema-validation-failed","analyzedSha":"294ef2aee85218b23ad30eda9dfe10e590b54a8c","analyzedAt":"2026-08-28T13:59:10.357Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}