Skip to main content

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