Skip to main content

dcp/dispatch/
router.rs

1//! Binary Trie Router for O(1) tool dispatch.
2
3use std::collections::HashMap;
4
5use crate::binary::SignedInvocation;
6use crate::dispatch::handler::{SharedArgs, ToolHandler, ToolResult};
7use crate::protocol::schema::SchemaValidator;
8use crate::protocol::ToolSchema;
9use crate::security::{NonceStore, Verifier};
10use crate::{CapabilityManifest, DCPError, SecurityError};
11
12/// Compile-time generated tool router with O(1) dispatch
13pub struct BinaryTrieRouter {
14    /// Direct dispatch table - tool_id is array index
15    handlers: Vec<Option<Box<dyn ToolHandler>>>,
16    /// Tool name to ID mapping (for MCP compatibility)
17    name_to_id: HashMap<String, u16>,
18    /// Maximum registered tool ID
19    max_tool_id: u16,
20}
21
22impl BinaryTrieRouter {
23    /// Maximum number of tools supported
24    pub const MAX_TOOLS: usize = CapabilityManifest::MAX_TOOLS;
25
26    /// Create a new empty router
27    pub fn new() -> Self {
28        Self {
29            handlers: Vec::new(),
30            name_to_id: HashMap::new(),
31            max_tool_id: 0,
32        }
33    }
34
35    /// Create a router with pre-allocated capacity
36    pub fn with_capacity(capacity: usize) -> Self {
37        Self {
38            handlers: Vec::with_capacity(capacity.min(Self::MAX_TOOLS)),
39            name_to_id: HashMap::with_capacity(capacity),
40            max_tool_id: 0,
41        }
42    }
43
44    /// Register a tool handler
45    pub fn register(&mut self, handler: Box<dyn ToolHandler>) -> Result<u16, DCPError> {
46        let tool_id = handler.tool_id();
47        let tool_name = handler.tool_name().to_string();
48
49        // Ensure handlers vec is large enough
50        let id_usize = tool_id as usize;
51        if id_usize >= Self::MAX_TOOLS {
52            return Err(DCPError::ValidationFailed);
53        }
54        if self.name_to_id.contains_key(&tool_name) {
55            return Err(DCPError::ValidationFailed);
56        }
57        if self
58            .handlers
59            .get(id_usize)
60            .and_then(|handler| handler.as_ref())
61            .is_some()
62        {
63            return Err(DCPError::ValidationFailed);
64        }
65
66        while self.handlers.len() <= id_usize {
67            self.handlers.push(None);
68        }
69
70        // Register the handler
71        self.handlers[id_usize] = Some(handler);
72        self.name_to_id.insert(tool_name, tool_id);
73
74        if tool_id > self.max_tool_id {
75            self.max_tool_id = tool_id;
76        }
77
78        Ok(tool_id)
79    }
80
81    /// O(1) internal dispatch by tool ID.
82    #[inline(always)]
83    pub(crate) fn dispatch(&self, tool_id: u16) -> Option<&dyn ToolHandler> {
84        self.handlers
85            .get(tool_id as usize)
86            .and_then(|h| h.as_ref())
87            .map(|h| h.as_ref())
88    }
89
90    /// Get the registered schema for a tool ID.
91    pub fn tool_schema(&self, tool_id: u16) -> Option<&ToolSchema> {
92        self.dispatch(tool_id).map(|handler| handler.schema())
93    }
94
95    /// Raw tool execution is deny-by-default.
96    ///
97    /// Runtime callers must use `execute_authorized` with a negotiated
98    /// capability manifest so tool execution cannot bypass authorization.
99    pub fn execute(&self, tool_id: u16, args: &SharedArgs) -> Result<ToolResult, DCPError> {
100        let _ = (tool_id, args);
101        Err(DCPError::CapabilityDenied)
102    }
103
104    /// Execute a tool only if it is present in the negotiated capability set.
105    pub fn execute_authorized(
106        &self,
107        capabilities: &CapabilityManifest,
108        tool_id: u16,
109        args: &SharedArgs,
110    ) -> Result<ToolResult, SecurityError> {
111        let handler = self.validate_authorized_tool(capabilities, tool_id, args)?;
112        handler
113            .execute(args)
114            .map_err(|_| SecurityError::InsufficientCapabilities)
115    }
116
117    /// Execute a signed invocation only after signature, args hash,
118    /// negotiated capability, schema, and replay checks all pass.
119    pub fn execute_signed_authorized(
120        &self,
121        capabilities: &CapabilityManifest,
122        invocation: &SignedInvocation,
123        public_key: &[u8; 32],
124        nonce_store: &mut NonceStore,
125        args: &SharedArgs,
126    ) -> Result<ToolResult, SecurityError> {
127        Verifier::verify_invocation(invocation, public_key)?;
128        if !Verifier::verify_args_hash(invocation, args.data()) {
129            return Err(SecurityError::ArgsHashMismatch);
130        }
131
132        let tool_id = u16::try_from(invocation.tool_id)
133            .map_err(|_| SecurityError::InsufficientCapabilities)?;
134        nonce_store.check_nonce(invocation.nonce, invocation.timestamp)?;
135
136        let handler = self.validate_authorized_tool(capabilities, tool_id, args)?;
137
138        handler
139            .execute(args)
140            .map_err(|_| SecurityError::InsufficientCapabilities)
141    }
142
143    fn validate_authorized_tool(
144        &self,
145        capabilities: &CapabilityManifest,
146        tool_id: u16,
147        args: &SharedArgs,
148    ) -> Result<&dyn ToolHandler, SecurityError> {
149        capabilities.require_tool(tool_id)?;
150        let handler = self
151            .dispatch(tool_id)
152            .ok_or(SecurityError::InsufficientCapabilities)?;
153        SchemaValidator::validate_shared_args(&handler.schema().input, args)
154            .map_err(|_| SecurityError::ValidationFailed)?;
155        Ok(handler)
156    }
157
158    /// Resolve tool name to ID (for MCP compatibility)
159    pub fn resolve_name(&self, name: &str) -> Option<u16> {
160        self.name_to_id.get(name).copied()
161    }
162
163    /// Get the maximum registered tool ID
164    pub fn max_tool_id(&self) -> u16 {
165        self.max_tool_id
166    }
167
168    /// Get the number of registered tools
169    pub fn tool_count(&self) -> usize {
170        self.handlers.iter().filter(|h| h.is_some()).count()
171    }
172
173    /// Check if a tool ID is registered
174    pub fn has_tool(&self, tool_id: u16) -> bool {
175        self.dispatch(tool_id).is_some()
176    }
177
178    /// Get all registered tool names
179    pub fn tool_names(&self) -> impl Iterator<Item = &str> {
180        self.name_to_id.keys().map(|s| s.as_str())
181    }
182
183    /// Get server capabilities based on registered tools
184    pub fn capabilities(&self) -> ServerCapabilities {
185        ServerCapabilities {
186            tools: self.tool_count() > 0,
187            resources: false, // TODO: implement resource handlers
188            prompts: false,   // TODO: implement prompt handlers
189            logging: true,
190        }
191    }
192}
193
194/// Server capabilities
195#[derive(Debug, Clone, Default)]
196pub struct ServerCapabilities {
197    pub tools: bool,
198    pub resources: bool,
199    pub prompts: bool,
200    pub logging: bool,
201}
202
203impl Default for BinaryTrieRouter {
204    fn default() -> Self {
205        Self::new()
206    }
207}
208
209#[cfg(test)]
210mod tests {
211    use super::*;
212    use crate::protocol::schema::{InputSchema, ToolSchema};
213
214    // Test handler implementation
215    struct TestHandler {
216        schema: ToolSchema,
217    }
218
219    impl TestHandler {
220        fn new(id: u16, name: &'static str) -> Self {
221            Self {
222                schema: ToolSchema {
223                    name,
224                    id,
225                    description: "Test tool",
226                    input: InputSchema::new(),
227                },
228            }
229        }
230    }
231
232    impl ToolHandler for TestHandler {
233        fn execute(&self, _args: &SharedArgs) -> Result<ToolResult, DCPError> {
234            Ok(ToolResult::success(vec![self.schema.id as u8]))
235        }
236
237        fn schema(&self) -> &ToolSchema {
238            &self.schema
239        }
240    }
241
242    #[test]
243    fn test_register_and_dispatch() {
244        let mut router = BinaryTrieRouter::new();
245
246        let handler = Box::new(TestHandler::new(1, "test_tool"));
247        router.register(handler).unwrap();
248
249        assert!(router.has_tool(1));
250        assert!(!router.has_tool(0));
251        assert!(!router.has_tool(2));
252
253        let dispatched = router.dispatch(1).unwrap();
254        assert_eq!(dispatched.tool_id(), 1);
255    }
256
257    #[test]
258    fn test_resolve_name() {
259        let mut router = BinaryTrieRouter::new();
260
261        router
262            .register(Box::new(TestHandler::new(42, "my_tool")))
263            .unwrap();
264
265        assert_eq!(router.resolve_name("my_tool"), Some(42));
266        assert_eq!(router.resolve_name("unknown"), None);
267    }
268
269    #[test]
270    fn test_raw_execute_is_deny_by_default() {
271        let mut router = BinaryTrieRouter::new();
272        router
273            .register(Box::new(TestHandler::new(5, "exec_test")))
274            .unwrap();
275
276        let args = SharedArgs::new(&[], 0);
277        let result = router.execute(5, &args);
278
279        assert_eq!(result, Err(DCPError::CapabilityDenied));
280    }
281
282    #[test]
283    fn test_raw_execute_hides_tool_existence() {
284        let router = BinaryTrieRouter::new();
285        let args = SharedArgs::new(&[], 0);
286
287        let result = router.execute(999, &args);
288        assert_eq!(result, Err(DCPError::CapabilityDenied));
289    }
290
291    #[test]
292    fn test_multiple_tools() {
293        let mut router = BinaryTrieRouter::new();
294
295        for i in 0..10 {
296            let name: &'static str = Box::leak(format!("tool_{}", i).into_boxed_str());
297            router
298                .register(Box::new(TestHandler::new(i, name)))
299                .unwrap();
300        }
301
302        assert_eq!(router.tool_count(), 10);
303        assert_eq!(router.max_tool_id(), 9);
304
305        for i in 0..10 {
306            assert!(router.has_tool(i));
307        }
308    }
309
310    #[test]
311    fn test_sparse_registration() {
312        let mut router = BinaryTrieRouter::new();
313
314        router
315            .register(Box::new(TestHandler::new(0, "first")))
316            .unwrap();
317        router
318            .register(Box::new(TestHandler::new(100, "hundredth")))
319            .unwrap();
320        router
321            .register(Box::new(TestHandler::new(1000, "thousandth")))
322            .unwrap();
323
324        assert_eq!(router.tool_count(), 3);
325        assert!(router.has_tool(0));
326        assert!(router.has_tool(100));
327        assert!(router.has_tool(1000));
328        assert!(!router.has_tool(50));
329    }
330}