Skip to main content

agentox_core/checks/conformance/
capability_negotiation.rs

1//! CONF-008: Declared capabilities match actual method support.
2
3use crate::checks::runner::{Check, CheckContext};
4use crate::checks::types::{CheckCategory, CheckResult, Severity};
5
6pub struct CapabilityNegotiation;
7
8#[async_trait::async_trait]
9impl Check for CapabilityNegotiation {
10    fn id(&self) -> &str {
11        "CONF-008"
12    }
13
14    fn name(&self) -> &str {
15        "Capability negotiation"
16    }
17
18    fn category(&self) -> CheckCategory {
19        CheckCategory::Conformance
20    }
21
22    async fn run(&self, ctx: &mut CheckContext) -> Vec<CheckResult> {
23        let desc = "Server capabilities must match actually supported methods";
24        let mut results = Vec::new();
25
26        let caps = match &ctx.init_result {
27            Some(init) => &init.capabilities,
28            None => {
29                return vec![CheckResult::fail(
30                    self.id(),
31                    self.name(),
32                    self.category(),
33                    Severity::High,
34                    desc,
35                    "No initialize result available",
36                )];
37            }
38        };
39
40        // If tools capability is declared, tools/list should work
41        if caps.tools.is_some() {
42            match ctx.session.list_tools().await {
43                Ok(tools) => {
44                    ctx.tools = Some(tools);
45                }
46                Err(e) => {
47                    results.push(CheckResult::fail(
48                        self.id(),
49                        self.name(),
50                        self.category(),
51                        Severity::Medium,
52                        desc,
53                        format!("Server declares tools capability but tools/list failed: {e}"),
54                    ));
55                }
56            }
57        }
58
59        // If tools capability is NOT declared, tools/list should return error or empty
60        if caps.tools.is_none() {
61            match ctx.session.list_tools().await {
62                Ok(tools) if !tools.is_empty() => {
63                    results.push(CheckResult::fail(
64                        self.id(),
65                        self.name(),
66                        self.category(),
67                        Severity::Medium,
68                        desc,
69                        format!(
70                            "Server does not declare tools capability but tools/list returned {} tools",
71                            tools.len()
72                        ),
73                    ));
74                }
75                _ => {} // Expected: error or empty
76            }
77        }
78
79        if results.is_empty() {
80            results.push(CheckResult::pass(
81                self.id(),
82                self.name(),
83                self.category(),
84                desc,
85            ));
86        }
87
88        results
89    }
90}