{"record":{"id":"0811335995545cc5","repo":"multica-ai/multica","slug":"missing-required-field-type-must-be-local-or","errorCode":null,"errorMessage":"missing required field `type` (must be \"local\" or \"remote\", or use bare {\"enabled\": bool} to override an inherited server)","messagePattern":"missing required field `type` \\(must be \"local\" or \"remote\", or use bare (.+?) to override an inherited server\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/pkg/agent/opencode_mcp.go","lineNumber":260,"sourceCode":"\t\t}\n\t\tif entry.Timeout != nil && *entry.Timeout <= 0 {\n\t\t\treturn nil, wrap(fmt.Errorf(\"`timeout` must be a positive integer, got %d\", *entry.Timeout))\n\t\t}\n\t\tif len(entry.OAuth) > 0 {\n\t\t\tif err := validateOpenCodeOAuth(entry.OAuth); err != nil {\n\t\t\t\treturn nil, wrap(fmt.Errorf(\"`oauth`: %w\", err))\n\t\t\t}\n\t\t}\n\tcase \"\":\n\t\t// No `type` field. The bare `{\"enabled\": bool}` override shape\n\t\t// is OpenCode's third native variant; anything else without a\n\t\t// type is a malformed local/remote attempt. Surface a single\n\t\t// friendly \"missing type\" error instead of the strict-decode\n\t\t// \"json: unknown field\" leak — the user usually didn't realise\n\t\t// they were mis-using the override shape.\n\t\tvar entry opencodeMCPEnabledOnly\n\t\tif err := strictDecode(raw, &entry); err != nil || entry.Enabled == nil {\n\t\t\treturn nil, wrap(errors.New(\"missing required field `type` (must be \\\"local\\\" or \\\"remote\\\", or use bare {\\\"enabled\\\": bool} to override an inherited server)\"))\n\t\t}\n\tdefault:\n\t\treturn nil, wrap(fmt.Errorf(\"invalid type %q (must be \\\"local\\\" or \\\"remote\\\")\", typeStr))\n\t}\n\n\t// Validation passed; re-decode the raw bytes into map[string]any for\n\t// the output. Identical observable representation, just typed as a\n\t// generic map for the caller.\n\tvar out map[string]any\n\tif err := json.Unmarshal(raw, &out); err != nil {\n\t\treturn nil, wrap(fmt.Errorf(\"parse: %w\", err))\n\t}\n\treturn out, nil\n}\n\n// strictDecode runs a json.Decoder with DisallowUnknownFields so any\n// field outside the target struct's tags is rejected, enforcing the\n// schema's `additionalProperties: false`.","sourceCodeStart":242,"sourceCodeEnd":278,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/pkg/agent/opencode_mcp.go#L242-L278","documentation":"Returned when a mcp_config entry has no `type` field and is not the bare {\"enabled\": bool} override shape that OpenCode supports for disabling an inherited server. The validator deliberately collapses two failure modes (strict-decode failure on unknown fields, or a missing/nullable `enabled`) into one friendly message, because the user usually tried to write a local/remote server and forgot `type`.","triggerScenarios":"Writing {\"command\": [\"npx\", ...]} or {\"url\": \"https://...\"} without \"type\"; writing {\"enabled\": \"true\"} (string, fails strict decode); writing {} (empty object, `enabled` is nil).","commonSituations":"Assuming the server kind is inferred from which fields are present; copying configs from tools that don't require a discriminator; attempting the override shape but with extra fields or a non-boolean enabled.","solutions":["Add \"type\": \"local\" (with `command`) or \"type\": \"remote\" (with `url`) to the entry.","For pure enable/disable of an inherited server, use exactly {\"enabled\": true} or {\"enabled\": false} with no other keys.","Move any transport fields (command/url/env) into the typed form rather than mixing them with the override shape."],"exampleFix":"// before\n\"mcp_config\": { \"fs\": { \"command\": [\"npx\", \"-y\", \"server-fs\"] } }\n\n// after\n\"mcp_config\": { \"fs\": { \"type\": \"local\", \"command\": [\"npx\", \"-y\", \"server-fs\"] } }","handlingStrategy":"validation","validationCode":"function validateMCPEntryShape(e: Record<string, unknown>): string | null {\n  const keys = Object.keys(e);\n  if (!('type' in e)) {\n    const bare = keys.length === 1 && typeof e['enabled'] === 'boolean';\n    if (!bare) return 'entry needs `type` (local/remote) or must be exactly {\"enabled\": boolean}';\n  }\n  return null;\n}","typeGuard":"function isMCPOverrideEntry(e: Record<string, unknown>): e is { enabled: boolean } {\n  return Object.keys(e).length === 1 && typeof e['enabled'] === 'boolean';\n}","tryCatchPattern":null,"preventionTips":["Remember the three legal shapes: {\"type\":\"local\",...}, {\"type\":\"remote\",...}, and exactly {\"enabled\": bool}.","Mixing transport fields into the override shape is always an error — pick the typed form instead."],"tags":["config","mcp","validation","opencode","schema"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}