nanocoai/nanoclaw · error · Error
type must be "stdio", "http", or "streamable-http"
Error message
type must be "stdio", "http", or "streamable-http"
What it means
parseMcpServerConfig requires a declared transport type to be one of stdio, http, or streamable-http; anything else (e.g. 'websocket', 'ws', typo'd 'http2') throws. Absence of type is allowed (inferred from url/command presence).
Source
Thrown at src/container-config.ts:136
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
// spelling of the internal "http".
const type = input.type === 'streamable-http' ? 'http' : input.type;
if (type === 'sse') throw new Error('unsupported transport "sse"');
if (type !== undefined && type !== 'stdio' && type !== 'http') {
throw new Error('type must be "stdio", "http", or "streamable-http"');
}
if (type === 'stdio' && !command) throw new Error('type "stdio" requires command');
if (type === 'http' && !url) throw new Error('type "http" requires url');
const instructions = input.instructions;
if (instructions !== undefined && typeof instructions !== 'string') {
throw new Error('MCP instructions must be a string');
}
if (url !== undefined) {
if (command !== undefined) throw new Error('Provide exactly one of command or url');
if (input.args !== undefined || input.env !== undefined || input.cwd !== undefined) {
throw new Error('args, env, and cwd are only valid with command');
}
let parsed: URL;
try {
parsed = new URL(url);
} catch (err) {View on GitHub (pinned to 294ef2aee8)
Solutions
- Set type to "stdio" (with command) or "http"/"streamable-http" (with url)
- Or omit type entirely and provide either url or command for inference
Example fix
// before
{"type": "websocket", "url": "https://mcp.example.com"}
// after
{"type": "http", "url": "https://mcp.example.com/mcp"} Defensive patterns
Strategy: type-guard
Validate before calling
const SUPPORTED = new Set(['stdio','http','streamable-http']);
if (entry.type !== undefined && !SUPPORTED.has(entry.type)) throw new Error('unsupported type'); Type guard
function isMcpTransport(t: unknown): t is 'stdio' | 'http' | 'streamable-http' {
return t === undefined || t === 'stdio' || t === 'http' || t === 'streamable-http';
} Prevention
- Omit type and rely on url/command inference when unsure
- Copy transport spellings exactly from `ncl groups config add-mcp-server` docs
When it happens
Trigger: An MCP server entry with "type": "websocket" or any string other than stdio|http|streamable-http in container config or add-mcp-server input.
Common situations: Speculative transport values copied from other tooling, or typos like 'strdio'.
Related errors
- server name must be 1-64 characters of letters, digits, "_"
- unsupported transport "sse"
- Dropping invalid stored MCP server
- Nothing to update — provide at least one of: --provider, --m
- --name is required
AI-assisted analysis of nanocoai/nanoclaw@294ef2aee8 (2026-08-28).
Data as JSON: /api/errors/c9f55d57bbefd4d3.
Report an issue: GitHub.