{"record":{"id":"9c2f82dfaec06b9a","repo":"nanocoai/nanoclaw","slug":"stripping-cwd-from-stored-mcp-server-without-plugi","errorCode":null,"errorMessage":"Stripping cwd from stored MCP server without plugin provenance","messagePattern":"Stripping cwd from stored MCP server without plugin provenance","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/container-config.ts","lineNumber":301,"sourceCode":"    }\n    try {\n      validateMcpServerName(name);\n      const server = parseMcpServerConfig(entry as Record<string, unknown>);\n      const pluginRoot = (entry as Record<string, unknown>).pluginRoot;\n      if (\n        server.type !== 'http' &&\n        typeof pluginRoot === 'string' &&\n        pluginRoot.startsWith(`${CONTAINER_PLUGINS_DIR}/`)\n      ) {\n        server.pluginRoot = pluginRoot;\n      }\n      if (server.type !== 'http' && server.cwd && !server.pluginRoot) {\n        // cwd resolves against a plugin root; without provenance nothing can\n        // resolve it. This strip is the ONLY layer (the runtime passes\n        // provenance-less servers through untouched), and the breadcrumb\n        // lands in host logs instead of nowhere.\n        delete server.cwd;\n        log.warn('Stripping cwd from stored MCP server without plugin provenance', { group: groupName, server: name });\n      }\n      servers[name] = server;\n      // eslint-disable-next-line no-catch-all/no-catch-all -- validation failures are data errors, not bugs\n    } catch (err) {\n      log.warn('Dropping invalid stored MCP server', {\n        group: groupName,\n        server: name,\n        reason: err instanceof Error ? err.message : String(err),\n      });\n    }\n  }\n  return servers;\n}\n\n/**\n * runtime_tier is an isolation control: dropping an unknown stored value would\n * silently compose the group at the default tier — a weaker boundary than the\n * one the value asked for. Fail closed instead: the group refuses to compose","sourceCodeStart":283,"sourceCodeEnd":319,"githubUrl":"https://github.com/nanocoai/nanoclaw/blob/294ef2aee85218b23ad30eda9dfe10e590b54a8c/src/container-config.ts#L283-L319","documentation":"A stored MCP server entry in the container_configs DB has a `cwd` set but no `pluginRoot` provenance. Since cwd is meant to resolve relative to a plugin root, without provenance nothing can resolve it, so the host strips it and warns. The runtime would pass the provenance-less server through untouched, making this sanitize layer the only protection.","triggerScenarios":"An MCP server config written directly to the container_configs table (e.g. via `ncl groups config add-mcp-server` or a manual DB edit / older migration) that includes `cwd` but omits `pluginRoot`. Only fires for non-`http` server types.","commonSituations":"Hand-crafted MCP server JSON from docs or an older schema version that predates plugin provenance; copying an MCP server config from a plugin-installed setup into a plain stored config.","solutions":["Remove `cwd` from the stored server config, or run the server command with an absolute path","If the server came from a plugin, reinstall it via the plugin flow so `pluginRoot` is recorded","Update the stored row via `ncl groups config update` / `add-mcp-server` with absolute paths"],"exampleFix":"// before\n{\"command\":\"node\",\"args\":[\"server.js\"],\"cwd\":\"./plugin\"}\n// after\n{\"command\":\"node\",\"args\":[\"/abs/path/to/plugin/server.js\"]}","handlingStrategy":"validation","validationCode":"const server = JSON.parse(row.mcp_servers)[name];\nif (server.type !== 'http' && server.cwd && !server.pluginRoot) {\n  delete server.cwd; // normalize before storing/using\n}","typeGuard":"function hasResolvedCwd(s: McpServerConfig): boolean {\n  return s.type === 'http' || !s.cwd || typeof s.pluginRoot === 'string';\n}","tryCatchPattern":null,"preventionTips":["Always store MCP servers with absolute command paths","Install plugin-provided MCP servers via the plugin flow so pluginRoot is recorded","Validate stored config with `ncl groups config get` after manual edits"],"tags":["mcp","container-config","plugin-provenance"],"backgroundTag":"config-field-sanitized","analyzedSha":"294ef2aee85218b23ad30eda9dfe10e590b54a8c","analyzedAt":"2026-08-28T13:59:10.357Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}