laburnum 1.17.0

An LSP framework for building language servers and compilers, powered by an incremental query tree with content-addressed storage, task-based dataflow, and parallel queries.
Documentation
// Copyright Two Neutron Stars Incorporated and contributors
// SPDX-License-Identifier: BlueOak-1.0.0

//! MCP wire types.
//!
//! Hand-rolled subset of the Model Context Protocol matching the methods this
//! server implements. See ADR0010 for the rationale on hand-rolling rather
//! than depending on `rmcp`.
//!
//! Reference: <https://modelcontextprotocol.io/specification>

use serde::{
  Deserialize,
  Serialize,
};

/// MCP protocol version this server speaks.
pub const PROTOCOL_VERSION: &str = "2025-03-26";

// -- initialize --------------------------------------------------------------

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InitializeParams {
  pub protocol_version: String,
  #[serde(default)]
  pub capabilities:     ClientCapabilities,
  #[serde(default)]
  pub client_info:      Option<Implementation>,
}

#[derive(Debug, Clone, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ClientCapabilities {
  #[serde(default, skip_serializing_if = "Option::is_none")]
  pub experimental: Option<serde_json::Value>,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Implementation {
  pub name:    String,
  pub version: String,
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct InitializeResult {
  pub protocol_version: String,
  pub capabilities:     ServerCapabilities,
  pub server_info:      Implementation,
}

#[derive(Debug, Clone, Default, Serialize)]
pub struct ServerCapabilities {
  #[serde(skip_serializing_if = "Option::is_none")]
  pub tools: Option<ToolsCapability>,
}

#[derive(Debug, Clone, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ToolsCapability {
  /// We do not change the tool list at runtime; always false in v1.
  pub list_changed: bool,
}

// -- tools/list --------------------------------------------------------------

#[derive(Debug, Clone, Deserialize)]
pub struct ListToolsParams {
  #[serde(default)]
  pub cursor: Option<String>,
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ListToolsResult {
  pub tools: Vec<ToolDescriptor>,
  #[serde(skip_serializing_if = "Option::is_none")]
  pub next_cursor: Option<String>,
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ToolDescriptor {
  pub name:         String,
  pub description:  String,
  pub input_schema: serde_json::Value,
}

// -- tools/call --------------------------------------------------------------

#[derive(Debug, Clone, Deserialize)]
pub struct CallToolParams {
  pub name:      String,
  #[serde(default)]
  pub arguments: serde_json::Value,
}

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CallToolResult {
  pub content:  Vec<Content>,
  #[serde(default)]
  pub is_error: bool,
}

#[derive(Debug, Clone, Serialize)]
#[serde(tag = "type", rename_all = "lowercase")]
pub enum Content {
  Text { text: String },
}

impl Content {
  pub fn text(s: impl Into<String>) -> Self {
    Self::Text { text: s.into() }
  }
}

impl CallToolResult {
  pub fn ok_text(s: impl Into<String>) -> Self {
    Self {
      content:  vec![Content::text(s)],
      is_error: false,
    }
  }

  pub fn ok_json(value: &serde_json::Value) -> Self {
    let text = serde_json::to_string_pretty(value).unwrap_or_default();
    Self {
      content:  vec![Content::text(text)],
      is_error: false,
    }
  }

  pub fn error_text(s: impl Into<String>) -> Self {
    Self {
      content:  vec![Content::text(s)],
      is_error: true,
    }
  }
}