mcp_execution_codegen/progressive/
types.rs

1//! Types for progressive loading code generation.
2//!
3//! Defines data structures used during progressive code generation,
4//! where each tool is generated as a separate file.
5
6use serde::{Deserialize, Serialize};
7
8/// Context for rendering a single tool template.
9///
10/// Contains all data needed to generate one tool file in the
11/// progressive loading pattern.
12///
13/// # Examples
14///
15/// ```
16/// use mcp_execution_codegen::progressive::ToolContext;
17/// use serde_json::json;
18///
19/// let context = ToolContext {
20///     server_id: "github".to_string(),
21///     name: "create_issue".to_string(),
22///     typescript_name: "createIssue".to_string(),
23///     description: "Creates a new issue".to_string(),
24///     input_schema: json!({"type": "object"}),
25///     properties: vec![],
26///     category: Some("issues".to_string()),
27///     keywords: Some("create,issue,new,bug".to_string()),
28///     short_description: Some("Create a new issue".to_string()),
29/// };
30///
31/// assert_eq!(context.server_id, "github");
32/// ```
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct ToolContext {
35    /// MCP server identifier
36    pub server_id: String,
37    /// Original tool name (snake_case)
38    pub name: String,
39    /// TypeScript-friendly name (camelCase)
40    pub typescript_name: String,
41    /// Human-readable description
42    pub description: String,
43    /// JSON Schema for input parameters
44    pub input_schema: serde_json::Value,
45    /// Extracted properties for template rendering
46    pub properties: Vec<PropertyInfo>,
47    /// Optional category for tool grouping
48    pub category: Option<String>,
49    /// Optional keywords for discovery via grep/search
50    pub keywords: Option<String>,
51    /// Optional short description for header comment
52    pub short_description: Option<String>,
53}
54
55/// Information about a single parameter property.
56///
57/// Used in Handlebars templates to render parameter type definitions.
58///
59/// # Examples
60///
61/// ```
62/// use mcp_execution_codegen::progressive::PropertyInfo;
63///
64/// let prop = PropertyInfo {
65///     name: "title".to_string(),
66///     typescript_type: "string".to_string(),
67///     description: Some("Issue title".to_string()),
68///     required: true,
69/// };
70///
71/// assert_eq!(prop.name, "title");
72/// assert!(prop.required);
73/// ```
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct PropertyInfo {
76    /// Property name
77    pub name: String,
78    /// TypeScript type (e.g., "string", "number", "boolean")
79    pub typescript_type: String,
80    /// Optional description from schema
81    pub description: Option<String>,
82    /// Whether the property is required
83    pub required: bool,
84}
85
86/// Context for rendering the index.ts template.
87///
88/// Contains server-level metadata and list of all tools.
89///
90/// # Examples
91///
92/// ```
93/// use mcp_execution_codegen::progressive::IndexContext;
94///
95/// let context = IndexContext {
96///     server_name: "GitHub".to_string(),
97///     server_version: "1.0.0".to_string(),
98///     tool_count: 30,
99///     tools: vec![],
100///     categories: None,
101/// };
102///
103/// assert_eq!(context.tool_count, 30);
104/// ```
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct IndexContext {
107    /// Server name for documentation
108    pub server_name: String,
109    /// Server version
110    pub server_version: String,
111    /// Total number of tools
112    pub tool_count: usize,
113    /// List of tool summaries
114    pub tools: Vec<ToolSummary>,
115    /// Tools grouped by category (optional, for categorized generation)
116    #[serde(skip_serializing_if = "Option::is_none")]
117    pub categories: Option<Vec<CategoryInfo>>,
118}
119
120/// Summary of a tool for index file generation.
121///
122/// Lighter-weight than full `ToolContext`, used only for
123/// re-exports and documentation in index.ts.
124///
125/// # Examples
126///
127/// ```
128/// use mcp_execution_codegen::progressive::ToolSummary;
129///
130/// let summary = ToolSummary {
131///     typescript_name: "createIssue".to_string(),
132///     description: "Creates a new issue".to_string(),
133///     category: Some("issues".to_string()),
134///     keywords: Some("create,issue,new".to_string()),
135///     short_description: Some("Create a new issue".to_string()),
136/// };
137///
138/// assert_eq!(summary.typescript_name, "createIssue");
139/// ```
140#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct ToolSummary {
142    /// TypeScript-friendly name (camelCase)
143    pub typescript_name: String,
144    /// Human-readable description
145    pub description: String,
146    /// Optional category for tool grouping
147    pub category: Option<String>,
148    /// Optional keywords for discovery via grep/search
149    pub keywords: Option<String>,
150    /// Optional short description for header comment
151    pub short_description: Option<String>,
152}
153
154/// Categorization metadata for a single tool.
155///
156/// Contains all categorization data from Claude's analysis.
157///
158/// # Examples
159///
160/// ```
161/// use mcp_execution_codegen::progressive::ToolCategorization;
162///
163/// let cat = ToolCategorization {
164///     category: "issues".to_string(),
165///     keywords: "create,issue,new,bug".to_string(),
166///     short_description: "Create a new issue in a repository".to_string(),
167/// };
168///
169/// assert_eq!(cat.category, "issues");
170/// ```
171#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct ToolCategorization {
173    /// Category for tool grouping
174    pub category: String,
175    /// Comma-separated keywords for discovery
176    pub keywords: String,
177    /// Concise description for header comment
178    pub short_description: String,
179}
180
181/// Category information for grouped tool display in index.
182///
183/// Groups tools by category for organized documentation.
184///
185/// # Examples
186///
187/// ```
188/// use mcp_execution_codegen::progressive::{CategoryInfo, ToolSummary};
189///
190/// let category = CategoryInfo {
191///     name: "issues".to_string(),
192///     tools: vec![
193///         ToolSummary {
194///             typescript_name: "createIssue".to_string(),
195///             description: "Creates a new issue".to_string(),
196///             category: Some("issues".to_string()),
197///             keywords: Some("create,issue".to_string()),
198///             short_description: Some("Create issue".to_string()),
199///         },
200///     ],
201/// };
202///
203/// assert_eq!(category.name, "issues");
204/// ```
205#[derive(Debug, Clone, Serialize, Deserialize)]
206pub struct CategoryInfo {
207    /// Category name
208    pub name: String,
209    /// Tools in this category
210    pub tools: Vec<ToolSummary>,
211}
212
213/// Context for rendering the runtime bridge template.
214///
215/// Currently minimal, but allows for future extension with
216/// server-specific configuration or metadata.
217///
218/// # Examples
219///
220/// ```
221/// use mcp_execution_codegen::progressive::BridgeContext;
222///
223/// let context = BridgeContext::default();
224/// // Currently no fields, but provides extensibility
225/// ```
226#[derive(Debug, Clone, Default, Serialize, Deserialize)]
227pub struct BridgeContext {
228    // Future: could include server-specific config, auth info, etc.
229}
230
231#[cfg(test)]
232mod tests {
233    use super::*;
234    use serde_json::json;
235
236    #[test]
237    fn test_tool_context() {
238        let context = ToolContext {
239            server_id: "github".to_string(),
240            name: "create_issue".to_string(),
241            typescript_name: "createIssue".to_string(),
242            description: "Creates an issue".to_string(),
243            input_schema: json!({"type": "object"}),
244            properties: vec![],
245            category: Some("issues".to_string()),
246            keywords: Some("create,issue,new".to_string()),
247            short_description: Some("Create a new issue".to_string()),
248        };
249
250        assert_eq!(context.server_id, "github");
251        assert_eq!(context.name, "create_issue");
252        assert_eq!(context.typescript_name, "createIssue");
253        assert_eq!(context.category, Some("issues".to_string()));
254        assert_eq!(context.keywords, Some("create,issue,new".to_string()));
255    }
256
257    #[test]
258    fn test_property_info() {
259        let prop = PropertyInfo {
260            name: "title".to_string(),
261            typescript_type: "string".to_string(),
262            description: Some("Issue title".to_string()),
263            required: true,
264        };
265
266        assert_eq!(prop.name, "title");
267        assert_eq!(prop.typescript_type, "string");
268        assert!(prop.required);
269    }
270
271    #[test]
272    fn test_index_context() {
273        let context = IndexContext {
274            server_name: "GitHub".to_string(),
275            server_version: "1.0.0".to_string(),
276            tool_count: 5,
277            tools: vec![],
278            categories: None,
279        };
280
281        assert_eq!(context.server_name, "GitHub");
282        assert_eq!(context.tool_count, 5);
283        assert!(context.categories.is_none());
284    }
285
286    #[test]
287    fn test_tool_summary() {
288        let summary = ToolSummary {
289            typescript_name: "createIssue".to_string(),
290            description: "Creates an issue".to_string(),
291            category: Some("issues".to_string()),
292            keywords: Some("create,issue".to_string()),
293            short_description: Some("Create issue".to_string()),
294        };
295
296        assert_eq!(summary.typescript_name, "createIssue");
297        assert_eq!(summary.category, Some("issues".to_string()));
298        assert_eq!(summary.keywords, Some("create,issue".to_string()));
299    }
300
301    #[test]
302    fn test_bridge_context_default() {
303        let context = BridgeContext::default();
304        // Just verify it can be constructed
305        let _serialized = serde_json::to_string(&context).unwrap();
306    }
307}