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}