From b9a95161282a7f0031d2eb2cd2bf2cb69b7714db Mon Sep 17 00:00:00 2001 From: Ali Hashemi Date: Thu, 1 May 2025 09:16:35 -0300 Subject: [PATCH 01/10] refactor: types --- src/lib.rs | 104 +-------------------------------- src/render_template.rs | 3 +- src/schema.rs | 2 +- src/types.rs | 103 ++++++++++++++++++++++++++++++++ tests/common/common.rs | 5 +- tests/render_template_tests.rs | 3 +- tests/test_std_output.rs | 2 +- 7 files changed, 116 insertions(+), 106 deletions(-) create mode 100644 src/types.rs diff --git a/src/lib.rs b/src/lib.rs index 95f7ff6..a3a8535 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ pub mod render_template; pub mod schema; pub mod std_output; pub mod templates; +pub mod types; pub mod utils; pub use cli::{CommandArguments, DiscoveryCommand, LogLevel, PrintOptions, Template, WriteOptions}; @@ -12,9 +13,10 @@ use colored::Colorize; use error::{DiscoveryError, DiscoveryResult}; use render_template::{detect_render_markers, render_template}; use schema::tool_params; -use std::{fmt::Display, io::stdout}; +use std::io::stdout; use std_output::{print_header, print_list, print_summary}; pub use templates::OutputTemplate; +use types::{McpCapabilities, McpServerInfo, McpToolMeta}; use std::sync::Arc; @@ -30,106 +32,6 @@ use rust_mcp_sdk::{ McpClient, StdioTransport, TransportOptions, }; -#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] -pub struct McpCapabilities { - pub tools: bool, - pub prompts: bool, - pub resources: bool, - pub logging: bool, - pub experimental: bool, -} - -impl Display for McpCapabilities { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "tools:{}, prompts:{}, resources:{}, logging:{}, experimental:{}", - self.tools, self.prompts, self.resources, self.logging, self.experimental - ) - } -} - -#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] -pub enum ParamTypes { - Primitive(String), - Object(Vec), - Array(Vec), -} - -impl Display for ParamTypes { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let type_name = match self { - ParamTypes::Primitive(type_name) => type_name.to_owned(), - ParamTypes::Object(items) => { - format!( - "{{{}}}", - items - .iter() - .map(|t| format!("{} : {}", t.param_name, t.param_type)) - .collect::>() - .join(", ") - ) - } - ParamTypes::Array(items) => format!("{} [ ]", items[0]), - }; - write!(f, "{}", type_name) - } -} - -// impl Serialize for ParamTypes { -// fn serialize(&self, serializer: S) -> Result -// where -// S: Serializer, -// { -// match self { -// ParamTypes::Primitive(s) => { -// let mut map = serializer.serialize_map(Some(1))?; -// map.serialize_entry("Primitive", s)?; -// map.end() -// } -// -// ParamTypes::Object(params) => params.serialize(serializer), // Inline as array -// ParamTypes::Array(arr) => { -// let mut map = serializer.serialize_map(Some(1))?; -// map.serialize_entry("Array", arr)?; -// map.end() -// } -// } -// } -// } - -#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] -pub struct McpToolSParams { - pub param_name: String, - pub param_type: ParamTypes, - #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] - pub param_description: Option, - pub required: bool, -} - -#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] -pub struct McpToolMeta { - pub name: String, - #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] - pub description: Option, - pub params: Vec, -} - -#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] -pub struct McpServerInfo { - pub name: String, - pub version: String, - pub capabilities: McpCapabilities, - #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] - pub tools: Option>, - #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] - pub prompts: Option>, - #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] - pub resources: Option>, - #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] - pub resource_templates: Option>, -} - pub struct McpDiscovery { options: DiscoveryCommand, pub server_info: Option, diff --git a/src/render_template.rs b/src/render_template.rs index 04ece57..80d38ad 100644 --- a/src/render_template.rs +++ b/src/render_template.rs @@ -15,10 +15,11 @@ use unicode_width::UnicodeWidthStr; use crate::{ error::{DiscoveryError, DiscoveryResult}, templates::{InlineTemplateInfo, PARTIALS}, + types::ParamTypes, utils::{ boolean_indicator, line_ending, match_template, RenderTemplateInfo, UpdateTemplateInfo, }, - McpServerInfo, OutputTemplate, ParamTypes, Template, WriteOptions, + McpServerInfo, OutputTemplate, Template, WriteOptions, }; /// Properties parsed from the `MCP_DISCOVERY_TEMPLATE_START` marker line in template files. diff --git a/src/schema.rs b/src/schema.rs index bfa7363..a004c1b 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -4,7 +4,7 @@ use serde_json::{Map, Value}; use crate::{ error::{DiscoveryError, DiscoveryResult}, - McpToolSParams, ParamTypes, + types::{McpToolSParams, ParamTypes}, }; /// Parses an object schema into a vector of `McpToolSParams`. diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..8ca2db7 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,103 @@ +use std::fmt::Display; + +use rust_mcp_schema::{Prompt, Resource, ResourceTemplate}; + +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct McpCapabilities { + pub tools: bool, + pub prompts: bool, + pub resources: bool, + pub logging: bool, + pub experimental: bool, +} + +impl Display for McpCapabilities { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "tools:{}, prompts:{}, resources:{}, logging:{}, experimental:{}", + self.tools, self.prompts, self.resources, self.logging, self.experimental + ) + } +} + +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub enum ParamTypes { + Primitive(String), + Object(Vec), + Array(Vec), +} + +impl Display for ParamTypes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let type_name = match self { + ParamTypes::Primitive(type_name) => type_name.to_owned(), + ParamTypes::Object(items) => { + format!( + "{{{}}}", + items + .iter() + .map(|t| format!("{} : {}", t.param_name, t.param_type)) + .collect::>() + .join(", ") + ) + } + ParamTypes::Array(items) => format!("{} [ ]", items[0]), + }; + write!(f, "{}", type_name) + } +} + +// impl Serialize for ParamTypes { +// fn serialize(&self, serializer: S) -> Result +// where +// S: Serializer, +// { +// match self { +// ParamTypes::Primitive(s) => { +// let mut map = serializer.serialize_map(Some(1))?; +// map.serialize_entry("Primitive", s)?; +// map.end() +// } +// +// ParamTypes::Object(params) => params.serialize(serializer), // Inline as array +// ParamTypes::Array(arr) => { +// let mut map = serializer.serialize_map(Some(1))?; +// map.serialize_entry("Array", arr)?; +// map.end() +// } +// } +// } +// } + +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct McpToolSParams { + pub param_name: String, + pub param_type: ParamTypes, + #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] + pub param_description: Option, + pub required: bool, +} + +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct McpToolMeta { + pub name: String, + #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] + pub description: Option, + pub params: Vec, +} + +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct McpServerInfo { + pub name: String, + pub version: String, + pub capabilities: McpCapabilities, + #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] + pub tools: Option>, + #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] + pub prompts: Option>, + #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] + pub resources: Option>, + #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] + pub resource_templates: Option>, +} diff --git a/tests/common/common.rs b/tests/common/common.rs index c03d380..382ae17 100644 --- a/tests/common/common.rs +++ b/tests/common/common.rs @@ -1,5 +1,8 @@ use clap::Parser; -use mcp_discovery::{CommandArguments, McpCapabilities, McpServerInfo}; +use mcp_discovery::{ + types::{McpCapabilities, McpServerInfo}, + CommandArguments, +}; use rust_mcp_sdk::macros::JsonSchema; // Helper function to parse arguments from a vector of strings diff --git a/tests/render_template_tests.rs b/tests/render_template_tests.rs index a2867e6..e879a77 100644 --- a/tests/render_template_tests.rs +++ b/tests/render_template_tests.rs @@ -5,7 +5,8 @@ use common::default_mcp_server_info; use handlebars::Handlebars; use mcp_discovery::{ render_template::{detect_render_markers, extract_template_file, register_helpers}, - OutputTemplate, ParamTypes, WriteOptions, + types::ParamTypes, + OutputTemplate, WriteOptions, }; use serde_json::json; use std::fs::write; diff --git a/tests/test_std_output.rs b/tests/test_std_output.rs index 6bffcae..d64d14e 100644 --- a/tests/test_std_output.rs +++ b/tests/test_std_output.rs @@ -2,7 +2,7 @@ use mcp_discovery::{ std_output::{ print_header, print_json, print_list, print_summary, table_bottom, table_content, table_top, }, - McpCapabilities, McpServerInfo, + types::{McpCapabilities, McpServerInfo}, }; use std::io::{self}; use unicode_width::UnicodeWidthStr; From 46229b62e2c176659b737d7f6605e1f7472d5847 Mon Sep 17 00:00:00 2001 From: Ali Hashemi Date: Thu, 1 May 2025 09:25:07 -0300 Subject: [PATCH 02/10] refactor: sub type modules --- src/types.rs | 106 ++------------------------------------ src/types/capabilities.rs | 102 ++++++++++++++++++++++++++++++++++++ src/types/commands.rs | 1 + 3 files changed, 107 insertions(+), 102 deletions(-) create mode 100644 src/types/capabilities.rs create mode 100644 src/types/commands.rs diff --git a/src/types.rs b/src/types.rs index 8ca2db7..7333909 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,103 +1,5 @@ -use std::fmt::Display; +mod capabilities; +mod commands; -use rust_mcp_schema::{Prompt, Resource, ResourceTemplate}; - -#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] -pub struct McpCapabilities { - pub tools: bool, - pub prompts: bool, - pub resources: bool, - pub logging: bool, - pub experimental: bool, -} - -impl Display for McpCapabilities { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "tools:{}, prompts:{}, resources:{}, logging:{}, experimental:{}", - self.tools, self.prompts, self.resources, self.logging, self.experimental - ) - } -} - -#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] -pub enum ParamTypes { - Primitive(String), - Object(Vec), - Array(Vec), -} - -impl Display for ParamTypes { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let type_name = match self { - ParamTypes::Primitive(type_name) => type_name.to_owned(), - ParamTypes::Object(items) => { - format!( - "{{{}}}", - items - .iter() - .map(|t| format!("{} : {}", t.param_name, t.param_type)) - .collect::>() - .join(", ") - ) - } - ParamTypes::Array(items) => format!("{} [ ]", items[0]), - }; - write!(f, "{}", type_name) - } -} - -// impl Serialize for ParamTypes { -// fn serialize(&self, serializer: S) -> Result -// where -// S: Serializer, -// { -// match self { -// ParamTypes::Primitive(s) => { -// let mut map = serializer.serialize_map(Some(1))?; -// map.serialize_entry("Primitive", s)?; -// map.end() -// } -// -// ParamTypes::Object(params) => params.serialize(serializer), // Inline as array -// ParamTypes::Array(arr) => { -// let mut map = serializer.serialize_map(Some(1))?; -// map.serialize_entry("Array", arr)?; -// map.end() -// } -// } -// } -// } - -#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] -pub struct McpToolSParams { - pub param_name: String, - pub param_type: ParamTypes, - #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] - pub param_description: Option, - pub required: bool, -} - -#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] -pub struct McpToolMeta { - pub name: String, - #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] - pub description: Option, - pub params: Vec, -} - -#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] -pub struct McpServerInfo { - pub name: String, - pub version: String, - pub capabilities: McpCapabilities, - #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] - pub tools: Option>, - #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] - pub prompts: Option>, - #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] - pub resources: Option>, - #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] - pub resource_templates: Option>, -} +pub use capabilities::*; +pub use commands::*; diff --git a/src/types/capabilities.rs b/src/types/capabilities.rs new file mode 100644 index 0000000..0cbac6c --- /dev/null +++ b/src/types/capabilities.rs @@ -0,0 +1,102 @@ +use rust_mcp_schema::{Prompt, Resource, ResourceTemplate}; +use std::fmt::Display; + +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct McpCapabilities { + pub tools: bool, + pub prompts: bool, + pub resources: bool, + pub logging: bool, + pub experimental: bool, +} + +impl Display for McpCapabilities { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "tools:{}, prompts:{}, resources:{}, logging:{}, experimental:{}", + self.tools, self.prompts, self.resources, self.logging, self.experimental + ) + } +} + +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub enum ParamTypes { + Primitive(String), + Object(Vec), + Array(Vec), +} + +impl Display for ParamTypes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let type_name = match self { + ParamTypes::Primitive(type_name) => type_name.to_owned(), + ParamTypes::Object(items) => { + format!( + "{{{}}}", + items + .iter() + .map(|t| format!("{} : {}", t.param_name, t.param_type)) + .collect::>() + .join(", ") + ) + } + ParamTypes::Array(items) => format!("{} [ ]", items[0]), + }; + write!(f, "{}", type_name) + } +} + +// impl Serialize for ParamTypes { +// fn serialize(&self, serializer: S) -> Result +// where +// S: Serializer, +// { +// match self { +// ParamTypes::Primitive(s) => { +// let mut map = serializer.serialize_map(Some(1))?; +// map.serialize_entry("Primitive", s)?; +// map.end() +// } +// +// ParamTypes::Object(params) => params.serialize(serializer), // Inline as array +// ParamTypes::Array(arr) => { +// let mut map = serializer.serialize_map(Some(1))?; +// map.serialize_entry("Array", arr)?; +// map.end() +// } +// } +// } +// } + +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct McpToolSParams { + pub param_name: String, + pub param_type: ParamTypes, + #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] + pub param_description: Option, + pub required: bool, +} + +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct McpToolMeta { + pub name: String, + #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] + pub description: Option, + pub params: Vec, +} + +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)] +pub struct McpServerInfo { + pub name: String, + pub version: String, + pub capabilities: McpCapabilities, + #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] + pub tools: Option>, + #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] + pub prompts: Option>, + #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] + pub resources: Option>, + #[serde(default, skip_serializing_if = "::std::option::Option::is_none")] + pub resource_templates: Option>, +} diff --git a/src/types/commands.rs b/src/types/commands.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/types/commands.rs @@ -0,0 +1 @@ + From 4885d3f6fea2e9d2e2cd2c895a3e1cfcfb2b3e39 Mon Sep 17 00:00:00 2001 From: Ali Hashemi Date: Thu, 1 May 2025 11:43:31 -0300 Subject: [PATCH 03/10] chore: update tests --- src/cli.rs | 316 +++++++++++++++++++++++++-------- src/error.rs | 2 + src/lib.rs | 6 +- src/main.rs | 15 +- src/render_template.rs | 9 +- src/templates.rs | 2 +- src/types/commands.rs | 151 ++++++++++++++++ src/utils.rs | 2 +- tests/common/common.rs | 11 +- tests/render_template_tests.rs | 4 +- tests/test_cli.rs | 180 ------------------- 11 files changed, 421 insertions(+), 277 deletions(-) delete mode 100644 tests/test_cli.rs diff --git a/src/cli.rs b/src/cli.rs index fd14aad..8a70af7 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,24 +1,30 @@ -use std::{ - fmt::Display, - io::{self, ErrorKind}, - path::PathBuf, -}; +use std::path::PathBuf; use clap::{arg, command, Parser, Subcommand, ValueEnum}; - -use crate::{error::DiscoveryResult, utils::match_template, OutputTemplate}; +use mcp_discovery::types::{DiscoveryCommand, LogLevel, PrintOptions, Template, WriteOptions}; #[derive(Debug, Clone, ValueEnum, PartialEq)] -pub enum Template { +pub enum CliTemplate { Md, MdPlain, Html, Txt, } +impl Into