///|
/// MCP protocol version implemented by this SDK.
///
/// Single source of truth for the `MCP-Protocol-Version` header and the
/// `_meta.io.modelcontextprotocol/protocolVersion` field. Per the 2026-07-28
/// revision, every request carries its own version; there is no handshake.
pub const ProtocolVersion : String = "2026-07-28"

///|
pub(all) struct ServerInfo {
  name : String
  title : String?
  version : String
  description : String?
} derive(Debug, Eq)

///|
pub(all) struct ServerCapabilities {
  tools : ToolCapabilities?
  resources : ResourceCapabilities?
  prompts : PromptCapabilities?
  /// Optional protocol extensions, per 2026-07-28 versioning: a map of
  /// extension identifiers (following `_meta` key naming rules) to
  /// per-extension settings objects.
  extensions : Map[String, Json]?
} derive(Debug, Eq)

///|
pub(all) struct ToolCapabilities {
  list_changed : Bool
} derive(Debug, Eq)

///|
pub(all) struct ResourceCapabilities {
  subscribe : Bool
  list_changed : Bool
} derive(Debug, Eq)

///|
pub(all) struct PromptCapabilities {
  list_changed : Bool
} derive(Debug, Eq)

///|
pub(all) struct ToolDefinition {
  name : String
  description : String
  input_schema : Json
  cached_schema_json : String
  icon : String?
} derive(Debug, Eq)

///|
/// Request ID supporting both Int and String per JSON-RPC 2.0 / MCP spec
pub(all) enum RequestId {
  Int(Int)
  Str(String)
} derive(Eq, Debug)

///|
/// Convert RequestId to its JSON string representation
pub fn RequestId::to_json_string(self : RequestId) -> String {
  match self {
    Int(n) => n.to_string()
    Str(s) => {
      // Produce a JSON-quoted string: "escaped_content"
      let escaped = s
        .replace(old="\\", new="\\\\")
        .replace(old="\"", new="\\\"")
        .replace(old="\n", new="\\n")
        .replace(old="\r", new="\\r")
        .replace(old="\t", new="\\t")
      "\"" + escaped + "\""
    }
  }
}

///|
pub(all) struct JsonRpcRequest {
  jsonrpc : String
  id : RequestId
  method_name : String
  params : Json
} derive(Debug, Eq)

///|
pub(all) struct JsonRpcResponse {
  jsonrpc : String
  id : RequestId
  result : Result[Json, JsonRpcError]
} derive(Debug, Eq)

///|
pub(all) struct JsonRpcError {
  code : Int
  message : String
  data : Json?
} derive(Debug, Eq)

///|
pub(all) struct PromptArgument {
  name : String
  description : String?
  required : Bool?
} derive(Debug, Eq)

///|
pub(all) struct Prompt {
  name : String
  description : String?
  arguments : Array[PromptArgument]?
} derive(Debug, Eq)

///|
/// Content item for MCP tool results
pub(all) enum ContentItem {
  Text(String)
  /// Image with base64 data and mime type
  Image(String, mime_type~ : String)
  /// Resource link (type: "resource_link") — references a resource by URI
  ResourceLink(String)
  /// Embedded resource (type: "resource") — contains inline text or blob content
  EmbeddedResource(EmbeddedResourceContent)
} derive(Eq, Debug)

///|
pub(all) enum EmbeddedResourceContent {
  Text(String, uri~ : String, mime_type~ : String?)
  Blob(String, uri~ : String, mime_type~ : String)
} derive(Eq, Debug)

///|
pub(all) struct PromptMessage {
  role : String
  content : ContentItem
} derive(Debug, Eq)

///|
pub(all) struct GetPromptResult {
  description : String?
  messages : Array[PromptMessage]
} derive(Debug, Eq)

///|
pub fn JsonRpcRequest::from_json(
  json : Json,
) -> Result[JsonRpcRequest, MCPError] {
  if json is Object(obj) {
    let jsonrpc = match obj.get("jsonrpc") {
      Some(String("2.0")) => "2.0"
      _ => return Err(InvalidRequest("Missing or invalid 'jsonrpc' field"))
    }
    let id = match obj.get("id") {
      Some(Number(n, ..)) => RequestId::Int(n.to_int())
      Some(String(s)) => RequestId::Str(s)
      _ => return Err(InvalidRequest("Missing or invalid 'id' field"))
    }
    let method_name = match obj.get("method") {
      Some(String(m)) => m
      _ => return Err(InvalidRequest("Missing or invalid 'method' field"))
    }
    let params = obj.get("params").unwrap_or(null)
    Ok({ jsonrpc, id, method_name, params })
  } else {
    Err(InvalidRequest("Request must be a JSON object"))
  }
}

///|
/// MRTR (Multi Round-Trip Requests) — 2026-07-28 spec §E.
///
/// When a server needs client input (sampling/elicitation/roots) to finish a
/// request, it returns an `InputRequiredResult` whose `inputRequests` lists
/// what it needs and `requestState` carries server-side continuation state.
/// The client fulfills the inputs and retries the original request with
/// `inputResponses` + the echoed `requestState`.

///|
/// The three server→client input request methods. Servers MUST NOT send an
/// input request the client did not declare support for in its capabilities.
pub(all) enum InputRequestKind {
  ElicitationCreate
  SamplingCreateMessage
  RootsList
} derive(Eq, Debug)

///|
/// One entry in an `inputRequests` map: a server-assigned key plus the
/// request method and params the client must fulfill.
pub(all) struct InputRequestEntry {
  method_name : InputRequestKind
  params : Json
} derive(Eq, Debug)

///|
/// Convert an `InputRequestKind` to its JSON-RPC method string.
pub fn InputRequestKind::to_method(self : InputRequestKind) -> String {
  match self {
    ElicitationCreate => "elicitation/create"
    SamplingCreateMessage => "sampling/createMessage"
    RootsList => "roots/list"
  }
}

///|
/// Which kind of change notification a subscription filter should match.
/// Used internally by the server to route notifications to the right streams.
pub(all) enum SubscriptionNotificationKind {
  ToolsListChanged
  PromptsListChanged
  ResourcesListChanged
  /// A specific subscribed resource changed; matched against the
  /// subscription's `resourceSubscriptions` URI list.
  ResourceUpdated(uri~ : String)
} derive(Eq, Debug)