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}