//! MCP (Model Context Protocol) server configuration for the coder.
//!
//! The coder exposes MCP servers through a meta-tool surface
//! (`mcp_list_tools` / `mcp_call`) rather than advertising every
//! server tool directly — see `specs/coder.md` § "MCP servers" and
//! ADR 0033. A small curated preset list lives in
//! `moon-coder::mcp`; user-defined servers and the per-workspace
//! enable set persist here, on
//! [`crate::session::WorkspaceSession::coder_mcp`].

use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use ts_rs::TS;

/// One MCP server definition — either a hardcoded preset
/// (moon-coder's `mcp::preset_servers`) or a user-defined custom
/// entry persisted per workspace.
///
/// Servers spawn via `docker exec` in the workspace shell
/// container — the same sandbox the coder's `bash` runs in — so
/// there is no per-server run-target knob. An early `runs:
/// host|container` field was removed before any server used the
/// host option; `#[serde(default)]` absorbs the key in existing
/// `session.json` files.
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, TS)]
#[ts(export)]
#[serde(default)]
pub struct McpServerConfig {
	/// Stable id — the preset's well-known name (`playwright`) or
	/// an opaque `mcp-<unix-ms>` for custom entries.
	pub id: String,
	/// Human label for the settings UI.
	pub label: String,
	/// Executable to spawn (stdio transport only).
	pub command: String,
	/// Arguments passed verbatim.
	pub args: Vec<String>,
	/// Extra environment for the server process, applied over the
	/// spawn environment (host env for a host spawn, the
	/// container's `docker exec` env otherwise). Empty for every
	/// preset; custom servers use it for config the server reads
	/// from env (`LD_LIBRARY_PATH`-style workarounds, tokens,
	/// feature flags).
	#[serde(skip_serializing_if = "BTreeMap::is_empty")]
	pub env: BTreeMap<String, String>,
	/// 1-2 sentences surfaced to the model in the meta-tool
	/// descriptions so it knows when the server is worth a
	/// `mcp_list_tools` round-trip.
	pub description: String,
}

/// Per-workspace MCP state, persisted on
/// [`crate::session::WorkspaceSession`]. Per-workspace by design:
/// enabling playwright is a statement about one project's testing
/// needs, not a global preference, and custom servers are usually
/// project-specific.
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, TS)]
#[ts(export)]
#[serde(default)]
pub struct CoderMcpWorkspaceConfig {
	/// Ids (preset or custom) enabled for this workspace. Empty =
	/// the meta-tools aren't advertised at all.
	#[serde(skip_serializing_if = "Vec::is_empty")]
	pub enabled: Vec<String>,
	/// User-defined servers for this workspace.
	#[serde(skip_serializing_if = "Vec::is_empty")]
	pub custom: Vec<McpServerConfig>,
}

impl CoderMcpWorkspaceConfig {
	pub fn is_empty(&self) -> bool {
		self.enabled.is_empty() && self.custom.is_empty()
	}
}

/// One row of the settings UI's server list: a preset or custom
/// server plus its enabled state for the current workspace.
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[ts(export)]
pub struct McpServerStatus {
	#[serde(flatten)]
	#[ts(flatten)]
	pub config: McpServerConfig,
	/// `true` for hardcoded presets (not removable).
	pub preset: bool,
	/// Enabled for the current workspace.
	pub enabled: bool,
}
