///|
async fn ConfigService::handle_get_claude_mcp_status(
  self : ConfigService,
  w : @httpx.ResponseWriter,
) -> Unit {
  write_usage_json(
    w,
    {
      "userConfigPath": Json::string(
        @pathx.join(moon_home_dir(), ".claude.json"),
      ),
      "userConfigExists": Json::boolean(
        @fsx.exists_as_file(@pathx.join(moon_home_dir(), ".claude.json")),
      ),
      "serverCount": Json::number(self.mcp_servers.length().to_double()),
    }.to_json(),
  )
}

///|
async fn ConfigService::handle_upsert_claude_mcp_server(
  self : ConfigService,
  r : @httpx.RequestReader,
  w : @httpx.ResponseWriter,
) -> Unit {
  let body = proxy_json_body(r)
  match moon_mcp_server_from_request(r.path, body, "claude") {
    Some((id, server)) => {
      self.mcp_servers[id] = moon_mcp_server_with_app(server, "claude", true)
      write_usage_json(w, Json::boolean(true))
    }
    None => write_usage_json(w, Json::boolean(false))
  }
}

///|
async fn ConfigService::handle_delete_mcp_server(
  self : ConfigService,
  r : @httpx.RequestReader,
  w : @httpx.ResponseWriter,
) -> Unit {
  let body = proxy_json_body(r)
  match proxy_string_request(r.path, body, "id") {
    Some(id) => {
      let existed = self.mcp_servers.get(id) is Some(_)
      self.mcp_servers.remove(id)
      write_usage_json(w, Json::boolean(existed))
    }
    None => write_usage_json(w, Json::boolean(false))
  }
}

///|
async fn ConfigService::handle_validate_mcp_command(
  self : ConfigService,
  r : @httpx.RequestReader,
  w : @httpx.ResponseWriter,
) -> Unit {
  ignore(self)
  let body = proxy_json_body(r)
  let command = proxy_string_request(r.path, body, "command").unwrap_or("")
  write_usage_json(w, Json::boolean(moon_validate_mcp_command_name(command)))
}

///|
async fn ConfigService::handle_set_mcp_enabled(
  self : ConfigService,
  r : @httpx.RequestReader,
  w : @httpx.ResponseWriter,
) -> Unit {
  let body = proxy_json_body(r)
  let app = moon_config_app_from_request(r.path, body).unwrap_or("claude")
  let enabled = proxy_bool_request(r.path, body, "enabled").unwrap_or(true)
  match proxy_string_request(r.path, body, "id") {
    Some(id) =>
      match self.mcp_servers.get(id) {
        Some(server) => {
          self.mcp_servers[id] = moon_mcp_server_with_app(server, app, enabled)
          write_usage_json(w, Json::boolean(true))
        }
        None => write_usage_json(w, Json::boolean(false))
      }
    None => write_usage_json(w, Json::boolean(false))
  }
}

///|
async fn ConfigService::handle_get_mcp_servers(
  self : ConfigService,
  w : @httpx.ResponseWriter,
) -> Unit {
  write_usage_json(w, moon_mcp_servers_object_for_app(self.mcp_servers, None))
}

///|
async fn ConfigService::handle_upsert_mcp_server(
  self : ConfigService,
  r : @httpx.RequestReader,
  w : @httpx.ResponseWriter,
) -> Unit {
  let body = proxy_json_body(r)
  match moon_mcp_server_from_request(r.path, body, "claude") {
    Some((id, server)) => {
      self.mcp_servers[id] = server
      write_usage_json(w, Json::null())
    }
    None => write_usage_json(w, Json::null())
  }
}

///|
async fn ConfigService::handle_toggle_mcp_app(
  self : ConfigService,
  r : @httpx.RequestReader,
  w : @httpx.ResponseWriter,
) -> Unit {
  let body = proxy_json_body(r)
  let app = moon_config_app_from_request(r.path, body).unwrap_or("claude")
  let enabled = proxy_bool_request(r.path, body, "enabled").unwrap_or(true)
  match proxy_string_request(r.path, body, "id") {
    Some(id) =>
      match self.mcp_servers.get(id) {
        Some(server) => {
          self.mcp_servers[id] = moon_mcp_server_with_app(server, app, enabled)
          write_usage_json(w, Json::null())
        }
        None => write_usage_json(w, Json::null())
      }
    None => write_usage_json(w, Json::null())
  }
}

///|
async fn ConfigService::handle_import_mcp_from_apps(
  self : ConfigService,
  w : @httpx.ResponseWriter,
) -> Unit {
  let imported = moon_import_mcp_from_apps_count(self.mcp_servers)
  write_usage_json(w, Json::number(imported.to_double()))
}

///|
async fn ConfigService::handle_get_prompts(
  self : ConfigService,
  r : @httpx.RequestReader,
  w : @httpx.ResponseWriter,
) -> Unit {
  let body = proxy_json_body(r)
  let app = moon_config_app_from_request(r.path, body).unwrap_or("claude")
  write_usage_json(w, moon_prompts_object_for_app(self.prompts, app))
}

///|
async fn ConfigService::handle_upsert_prompt(
  self : ConfigService,
  r : @httpx.RequestReader,
  w : @httpx.ResponseWriter,
) -> Unit {
  let body = proxy_json_body(r)
  match moon_prompt_from_request(r.path, body) {
    Some((app, id, prompt)) =>
      self.prompts[moon_prompt_store_key(app, id)] = prompt
    None => ()
  }
  write_usage_json(w, Json::null())
}

///|
async fn ConfigService::handle_delete_prompt(
  self : ConfigService,
  r : @httpx.RequestReader,
  w : @httpx.ResponseWriter,
) -> Unit {
  let body = proxy_json_body(r)
  let app = moon_config_app_from_request(r.path, body).unwrap_or("claude")
  match proxy_string_request(r.path, body, "id") {
    Some(id) => self.prompts.remove(moon_prompt_store_key(app, id))
    None => ()
  }
  write_usage_json(w, Json::null())
}

///|
async fn ConfigService::handle_enable_prompt(
  self : ConfigService,
  r : @httpx.RequestReader,
  w : @httpx.ResponseWriter,
) -> Unit {
  let body = proxy_json_body(r)
  let app = moon_config_app_from_request(r.path, body).unwrap_or("claude")
  match proxy_string_request(r.path, body, "id") {
    Some(id) =>
      match moon_prompt_file_path_from_request(r.path, body, app) {
        Ok(file_path) =>
          match moon_enable_prompt_json(self.prompts, app, id, file_path) {
            Ok(result) => write_usage_json(w, result)
            Err(error) => write_usage_bad_request(w, error)
          }
        Err(error) => write_usage_bad_request(w, error)
      }
    None => write_usage_json(w, Json::null())
  }
}

///|
fn moon_mcp_apps_json(app : String, enabled? : Bool = true) -> Json {
  let apps = {
    "claude": Json::boolean(false),
    "claude-desktop": Json::boolean(false),
    "codex": Json::boolean(false),
    "gemini": Json::boolean(false),
    "opencode": Json::boolean(false),
    "openclaw": Json::boolean(false),
    "hermes": Json::boolean(false),
  }
  apps[moon_normalize_app_type(app)] = Json::boolean(enabled)
  Json::object(apps)
}

///|
fn moon_mcp_app_enabled(server : Json, app : String) -> Bool {
  guard server is Object(obj) else { return false }
  guard obj.get("apps") is Some(Object(apps)) else { return false }
  match apps.get(moon_normalize_app_type(app)) {
    Some(true) => true
    Some(false) => false
    _ => false
  }
}

///|
fn moon_mcp_server_with_app(
  server : Json,
  app : String,
  enabled : Bool,
) -> Json {
  let normalized = moon_normalize_app_type(app)
  match server {
    Object(obj) => {
      let next = obj
      let apps = match obj.get("apps") {
        Some(Object(existing)) => existing
        _ =>
          match moon_mcp_apps_json("claude", enabled=false) {
            Object(empty) => empty
            _ => {}
          }
      }
      apps[normalized] = Json::boolean(enabled)
      next["apps"] = Json::object(apps)
      Json::object(next)
    }
    _ => server
  }
}

///|
fn moon_mcp_server_from_request(
  path : StringView,
  body : Json?,
  default_app : String,
) -> (String, Json)? {
  match body {
    Some(Object(server)) =>
      if json_string_from_map(server, "id") is Some(id) {
        return Some(
          (id, moon_mcp_normalize_server(id, Json::object(server), default_app)),
        )
      }
    _ => ()
  }
  guard proxy_string_request(path, body, "id") is Some(id) else { return None }
  Some((id, moon_mcp_server_from_spec(id, Json::object({}), default_app)))
}

///|
fn moon_mcp_server_from_spec(id : String, spec : Json, app : String) -> Json {
  let name = match spec {
    Object(obj) => json_string_from_map(obj, "name").unwrap_or(id)
    _ => id
  }
  Json::object({
    "id": Json::string(id),
    "name": Json::string(name),
    "server": spec,
    "apps": moon_mcp_apps_json(app),
    "description": Json::null(),
    "homepage": Json::null(),
    "docs": Json::null(),
    "tags": Json::array([]),
  })
}

///|
fn moon_mcp_normalize_server(
  id : String,
  server : Json,
  default_app : String,
) -> Json {
  match server {
    Object(obj) => {
      let next = obj
      if next.get("id") is None {
        next["id"] = Json::string(id)
      }
      if next.get("name") is None {
        next["name"] = Json::string(id)
      }
      if next.get("server") is None {
        next["server"] = Json::object({})
      }
      if next.get("apps") is None {
        next["apps"] = moon_mcp_apps_json(default_app)
      }
      if next.get("tags") is None {
        next["tags"] = Json::array([])
      }
      Json::object(next)
    }
    _ => moon_mcp_server_from_spec(id, Json::object({}), default_app)
  }
}

///|
fn moon_mcp_servers_object_for_app(
  servers : Map[String, Json],
  app : String?,
) -> Json {
  let result : Map[String, Json] = {}
  for id in servers.keys() {
    guard servers.get(id) is Some(server) else { continue }
    match app {
      Some(app_type) =>
        if moon_mcp_app_enabled(server, app_type) {
          result[id] = server
        }
      None => result[id] = server
    }
  }
  Json::object(result)
}

///|
fn moon_mcp_valid_server_spec(spec : Json) -> Bool {
  guard spec is Object(obj) else { return false }
  let typ = json_string_from_map(obj, "type").unwrap_or("stdio")
  match typ {
    "stdio" => json_string_from_map(obj, "command").unwrap_or("").trim() != ""
    "http" | "sse" =>
      json_string_from_map(obj, "url").unwrap_or("").trim() != ""
    _ => false
  }
}

///|
fn moon_mcp_import_spec_for_app(app : String, spec : Json) -> Json {
  let normalized = moon_normalize_app_type(app)
  if normalized != "gemini" {
    return spec
  }
  match spec {
    Object(obj) => {
      let next = obj
      match next.get("httpUrl") {
        Some(http_url) => {
          next.remove("httpUrl")
          next["url"] = http_url
          next["type"] = Json::string("http")
        }
        None => ()
      }
      if next.get("type") is None {
        if next.get("command") is Some(_) {
          next["type"] = Json::string("stdio")
        } else if next.get("url") is Some(_) {
          next["type"] = Json::string("sse")
        }
      }
      Json::object(next)
    }
    _ => spec
  }
}

///|
fn moon_import_mcp_servers_map(
  servers : Map[String, Json],
  app : String,
  mcp_servers : Map[String, Json],
) -> Int {
  let mut changed = 0
  for id in mcp_servers.keys() {
    guard mcp_servers.get(id) is Some(spec) else { continue }
    let import_spec = moon_mcp_import_spec_for_app(app, spec)
    if !moon_mcp_valid_server_spec(import_spec) {
      continue
    }
    match servers.get(id) {
      Some(existing) =>
        if !moon_mcp_app_enabled(existing, app) {
          servers[id] = moon_mcp_server_with_app(existing, app, true)
          changed += 1
        }
      None => {
        servers[id] = moon_mcp_server_from_spec(id, import_spec, app)
        changed += 1
      }
    }
  }
  changed
}

///|
fn moon_import_mcp_from_root_json(
  servers : Map[String, Json],
  app : String,
  root : Json,
) -> Int {
  guard root is Object(obj) else { return 0 }
  guard obj.get("mcpServers") is Some(Object(mcp_servers)) else { return 0 }
  moon_import_mcp_servers_map(servers, app, mcp_servers)
}

///|
fn moon_import_mcp_from_codex_toml_text(
  servers : Map[String, Json],
  content : String,
) -> Int {
  let raw_specs : Map[String, Json] = {}
  let mut current_id : String? = None
  let mut current_table = ""
  for line_view in content.split("\n") {
    let line = moon_toml_strip_comment(line_view.to_owned()).trim().to_owned()
    if line.is_empty() {
      continue
    }
    if line.has_prefix("[") && line.has_suffix("]") {
      let section = line[1:line.length() - 1].trim().to_owned()
      match moon_codex_mcp_section(section) {
        Some((id, table)) => {
          current_id = Some(id)
          current_table = table
          if raw_specs.get(id) is None {
            raw_specs[id] = Json::object({})
          }
        }
        None => {
          current_id = None
          current_table = ""
        }
      }
      continue
    }
    guard current_id is Some(id) else { continue }
    guard moon_toml_assignment(line) is Some((key, raw_value)) else { continue }
    let spec = moon_codex_raw_mcp_spec(raw_specs, id)
    if current_table.is_empty() {
      match moon_toml_value(raw_value) {
        Some(value) => spec[key] = value
        None => ()
      }
    } else {
      let nested_key = if current_table == "http_headers" {
        "headers"
      } else {
        current_table
      }
      let nested = match spec.get(nested_key) {
        Some(Object(obj)) => obj
        _ => {
          let obj : Map[String, Json] = {}
          spec[nested_key] = Json::object(obj)
          obj
        }
      }
      match moon_toml_value(raw_value) {
        Some(String(value)) => nested[key] = Json::string(value)
        _ => ()
      }
    }
    raw_specs[id] = Json::object(spec)
  }
  let normalized : Map[String, Json] = {}
  for id in raw_specs.keys() {
    guard raw_specs.get(id) is Some(raw_spec) else { continue }
    normalized[id] = moon_codex_mcp_unified_spec(raw_spec)
  }
  moon_import_mcp_servers_map(servers, "codex", normalized)
}

///|
fn moon_codex_raw_mcp_spec(
  specs : Map[String, Json],
  id : String,
) -> Map[String, Json] {
  match specs.get(id) {
    Some(Object(obj)) => obj
    _ => {
      let obj : Map[String, Json] = {}
      specs[id] = Json::object(obj)
      obj
    }
  }
}

///|
fn moon_codex_mcp_section(section : String) -> (String, String)? {
  let rest = if section.has_prefix("mcp_servers.") {
    section["mcp_servers.".length():].to_owned()
  } else if section.has_prefix("mcp.servers.") {
    section["mcp.servers.".length():].to_owned()
  } else {
    return None
  }
  let parts = rest.split(".").collect()
  if parts.length() == 0 {
    return None
  }
  let id = parts[0].trim().to_owned()
  if id.is_empty() {
    return None
  }
  let table = if parts.length() > 1 { parts[1].trim().to_owned() } else { "" }
  match table {
    "" | "env" | "http_headers" | "headers" => Some((id, table))
    _ => None
  }
}

///|
fn moon_codex_mcp_unified_spec(raw_spec : Json) -> Json {
  guard raw_spec is Object(raw) else { return Json::object({}) }
  let typ = json_string_from_map(raw, "type").unwrap_or("stdio")
  let spec : Map[String, Json] = { "type": Json::string(typ) }
  match typ {
    "stdio" => {
      match raw.get("command") {
        Some(String(command)) => spec["command"] = Json::string(command)
        _ => ()
      }
      match raw.get("args") {
        Some(Array(args)) => spec["args"] = Json::array(args)
        _ => ()
      }
      match raw.get("cwd") {
        Some(String(cwd)) if !cwd.trim().is_empty() =>
          spec["cwd"] = Json::string(cwd)
        _ => ()
      }
      match raw.get("env") {
        Some(Object(env)) => spec["env"] = Json::object(env)
        _ => ()
      }
    }
    "http" | "sse" => {
      match raw.get("url") {
        Some(String(url)) => spec["url"] = Json::string(url)
        _ => ()
      }
      match raw.get("http_headers") {
        Some(Object(headers)) => spec["headers"] = Json::object(headers)
        _ =>
          match raw.get("headers") {
            Some(Object(headers)) => spec["headers"] = Json::object(headers)
            _ => ()
          }
      }
    }
    _ => ()
  }
  for key in raw.keys() {
    if moon_codex_mcp_core_field(typ, key) {
      continue
    }
    guard raw.get(key) is Some(value) else { continue }
    spec[key] = value
  }
  Json::object(spec)
}

///|
fn moon_codex_mcp_core_field(typ : String, key : String) -> Bool {
  match typ {
    "stdio" =>
      key == "type" ||
      key == "command" ||
      key == "args" ||
      key == "env" ||
      key == "cwd"
    "http" | "sse" =>
      key == "type" || key == "url" || key == "http_headers" || key == "headers"
    _ => key == "type"
  }
}

///|
fn moon_toml_strip_comment(line : String) -> String {
  let mut in_quote = false
  let mut quote = 0
  let mut escaped = false
  for i in 0.. (String, String)? {
  guard line.find("=") is Some(index) else { return None }
  let key = line[:index].trim().to_owned()
  let value = line[index + 1:].trim().to_owned()
  if key.is_empty() {
    None
  } else {
    Some((key, value))
  }
}

///|
fn moon_toml_value(raw : String) -> Json? {
  let value = raw.trim().to_owned()
  if value.is_empty() {
    return None
  }
  if value.has_prefix("[") && value.has_suffix("]") {
    let items : Array[Json] = []
    for item in moon_toml_split_csv(value[1:value.length() - 1].to_owned()) {
      match moon_toml_value(item) {
        Some(String(text)) => items.push(Json::string(text))
        Some(true) => items.push(Json::boolean(true))
        Some(false) => items.push(Json::boolean(false))
        Some(Number(number, ..)) => items.push(Json::number(number))
        _ => ()
      }
    }
    return Some(Json::array(items))
  }
  if value.has_prefix("{") && value.has_suffix("}") {
    let obj : Map[String, Json] = {}
    for item in moon_toml_split_csv(value[1:value.length() - 1].to_owned()) {
      match moon_toml_assignment(item) {
        Some((key, nested_raw)) =>
          match moon_toml_value(nested_raw) {
            Some(String(text)) => obj[key] = Json::string(text)
            _ => ()
          }
        None => ()
      }
    }
    return Some(Json::object(obj))
  }
  if (value.has_prefix("\"") && value.has_suffix("\"")) ||
    (value.has_prefix("'") && value.has_suffix("'")) {
    return Some(Json::string(value[1:value.length() - 1].to_owned()))
  }
  match value {
    "true" => Some(Json::boolean(true))
    "false" => Some(Json::boolean(false))
    _ =>
      try @string.parse_int(value, base=10) catch {
        _ => Some(Json::string(value))
      } noraise {
        parsed => Some(Json::number(parsed.to_double()))
      }
  }
}

///|
fn moon_toml_split_csv(text : String) -> Array[String] {
  let parts : Array[String] = []
  let mut start = 0
  let mut in_quote = false
  let mut quote = 0
  let mut escaped = false
  for i in 0.. String {
  let cleaned : Array[String] = []
  let mut blank_run = 0
  for line in lines {
    if line.trim().is_empty() {
      blank_run += 1
      if blank_run <= 1 {
        cleaned.push("")
      }
    } else {
      blank_run = 0
      cleaned.push(line)
    }
  }
  cleaned.join("\n")
}

///|
async fn moon_import_mcp_from_apps_count(servers : Map[String, Json]) -> Int {
  let mut total = 0
  total += moon_import_mcp_from_file(
    servers,
    "claude",
    @pathx.join(moon_home_dir(), ".claude.json"),
  )
  total += moon_import_mcp_from_codex_file(
    servers,
    moon_config_file_path("codex"),
  )
  total += moon_import_mcp_from_file(
    servers,
    "gemini",
    @pathx.join(moon_config_dir("gemini"), "settings.json"),
  )
  total
}

///|
fn moon_validate_mcp_command_name(command : String) -> Bool {
  let trimmed = command.trim()
  !trimmed.is_empty() &&
  !trimmed.contains("/") &&
  !trimmed.contains("\\") &&
  !trimmed.contains("\u{0000}")
}

///|
fn moon_prompt_store_key(app : String, id : String) -> String {
  "\{moon_normalize_app_type(app)}:\{id}"
}

///|
fn moon_prompt_from_request(
  path : StringView,
  body : Json?,
) -> (String, String, Json)? {
  let app = moon_config_app_from_request(path, body).unwrap_or("claude")
  match body {
    Some(Object(prompt)) =>
      if json_string_from_map(prompt, "id") is Some(id) {
        return Some((app, id, moon_normalize_prompt(id, Json::object(prompt))))
      }
    _ => ()
  }
  guard proxy_string_request(path, body, "id") is Some(id) else { return None }
  let name = proxy_string_request(path, body, "name").unwrap_or(id)
  let content = proxy_string_request(path, body, "content").unwrap_or("")
  let description = proxy_string_request(path, body, "description")
  let enabled = proxy_bool_request(path, body, "enabled").unwrap_or(true)
  let now = Json::string(@async.now().to_string())
  let prompt : Map[String, Json] = {
    "id": Json::string(id),
    "name": Json::string(name),
    "content": Json::string(content),
    "enabled": Json::boolean(enabled),
    "createdAt": now,
    "updatedAt": now,
  }
  match description {
    Some(value) => prompt["description"] = Json::string(value)
    None => ()
  }
  Some((app, id, Json::object(prompt)))
}

///|
async fn moon_enable_prompt_json(
  prompts : Map[String, Json],
  app : String,
  id : String,
  file_path : String,
) -> Result[Json, String] {
  let normalized = moon_normalize_app_type(app)
  let prefix = "\{normalized}:"
  let mut selected_content = ""
  let mut found = false
  for key in prompts.keys() {
    if !key.has_prefix(prefix) {
      continue
    }
    guard prompts.get(key) is Some(Object(prompt)) else { continue }
    guard json_string_from_map(prompt, "id") is Some(prompt_id) else {
      continue
    }
    if prompt_id == id {
      selected_content = json_string_from_map(prompt, "content").unwrap_or("")
      found = true
      break
    }
  }
  if !found {
    return Err("提示词 \{id} 不存在")
  }
  @fsx.make_directory(@pathx.dirname(file_path), recursive=true, exists_ok=true) catch {
    error => return Err("创建提示词目录失败: \{error}")
  }
  @fsx.write_to_file(file_path, selected_content) catch {
    error => return Err("写入提示词文件失败: \{error}")
  }
  let now = Json::string(@async.now().to_string())
  for key in prompts.keys() {
    if !key.has_prefix(prefix) {
      continue
    }
    guard prompts.get(key) is Some(Object(prompt)) else { continue }
    let next = prompt
    let enabled = match json_string_from_map(next, "id") {
      Some(prompt_id) => prompt_id == id
      None => false
    }
    next["enabled"] = Json::boolean(enabled)
    if enabled {
      next["updatedAt"] = now
    }
    prompts[key] = Json::object(next)
  }
  Ok(Json::null())
}

///|
fn moon_normalize_prompt(id : String, prompt : Json) -> Json {
  match prompt {
    Object(obj) => {
      let next = obj
      if next.get("id") is None {
        next["id"] = Json::string(id)
      }
      if next.get("name") is None {
        next["name"] = Json::string(id)
      }
      if next.get("content") is None {
        next["content"] = Json::string("")
      }
      if next.get("enabled") is None {
        next["enabled"] = Json::boolean(true)
      }
      let now = Json::string(@async.now().to_string())
      if next.get("createdAt") is None {
        next["createdAt"] = now
      }
      next["updatedAt"] = now
      Json::object(next)
    }
    _ => Json::object({})
  }
}

///|
fn moon_prompts_object_for_app(
  prompts : Map[String, Json],
  app : String,
) -> Json {
  let normalized = moon_normalize_app_type(app)
  let prefix = "\{normalized}:"
  let result : Map[String, Json] = {}
  for key in prompts.keys() {
    if key.has_prefix(prefix) {
      guard prompts.get(key) is Some(Object(prompt)) else { continue }
      match json_string_from_map(prompt, "id") {
        Some(id) => result[id] = Json::object(prompt)
        None => ()
      }
    }
  }
  Json::object(result)
}