diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 8f24f673..7a983909 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -40,7 +40,8 @@ "Bash(./target/release/timedate-mcp-server:*)", "Bash(gh issue list:*)", "Bash(gh pr view:*)", - "WebSearch" + "WebSearch", + "Bash(git log:*)" ], "deny": [] } diff --git a/Cargo.lock b/Cargo.lock index 94668a7e..31ff18f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1154,6 +1154,8 @@ dependencies = [ "pulseengine-mcp-protocol", "pulseengine-mcp-server", "pulseengine-mcp-transport", + "schemars 1.0.4", + "serde", "serde_json", "thiserror 1.0.69", "tokio", @@ -1192,6 +1194,8 @@ dependencies = [ "pulseengine-mcp-protocol", "pulseengine-mcp-security-middleware", "pulseengine-mcp-server", + "schemars 0.8.22", + "serde", "serde_json", "tokio", "tower 0.4.13", @@ -2293,7 +2297,7 @@ dependencies = [ [[package]] name = "pulseengine-mcp-auth" -version = "0.10.0" +version = "0.10.1" dependencies = [ "aes-gcm", "anyhow", @@ -2332,7 +2336,7 @@ dependencies = [ [[package]] name = "pulseengine-mcp-cli" -version = "0.10.0" +version = "0.10.1" dependencies = [ "clap", "pulseengine-mcp-cli-derive", @@ -2351,7 +2355,7 @@ dependencies = [ [[package]] name = "pulseengine-mcp-cli-derive" -version = "0.10.0" +version = "0.10.1" dependencies = [ "async-trait", "clap", @@ -2369,7 +2373,7 @@ dependencies = [ [[package]] name = "pulseengine-mcp-external-validation" -version = "0.10.0" +version = "0.10.1" dependencies = [ "anyhow", "arbitrary", @@ -2407,7 +2411,7 @@ dependencies = [ [[package]] name = "pulseengine-mcp-integration-tests" -version = "0.10.0" +version = "0.10.1" dependencies = [ "anyhow", "assert_matches", @@ -2435,7 +2439,7 @@ dependencies = [ [[package]] name = "pulseengine-mcp-logging" -version = "0.10.0" +version = "0.10.1" dependencies = [ "chrono", "hex", @@ -2454,7 +2458,7 @@ dependencies = [ [[package]] name = "pulseengine-mcp-macros" -version = "0.10.0" +version = "0.10.1" dependencies = [ "anyhow", "async-trait", @@ -2480,7 +2484,7 @@ dependencies = [ [[package]] name = "pulseengine-mcp-monitoring" -version = "0.10.0" +version = "0.10.1" dependencies = [ "anyhow", "chrono", @@ -2500,7 +2504,7 @@ dependencies = [ [[package]] name = "pulseengine-mcp-protocol" -version = "0.10.0" +version = "0.10.1" dependencies = [ "async-trait", "chrono", @@ -2516,7 +2520,7 @@ dependencies = [ [[package]] name = "pulseengine-mcp-security" -version = "0.10.0" +version = "0.10.1" dependencies = [ "anyhow", "async-trait", @@ -2538,7 +2542,7 @@ dependencies = [ [[package]] name = "pulseengine-mcp-security-middleware" -version = "0.10.0" +version = "0.10.1" dependencies = [ "anyhow", "assert_matches", @@ -2570,7 +2574,7 @@ dependencies = [ [[package]] name = "pulseengine-mcp-server" -version = "0.10.0" +version = "0.10.1" dependencies = [ "anyhow", "async-trait", @@ -2598,7 +2602,7 @@ dependencies = [ [[package]] name = "pulseengine-mcp-transport" -version = "0.10.0" +version = "0.10.1" dependencies = [ "anyhow", "async-stream", @@ -3438,6 +3442,7 @@ dependencies = [ "pulseengine-mcp-macros", "pulseengine-mcp-protocol", "pulseengine-mcp-server", + "schemars 0.8.22", "serde", "serde_json", "tokio", @@ -3970,6 +3975,8 @@ dependencies = [ "pulseengine-mcp-macros", "pulseengine-mcp-protocol", "pulseengine-mcp-server", + "schemars 1.0.4", + "serde", "serde_json", "tokio", ] diff --git a/Cargo.toml b/Cargo.toml index 96857767..de92b81b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ members = [ resolver = "2" [workspace.package] -version = "0.10.0" +version = "0.10.1" rust-version = "1.88" edition = "2024" license = "MIT OR Apache-2.0" @@ -108,18 +108,18 @@ assert_matches = "1.5" serde_yaml = "0.9" # Framework internal dependencies (published versions) -pulseengine-mcp-protocol = { version = "0.10.0", path = "mcp-protocol" } -pulseengine-mcp-logging = { version = "0.10.0", path = "mcp-logging" } -pulseengine-mcp-auth = { version = "0.10.0", path = "mcp-auth" } -pulseengine-mcp-security = { version = "0.10.0", path = "mcp-security" } -pulseengine-mcp-security-middleware = { version = "0.10.0", path = "mcp-security-middleware" } -pulseengine-mcp-monitoring = { version = "0.10.0", path = "mcp-monitoring" } -pulseengine-mcp-transport = { version = "0.10.0", path = "mcp-transport" } -pulseengine-mcp-cli = { version = "0.10.0", path = "mcp-cli" } -pulseengine-mcp-cli-derive = { version = "0.10.0", path = "mcp-cli-derive" } -pulseengine-mcp-server = { version = "0.10.0", path = "mcp-server" } -pulseengine-mcp-macros = { version = "0.10.0", path = "mcp-macros" } -pulseengine-mcp-external-validation = { version = "0.10.0", path = "mcp-external-validation" } +pulseengine-mcp-protocol = { version = "0.10.1", path = "mcp-protocol" } +pulseengine-mcp-logging = { version = "0.10.1", path = "mcp-logging" } +pulseengine-mcp-auth = { version = "0.10.1", path = "mcp-auth" } +pulseengine-mcp-security = { version = "0.10.1", path = "mcp-security" } +pulseengine-mcp-security-middleware = { version = "0.10.1", path = "mcp-security-middleware" } +pulseengine-mcp-monitoring = { version = "0.10.1", path = "mcp-monitoring" } +pulseengine-mcp-transport = { version = "0.10.1", path = "mcp-transport" } +pulseengine-mcp-cli = { version = "0.10.1", path = "mcp-cli" } +pulseengine-mcp-cli-derive = { version = "0.10.1", path = "mcp-cli-derive" } +pulseengine-mcp-server = { version = "0.10.1", path = "mcp-server" } +pulseengine-mcp-macros = { version = "0.10.1", path = "mcp-macros" } +pulseengine-mcp-external-validation = { version = "0.10.1", path = "mcp-external-validation" } [profile.release] opt-level = "s" diff --git a/examples/hello-world-with-auth/Cargo.toml b/examples/hello-world-with-auth/Cargo.toml index cb4532af..03944f33 100644 --- a/examples/hello-world-with-auth/Cargo.toml +++ b/examples/hello-world-with-auth/Cargo.toml @@ -18,7 +18,9 @@ tracing-subscriber = { workspace = true } anyhow = { workspace = true } # Serialization (required by mcp_tools macro) +serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } +schemars = { workspace = true, features = ["derive"] } # HTTP server axum = { workspace = true } diff --git a/examples/hello-world-with-auth/src/main.rs b/examples/hello-world-with-auth/src/main.rs index 818692e7..f396ffe9 100644 --- a/examples/hello-world-with-auth/src/main.rs +++ b/examples/hello-world-with-auth/src/main.rs @@ -29,8 +29,16 @@ use pulseengine_mcp_macros::{mcp_server, mcp_tools}; use pulseengine_mcp_security_middleware::*; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; use tracing::{info, warn}; +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct SayHelloParams { + /// The name to greet (optional) + pub name: Option, +} + #[mcp_server(name = "Hello World with Auth")] #[derive(Default, Clone)] pub struct HelloWorldAuth; @@ -41,8 +49,10 @@ impl HelloWorldAuth { /// /// This tool demonstrates how authentication context can be used in tools. /// In development mode, authentication is optional but logged when present. - pub async fn say_hello(&self, name: Option) -> anyhow::Result { - let name = name.unwrap_or_else(|| "Authenticated World".to_string()); + pub async fn say_hello(&self, params: SayHelloParams) -> anyhow::Result { + let name = params + .name + .unwrap_or_else(|| "Authenticated World".to_string()); // In a real implementation, you could access the auth context here // let auth = request_context.auth_context(); diff --git a/examples/hello-world/Cargo.toml b/examples/hello-world/Cargo.toml index 92080c8e..314b02a8 100644 --- a/examples/hello-world/Cargo.toml +++ b/examples/hello-world/Cargo.toml @@ -18,4 +18,6 @@ async-trait = "0.1" thiserror = "1.0" tracing = "0.1" tracing-subscriber = "0.3" +serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +schemars = { version = "1.0", features = ["derive"] } diff --git a/examples/hello-world/src/main.rs b/examples/hello-world/src/main.rs index 37f2d1d8..359e1f2c 100644 --- a/examples/hello-world/src/main.rs +++ b/examples/hello-world/src/main.rs @@ -5,6 +5,14 @@ use pulseengine_mcp_macros::{mcp_server, mcp_tools}; use pulseengine_mcp_server::McpServerBuilder; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct SayHelloParams { + /// The name to greet (optional) + pub name: Option, +} #[mcp_server(name = "Hello World")] #[derive(Default, Clone)] @@ -13,8 +21,8 @@ pub struct HelloWorld; #[mcp_tools] impl HelloWorld { /// Say hello to someone - pub async fn say_hello(&self, name: Option) -> anyhow::Result { - let name = name.unwrap_or_else(|| "World".to_string()); + pub async fn say_hello(&self, params: SayHelloParams) -> anyhow::Result { + let name = params.name.unwrap_or_else(|| "World".to_string()); Ok(format!("Hello, {name}!")) } } diff --git a/examples/test-tools-server/Cargo.toml b/examples/test-tools-server/Cargo.toml index dfa636ce..60f0b223 100644 --- a/examples/test-tools-server/Cargo.toml +++ b/examples/test-tools-server/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" pulseengine-mcp-macros = { path = "../../mcp-macros" } pulseengine-mcp-server = { path = "../../mcp-server" } pulseengine-mcp-protocol = { path = "../../mcp-protocol" } +schemars = { workspace = true } tokio = { version = "1.0", features = ["full"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/examples/ultra-simple/Cargo.toml b/examples/ultra-simple/Cargo.toml index 4dc42e89..152af5d5 100644 --- a/examples/ultra-simple/Cargo.toml +++ b/examples/ultra-simple/Cargo.toml @@ -12,6 +12,8 @@ pulseengine-mcp-macros = { path = "../../mcp-macros" } pulseengine-mcp-server = { path = "../../mcp-server" } pulseengine-mcp-protocol = { path = "../../mcp-protocol" } async-trait = "0.1" +serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +schemars = { version = "1.0", features = ["derive"] } tokio = { version = "1.0", features = ["full"] } anyhow = "1.0" \ No newline at end of file diff --git a/examples/ultra-simple/src/main.rs b/examples/ultra-simple/src/main.rs index 03c741b7..a3af52c9 100644 --- a/examples/ultra-simple/src/main.rs +++ b/examples/ultra-simple/src/main.rs @@ -4,6 +4,24 @@ use pulseengine_mcp_macros::{mcp_server, mcp_tools}; use pulseengine_mcp_server::McpServerBuilder; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct SayHelloParams { + /// The name to greet + pub name: String, + /// Optional greeting to use (defaults to "Hello") + pub greeting: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct AddParams { + /// First number + pub a: i32, + /// Second number + pub b: i32, +} #[mcp_server(name = "Ultra Simple")] #[derive(Default, Clone)] @@ -12,18 +30,14 @@ pub struct UltraSimple; #[mcp_tools] impl UltraSimple { /// Say hello to someone with customizable greeting - pub async fn say_hello( - &self, - name: String, - greeting: Option, - ) -> anyhow::Result { - let greeting = greeting.unwrap_or_else(|| "Hello".to_string()); - Ok(format!("{greeting}, {name}! 👋")) + pub async fn say_hello(&self, params: SayHelloParams) -> anyhow::Result { + let greeting = params.greeting.unwrap_or_else(|| "Hello".to_string()); + Ok(format!("{greeting}, {}! 👋", params.name)) } /// Add two numbers together - pub fn add(&self, a: i32, b: i32) -> i32 { - a + b + pub fn add(&self, params: AddParams) -> i32 { + params.a + params.b } /// Get the answer to the ultimate question diff --git a/mcp-macros/src/mcp_tool.rs b/mcp-macros/src/mcp_tool.rs index 52d50107..511cec6b 100644 --- a/mcp-macros/src/mcp_tool.rs +++ b/mcp-macros/src/mcp_tool.rs @@ -762,71 +762,111 @@ fn generate_tool_implementation( /// Generate JSON schema for method parameters from function signature fn generate_input_schema_for_method(sig: &syn::Signature) -> syn::Result { - let mut properties = Vec::new(); - let mut required = Vec::new(); - - // Process function parameters (skip self) - for input in &sig.inputs { - if let syn::FnArg::Typed(pat_type) = input { - if let syn::Pat::Ident(pat_ident) = &*pat_type.pat { - let param_name = pat_ident.ident.to_string(); - - // Determine if parameter is optional (Option) - let (is_optional, _inner_type) = extract_option_inner_type(&pat_type.ty); + // Collect non-self parameters + let params: Vec<_> = sig + .inputs + .iter() + .filter_map(|input| { + if let syn::FnArg::Typed(pat_type) = input { + Some(pat_type) + } else { + None + } + }) + .collect(); - if !is_optional { - required.push(param_name.clone()); + match params.len() { + 0 => { + // No parameters - return empty schema + Ok(quote! { + serde_json::json!({ + "type": "object", + "properties": {} + }) + }) + } + 1 => { + // Single parameter - use JsonSchema trait (requires JsonSchema derive) + let param_type = ¶ms[0].ty; + Ok(quote! { + { + use ::schemars::JsonSchema; + // Use the JsonSchema trait to get the schema + let mut schema_gen = ::schemars::SchemaGenerator::default(); + let schema = <#param_type as ::schemars::JsonSchema>::json_schema(&mut schema_gen); + ::serde_json::to_value(&schema).unwrap_or_else(|_| + ::serde_json::json!({"type": "object", "properties": {}}) + ) } - - // Generate schema based on type - let type_schema = generate_type_schema(&pat_type.ty)?; - - properties.push((param_name, type_schema)); - } + }) + } + _ => { + // Multiple parameters - generate schema from individual parameter types + generate_multi_parameter_schema(¶ms) } } +} - let properties_map = if properties.is_empty() { - quote! { serde_json::Map::new() } - } else { - let prop_insertions: Vec<_> = properties - .into_iter() - .map(|(name, schema)| { - quote! { props.insert(#name.to_string(), #schema); } - }) - .collect(); - - quote! { - { - let mut props = serde_json::Map::new(); - #(#prop_insertions)* - props +/// Generate JSON schema for multiple individual parameters +fn generate_multi_parameter_schema(params: &[&syn::PatType]) -> syn::Result { + let mut properties = Vec::new(); + let mut required_fields = Vec::new(); + + for param in params { + // Extract parameter name + let param_name = match &*param.pat { + syn::Pat::Ident(ident) => ident.ident.to_string(), + _ => { + return Err(syn::Error::new_spanned( + param.pat.clone(), + "Complex parameter patterns are not supported", + )); } + }; + + // Extract parameter type and determine if it's optional + let param_type = ¶m.ty; + let (is_optional, inner_type) = extract_option_inner_type(param_type); + + if !is_optional { + required_fields.push(param_name.clone()); } - }; - let required_array = if required.is_empty() { - quote! { Vec::::new() } - } else { - quote! { vec![#(#required.to_string()),*] } - }; + // Generate schema for this parameter type + let type_schema = + generate_type_schema_for_type(if is_optional { inner_type } else { param_type }); + + properties.push(quote! { + (#param_name, #type_schema) + }); + } Ok(quote! { { - let mut schema = serde_json::Map::new(); - schema.insert("type".to_string(), serde_json::Value::String("object".to_string())); - schema.insert("properties".to_string(), serde_json::Value::Object(#properties_map)); - schema.insert("required".to_string(), serde_json::Value::Array(#required_array.into_iter().map(serde_json::Value::String).collect())); - serde_json::Value::Object(schema) + let mut properties = ::serde_json::Map::new(); + #( + properties.insert(#properties.0.to_string(), #properties.1); + )* + + let mut schema = ::serde_json::json!({ + "type": "object", + "properties": properties + }); + + let required_fields: Vec<&str> = vec![#(#required_fields),*]; + if !required_fields.is_empty() { + schema["required"] = ::serde_json::json!(required_fields); + } + + schema } }) } -/// Extract inner type from Option, returns (is_optional, inner_type_or_original) +/// Check if a type is Option and extract the inner type T fn extract_option_inner_type(ty: &syn::Type) -> (bool, &syn::Type) { if let syn::Type::Path(type_path) = ty { - if type_path.path.segments.len() == 1 { - let segment = &type_path.path.segments[0]; + if let Some(segment) = type_path.path.segments.last() { if segment.ident == "Option" { if let syn::PathArguments::AngleBracketed(args) = &segment.arguments { if let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() { @@ -839,44 +879,57 @@ fn extract_option_inner_type(ty: &syn::Type) -> (bool, &syn::Type) { (false, ty) } -/// Generate JSON schema for a specific Rust type -fn generate_type_schema(ty: &syn::Type) -> syn::Result { - let (_, actual_type) = extract_option_inner_type(ty); - - // Handle common types - if let syn::Type::Path(type_path) = actual_type { - if type_path.path.segments.len() == 1 { - let segment = &type_path.path.segments[0]; - let type_name = segment.ident.to_string(); - - match type_name.as_str() { - "String" | "str" => { - return Ok( - quote! { serde_json::Value::Object(serde_json::Map::from_iter([("type".to_string(), serde_json::Value::String("string".to_string()))])) }, - ); - } - "i32" | "i64" | "i8" | "i16" | "isize" | "u32" | "u64" | "u8" | "u16" | "usize" => { - return Ok( - quote! { serde_json::Value::Object(serde_json::Map::from_iter([("type".to_string(), serde_json::Value::String("integer".to_string()))])) }, - ); - } - "f32" | "f64" => { - return Ok( - quote! { serde_json::Value::Object(serde_json::Map::from_iter([("type".to_string(), serde_json::Value::String("number".to_string()))])) }, - ); - } - "bool" => { - return Ok( - quote! { serde_json::Value::Object(serde_json::Map::from_iter([("type".to_string(), serde_json::Value::String("boolean".to_string()))])) }, - ); +/// Generate JSON schema for a specific type +fn generate_type_schema_for_type(ty: &syn::Type) -> TokenStream { + // Convert Rust type to JSON schema + match ty { + syn::Type::Path(type_path) => { + if let Some(segment) = type_path.path.segments.last() { + match segment.ident.to_string().as_str() { + "String" | "str" => quote! { ::serde_json::json!({"type": "string"}) }, + "i8" | "i16" | "i32" | "i64" | "isize" => { + quote! { ::serde_json::json!({"type": "integer"}) } + } + "u8" | "u16" | "u32" | "u64" | "usize" => { + quote! { ::serde_json::json!({"type": "integer", "minimum": 0}) } + } + "f32" | "f64" => quote! { ::serde_json::json!({"type": "number"}) }, + "bool" => quote! { ::serde_json::json!({"type": "boolean"}) }, + "Vec" => { + // Handle Vec - extract T and create array schema + if let syn::PathArguments::AngleBracketed(args) = &segment.arguments { + if let Some(syn::GenericArgument::Type(inner_ty)) = args.args.first() { + let items_schema = generate_type_schema_for_type(inner_ty); + return quote! { ::serde_json::json!({"type": "array", "items": #items_schema}) }; + } + } + quote! { ::serde_json::json!({"type": "array"}) } + } + _ => { + // For custom types, try to use JsonSchema trait as fallback + quote! { + { + // Try to use JsonSchema trait for custom types + match std::panic::catch_unwind(|| { + use ::schemars::JsonSchema; + let mut schema_gen = ::schemars::SchemaGenerator::default(); + <#ty as ::schemars::JsonSchema>::json_schema(&mut schema_gen) + }) { + Ok(schema) => ::serde_json::to_value(&schema).unwrap_or_else(|_| + ::serde_json::json!({"type": "object"}) + ), + Err(_) => ::serde_json::json!({"type": "object"}) + } + } + } + } } - _ => {} + } else { + quote! { ::serde_json::json!({"type": "object"}) } } } + _ => quote! { ::serde_json::json!({"type": "object"}) }, } - - // Default to allowing any type for complex types - Ok(quote! { serde_json::Value::Object(serde_json::Map::new()) }) } /// Generate JSON schema for method parameters (legacy function - keeping for compatibility) diff --git a/mcp-macros/tests/async_sync_tests.rs b/mcp-macros/tests/async_sync_tests.rs index d6122e32..cf3b7353 100644 --- a/mcp-macros/tests/async_sync_tests.rs +++ b/mcp-macros/tests/async_sync_tests.rs @@ -2,6 +2,66 @@ use pulseengine_mcp_macros::{mcp_server, mcp_tools}; use pulseengine_mcp_server::McpServerBuilder; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct StringParam { + pub input: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct I32Param { + pub value: i32, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct ComplexAsyncParams { + pub name: String, + pub age: u32, + pub active: bool, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct ComplexSyncParams { + pub required: String, + pub optional: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct CalculateParams { + pub a: f64, + pub b: f64, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct FormatTextParams { + pub text: String, + pub uppercase: bool, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct MultipleParams { + pub a: String, + pub b: i32, + pub c: bool, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct OptionalParams { + pub required: String, + pub optional: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct VecParams { + pub items: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct JsonParam { + pub data: serde_json::Value, +} #[test] fn test_mixed_async_sync_server() { @@ -13,55 +73,57 @@ fn test_mixed_async_sync_server() { #[allow(dead_code)] impl MixedServer { /// Synchronous tool - pub fn sync_tool(&self, input: String) -> String { - format!("Sync: {input}") + pub fn sync_tool(&self, params: StringParam) -> String { + format!("Sync: {}", params.input) } /// Asynchronous tool - pub async fn async_tool(&self, input: String) -> String { + pub async fn async_tool(&self, params: StringParam) -> String { tokio::time::sleep(tokio::time::Duration::from_millis(1)).await; - format!("Async: {input}") + format!("Async: {}", params.input) } /// Synchronous tool with Result - pub fn sync_result_tool(&self, value: i32) -> Result { - if value < 0 { + pub fn sync_result_tool(&self, params: I32Param) -> Result { + if params.value < 0 { Err(std::io::Error::new( std::io::ErrorKind::InvalidInput, "Negative value", )) } else { - Ok(value * 2) + Ok(params.value * 2) } } /// Asynchronous tool with Result - pub async fn async_result_tool(&self, value: i32) -> Result { + pub async fn async_result_tool(&self, params: I32Param) -> Result { tokio::time::sleep(tokio::time::Duration::from_millis(1)).await; - if value == 0 { + if params.value == 0 { Err(std::io::Error::new( std::io::ErrorKind::InvalidInput, "Zero value", )) } else { - Ok(value * 3) + Ok(params.value * 3) } } /// Complex async tool with multiple parameters - pub async fn complex_async_tool(&self, name: String, age: u32, active: bool) -> String { + pub async fn complex_async_tool(&self, params: ComplexAsyncParams) -> String { tokio::time::sleep(tokio::time::Duration::from_millis(1)).await; format!( - "User {name} is {age} years old and {}", - if active { "active" } else { "inactive" } + "User {} is {} years old and {}", + params.name, + params.age, + if params.active { "active" } else { "inactive" } ) } /// Complex sync tool with optional parameters - pub fn complex_sync_tool(&self, required: String, optional: Option) -> String { - match optional { - Some(opt) => format!("Required: {required}, Optional: {opt}"), - None => format!("Required: {required}, Optional: None"), + pub fn complex_sync_tool(&self, params: ComplexSyncParams) -> String { + match params.optional { + Some(opt) => format!("Required: {}, Optional: {}", params.required, opt), + None => format!("Required: {}, Optional: None", params.required), } } } @@ -79,26 +141,26 @@ fn test_pure_sync_server() { #[allow(dead_code)] impl PureSyncServer { /// All tools are synchronous - pub fn calculate(&self, a: f64, b: f64) -> f64 { - a + b + pub fn calculate(&self, params: CalculateParams) -> f64 { + params.a + params.b } - pub fn format_text(&self, text: String, uppercase: bool) -> String { - if uppercase { - text.to_uppercase() + pub fn format_text(&self, params: FormatTextParams) -> String { + if params.uppercase { + params.text.to_uppercase() } else { - text.to_lowercase() + params.text.to_lowercase() } } - pub fn validate_input(&self, input: String) -> Result { - if input.len() < 3 { + pub fn validate_input(&self, params: StringParam) -> Result { + if params.input.len() < 3 { Err(std::io::Error::new( std::io::ErrorKind::InvalidInput, "Input too short", )) } else { - Ok(format!("Valid: {input}")) + Ok(format!("Valid: {}", params.input)) } } } @@ -163,28 +225,31 @@ fn test_parameter_combinations() { } // Single parameter - pub fn single_param(&self, input: String) -> String { - input + pub fn single_param(&self, params: StringParam) -> String { + params.input } // Multiple parameters - pub fn multiple_params(&self, a: String, b: i32, c: bool) -> String { - format!("{a}-{b}-{c}") + pub fn multiple_params(&self, params: MultipleParams) -> String { + format!("{}-{}-{}", params.a, params.b, params.c) } // Optional parameters - pub fn optional_params(&self, required: String, optional: Option) -> String { - format!("Required: {required}, Optional: {optional:?}") + pub fn optional_params(&self, params: OptionalParams) -> String { + format!( + "Required: {}, Optional: {:?}", + params.required, params.optional + ) } // Vector parameters - pub fn vec_params(&self, items: Vec) -> String { - items.join(",") + pub fn vec_params(&self, params: VecParams) -> String { + params.items.join(",") } // JSON parameter - pub fn json_param(&self, data: serde_json::Value) -> String { - data.to_string() + pub fn json_param(&self, params: JsonParam) -> String { + params.data.to_string() } } diff --git a/mcp-macros/tests/core_functionality.rs b/mcp-macros/tests/core_functionality.rs index defeaf4b..9f72f0ce 100644 --- a/mcp-macros/tests/core_functionality.rs +++ b/mcp-macros/tests/core_functionality.rs @@ -13,6 +13,14 @@ use pulseengine_mcp_macros::{mcp_server, mcp_tools}; use pulseengine_mcp_server::McpServerBuilder; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct ToolParams { + pub message: String, + pub count: Option, +} #[mcp_server(name = "Test Server")] #[derive(Default, Clone)] @@ -26,13 +34,9 @@ impl TestServer { } #[allow(dead_code)] - pub async fn tool_with_params( - &self, - message: String, - count: Option, - ) -> anyhow::Result { - let count = count.unwrap_or(1); - Ok(format!("{message} (repeated {count} times)")) + pub async fn tool_with_params(&self, params: ToolParams) -> anyhow::Result { + let count = params.count.unwrap_or(1); + Ok(format!("{} (repeated {} times)", params.message, count)) } } diff --git a/mcp-macros/tests/dual_pattern_test.rs b/mcp-macros/tests/dual_pattern_test.rs new file mode 100644 index 00000000..531d92b7 --- /dev/null +++ b/mcp-macros/tests/dual_pattern_test.rs @@ -0,0 +1,181 @@ +//! Test that all three parameter patterns work correctly: +//! 1. Multi-parameter (auto-generated schema) +//! 2. Struct with JsonSchema (uses JsonSchema trait) +//! 3. Struct without JsonSchema (fallback schema) + +use pulseengine_mcp_macros::{mcp_server, mcp_tools}; +use pulseengine_mcp_server::McpServerBuilder; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +/// Pattern 2: Struct with JsonSchema derive (rich schema) +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct RichStructParams { + /// Required message with description + pub message: String, + /// Optional count with validation + pub count: Option, +} + +/// Pattern 2b: Another JsonSchema struct to show variety +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct AnotherRichParams { + pub title: String, + pub enabled: bool, +} + +#[mcp_server(name = "Dual Pattern Test Server")] +#[derive(Default, Clone)] +struct DualPatternServer; + +#[mcp_tools] +impl DualPatternServer { + /// Pattern 1: Multi-parameter (auto-generated schema) + pub fn multi_param_tool(&self, name: String, age: u32, active: bool) -> String { + format!("Name: {name}, Age: {age}, Active: {active}") + } + + /// Pattern 2: Rich struct with JsonSchema (uses JsonSchema trait) + pub fn rich_struct_tool(&self, params: RichStructParams) -> String { + format!("Message: {}, Count: {:?}", params.message, params.count) + } + + /// Pattern 2b: Another JsonSchema struct + pub fn another_rich_tool(&self, params: AnotherRichParams) -> String { + format!("Title: {}, Enabled: {}", params.title, params.enabled) + } + + /// Test multi-parameter with optional (should detect optional correctly) + pub fn optional_multi_tool(&self, title: String, description: Option) -> String { + match description { + Some(desc) => format!("Title: {title}, Description: {desc}"), + None => format!("Title: {title}"), + } + } + + /// Test complex types in multi-parameter + pub fn complex_multi_tool(&self, items: Vec, metadata: serde_json::Value) -> String { + format!("Items: {items:?}, Metadata: {metadata}") + } +} + +#[tokio::test] +async fn test_dual_pattern_compilation() { + let server = DualPatternServer::with_defaults(); + let info = server.get_server_info(); + assert_eq!(info.server_info.name, "Dual Pattern Test Server"); +} + +#[tokio::test] +async fn test_all_three_patterns_generate_schemas() { + let server = DualPatternServer::with_defaults(); + + if let Some(tools) = server.try_get_tools_default() { + assert!(!tools.is_empty()); + + // Find tools + let multi_param_tool = tools.iter().find(|t| t.name == "multi_param_tool"); + let rich_struct_tool = tools.iter().find(|t| t.name == "rich_struct_tool"); + let another_rich_tool = tools.iter().find(|t| t.name == "another_rich_tool"); + let optional_multi_tool = tools.iter().find(|t| t.name == "optional_multi_tool"); + + assert!( + multi_param_tool.is_some(), + "multi_param_tool should be found" + ); + assert!( + rich_struct_tool.is_some(), + "rich_struct_tool should be found" + ); + assert!( + another_rich_tool.is_some(), + "another_rich_tool should be found" + ); + assert!( + optional_multi_tool.is_some(), + "optional_multi_tool should be found" + ); + + // Pattern 1: Multi-parameter schema (auto-generated) + let multi_schema = &multi_param_tool.unwrap().input_schema; + println!( + "Pattern 1 (Multi-parameter): {}", + serde_json::to_string_pretty(multi_schema).unwrap() + ); + + assert_eq!(multi_schema["type"].as_str().unwrap(), "object"); + let multi_properties = multi_schema["properties"].as_object().unwrap(); + assert!(multi_properties.contains_key("name")); + assert!(multi_properties.contains_key("age")); + assert!(multi_properties.contains_key("active")); + + // Check types + assert_eq!(multi_properties["name"]["type"].as_str().unwrap(), "string"); + assert_eq!(multi_properties["age"]["type"].as_str().unwrap(), "integer"); + assert_eq!( + multi_properties["active"]["type"].as_str().unwrap(), + "boolean" + ); + + // Pattern 2: Rich struct schema (JsonSchema trait) + let rich_schema = &rich_struct_tool.unwrap().input_schema; + println!( + "Pattern 2 (Rich JsonSchema): {}", + serde_json::to_string_pretty(rich_schema).unwrap() + ); + + assert_eq!(rich_schema["type"].as_str().unwrap(), "object"); + let rich_properties = rich_schema["properties"].as_object().unwrap(); + assert!(rich_properties.contains_key("message")); + assert!(rich_properties.contains_key("count")); + + // Rich schema should have descriptions + assert!(rich_properties["message"].get("description").is_some()); + assert!(rich_properties["count"].get("description").is_some()); + + let rich_required = rich_schema["required"].as_array().unwrap(); + assert!( + rich_required + .iter() + .any(|v| v.as_str().unwrap() == "message") + ); + assert!(!rich_required.iter().any(|v| v.as_str().unwrap() == "count")); // count is optional + + // Pattern 2b: Another JsonSchema struct + let another_schema = &another_rich_tool.unwrap().input_schema; + println!( + "Pattern 2b (Another JsonSchema): {}", + serde_json::to_string_pretty(another_schema).unwrap() + ); + + assert_eq!(another_schema["type"].as_str().unwrap(), "object"); + let another_properties = another_schema["properties"].as_object().unwrap(); + assert!(another_properties.contains_key("title")); + assert!(another_properties.contains_key("enabled")); + + // Test optional detection in multi-parameter + let optional_schema = &optional_multi_tool.unwrap().input_schema; + println!( + "Optional multi-parameter schema: {}", + serde_json::to_string_pretty(optional_schema).unwrap() + ); + + let optional_required = optional_schema["required"].as_array().unwrap(); + assert!( + optional_required + .iter() + .any(|v| v.as_str().unwrap() == "title") + ); // required + assert!( + !optional_required + .iter() + .any(|v| v.as_str().unwrap() == "description") + ); // optional + + println!("✅ Both patterns work correctly and generate schemas!"); + println!(" 1. Multi-parameter → Auto-generated from types"); + println!(" 2. JsonSchema struct → Rich schema with descriptions"); + } else { + panic!("Tools should be available"); + } +} diff --git a/mcp-macros/tests/mcp_resource_tests.rs b/mcp-macros/tests/mcp_resource_tests.rs index 0ab2ec53..9e1e4885 100644 --- a/mcp-macros/tests/mcp_resource_tests.rs +++ b/mcp-macros/tests/mcp_resource_tests.rs @@ -1,6 +1,25 @@ //! Tests for resource-related functionality with macro-generated code use pulseengine_mcp_macros::{mcp_server, mcp_tools}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct PathParam { + pub path: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct DatabaseTableParams { + pub database: String, + pub table: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct ApiDataParams { + pub endpoint: String, + pub version: String, +} mod basic_resource { use super::*; @@ -12,14 +31,14 @@ mod basic_resource { #[mcp_tools] impl ResourceServer { /// Read a file from the filesystem - pub async fn read_file(&self, path: String) -> Result { - if path.is_empty() { + pub async fn read_file(&self, params: PathParam) -> Result { + if params.path.is_empty() { return Err(std::io::Error::new( std::io::ErrorKind::InvalidInput, "Path cannot be empty", )); } - Ok(format!("Content of file: {path}")) + Ok(format!("Content of file: {}", params.path)) } } } @@ -36,25 +55,23 @@ mod complex_resource { /// Read database table contents pub async fn read_database_table( &self, - database: String, - table: String, + params: DatabaseTableParams, ) -> Result { - if database.is_empty() || table.is_empty() { + if params.database.is_empty() || params.table.is_empty() { return Err(std::io::Error::new( std::io::ErrorKind::InvalidInput, "Database and table names cannot be empty", )); } - Ok(format!("Data from {database}.{table}")) + Ok(format!("Data from {}.{}", params.database, params.table)) } /// Get API data from external service - pub async fn get_api_data( - &self, - endpoint: String, - version: String, - ) -> Result { - Ok(format!("API data from {endpoint} (version {version})")) + pub async fn get_api_data(&self, params: ApiDataParams) -> Result { + Ok(format!( + "API data from {} (version {})", + params.endpoint, params.version + )) } } } @@ -103,12 +120,20 @@ mod tests { let server = ResourceServer::with_defaults(); // Test valid path - let result = server.read_file("test.txt".to_string()).await; + let result = server + .read_file(PathParam { + path: "test.txt".to_string(), + }) + .await; assert!(result.is_ok()); assert!(result.unwrap().contains("test.txt")); // Test empty path - let result = server.read_file("".to_string()).await; + let result = server + .read_file(PathParam { + path: "".to_string(), + }) + .await; assert!(result.is_err()); } @@ -118,14 +143,20 @@ mod tests { // Test database table access let result = server - .read_database_table("users".to_string(), "accounts".to_string()) + .read_database_table(DatabaseTableParams { + database: "users".to_string(), + table: "accounts".to_string(), + }) .await; assert!(result.is_ok()); assert!(result.unwrap().contains("users.accounts")); // Test API data access let result = server - .get_api_data("https://api.example.com".to_string(), "v1".to_string()) + .get_api_data(ApiDataParams { + endpoint: "https://api.example.com".to_string(), + version: "v1".to_string(), + }) .await; assert!(result.is_ok()); assert!(result.unwrap().contains("api.example.com")); diff --git a/mcp-macros/tests/performance_tests.rs b/mcp-macros/tests/performance_tests.rs index 5db2f538..c3992987 100644 --- a/mcp-macros/tests/performance_tests.rs +++ b/mcp-macros/tests/performance_tests.rs @@ -2,10 +2,49 @@ use pulseengine_mcp_macros::{mcp_server, mcp_tools}; use pulseengine_mcp_server::McpServerBuilder; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; use std::sync::Arc; use std::sync::atomic::{AtomicU64, Ordering}; use tokio::time::{Duration, Instant}; +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct VecStringParams { + pub keys: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct UsizeParams { + pub size: usize, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct U64Params { + pub iterations: u64, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct DurationParams { + pub duration_ms: u64, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct U32Params { + pub operations: u32, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct ResourceParams { + pub resource_type: String, + pub resource_id: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct PromptParams { + pub query: String, + pub optimization_level: String, +} + mod performance_server { use super::*; @@ -43,7 +82,8 @@ mod performance_server { } /// Bulk data lookup operation - pub async fn bulk_lookup(&self, keys: Vec) -> String { + pub async fn bulk_lookup(&self, params: VecStringParams) -> String { + let keys = params.keys; let mut results = Vec::new(); for key in keys { results.push(self.data.get(&key).cloned()); @@ -52,7 +92,8 @@ mod performance_server { } /// Memory-intensive operation - pub async fn memory_intensive(&self, size: usize) -> String { + pub async fn memory_intensive(&self, params: UsizeParams) -> String { + let size = params.size; let _data: Vec = vec![42; size]; let checksum = if size > 0 { 42u64 * (size as u64 % 100) @@ -63,7 +104,8 @@ mod performance_server { } /// CPU-intensive operation - pub async fn cpu_intensive(&self, iterations: u64) -> u64 { + pub async fn cpu_intensive(&self, params: U64Params) -> u64 { + let iterations = params.iterations; let mut result = 0u64; for i in 0..iterations { result = result.wrapping_add(i * i); @@ -72,13 +114,15 @@ mod performance_server { } /// Simulated I/O operation - pub async fn simulated_io(&self, duration_ms: u64) -> String { + pub async fn simulated_io(&self, params: DurationParams) -> String { + let duration_ms = params.duration_ms; tokio::time::sleep(Duration::from_millis(duration_ms)).await; format!("IO operation completed after {duration_ms}ms") } /// Concurrent data access - pub async fn concurrent_access(&self, operations: u32) -> String { + pub async fn concurrent_access(&self, params: U32Params) -> String { + let operations = params.operations; let mut results = Vec::new(); for _ in 0..operations { let value = self.counter.fetch_add(1, Ordering::Relaxed); @@ -90,10 +134,9 @@ mod performance_server { /// Performance resource access pub async fn performance_resource( &self, - resource_type: String, - resource_id: String, + params: ResourceParams, ) -> Result { - if resource_type.is_empty() || resource_id.is_empty() { + if params.resource_type.is_empty() || params.resource_id.is_empty() { return Err(std::io::Error::new( std::io::ErrorKind::InvalidInput, "Resource type and ID cannot be empty", @@ -106,18 +149,16 @@ mod performance_server { let elapsed = start.elapsed(); Ok(format!( - "Resource {resource_type}/{resource_id} accessed in {elapsed:?}" + "Resource {}/{} accessed in {elapsed:?}", + params.resource_type, params.resource_id )) } /// Generate performance prompt - pub async fn performance_prompt( - &self, - query: String, - optimization_level: String, - ) -> String { + pub async fn performance_prompt(&self, params: PromptParams) -> String { format!( - "Performance analysis for '{query}' with optimization level: {optimization_level}" + "Performance analysis for '{}' with optimization level: {}", + params.query, params.optimization_level ) } } @@ -160,7 +201,7 @@ mod tests { "key_999".to_string(), "nonexistent".to_string(), ]; - let results = server.bulk_lookup(keys).await; + let results = server.bulk_lookup(VecStringParams { keys }).await; // Results is now a debug-formatted string assert!(results.contains("value_1")); @@ -174,11 +215,11 @@ mod tests { let server = PerformanceServer::with_defaults(); // Test memory intensive - let memory_result = server.memory_intensive(1000).await; + let memory_result = server.memory_intensive(UsizeParams { size: 1000 }).await; assert!(memory_result.contains("1000 bytes")); // Test CPU intensive - let cpu_result = server.cpu_intensive(100).await; + let cpu_result = server.cpu_intensive(U64Params { iterations: 100 }).await; assert!(cpu_result > 0); } @@ -187,7 +228,9 @@ mod tests { let server = PerformanceServer::with_defaults(); let start = Instant::now(); - let result = server.simulated_io(50).await; + let result = server + .simulated_io(DurationParams { duration_ms: 50 }) + .await; let elapsed = start.elapsed(); assert!(result.contains("50ms")); @@ -198,7 +241,7 @@ mod tests { async fn test_concurrent_access() { let server = PerformanceServer::with_defaults(); - let results = server.concurrent_access(10).await; + let results = server.concurrent_access(U32Params { operations: 10 }).await; // Results is now a debug-formatted string of Vec // Just check that it contains some numbers @@ -211,14 +254,20 @@ mod tests { let server = PerformanceServer::with_defaults(); let result = server - .performance_resource("cache".to_string(), "item_1".to_string()) + .performance_resource(ResourceParams { + resource_type: "cache".to_string(), + resource_id: "item_1".to_string(), + }) .await; assert!(result.is_ok()); assert!(result.unwrap().contains("cache/item_1")); // Test error case let result = server - .performance_resource("".to_string(), "item_1".to_string()) + .performance_resource(ResourceParams { + resource_type: "".to_string(), + resource_id: "item_1".to_string(), + }) .await; assert!(result.is_err()); } @@ -228,7 +277,10 @@ mod tests { let server = PerformanceServer::with_defaults(); let result = server - .performance_prompt("database query".to_string(), "O3".to_string()) + .performance_prompt(PromptParams { + query: "database query".to_string(), + optimization_level: "O3".to_string(), + }) .await; assert!(result.contains("database query")); assert!(result.contains("O3")); diff --git a/mcp-macros/tests/schema_fix_test.rs b/mcp-macros/tests/schema_fix_test.rs new file mode 100644 index 00000000..ab939c71 --- /dev/null +++ b/mcp-macros/tests/schema_fix_test.rs @@ -0,0 +1,65 @@ +//! Test for the JsonSchema fix + +use pulseengine_mcp_macros::{mcp_server, mcp_tools}; +use pulseengine_mcp_server::McpServerBuilder; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct TestParams { + pub message: String, + pub count: Option, +} + +#[mcp_server(name = "Schema Fix Test Server")] +#[derive(Clone, Default)] +struct SchemaTestServer; + +#[mcp_tools] +impl SchemaTestServer { + /// Test tool with JsonSchema parameters + pub fn test_schema(&self, params: TestParams) -> String { + format!("Message: {}, Count: {:?}", params.message, params.count) + } + + /// Tool with no parameters + pub fn no_params(&self) -> String { + "No parameters needed".to_string() + } +} + +#[test] +fn test_schema_generation_works() { + let server = SchemaTestServer::with_defaults(); + let info = server.get_server_info(); + assert_eq!(info.server_info.name, "Schema Fix Test Server"); + + // Test that tools can be retrieved + if let Some(tools) = server.try_get_tools_default() { + assert!(!tools.is_empty()); + + // Find our test tool + let test_tool = tools.iter().find(|t| t.name == "test_schema"); + assert!(test_tool.is_some(), "test_schema tool should exist"); + + let tool = test_tool.unwrap(); + + // The input schema should not be empty anymore + let schema = &tool.input_schema; + println!( + "Generated schema: {}", + serde_json::to_string_pretty(schema).unwrap() + ); + + // Should be an object type + assert_eq!(schema.get("type").unwrap().as_str().unwrap(), "object"); + + // Should have properties (not empty) + let properties = schema.get("properties").unwrap().as_object().unwrap(); + assert!(!properties.is_empty(), "Properties should not be empty"); + assert!( + properties.contains_key("message"), + "Should have 'message' property" + ); + } +} diff --git a/mcp-macros/tests/tool_discovery_test.rs b/mcp-macros/tests/tool_discovery_test.rs index b46ab7ac..c455a34d 100644 --- a/mcp-macros/tests/tool_discovery_test.rs +++ b/mcp-macros/tests/tool_discovery_test.rs @@ -4,6 +4,26 @@ use pulseengine_mcp_macros::{mcp_server, mcp_tools}; use pulseengine_mcp_server::McpServerBuilder; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +/// Parameter struct for echo tool +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct EchoParams { + pub message: String, +} + +/// Parameter struct for greet tool +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct GreetParams { + pub name: Option, +} + +/// Parameter struct for result tool +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct ResultParams { + pub should_error: Option, +} /// Test server for tool discovery #[mcp_server(name = "Tool Discovery Test Server")] @@ -18,19 +38,19 @@ impl ToolDiscoveryServer { } /// Tool with required parameter - pub fn echo_tool(&self, message: String) -> String { - format!("Echo: {}", message) + pub fn echo_tool(&self, params: EchoParams) -> String { + format!("Echo: {}", params.message) } /// Tool with optional parameter - pub fn greet_tool(&self, name: Option) -> String { - let name = name.unwrap_or_else(|| "World".to_string()); + pub fn greet_tool(&self, params: GreetParams) -> String { + let name = params.name.unwrap_or_else(|| "World".to_string()); format!("Hello, {}!", name) } /// Tool that returns a result - pub fn result_tool(&self, should_error: Option) -> String { - if should_error.unwrap_or(false) { + pub fn result_tool(&self, params: ResultParams) -> String { + if params.should_error.unwrap_or(false) { "Error: Test error".to_string() } else { "Success!".to_string() @@ -120,12 +140,19 @@ async fn test_enhanced_schema_generation() { // Find the greet_tool which has an optional parameter let greet_tool = tools.iter().find(|t| t.name == "greet_tool").unwrap(); let greet_schema = &greet_tool.input_schema; - let greet_required = greet_schema.get("required").unwrap().as_array().unwrap(); - // The optional parameter should NOT be in required array - assert!(!greet_required.iter().any(|v| v.as_str().unwrap() == "name")); + // Check if there's a required array, and if so, ensure "name" is not in it + if let Some(greet_required) = greet_schema.get("required").and_then(|r| r.as_array()) { + // The optional parameter should NOT be in required array + assert!(!greet_required.iter().any(|v| v.as_str().unwrap() == "name")); + } + // If there's no required array, that's fine - all parameters are optional println!("✅ Optional parameter handling verified!"); - println!(" Greet tool required fields: {:?}", greet_required); + if let Some(required) = greet_schema.get("required") { + println!(" Greet tool required fields: {:?}", required); + } else { + println!(" Greet tool required fields: None (all optional)"); + } } } diff --git a/mcp-macros/tests/type_system_tests.rs b/mcp-macros/tests/type_system_tests.rs index 64e1253a..f5ee9ffe 100644 --- a/mcp-macros/tests/type_system_tests.rs +++ b/mcp-macros/tests/type_system_tests.rs @@ -2,6 +2,7 @@ use pulseengine_mcp_macros::{mcp_server, mcp_tools}; use pulseengine_mcp_server::McpServerBuilder; +use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -17,14 +18,14 @@ mod custom_types { pub metadata: HashMap, } - #[derive(Debug, Clone, Serialize, Deserialize)] + #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct CreateUserRequest { pub name: String, pub email: String, pub initial_metadata: Option>, } - #[derive(Debug, Clone, Serialize, Deserialize)] + #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct UpdateUserRequest { pub name: Option, pub email: Option, @@ -32,7 +33,7 @@ mod custom_types { pub metadata_updates: Option>, } - #[derive(Debug, Clone, Serialize, Deserialize)] + #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub enum UserRole { Admin, Moderator, @@ -40,7 +41,7 @@ mod custom_types { Guest, } - #[derive(Debug, Clone, Serialize, Deserialize)] + #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] pub struct PaginationParams { pub limit: Option, pub offset: Option, diff --git a/mcp-security-middleware/Cargo.toml b/mcp-security-middleware/Cargo.toml index fe5b075c..1a4618b3 100644 --- a/mcp-security-middleware/Cargo.toml +++ b/mcp-security-middleware/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pulseengine-mcp-security-middleware" -version = "0.10.0" +version = "0.10.1" rust-version = "1.88" edition = "2024" license = "MIT OR Apache-2.0" diff --git a/security-coverage.info b/security-coverage.info new file mode 100644 index 00000000..b9fbc788 --- /dev/null +++ b/security-coverage.info @@ -0,0 +1,1916 @@ +SF:/Users/r/git/mcp-loxone-seperation/pulseengine-mcp/mcp-security-middleware/src/auth.rs +FN:188,_RNvXs0_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB5_14TokenValidatorNtNtCsd2KPTObkMr_4core3fmt5Debug3fmt +FN:310,_RNvXs3_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB5_15ApiKeyValidatorNtNtCsd2KPTObkMr_4core7default7Default7default +FN:73,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB2_11AuthContext15with_jwt_claims +FN:38,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB2_11AuthContext3new +FN:89,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB2_11AuthContext8has_role +FN:240,_RNvMs1_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB5_14TokenValidator12create_token +FN:218,_RNvMs1_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB5_14TokenValidator14validate_token +FN:199,_RNvMs1_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB5_14TokenValidator3new +FN:265,_RNvMs2_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB5_15ApiKeyValidator11add_api_key +FN:291,_RNvMs2_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB5_15ApiKeyValidator14remove_api_key +FN:275,_RNvMs2_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB5_15ApiKeyValidator16validate_api_key +FN:299,_RNvMs2_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB5_15ApiKeyValidator3len +FN:258,_RNvMs2_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB5_15ApiKeyValidator3new +FN:304,_RNvMs2_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB5_15ApiKeyValidator8is_empty +FN:164,_RNvMs_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB4_9JwtClaims10is_expired +FN:158,_RNvMs_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB4_9JwtClaims10with_roles +FN:141,_RNvMs_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB4_9JwtClaims3new +FN:337,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4auth5testss_15test_jwt_claims +FN:379,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4auth5testss_20test_token_validator +FN:354,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4auth5testss_22test_api_key_validator +FN:320,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4auth5testss_26test_auth_context_creation +FN:57,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB3_11AuthContext10with_rolesINtNtCs7fnLZwG9eJV_5alloc3vec3VecReEB20_EB5_ +FN:94,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB3_11AuthContext12has_any_roleRARej2_EB5_ +FN:67,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB3_11AuthContext12with_api_keyNtNtCs7fnLZwG9eJV_5alloc6string6StringEB5_ +FN:79,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB3_11AuthContext13with_metadataReB1w_EB5_ +FN:51,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB3_11AuthContext9with_roleReEB5_ +FN:62,_RNCINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB5_11AuthContext10with_rolesINtNtCs7fnLZwG9eJV_5alloc3vec3VecReEB22_E0B7_ +FNDA:0,_RNvXs0_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB5_14TokenValidatorNtNtCsd2KPTObkMr_4core3fmt5Debug3fmt +FNDA:0,_RNvXs3_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB5_15ApiKeyValidatorNtNtCsd2KPTObkMr_4core7default7Default7default +FNDA:0,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB2_11AuthContext15with_jwt_claims +FNDA:1,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB2_11AuthContext3new +FNDA:7,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB2_11AuthContext8has_role +FNDA:1,_RNvMs1_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB5_14TokenValidator12create_token +FNDA:1,_RNvMs1_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB5_14TokenValidator14validate_token +FNDA:2,_RNvMs1_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB5_14TokenValidator3new +FNDA:2,_RNvMs2_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB5_15ApiKeyValidator11add_api_key +FNDA:1,_RNvMs2_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB5_15ApiKeyValidator14remove_api_key +FNDA:2,_RNvMs2_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB5_15ApiKeyValidator16validate_api_key +FNDA:2,_RNvMs2_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB5_15ApiKeyValidator3len +FNDA:2,_RNvMs2_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB5_15ApiKeyValidator3new +FNDA:1,_RNvMs2_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB5_15ApiKeyValidator8is_empty +FNDA:2,_RNvMs_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB4_9JwtClaims10is_expired +FNDA:1,_RNvMs_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB4_9JwtClaims10with_roles +FNDA:2,_RNvMs_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB4_9JwtClaims3new +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4auth5testss_15test_jwt_claims +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4auth5testss_20test_token_validator +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4auth5testss_22test_api_key_validator +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4auth5testss_26test_auth_context_creation +FNDA:1,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB3_11AuthContext10with_rolesINtNtCs7fnLZwG9eJV_5alloc3vec3VecReEB20_EB5_ +FNDA:2,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB3_11AuthContext12has_any_roleRARej2_EB5_ +FNDA:0,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB3_11AuthContext12with_api_keyNtNtCs7fnLZwG9eJV_5alloc6string6StringEB5_ +FNDA:1,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB3_11AuthContext13with_metadataReB1w_EB5_ +FNDA:1,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB3_11AuthContext9with_roleReEB5_ +FNDA:2,_RNCINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware4authNtB5_11AuthContext10with_rolesINtNtCs7fnLZwG9eJV_5alloc3vec3VecReEB22_E0B7_ +FNF:27 +FNH:23 +DA:38,1 +DA:39,1 +DA:40,1 +DA:41,1 +DA:42,1 +DA:43,1 +DA:44,1 +DA:45,1 +DA:46,1 +DA:47,1 +DA:48,1 +DA:51,1 +DA:52,1 +DA:53,1 +DA:54,1 +DA:57,1 +DA:58,1 +DA:59,1 +DA:60,1 +DA:62,2 +DA:63,1 +DA:64,1 +DA:67,0 +DA:68,0 +DA:69,0 +DA:70,0 +DA:73,0 +DA:74,0 +DA:75,0 +DA:76,0 +DA:79,1 +DA:80,1 +DA:81,1 +DA:82,1 +DA:84,1 +DA:85,1 +DA:86,1 +DA:89,7 +DA:90,7 +DA:91,7 +DA:94,2 +DA:95,2 +DA:96,2 +DA:97,2 +DA:99,4 +DA:100,3 +DA:101,1 +DA:102,2 +DA:104,1 +DA:105,2 +DA:141,2 +DA:142,2 +DA:144,2 +DA:145,2 +DA:146,2 +DA:147,2 +DA:148,2 +DA:149,2 +DA:150,2 +DA:151,2 +DA:152,2 +DA:153,2 +DA:154,2 +DA:155,2 +DA:158,1 +DA:159,1 +DA:160,1 +DA:161,1 +DA:164,2 +DA:165,2 +DA:166,2 +DA:188,0 +DA:189,0 +DA:190,0 +DA:191,0 +DA:192,0 +DA:193,0 +DA:194,0 +DA:199,2 +DA:200,2 +DA:202,2 +DA:203,2 +DA:204,2 +DA:205,2 +DA:206,2 +DA:208,2 +DA:209,2 +DA:210,2 +DA:211,2 +DA:212,2 +DA:213,2 +DA:214,2 +DA:215,2 +DA:218,1 +DA:219,1 +DA:221,1 +DA:224,1 +DA:225,0 +DA:226,1 +DA:228,1 +DA:229,0 +DA:230,1 +DA:232,1 +DA:233,0 +DA:234,1 +DA:236,1 +DA:237,1 +DA:240,1 +DA:241,1 +DA:243,1 +DA:245,1 +DA:246,1 +DA:258,2 +DA:259,2 +DA:260,2 +DA:261,2 +DA:262,2 +DA:265,2 +DA:266,2 +DA:268,2 +DA:269,2 +DA:271,2 +DA:272,2 +DA:275,2 +DA:276,2 +DA:278,2 +DA:281,3 +DA:282,2 +DA:283,1 +DA:284,1 +DA:287,1 +DA:288,2 +DA:291,1 +DA:292,1 +DA:294,1 +DA:295,1 +DA:296,1 +DA:299,2 +DA:300,2 +DA:301,2 +DA:304,1 +DA:305,1 +DA:306,1 +DA:310,0 +DA:311,0 +DA:312,0 +DA:320,1 +DA:321,1 +DA:322,1 +DA:323,1 +DA:324,1 +DA:326,1 +DA:327,1 +DA:328,1 +DA:329,1 +DA:330,1 +DA:331,1 +DA:332,1 +DA:333,1 +DA:334,1 +DA:337,1 +DA:338,1 +DA:339,1 +DA:340,1 +DA:341,1 +DA:344,1 +DA:346,1 +DA:347,1 +DA:348,1 +DA:349,1 +DA:350,1 +DA:351,1 +DA:354,1 +DA:355,1 +DA:356,1 +DA:359,1 +DA:360,1 +DA:361,1 +DA:362,1 +DA:365,1 +DA:366,1 +DA:369,1 +DA:370,1 +DA:373,1 +DA:374,1 +DA:375,1 +DA:376,1 +DA:379,1 +DA:380,1 +DA:381,1 +DA:382,1 +DA:383,1 +DA:386,1 +DA:387,1 +DA:388,1 +DA:389,1 +DA:394,1 +DA:395,1 +DA:397,1 +DA:398,1 +DA:399,1 +DA:400,1 +BRF:0 +BRH:0 +LF:203 +LH:182 +end_of_record +SF:/Users/r/git/mcp-loxone-seperation/pulseengine-mcp/mcp-security-middleware/src/config.rs +FN:81,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB2_14SecurityConfig10production +FN:51,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB2_14SecurityConfig11development +FN:109,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB2_14SecurityConfig13load_from_env +FN:273,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB2_14SecurityConfig17create_middleware +FN:259,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB2_14SecurityConfig22create_token_validator +FN:248,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB2_14SecurityConfig24create_api_key_validator +FN:38,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB2_14SecurityConfig3new +FN:66,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB2_14SecurityConfig7staging +FN:289,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB2_14SecurityConfig7summary +FN:88,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB2_14SecurityConfig8from_env +FN:206,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB2_14SecurityConfig8validate +FN:417,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6config16parse_rate_limit +FN:345,_RNvXs_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB4_13ConfigSummaryNtNtCsd2KPTObkMr_4core3fmt7Display3fmt +FN:512,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6config5testss_19test_config_summary +FN:499,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6config5testss_21test_parse_rate_limit +FN:464,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6config5testss_22test_config_validation +FN:454,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6config5testss_22test_production_config +FN:445,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6config5testss_23test_development_config +FN:476,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6config5testss_23test_env_config_loading +FN:523,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6config5testss_27test_config_builder_methods +FN:305,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB3_14SecurityConfig12with_api_keyReEB5_ +FN:317,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB3_14SecurityConfig15with_jwt_issuerReEB5_ +FN:311,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB3_14SecurityConfig15with_jwt_secretReEB5_ +FN:323,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB3_14SecurityConfig17with_jwt_audienceReEB5_ +FN:197,_RNCNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB4_14SecurityConfig13load_from_env0B6_ +FN:273,_RNCNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB4_14SecurityConfig17create_middleware0B6_ +FN:91,_RNCNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB4_14SecurityConfig8from_env0B6_ +FNDA:4,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB2_14SecurityConfig10production +FNDA:9,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB2_14SecurityConfig11development +FNDA:4,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB2_14SecurityConfig13load_from_env +FNDA:1,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB2_14SecurityConfig17create_middleware +FNDA:1,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB2_14SecurityConfig22create_token_validator +FNDA:1,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB2_14SecurityConfig24create_api_key_validator +FNDA:14,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB2_14SecurityConfig3new +FNDA:1,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB2_14SecurityConfig7staging +FNDA:1,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB2_14SecurityConfig7summary +FNDA:5,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB2_14SecurityConfig8from_env +FNDA:3,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB2_14SecurityConfig8validate +FNDA:3,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6config16parse_rate_limit +FNDA:0,_RNvXs_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB4_13ConfigSummaryNtNtCsd2KPTObkMr_4core3fmt7Display3fmt +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6config5testss_19test_config_summary +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6config5testss_21test_parse_rate_limit +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6config5testss_22test_config_validation +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6config5testss_22test_production_config +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6config5testss_23test_development_config +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6config5testss_23test_env_config_loading +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6config5testss_27test_config_builder_methods +FNDA:1,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB3_14SecurityConfig12with_api_keyReEB5_ +FNDA:1,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB3_14SecurityConfig15with_jwt_issuerReEB5_ +FNDA:1,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB3_14SecurityConfig15with_jwt_secretReEB5_ +FNDA:1,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB3_14SecurityConfig17with_jwt_audienceReEB5_ +FNDA:0,_RNCNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB4_14SecurityConfig13load_from_env0B6_ +FNDA:1,_RNCNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB4_14SecurityConfig17create_middleware0B6_ +FNDA:0,_RNCNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware6configNtB4_14SecurityConfig8from_env0B6_ +FNF:27 +FNH:24 +DA:38,14 +DA:39,14 +DA:40,14 +DA:41,14 +DA:42,14 +DA:43,14 +DA:44,14 +DA:45,14 +DA:46,14 +DA:47,14 +DA:48,14 +DA:51,9 +DA:52,9 +DA:53,9 +DA:54,9 +DA:57,9 +DA:58,9 +DA:59,9 +DA:60,9 +DA:62,9 +DA:63,9 +DA:66,1 +DA:67,1 +DA:68,1 +DA:69,1 +DA:72,1 +DA:73,1 +DA:74,1 +DA:75,1 +DA:77,1 +DA:78,1 +DA:81,4 +DA:82,4 +DA:83,4 +DA:84,4 +DA:85,4 +DA:88,5 +DA:90,5 +DA:91,5 +DA:92,5 +DA:95,4 +DA:96,2 +DA:97,1 +DA:98,1 +DA:99,0 +DA:103,4 +DA:105,4 +DA:106,5 +DA:109,4 +DA:111,4 +DA:112,1 +DA:113,0 +DA:114,0 +DA:115,0 +DA:117,0 +DA:118,0 +DA:119,0 +DA:121,1 +DA:122,1 +DA:123,1 +DA:124,3 +DA:127,4 +DA:128,1 +DA:129,0 +DA:130,0 +DA:131,0 +DA:133,0 +DA:134,0 +DA:135,0 +DA:137,1 +DA:138,1 +DA:139,1 +DA:140,3 +DA:143,4 +DA:144,0 +DA:145,4 +DA:148,4 +DA:149,0 +DA:150,4 +DA:153,4 +DA:154,0 +DA:155,0 +DA:156,4 +DA:159,4 +DA:160,0 +DA:161,0 +DA:162,0 +DA:163,4 +DA:166,4 +DA:167,0 +DA:168,0 +DA:169,0 +DA:170,4 +DA:173,4 +DA:174,0 +DA:175,0 +DA:176,0 +DA:177,4 +DA:180,4 +DA:181,0 +DA:182,0 +DA:183,0 +DA:184,4 +DA:187,4 +DA:188,0 +DA:189,0 +DA:190,0 +DA:191,0 +DA:192,0 +DA:193,0 +DA:195,0 +DA:196,0 +DA:197,0 +DA:198,0 +DA:200,4 +DA:202,4 +DA:203,4 +DA:206,3 +DA:208,3 +DA:211,3 +DA:212,1 +DA:213,1 +DA:215,1 +DA:216,1 +DA:217,1 +DA:218,1 +DA:219,1 +DA:220,2 +DA:223,2 +DA:224,2 +DA:225,0 +DA:226,0 +DA:227,0 +DA:228,2 +DA:230,2 +DA:231,0 +DA:232,2 +DA:234,2 +DA:235,0 +DA:236,2 +DA:237,0 +DA:240,2 +DA:241,2 +DA:242,0 +DA:244,2 +DA:245,3 +DA:248,1 +DA:249,1 +DA:250,1 +DA:251,1 +DA:252,1 +DA:254,0 +DA:256,1 +DA:259,1 +DA:260,1 +DA:261,1 +DA:262,1 +DA:263,1 +DA:264,1 +DA:266,1 +DA:268,0 +DA:270,1 +DA:273,1 +DA:274,1 +DA:276,1 +DA:277,1 +DA:279,1 +DA:280,1 +DA:281,1 +DA:282,1 +DA:285,1 +DA:286,1 +DA:289,1 +DA:290,1 +DA:291,1 +DA:292,1 +DA:293,1 +DA:294,1 +DA:295,1 +DA:296,1 +DA:297,1 +DA:298,1 +DA:299,1 +DA:300,1 +DA:301,1 +DA:302,1 +DA:305,1 +DA:306,1 +DA:307,1 +DA:308,1 +DA:311,1 +DA:312,1 +DA:313,1 +DA:314,1 +DA:317,1 +DA:318,1 +DA:319,1 +DA:320,1 +DA:323,1 +DA:324,1 +DA:325,1 +DA:326,1 +DA:345,0 +DA:346,0 +DA:347,0 +DA:348,0 +DA:349,0 +DA:350,0 +DA:351,0 +DA:352,0 +DA:353,0 +DA:355,0 +DA:357,0 +DA:358,0 +DA:359,0 +DA:360,0 +DA:361,0 +DA:362,0 +DA:364,0 +DA:366,0 +DA:367,0 +DA:368,0 +DA:369,0 +DA:370,0 +DA:371,0 +DA:373,0 +DA:375,0 +DA:376,0 +DA:377,0 +DA:378,0 +DA:379,0 +DA:380,0 +DA:382,0 +DA:384,0 +DA:385,0 +DA:386,0 +DA:387,0 +DA:388,0 +DA:389,0 +DA:391,0 +DA:393,0 +DA:394,0 +DA:395,0 +DA:396,0 +DA:397,0 +DA:398,0 +DA:400,0 +DA:402,0 +DA:403,0 +DA:404,0 +DA:405,0 +DA:406,0 +DA:407,0 +DA:409,0 +DA:411,0 +DA:412,0 +DA:413,0 +DA:417,3 +DA:418,3 +DA:419,3 +DA:420,1 +DA:421,2 +DA:423,2 +DA:424,2 +DA:425,2 +DA:426,2 +DA:427,1 +DA:428,0 +DA:429,0 +DA:432,2 +DA:433,2 +DA:434,2 +DA:435,2 +DA:436,2 +DA:437,3 +DA:445,1 +DA:446,1 +DA:447,1 +DA:448,1 +DA:449,1 +DA:450,1 +DA:451,1 +DA:454,1 +DA:455,1 +DA:456,1 +DA:457,1 +DA:458,1 +DA:459,1 +DA:460,1 +DA:461,1 +DA:464,1 +DA:465,1 +DA:466,1 +DA:469,1 +DA:470,1 +DA:471,1 +DA:472,1 +DA:473,1 +DA:476,1 +DA:477,1 +DA:478,1 +DA:479,1 +DA:480,1 +DA:481,1 +DA:482,1 +DA:483,1 +DA:484,1 +DA:486,1 +DA:487,1 +DA:488,1 +DA:491,1 +DA:492,1 +DA:493,1 +DA:494,1 +DA:495,1 +DA:496,1 +DA:499,1 +DA:500,1 +DA:501,1 +DA:502,1 +DA:504,1 +DA:505,1 +DA:506,1 +DA:508,1 +DA:509,1 +DA:512,1 +DA:513,1 +DA:514,1 +DA:516,1 +DA:517,1 +DA:518,1 +DA:519,1 +DA:520,1 +DA:523,1 +DA:524,1 +DA:525,1 +DA:526,1 +DA:527,1 +DA:528,1 +DA:530,1 +DA:531,1 +DA:532,1 +DA:533,1 +BRF:0 +BRH:0 +LF:346 +LH:239 +end_of_record +SF:/Users/r/git/mcp-loxone-seperation/pulseengine-mcp/mcp-security-middleware/src/error.rs +FN:98,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5errorNtB3_13SecurityError3jwtpEB5_ +FN:83,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5errorNtB3_13SecurityError4authpEB5_ +FN:113,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5errorNtB3_13SecurityError4httppEB5_ +FN:88,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5errorNtB3_13SecurityError5authzpEB5_ +FN:108,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5errorNtB3_13SecurityError6cryptopEB5_ +FN:103,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5errorNtB3_13SecurityError6randompEB5_ +FN:118,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5errorNtB3_13SecurityError8internalpEB5_ +FN:93,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5errorNtB3_13SecurityError13invalid_tokenReEB5_ +FN:78,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5errorNtB3_13SecurityError6configNtNtCs7fnLZwG9eJV_5alloc6string6StringEB5_ +FN:78,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5errorNtB3_13SecurityError6configReEB5_ +FN:125,_RNvXs_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5errorNtB4_13SecurityErrorINtNtCsd2KPTObkMr_4core7convert4FromNtNtCsb6o9RoxCfT0_12jsonwebtoken6errors5ErrorE4from +FNDA:0,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5errorNtB3_13SecurityError3jwtpEB5_ +FNDA:0,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5errorNtB3_13SecurityError4authpEB5_ +FNDA:0,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5errorNtB3_13SecurityError4httppEB5_ +FNDA:0,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5errorNtB3_13SecurityError5authzpEB5_ +FNDA:0,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5errorNtB3_13SecurityError6cryptopEB5_ +FNDA:0,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5errorNtB3_13SecurityError6randompEB5_ +FNDA:0,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5errorNtB3_13SecurityError8internalpEB5_ +FNDA:11,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5errorNtB3_13SecurityError13invalid_tokenReEB5_ +FNDA:2,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5errorNtB3_13SecurityError6configNtNtCs7fnLZwG9eJV_5alloc6string6StringEB5_ +FNDA:3,_RINvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5errorNtB3_13SecurityError6configReEB5_ +FNDA:0,_RNvXs_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5errorNtB4_13SecurityErrorINtNtCsd2KPTObkMr_4core7convert4FromNtNtCsb6o9RoxCfT0_12jsonwebtoken6errors5ErrorE4from +FNF:10 +FNH:2 +DA:78,5 +DA:79,5 +DA:80,5 +DA:83,0 +DA:84,0 +DA:85,0 +DA:88,0 +DA:89,0 +DA:90,0 +DA:93,11 +DA:94,11 +DA:95,11 +DA:98,0 +DA:99,0 +DA:100,0 +DA:103,0 +DA:104,0 +DA:105,0 +DA:108,0 +DA:109,0 +DA:110,0 +DA:113,0 +DA:114,0 +DA:115,0 +DA:118,0 +DA:119,0 +DA:120,0 +DA:125,0 +DA:128,0 +DA:129,0 +DA:130,0 +DA:132,0 +DA:135,0 +DA:138,0 +DA:140,0 +DA:142,0 +BRF:0 +BRH:0 +LF:36 +LH:6 +end_of_record +SF:/Users/r/git/mcp-loxone-seperation/pulseengine-mcp/mcp-security-middleware/src/lib.rs +FN:115,_RNvCskktBmG9IcvD_35pulseengine_mcp_security_middleware12dev_security +FN:146,_RNvCskktBmG9IcvD_35pulseengine_mcp_security_middleware12env_security +FN:130,_RNvCskktBmG9IcvD_35pulseengine_mcp_security_middleware13prod_security +FN:220,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5testss_19test_module_exports +FN:167,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5testss_19test_version_format +FN:210,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5testss_21test_version_constant +FN:155,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5testss_26test_dev_security_creation +FN:161,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5testss_27test_prod_security_creation +FN:194,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5testss_37test_env_security_with_valid_profiles +FN:176,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5testss_38test_env_security_with_invalid_profile +FN:170,_RNCNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5testss_19test_version_format0B5_ +FNDA:2,_RNvCskktBmG9IcvD_35pulseengine_mcp_security_middleware12dev_security +FNDA:4,_RNvCskktBmG9IcvD_35pulseengine_mcp_security_middleware12env_security +FNDA:2,_RNvCskktBmG9IcvD_35pulseengine_mcp_security_middleware13prod_security +FNDA:1,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5testss_19test_module_exports +FNDA:1,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5testss_19test_version_format +FNDA:1,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5testss_21test_version_constant +FNDA:1,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5testss_26test_dev_security_creation +FNDA:1,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5testss_27test_prod_security_creation +FNDA:1,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5testss_37test_env_security_with_valid_profiles +FNDA:1,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5testss_38test_env_security_with_invalid_profile +FNDA:1,_RNCNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5testss_19test_version_format0B5_ +FNF:11 +FNH:11 +DA:115,2 +DA:116,2 +DA:117,2 +DA:130,2 +DA:131,2 +DA:132,2 +DA:146,4 +DA:147,4 +DA:148,4 +DA:155,1 +DA:156,1 +DA:157,1 +DA:158,1 +DA:161,1 +DA:162,1 +DA:163,1 +DA:164,1 +DA:167,1 +DA:169,1 +DA:170,1 +DA:171,0 +DA:173,1 +DA:176,1 +DA:180,1 +DA:181,1 +DA:182,1 +DA:184,1 +DA:185,1 +DA:188,1 +DA:189,1 +DA:190,1 +DA:191,1 +DA:194,1 +DA:197,4 +DA:198,3 +DA:199,3 +DA:200,3 +DA:201,3 +DA:202,3 +DA:203,3 +DA:204,3 +DA:205,3 +DA:207,1 +DA:210,1 +DA:212,1 +DA:213,1 +DA:214,1 +DA:215,0 +DA:217,1 +DA:220,1 +DA:222,1 +DA:223,1 +DA:227,1 +DA:228,1 +DA:229,1 +DA:230,1 +BRF:0 +BRH:0 +LF:57 +LH:55 +end_of_record +SF:/Users/r/git/mcp-loxone-seperation/pulseengine-mcp/mcp-security-middleware/src/middleware.rs +FN:180,_RNCNCNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middlewareNtB6_18SecurityMiddleware7process00B8_ +FN:48,_RNCNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middlewareNtB4_18SecurityMiddleware12authenticate0B6_ +FN:106,_RNCNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middlewareNtB4_18SecurityMiddleware7process0B6_ +FN:397,_RNCNvMs_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middlewareNtB6_11RateLimiter19cleanup_old_entries0B8_ +FN:498,_RNCNCNCNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5testss_27test_development_middleware000Bb_ +FN:496,_RNCNCNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5testss_27test_development_middleware00B9_ +FN:485,_RNCNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5tests12test_handler0B7_ +FN:490,_RNCNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5testss_27test_development_middleware0B7_ +FN:437,_RNCNCNCNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware19mcp_auth_middleware000B9_ +FN:457,_RNCNCNCNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware25mcp_rate_limit_middleware000B9_ +FN:435,_RNCNCNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware19mcp_auth_middleware00B7_ +FN:455,_RNCNCNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware25mcp_rate_limit_middleware00B7_ +FN:434,_RNCNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware19mcp_auth_middleware0B5_ +FN:449,_RNCNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware25mcp_rate_limit_middleware0B5_ +FN:427,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware19mcp_auth_middleware +FN:442,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware25mcp_rate_limit_middleware +FN:485,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5tests12test_handler +FN:558,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5testss_17test_rate_limiter +FN:512,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5testss_20test_extract_api_key +FN:575,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5testss_21test_is_https_request +FN:539,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5testss_25test_extract_bearer_token +FN:490,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5testss_27test_development_middleware +FN:601,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5testss_28test_rate_limiter_edge_cases +FN:642,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5testss_31test_extract_api_key_edge_cases +FN:669,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5testss_32test_is_https_request_edge_cases +FN:614,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5testss_36test_extract_bearer_token_edge_cases +FN:48,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middlewareNtB2_18SecurityMiddleware12authenticate +FN:92,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middlewareNtB2_18SecurityMiddleware16check_rate_limit +FN:29,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middlewareNtB2_18SecurityMiddleware3new +FN:106,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middlewareNtB2_18SecurityMiddleware7process +FN:365,_RNvMs_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middlewareNtB4_11RateLimiter13allow_request +FN:396,_RNvMs_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middlewareNtB4_11RateLimiter19cleanup_old_entries +FN:357,_RNvMs_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middlewareNtB4_11RateLimiter3new +FN:203,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware15extract_api_key +FN:269,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware16is_https_request +FN:245,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware17extract_client_id +FN:303,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware20add_security_headers +FN:229,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware20extract_bearer_token +FNDA:0,_RNCNCNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middlewareNtB6_18SecurityMiddleware7process00B8_ +FNDA:1,_RNCNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middlewareNtB4_18SecurityMiddleware12authenticate0B6_ +FNDA:1,_RNCNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middlewareNtB4_18SecurityMiddleware7process0B6_ +FNDA:0,_RNCNvMs_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middlewareNtB6_11RateLimiter19cleanup_old_entries0B8_ +FNDA:1,_RNCNCNCNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5testss_27test_development_middleware000Bb_ +FNDA:1,_RNCNCNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5testss_27test_development_middleware00B9_ +FNDA:1,_RNCNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5tests12test_handler0B7_ +FNDA:1,_RNCNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5testss_27test_development_middleware0B7_ +FNDA:0,_RNCNCNCNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware19mcp_auth_middleware000B9_ +FNDA:0,_RNCNCNCNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware25mcp_rate_limit_middleware000B9_ +FNDA:0,_RNCNCNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware19mcp_auth_middleware00B7_ +FNDA:0,_RNCNCNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware25mcp_rate_limit_middleware00B7_ +FNDA:0,_RNCNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware19mcp_auth_middleware0B5_ +FNDA:0,_RNCNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware25mcp_rate_limit_middleware0B5_ +FNDA:0,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware19mcp_auth_middleware +FNDA:0,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware25mcp_rate_limit_middleware +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5tests12test_handler +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5testss_17test_rate_limiter +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5testss_20test_extract_api_key +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5testss_21test_is_https_request +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5testss_25test_extract_bearer_token +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5testss_27test_development_middleware +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5testss_28test_rate_limiter_edge_cases +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5testss_31test_extract_api_key_edge_cases +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5testss_32test_is_https_request_edge_cases +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware5testss_36test_extract_bearer_token_edge_cases +FNDA:1,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middlewareNtB2_18SecurityMiddleware12authenticate +FNDA:1,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middlewareNtB2_18SecurityMiddleware16check_rate_limit +FNDA:1,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middlewareNtB2_18SecurityMiddleware3new +FNDA:1,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middlewareNtB2_18SecurityMiddleware7process +FNDA:7,_RNvMs_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middlewareNtB4_11RateLimiter13allow_request +FNDA:0,_RNvMs_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middlewareNtB4_11RateLimiter19cleanup_old_entries +FNDA:3,_RNvMs_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middlewareNtB4_11RateLimiter3new +FNDA:6,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware15extract_api_key +FNDA:7,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware16is_https_request +FNDA:1,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware17extract_client_id +FNDA:1,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware20add_security_headers +FNDA:5,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware10middleware20extract_bearer_token +FNF:38 +FNH:27 +DA:29,1 +DA:30,1 +DA:31,1 +DA:32,1 +DA:33,1 +DA:34,1 +DA:35,1 +DA:36,1 +DA:39,1 +DA:40,1 +DA:41,1 +DA:42,1 +DA:43,1 +DA:44,1 +DA:45,1 +DA:48,1 +DA:50,1 +DA:51,1 +DA:52,0 +DA:55,0 +DA:56,0 +DA:57,0 +DA:58,0 +DA:59,0 +DA:60,0 +DA:61,0 +DA:62,0 +DA:64,0 +DA:65,0 +DA:68,0 +DA:69,0 +DA:72,0 +DA:73,0 +DA:74,0 +DA:75,0 +DA:76,0 +DA:77,0 +DA:78,0 +DA:80,0 +DA:81,0 +DA:84,0 +DA:85,0 +DA:88,0 +DA:89,1 +DA:92,1 +DA:93,1 +DA:94,1 +DA:95,0 +DA:97,0 +DA:98,0 +DA:99,0 +DA:100,0 +DA:102,0 +DA:103,1 +DA:106,1 +DA:107,1 +DA:108,1 +DA:111,1 +DA:113,1 +DA:114,0 +DA:119,1 +DA:120,0 +DA:121,0 +DA:122,1 +DA:125,1 +DA:126,1 +DA:128,0 +DA:129,0 +DA:130,0 +DA:133,0 +DA:135,0 +DA:138,0 +DA:139,0 +DA:140,0 +DA:141,0 +DA:142,0 +DA:143,0 +DA:144,0 +DA:150,1 +DA:151,0 +DA:152,0 +DA:153,1 +DA:156,1 +DA:157,1 +DA:158,0 +DA:159,0 +DA:160,0 +DA:163,1 +DA:166,1 +DA:167,1 +DA:168,1 +DA:171,1 +DA:174,1 +DA:177,1 +DA:179,1 +DA:180,1 +DA:184,1 +DA:185,1 +DA:186,1 +DA:187,0 +DA:190,0 +DA:192,0 +DA:194,1 +DA:195,1 +DA:203,6 +DA:205,6 +DA:206,3 +DA:207,3 +DA:208,1 +DA:209,2 +DA:210,2 +DA:211,2 +DA:212,2 +DA:213,0 +DA:214,0 +DA:215,0 +DA:216,3 +DA:219,3 +DA:220,3 +DA:221,3 +DA:222,0 +DA:223,0 +DA:225,0 +DA:226,6 +DA:229,5 +DA:230,5 +DA:231,5 +DA:232,4 +DA:234,4 +DA:235,3 +DA:236,1 +DA:237,0 +DA:238,1 +DA:239,0 +DA:241,2 +DA:242,5 +DA:245,1 +DA:247,1 +DA:249,1 +DA:250,0 +DA:251,0 +DA:252,0 +DA:253,0 +DA:254,0 +DA:255,1 +DA:257,1 +DA:258,0 +DA:259,0 +DA:260,0 +DA:261,1 +DA:265,1 +DA:266,1 +DA:269,7 +DA:271,7 +DA:272,1 +DA:273,6 +DA:276,6 +DA:278,6 +DA:279,2 +DA:280,2 +DA:281,0 +DA:282,4 +DA:284,4 +DA:285,0 +DA:286,0 +DA:287,0 +DA:288,4 +DA:291,4 +DA:292,2 +DA:293,2 +DA:294,2 +DA:295,0 +DA:296,0 +DA:297,2 +DA:299,2 +DA:300,7 +DA:303,1 +DA:304,1 +DA:307,1 +DA:309,1 +DA:313,1 +DA:316,1 +DA:318,1 +DA:322,1 +DA:324,1 +DA:328,1 +DA:329,0 +DA:330,0 +DA:331,0 +DA:332,0 +DA:333,1 +DA:336,1 +DA:338,1 +DA:340,1 +DA:357,3 +DA:358,3 +DA:359,3 +DA:360,3 +DA:361,3 +DA:362,3 +DA:363,3 +DA:365,7 +DA:366,7 +DA:369,7 +DA:370,0 +DA:371,7 +DA:373,7 +DA:374,7 +DA:375,7 +DA:376,7 +DA:377,7 +DA:378,7 +DA:379,7 +DA:382,7 +DA:383,0 +DA:384,0 +DA:385,7 +DA:388,7 +DA:389,2 +DA:391,5 +DA:392,5 +DA:394,7 +DA:396,0 +DA:397,0 +DA:398,0 +DA:399,0 +DA:400,0 +DA:427,0 +DA:428,0 +DA:429,0 +DA:430,0 +DA:431,0 +DA:432,0 +DA:433,0 +DA:434,0 +DA:435,0 +DA:436,0 +DA:437,0 +DA:438,0 +DA:439,0 +DA:442,0 +DA:443,0 +DA:444,0 +DA:445,0 +DA:446,0 +DA:447,0 +DA:448,0 +DA:449,0 +DA:450,0 +DA:451,0 +DA:452,0 +DA:455,0 +DA:456,0 +DA:457,0 +DA:458,0 +DA:461,0 +DA:462,0 +DA:463,0 +DA:464,0 +DA:467,0 +DA:468,0 +DA:469,0 +DA:470,0 +DA:471,0 +DA:485,1 +DA:486,1 +DA:487,1 +DA:490,1 +DA:491,1 +DA:492,1 +DA:494,1 +DA:495,1 +DA:496,1 +DA:497,1 +DA:498,1 +DA:499,1 +DA:501,1 +DA:502,1 +DA:503,1 +DA:504,1 +DA:505,1 +DA:507,1 +DA:508,1 +DA:509,1 +DA:512,1 +DA:514,1 +DA:515,1 +DA:517,1 +DA:519,1 +DA:522,1 +DA:523,1 +DA:525,1 +DA:527,1 +DA:528,1 +DA:529,1 +DA:533,1 +DA:534,1 +DA:535,1 +DA:536,1 +DA:539,1 +DA:540,1 +DA:543,1 +DA:545,1 +DA:547,1 +DA:550,1 +DA:552,1 +DA:554,1 +DA:555,1 +DA:558,1 +DA:559,1 +DA:562,1 +DA:565,1 +DA:568,1 +DA:571,1 +DA:572,1 +DA:575,1 +DA:577,1 +DA:578,1 +DA:579,1 +DA:580,1 +DA:581,1 +DA:584,1 +DA:585,1 +DA:586,1 +DA:587,1 +DA:588,1 +DA:589,1 +DA:592,1 +DA:593,1 +DA:594,1 +DA:595,1 +DA:596,1 +DA:597,1 +DA:598,1 +DA:601,1 +DA:602,1 +DA:605,1 +DA:606,1 +DA:609,1 +DA:610,1 +DA:611,1 +DA:614,1 +DA:617,1 +DA:620,1 +DA:621,1 +DA:624,1 +DA:625,1 +DA:627,1 +DA:629,1 +DA:630,1 +DA:631,1 +DA:635,1 +DA:636,1 +DA:637,1 +DA:638,1 +DA:639,1 +DA:642,1 +DA:645,1 +DA:648,1 +DA:649,1 +DA:652,1 +DA:653,1 +DA:654,1 +DA:657,1 +DA:658,1 +DA:660,1 +DA:662,1 +DA:663,1 +DA:664,1 +DA:666,1 +DA:669,1 +DA:671,1 +DA:672,1 +DA:673,1 +DA:674,1 +DA:675,1 +DA:678,1 +DA:679,1 +DA:680,1 +DA:681,1 +DA:682,1 +DA:683,1 +DA:686,1 +DA:687,1 +DA:688,1 +DA:689,1 +DA:690,1 +DA:691,1 +DA:694,1 +DA:695,1 +DA:696,1 +BRF:0 +BRH:0 +LF:403 +LH:272 +end_of_record +SF:/Users/r/git/mcp-loxone-seperation/pulseengine-mcp/mcp-security-middleware/src/profiles.rs +FN:460,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profiles5testss_17test_cors_configs +FN:427,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profiles5testss_20test_staging_profile +FN:438,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profiles5testss_23test_production_profile +FN:449,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profiles5testss_23test_rate_limit_configs +FN:417,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profiles5testss_24test_development_profile +FN:410,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profiles5testss_29test_security_profile_display +FN:388,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profiles5testss_29test_security_profile_parsing +FN:479,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profiles5testss_33test_security_settings_validation +FN:499,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profiles5testss_35test_security_settings_for_profiles +FN:40,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB2_15SecurityProfile6as_str +FN:27,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB2_15SecurityProfile8from_str +FN:79,_RNvMs1_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_15RateLimitConfig10permissive +FN:97,_RNvMs1_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_15RateLimitConfig6strict +FN:88,_RNvMs1_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_15RateLimitConfig8moderate +FN:127,_RNvMs2_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_10CorsConfig10permissive +FN:144,_RNvMs2_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_10CorsConfig14localhost_only +FN:166,_RNvMs2_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_10CorsConfig6strict +FN:182,_RNvMs3_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_18DevelopmentProfile17security_settings +FN:213,_RNvMs4_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_14StagingProfile17security_settings +FN:245,_RNvMs5_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_17ProductionProfile17security_settings +FN:310,_RNvMs6_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_16SecuritySettings11for_profile +FN:354,_RNvMs6_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_16SecuritySettings26security_level_description +FN:320,_RNvMs6_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_16SecuritySettings8validate +FN:59,_RNvXs0_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_15SecurityProfileNtNtCsd2KPTObkMr_4core3fmt7Display3fmt +FN:368,_RNvXs7_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_16SecuritySettingsNtNtCsd2KPTObkMr_4core7default7Default7default +FN:53,_RNvXs_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB4_15SecurityProfileNtNtNtCsd2KPTObkMr_4core3str6traits7FromStr8from_str +FN:196,_RNvMs3_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_18DevelopmentProfile20recommended_env_vars +FN:227,_RNvMs4_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_14StagingProfile20recommended_env_vars +FN:259,_RNvMs5_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_17ProductionProfile17required_env_vars +FN:269,_RNvMs5_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_17ProductionProfile20recommended_env_vars +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profiles5testss_17test_cors_configs +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profiles5testss_20test_staging_profile +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profiles5testss_23test_production_profile +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profiles5testss_23test_rate_limit_configs +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profiles5testss_24test_development_profile +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profiles5testss_29test_security_profile_display +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profiles5testss_29test_security_profile_parsing +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profiles5testss_33test_security_settings_validation +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profiles5testss_35test_security_settings_for_profiles +FNDA:3,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB2_15SecurityProfile6as_str +FNDA:10,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB2_15SecurityProfile8from_str +FNDA:12,_RNvMs1_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_15RateLimitConfig10permissive +FNDA:7,_RNvMs1_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_15RateLimitConfig6strict +FNDA:3,_RNvMs1_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_15RateLimitConfig8moderate +FNDA:12,_RNvMs2_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_10CorsConfig10permissive +FNDA:3,_RNvMs2_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_10CorsConfig14localhost_only +FNDA:8,_RNvMs2_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_10CorsConfig6strict +FNDA:11,_RNvMs3_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_18DevelopmentProfile17security_settings +FNDA:2,_RNvMs4_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_14StagingProfile17security_settings +FNDA:6,_RNvMs5_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_17ProductionProfile17security_settings +FNDA:16,_RNvMs6_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_16SecuritySettings11for_profile +FNDA:1,_RNvMs6_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_16SecuritySettings26security_level_description +FNDA:7,_RNvMs6_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_16SecuritySettings8validate +FNDA:3,_RNvXs0_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_15SecurityProfileNtNtCsd2KPTObkMr_4core3fmt7Display3fmt +FNDA:1,_RNvXs7_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_16SecuritySettingsNtNtCsd2KPTObkMr_4core7default7Default7default +FNDA:5,_RNvXs_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB4_15SecurityProfileNtNtNtCsd2KPTObkMr_4core3str6traits7FromStr8from_str +FNDA:0,_RNvMs3_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_18DevelopmentProfile20recommended_env_vars +FNDA:0,_RNvMs4_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_14StagingProfile20recommended_env_vars +FNDA:0,_RNvMs5_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_17ProductionProfile17required_env_vars +FNDA:0,_RNvMs5_NtCskktBmG9IcvD_35pulseengine_mcp_security_middleware8profilesNtB5_17ProductionProfile20recommended_env_vars +FNF:30 +FNH:26 +DA:27,10 +DA:28,10 +DA:29,10 +DA:30,6 +DA:31,4 +DA:32,2 +DA:33,2 +DA:34,2 +DA:35,2 +DA:37,10 +DA:40,3 +DA:41,3 +DA:42,1 +DA:43,1 +DA:44,1 +DA:45,0 +DA:47,3 +DA:53,5 +DA:54,5 +DA:55,5 +DA:59,3 +DA:60,3 +DA:61,3 +DA:79,12 +DA:80,12 +DA:81,12 +DA:82,12 +DA:83,12 +DA:84,12 +DA:85,12 +DA:88,3 +DA:89,3 +DA:90,3 +DA:91,3 +DA:92,3 +DA:93,3 +DA:94,3 +DA:97,7 +DA:98,7 +DA:99,7 +DA:100,7 +DA:101,7 +DA:102,7 +DA:103,7 +DA:127,12 +DA:128,12 +DA:129,12 +DA:130,12 +DA:131,12 +DA:132,12 +DA:133,12 +DA:134,12 +DA:135,12 +DA:136,12 +DA:137,12 +DA:138,12 +DA:139,12 +DA:140,12 +DA:141,12 +DA:144,3 +DA:145,3 +DA:146,3 +DA:147,3 +DA:148,3 +DA:149,3 +DA:150,3 +DA:151,3 +DA:152,3 +DA:153,3 +DA:154,3 +DA:155,3 +DA:156,3 +DA:157,3 +DA:158,3 +DA:159,3 +DA:160,3 +DA:161,3 +DA:162,3 +DA:163,3 +DA:166,8 +DA:167,8 +DA:168,8 +DA:169,8 +DA:170,8 +DA:171,8 +DA:172,8 +DA:173,8 +DA:174,8 +DA:182,11 +DA:183,11 +DA:184,11 +DA:185,11 +DA:186,11 +DA:187,11 +DA:188,11 +DA:189,11 +DA:190,11 +DA:191,11 +DA:192,11 +DA:193,11 +DA:196,0 +DA:197,0 +DA:198,0 +DA:199,0 +DA:200,0 +DA:201,0 +DA:202,0 +DA:203,0 +DA:205,0 +DA:213,2 +DA:214,2 +DA:215,2 +DA:216,2 +DA:217,2 +DA:218,2 +DA:219,2 +DA:220,2 +DA:221,2 +DA:222,2 +DA:223,2 +DA:224,2 +DA:227,0 +DA:228,0 +DA:229,0 +DA:230,0 +DA:231,0 +DA:232,0 +DA:233,0 +DA:234,0 +DA:235,0 +DA:237,0 +DA:245,6 +DA:246,6 +DA:247,6 +DA:248,6 +DA:249,6 +DA:250,6 +DA:251,6 +DA:252,6 +DA:253,6 +DA:254,6 +DA:255,6 +DA:256,6 +DA:259,0 +DA:260,0 +DA:261,0 +DA:262,0 +DA:263,0 +DA:264,0 +DA:266,0 +DA:269,0 +DA:270,0 +DA:271,0 +DA:272,0 +DA:273,0 +DA:274,0 +DA:275,0 +DA:277,0 +DA:310,16 +DA:311,16 +DA:312,10 +DA:313,1 +DA:314,5 +DA:315,0 +DA:317,16 +DA:320,7 +DA:322,7 +DA:324,1 +DA:325,6 +DA:327,6 +DA:328,3 +DA:329,1 +DA:331,1 +DA:332,1 +DA:333,1 +DA:334,5 +DA:336,5 +DA:338,0 +DA:339,0 +DA:341,5 +DA:343,5 +DA:345,1 +DA:346,1 +DA:347,1 +DA:348,4 +DA:350,4 +DA:351,7 +DA:354,1 +DA:355,1 +DA:356,1 +DA:357,0 +DA:358,0 +DA:359,0 +DA:360,0 +DA:362,0 +DA:364,1 +DA:368,1 +DA:370,1 +DA:371,1 +DA:372,1 +DA:373,1 +DA:374,1 +DA:375,1 +DA:376,1 +DA:377,1 +DA:378,1 +DA:379,1 +DA:380,1 +DA:388,1 +DA:389,1 +DA:390,1 +DA:393,1 +DA:394,1 +DA:397,1 +DA:398,1 +DA:401,1 +DA:402,1 +DA:406,1 +DA:407,1 +DA:410,1 +DA:411,1 +DA:412,1 +DA:413,1 +DA:414,1 +DA:417,1 +DA:418,1 +DA:419,1 +DA:420,1 +DA:421,1 +DA:422,1 +DA:423,1 +DA:424,1 +DA:427,1 +DA:428,1 +DA:429,1 +DA:430,1 +DA:431,1 +DA:432,1 +DA:433,1 +DA:434,1 +DA:435,1 +DA:438,1 +DA:439,1 +DA:440,1 +DA:441,1 +DA:442,1 +DA:443,1 +DA:444,1 +DA:445,1 +DA:446,1 +DA:449,1 +DA:450,1 +DA:451,1 +DA:452,1 +DA:454,1 +DA:455,1 +DA:456,1 +DA:457,1 +DA:460,1 +DA:461,1 +DA:462,1 +DA:463,1 +DA:465,1 +DA:466,1 +DA:467,1 +DA:468,1 +DA:469,1 +DA:470,1 +DA:473,1 +DA:474,1 +DA:475,1 +DA:476,1 +DA:479,1 +DA:480,1 +DA:481,1 +DA:484,1 +DA:485,1 +DA:486,1 +DA:488,1 +DA:489,1 +DA:492,1 +DA:493,1 +DA:494,1 +DA:495,1 +DA:496,1 +DA:499,1 +DA:500,1 +DA:501,1 +DA:503,1 +DA:504,1 +DA:505,1 +DA:506,1 +BRF:0 +BRH:0 +LF:292 +LH:249 +end_of_record +SF:/Users/r/git/mcp-loxone-seperation/pulseengine-mcp/mcp-security-middleware/src/utils.rs +FN:16,_RNCNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utilsNtB4_12SecureRandom5bytes0B6_ +FN:252,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_18test_secure_random +FN:215,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_19test_secure_compare +FN:160,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_21test_generate_api_key +FN:243,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_22test_current_timestamp +FN:177,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_24test_generate_jwt_secret +FN:233,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_26test_request_id_generation +FN:223,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_26test_session_id_generation +FN:347,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_26test_timestamp_consistency +FN:190,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_28test_hash_and_verify_api_key +FN:203,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_28test_validate_api_key_format +FN:269,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_29test_secure_random_edge_cases +FN:332,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_30test_secure_compare_edge_cases +FN:314,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_32test_hash_and_verify_consistency +FN:286,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_39test_validate_api_key_format_edge_cases +FN:29,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utilsNtB2_12SecureRandom13base64_string +FN:35,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utilsNtB2_12SecureRandom17base64_url_string +FN:14,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utilsNtB2_12SecureRandom5bytes +FN:20,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utilsNtB2_12SecureRandom6string +FN:79,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils12hash_api_key +FN:142,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils14secure_compare +FN:89,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils14verify_api_key +FN:54,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils16generate_api_key +FN:95,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils17current_timestamp +FN:71,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils19generate_jwt_secret +FN:137,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils19generate_request_id +FN:132,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils19generate_session_id +FN:103,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils23validate_api_key_format +FNDA:1505,_RNCNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utilsNtB4_12SecureRandom5bytes0B6_ +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_18test_secure_random +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_19test_secure_compare +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_21test_generate_api_key +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_22test_current_timestamp +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_24test_generate_jwt_secret +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_26test_request_id_generation +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_26test_session_id_generation +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_26test_timestamp_consistency +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_28test_hash_and_verify_api_key +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_28test_validate_api_key_format +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_29test_secure_random_edge_cases +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_30test_secure_compare_edge_cases +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_32test_hash_and_verify_consistency +FNDA:1,_RNvNtNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils5testss_39test_validate_api_key_format_edge_cases +FNDA:12,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utilsNtB2_12SecureRandom13base64_string +FNDA:23,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utilsNtB2_12SecureRandom17base64_url_string +FNDA:39,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utilsNtB2_12SecureRandom5bytes +FNDA:4,_RNvMNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utilsNtB2_12SecureRandom6string +FNDA:14,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils12hash_api_key +FNDA:12,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils14secure_compare +FNDA:6,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils14verify_api_key +FNDA:17,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils16generate_api_key +FNDA:8,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils17current_timestamp +FNDA:12,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils19generate_jwt_secret +FNDA:4,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils19generate_request_id +FNDA:2,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils19generate_session_id +FNDA:24,_RNvNtCskktBmG9IcvD_35pulseengine_mcp_security_middleware5utils23validate_api_key_format +FNF:28 +FNH:28 +DA:14,39 +DA:15,39 +DA:16,1505 +DA:17,39 +DA:20,4 +DA:21,4 +DA:22,4 +DA:23,4 +DA:24,4 +DA:25,4 +DA:26,4 +DA:29,12 +DA:30,12 +DA:31,12 +DA:32,12 +DA:35,23 +DA:36,23 +DA:37,23 +DA:38,23 +DA:54,17 +DA:55,17 +DA:56,17 +DA:57,17 +DA:71,12 +DA:72,12 +DA:73,12 +DA:79,14 +DA:80,14 +DA:81,14 +DA:82,14 +DA:83,14 +DA:84,14 +DA:89,6 +DA:90,6 +DA:91,6 +DA:92,6 +DA:95,8 +DA:96,8 +DA:97,8 +DA:98,8 +DA:99,8 +DA:100,8 +DA:103,24 +DA:104,24 +DA:105,7 +DA:106,7 +DA:107,7 +DA:108,17 +DA:110,17 +DA:111,2 +DA:112,15 +DA:114,15 +DA:115,0 +DA:116,15 +DA:119,15 +DA:120,458 +DA:121,458 +DA:122,2 +DA:123,2 +DA:124,2 +DA:125,456 +DA:128,13 +DA:129,24 +DA:132,2 +DA:133,2 +DA:134,2 +DA:137,4 +DA:138,4 +DA:139,4 +DA:142,12 +DA:143,12 +DA:144,6 +DA:145,6 +DA:147,6 +DA:148,103 +DA:149,103 +DA:150,103 +DA:152,6 +DA:153,12 +DA:160,1 +DA:161,1 +DA:162,1 +DA:165,1 +DA:168,1 +DA:169,1 +DA:172,1 +DA:173,1 +DA:174,1 +DA:177,1 +DA:178,1 +DA:179,1 +DA:182,1 +DA:185,1 +DA:186,1 +DA:187,1 +DA:190,1 +DA:191,1 +DA:192,1 +DA:195,1 +DA:198,1 +DA:199,1 +DA:200,1 +DA:203,1 +DA:205,1 +DA:206,1 +DA:209,1 +DA:210,1 +DA:211,1 +DA:212,1 +DA:215,1 +DA:216,1 +DA:217,1 +DA:218,1 +DA:219,1 +DA:220,1 +DA:223,1 +DA:224,1 +DA:225,1 +DA:227,1 +DA:228,1 +DA:229,1 +DA:230,1 +DA:233,1 +DA:234,1 +DA:235,1 +DA:237,1 +DA:238,1 +DA:239,1 +DA:240,1 +DA:243,1 +DA:244,1 +DA:245,1 +DA:246,1 +DA:248,1 +DA:249,1 +DA:252,1 +DA:253,1 +DA:254,1 +DA:256,1 +DA:257,1 +DA:258,1 +DA:260,1 +DA:261,1 +DA:263,1 +DA:264,1 +DA:265,1 +DA:266,1 +DA:269,1 +DA:271,1 +DA:272,1 +DA:274,1 +DA:275,1 +DA:278,1 +DA:279,1 +DA:281,1 +DA:282,1 +DA:283,1 +DA:286,1 +DA:288,1 +DA:289,1 +DA:290,1 +DA:293,1 +DA:296,1 +DA:299,1 +DA:302,1 +DA:303,1 +DA:304,1 +DA:305,1 +DA:308,1 +DA:309,1 +DA:310,1 +DA:311,1 +DA:314,1 +DA:315,1 +DA:316,1 +DA:317,1 +DA:320,1 +DA:323,1 +DA:324,1 +DA:327,1 +DA:328,1 +DA:329,1 +DA:332,1 +DA:334,1 +DA:335,1 +DA:336,1 +DA:339,1 +DA:342,1 +DA:343,1 +DA:344,1 +DA:347,1 +DA:348,1 +DA:349,1 +DA:352,1 +DA:353,1 +DA:354,1 +BRF:0 +BRH:0 +LF:197 +LH:196 +end_of_record \ No newline at end of file