///|
/// One environment variable passed to a stdio MCP server.
pub(all) struct McpEnvVariable {
  name : String
  value : String
  meta : ProtocolNullable[Json]
} derive(Eq, Debug)

///|
/// One HTTP header passed to an HTTP or SSE MCP server.
pub(all) struct McpHttpHeader {
  name : String
  value : String
  meta : ProtocolNullable[Json]
} derive(Eq, Debug)

///|
/// Stdio MCP transport configuration.
pub(all) struct McpServerStdio {
  name : String
  command : String
  args : Array[String]
  env : Array[McpEnvVariable]
  meta : ProtocolNullable[Json]
} derive(Eq, Debug)

///|
/// HTTP MCP transport configuration.
pub(all) struct McpServerHttp {
  name : String
  url : String
  headers : Array[McpHttpHeader]
  meta : ProtocolNullable[Json]
} derive(Eq, Debug)

///|
/// Server-sent-events MCP transport configuration.
pub(all) struct McpServerSse {
  name : String
  url : String
  headers : Array[McpHttpHeader]
  meta : ProtocolNullable[Json]
} derive(Eq, Debug)

///|
/// MCP server transport union.  The wire discriminator is `type`.
pub(all) enum McpServer {
  Stdio(McpServerStdio)
  Http(McpServerHttp)
  Sse(McpServerSse)
} derive(Eq, Debug)

///|
fn mcp_decode_string(
  value : Json,
  path~ : String,
) -> String raise ProtocolDecodeError {
  protocol_decode_string(value, path~)
}

///|
fn mcp_decode_string_array(
  value : Json,
  path~ : String,
) -> Array[String] raise ProtocolDecodeError {
  let values = protocol_require_array(value, path~)
  let result : Array[String] = []
  for index, value in values {
    result.push(
      mcp_decode_string(value, path=path + "[" + index.to_string() + "]"),
    )
  }
  result
}

///|
fn mcp_nullable_meta(
  fields : Map[String, Json],
  path~ : String,
) -> ProtocolNullable[Json] raise ProtocolDecodeError {
  protocol_meta(fields, path~)
}

///|
fn mcp_decode_env_variable(
  value : Json,
  path~ : String,
) -> McpEnvVariable raise ProtocolDecodeError {
  let fields = protocol_require_object(value, path~)
  protocol_reject_unknown(fields, ["_meta", "name", "value"], prefix=path)
  let name = protocol_required_string(fields, "name", path=path + ".name")
  let variable_value = protocol_required_string(
    fields,
    "value",
    path=path + ".value",
  )
  let meta = mcp_nullable_meta(fields, path=path + "._meta")
  { name, value: variable_value, meta }
}

///|
fn mcp_encode_env_variable(
  value : McpEnvVariable,
) -> Json raise ProtocolDecodeError {
  let fields : Map[String, Json] = Map([])
  fields["name"] = Json::string(value.name)
  fields["value"] = Json::string(value.value)
  protocol_put_meta(fields, value.meta)
  Json::object(fields)
}

///|
fn mcp_decode_env_array(
  value : Json,
  path~ : String,
) -> Array[McpEnvVariable] raise ProtocolDecodeError {
  let values = protocol_require_array(value, path~)
  let result : Array[McpEnvVariable] = []
  for index, value in values {
    result.push(
      mcp_decode_env_variable(value, path=path + "[" + index.to_string() + "]"),
    )
  }
  result
}

///|
fn mcp_encode_env_array(
  values : Array[McpEnvVariable],
) -> Json raise ProtocolDecodeError {
  let encoded : Array[Json] = []
  for value in values {
    encoded.push(mcp_encode_env_variable(value))
  }
  Json::array(encoded)
}

///|
fn mcp_decode_http_header(
  value : Json,
  path~ : String,
) -> McpHttpHeader raise ProtocolDecodeError {
  let fields = protocol_require_object(value, path~)
  protocol_reject_unknown(fields, ["_meta", "name", "value"], prefix=path)
  let name = protocol_required_string(fields, "name", path=path + ".name")
  let header_value = protocol_required_string(
    fields,
    "value",
    path=path + ".value",
  )
  let meta = mcp_nullable_meta(fields, path=path + "._meta")
  { name, value: header_value, meta }
}

///|
fn mcp_encode_http_header(
  value : McpHttpHeader,
) -> Json raise ProtocolDecodeError {
  let fields : Map[String, Json] = Map([])
  fields["name"] = Json::string(value.name)
  fields["value"] = Json::string(value.value)
  protocol_put_meta(fields, value.meta)
  Json::object(fields)
}

///|
fn mcp_decode_http_headers(
  value : Json,
  path~ : String,
) -> Array[McpHttpHeader] raise ProtocolDecodeError {
  let values = protocol_require_array(value, path~)
  let result : Array[McpHttpHeader] = []
  for index, value in values {
    result.push(
      mcp_decode_http_header(value, path=path + "[" + index.to_string() + "]"),
    )
  }
  result
}

///|
fn mcp_encode_http_headers(
  values : Array[McpHttpHeader],
) -> Json raise ProtocolDecodeError {
  let encoded : Array[Json] = []
  for value in values {
    encoded.push(mcp_encode_http_header(value))
  }
  Json::array(encoded)
}

///|
fn mcp_decode_stdio_fields(
  fields : Map[String, Json],
  path~ : String,
) -> McpServerStdio raise ProtocolDecodeError {
  protocol_reject_unknown(
    fields,
    ["type", "_meta", "name", "command", "args", "env"],
    prefix=path,
  )
  let name = protocol_required_string(fields, "name", path=path + ".name")
  let command = protocol_validate_absolute_path(
    protocol_required_string(fields, "command", path=path + ".command"),
    field_path=path + ".command",
  )
  let args = mcp_decode_string_array(
    protocol_required(fields, "args", path=path + ".args"),
    path=path + ".args",
  )
  let env = mcp_decode_env_array(
    protocol_required(fields, "env", path=path + ".env"),
    path=path + ".env",
  )
  let meta = mcp_nullable_meta(fields, path=path + "._meta")
  { name, command, args, env, meta }
}

///|
fn mcp_encode_stdio(value : McpServerStdio) -> Json raise ProtocolDecodeError {
  let fields : Map[String, Json] = Map([])
  fields["type"] = Json::string("stdio")
  fields["name"] = Json::string(value.name)
  fields["command"] = Json::string(
    protocol_validate_absolute_path(value.command, field_path="command"),
  )
  fields["args"] = Json::array(value.args.map(value => Json::string(value)))
  fields["env"] = mcp_encode_env_array(value.env)
  protocol_put_meta(fields, value.meta)
  Json::object(fields)
}

///|
fn mcp_decode_http_fields(
  fields : Map[String, Json],
  path~ : String,
) -> McpServerHttp raise ProtocolDecodeError {
  protocol_reject_unknown(
    fields,
    ["type", "_meta", "name", "url", "headers"],
    prefix=path,
  )
  let name = protocol_required_string(fields, "name", path=path + ".name")
  let url = protocol_required_string(fields, "url", path=path + ".url")
  let headers = mcp_decode_http_headers(
    protocol_required(fields, "headers", path=path + ".headers"),
    path=path + ".headers",
  )
  let meta = mcp_nullable_meta(fields, path=path + "._meta")
  { name, url, headers, meta }
}

///|
fn mcp_encode_http(value : McpServerHttp) -> Json raise ProtocolDecodeError {
  let fields : Map[String, Json] = Map([])
  fields["type"] = Json::string("http")
  fields["name"] = Json::string(value.name)
  fields["url"] = Json::string(value.url)
  fields["headers"] = mcp_encode_http_headers(value.headers)
  protocol_put_meta(fields, value.meta)
  Json::object(fields)
}

///|
fn mcp_decode_sse_fields(
  fields : Map[String, Json],
  path~ : String,
) -> McpServerSse raise ProtocolDecodeError {
  protocol_reject_unknown(
    fields,
    ["type", "_meta", "name", "url", "headers"],
    prefix=path,
  )
  let name = protocol_required_string(fields, "name", path=path + ".name")
  let url = protocol_required_string(fields, "url", path=path + ".url")
  let headers = mcp_decode_http_headers(
    protocol_required(fields, "headers", path=path + ".headers"),
    path=path + ".headers",
  )
  let meta = mcp_nullable_meta(fields, path=path + "._meta")
  { name, url, headers, meta }
}

///|
fn mcp_encode_sse(value : McpServerSse) -> Json raise ProtocolDecodeError {
  let fields : Map[String, Json] = Map([])
  fields["type"] = Json::string("sse")
  fields["name"] = Json::string(value.name)
  fields["url"] = Json::string(value.url)
  fields["headers"] = mcp_encode_http_headers(value.headers)
  protocol_put_meta(fields, value.meta)
  Json::object(fields)
}

///|
/// Decode one MCP server transport configuration.
///
/// Per the wire schema only `http`/`sse` carry a `type` tag; stdio is the
/// untagged variant, so an absent `type` decodes as stdio. An explicit
/// `"stdio"` tag stays accepted for backwards compatibility with payloads
/// produced before this fix.
pub fn mcp_server_from_json(
  value : Json,
  path? : String = "mcpServer",
) -> McpServer raise ProtocolDecodeError {
  let fields = protocol_require_object(value, path~)
  let tag : String? = match protocol_field(fields, "type") {
    Omitted => None
    Null => raise ExpectedString(path=path + ".type")
    Value(discriminator) =>
      Some(protocol_decode_string(discriminator, path=path + ".type"))
  }
  match tag {
    None => Stdio(mcp_decode_stdio_fields(fields, path~))
    Some("stdio") => Stdio(mcp_decode_stdio_fields(fields, path~))
    Some("http") => Http(mcp_decode_http_fields(fields, path~))
    Some("sse") => Sse(mcp_decode_sse_fields(fields, path~))
    Some(other) => raise InvalidDiscriminator(path=path + ".type", value=other)
  }
}

///|
/// Encode one MCP server transport configuration with its wire discriminator.
pub fn mcp_server_to_json(value : McpServer) -> Json raise ProtocolDecodeError {
  match value {
    Stdio(value) => mcp_encode_stdio(value)
    Http(value) => mcp_encode_http(value)
    Sse(value) => mcp_encode_sse(value)
  }
}