Skip to content

Commit a6d8814

Browse files
committed
feat(codegen): add keywords and short_description to categorization
- Add ToolCategorization struct with category, keywords, short_description - Update ToolContext and ToolSummary with keywords and short_description fields - Update generate_with_categories to accept HashMap<String, ToolCategorization> - Update templates: - tool.ts.hbs: Add @Keywords and @description JSDoc tags in header - index.ts.hbs: Show keywords in brackets after tool description - Update mcp-server to pass full categorization metadata Now Claude's categorization metadata (keywords for grep discovery, short descriptions for headers) is fully passed through to generated files.
1 parent 0cee056 commit a6d8814

File tree

6 files changed

+129
-37
lines changed

6 files changed

+129
-37
lines changed

crates/mcp-codegen/src/progressive/generator.rs

Lines changed: 50 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@
3333
use crate::common::types::{GeneratedCode, GeneratedFile};
3434
use crate::common::typescript::{extract_properties, to_camel_case};
3535
use crate::progressive::types::{
36-
BridgeContext, CategoryInfo, IndexContext, PropertyInfo, ToolContext, ToolSummary,
36+
BridgeContext, CategoryInfo, IndexContext, PropertyInfo, ToolCategorization, ToolContext,
37+
ToolSummary,
3738
};
3839
use crate::template_engine::TemplateEngine;
3940
use mcp_core::{Error, Result};
@@ -191,20 +192,20 @@ impl<'a> ProgressiveGenerator<'a> {
191192
Ok(code)
192193
}
193194

194-
/// Generates progressive loading files with category metadata.
195+
/// Generates progressive loading files with categorization metadata.
195196
///
196-
/// Like `generate`, but includes category information from Claude's
197-
/// categorization. Categories are displayed in the index file and
198-
/// included in individual tool file headers.
197+
/// Like `generate`, but includes full categorization information from Claude's
198+
/// analysis. Categories, keywords, and short descriptions are displayed in
199+
/// the index file and included in individual tool file headers.
199200
///
200201
/// # Arguments
201202
///
202203
/// * `server_info` - MCP server introspection data
203-
/// * `categories` - Map of tool name to category name
204+
/// * `categorizations` - Map of tool name to categorization metadata
204205
///
205206
/// # Returns
206207
///
207-
/// Generated code with category metadata included.
208+
/// Generated code with categorization metadata included.
208209
///
209210
/// # Errors
210211
///
@@ -213,7 +214,7 @@ impl<'a> ProgressiveGenerator<'a> {
213214
/// # Examples
214215
///
215216
/// ```no_run
216-
/// use mcp_codegen::progressive::ProgressiveGenerator;
217+
/// use mcp_codegen::progressive::{ProgressiveGenerator, ToolCategorization};
217218
/// use mcp_introspector::{ServerInfo, ServerCapabilities};
218219
/// use mcp_core::ServerId;
219220
/// use std::collections::HashMap;
@@ -233,33 +234,35 @@ impl<'a> ProgressiveGenerator<'a> {
233234
/// },
234235
/// };
235236
///
236-
/// let mut categories = HashMap::new();
237-
/// categories.insert("create_issue".to_string(), "issues".to_string());
238-
/// categories.insert("list_issues".to_string(), "issues".to_string());
239-
/// categories.insert("create_pr".to_string(), "pull-requests".to_string());
237+
/// let mut categorizations = HashMap::new();
238+
/// categorizations.insert("create_issue".to_string(), ToolCategorization {
239+
/// category: "issues".to_string(),
240+
/// keywords: "create,issue,new,bug".to_string(),
241+
/// short_description: "Create a new issue".to_string(),
242+
/// });
240243
///
241-
/// let code = generator.generate_with_categories(&info, &categories)?;
244+
/// let code = generator.generate_with_categories(&info, &categorizations)?;
242245
/// # Ok(())
243246
/// # }
244247
/// ```
245248
pub fn generate_with_categories(
246249
&self,
247250
server_info: &ServerInfo,
248-
categories: &HashMap<String, String>,
251+
categorizations: &HashMap<String, ToolCategorization>,
249252
) -> Result<GeneratedCode> {
250253
tracing::info!(
251-
"Generating progressive loading code with categories for server: {}",
254+
"Generating progressive loading code with categorizations for server: {}",
252255
server_info.name
253256
);
254257

255258
let mut code = GeneratedCode::new();
256259
let server_id = server_info.id.as_str();
257260

258-
// Generate tool files (one per tool) with category metadata
261+
// Generate tool files (one per tool) with categorization metadata
259262
for tool in &server_info.tools {
260263
let tool_name = tool.name.as_str();
261-
let category = categories.get(tool_name).map(String::as_str);
262-
let tool_context = self.create_tool_context(server_id, tool, category)?;
264+
let categorization = categorizations.get(tool_name);
265+
let tool_context = self.create_tool_context(server_id, tool, categorization)?;
263266
let tool_code = self.engine.render("progressive/tool", &tool_context)?;
264267

265268
code.add_file(GeneratedFile {
@@ -270,20 +273,23 @@ impl<'a> ProgressiveGenerator<'a> {
270273
tracing::debug!(
271274
"Generated tool file: {}.ts (category: {:?})",
272275
tool_context.typescript_name,
273-
category
276+
categorization.map(|c| &c.category)
274277
);
275278
}
276279

277280
// Generate index.ts with category grouping
278-
let index_context = self.create_index_context(server_info, Some(categories))?;
281+
let index_context = self.create_index_context(server_info, Some(categorizations))?;
279282
let index_code = self.engine.render("progressive/index", &index_context)?;
280283

281284
code.add_file(GeneratedFile {
282285
path: "index.ts".to_string(),
283286
content: index_code,
284287
});
285288

286-
tracing::debug!("Generated index.ts with {} categories", categories.len());
289+
tracing::debug!(
290+
"Generated index.ts with {} categorizations",
291+
categorizations.len()
292+
);
287293

288294
// Generate runtime bridge (same as non-categorized)
289295
let bridge_context = BridgeContext::default();
@@ -299,7 +305,7 @@ impl<'a> ProgressiveGenerator<'a> {
299305
tracing::debug!("Generated _runtime/mcp-bridge.ts");
300306

301307
tracing::info!(
302-
"Successfully generated {} files for {} with categories (progressive loading)",
308+
"Successfully generated {} files for {} with categorizations (progressive loading)",
303309
code.file_count(),
304310
server_info.name
305311
);
@@ -318,7 +324,7 @@ impl<'a> ProgressiveGenerator<'a> {
318324
&self,
319325
server_id: &str,
320326
tool: &mcp_introspector::ToolInfo,
321-
category: Option<&str>,
327+
categorization: Option<&ToolCategorization>,
322328
) -> Result<ToolContext> {
323329
let typescript_name = to_camel_case(tool.name.as_str());
324330

@@ -332,32 +338,36 @@ impl<'a> ProgressiveGenerator<'a> {
332338
description: tool.description.clone(),
333339
input_schema: tool.input_schema.clone(),
334340
properties,
335-
category: category.map(String::from),
341+
category: categorization.map(|c| c.category.clone()),
342+
keywords: categorization.map(|c| c.keywords.clone()),
343+
short_description: categorization.map(|c| c.short_description.clone()),
336344
})
337345
}
338346

339347
/// Creates index context from server information.
340348
fn create_index_context(
341349
&self,
342350
server_info: &ServerInfo,
343-
categories: Option<&HashMap<String, String>>,
351+
categorizations: Option<&HashMap<String, ToolCategorization>>,
344352
) -> Result<IndexContext> {
345353
let tools: Vec<ToolSummary> = server_info
346354
.tools
347355
.iter()
348356
.map(|tool| {
349357
let tool_name = tool.name.as_str();
350-
let category = categories.and_then(|c| c.get(tool_name)).cloned();
358+
let cat = categorizations.and_then(|c| c.get(tool_name));
351359
ToolSummary {
352360
typescript_name: to_camel_case(tool_name),
353361
description: tool.description.clone(),
354-
category,
362+
category: cat.map(|c| c.category.clone()),
363+
keywords: cat.map(|c| c.keywords.clone()),
364+
short_description: cat.map(|c| c.short_description.clone()),
355365
}
356366
})
357367
.collect();
358368

359-
// Build category groups if categories are provided
360-
let category_groups = categories.map(|_| {
369+
// Build category groups if categorizations are provided
370+
let category_groups = categorizations.map(|_| {
361371
let mut groups: HashMap<String, Vec<ToolSummary>> = HashMap::new();
362372

363373
for tool in &tools {
@@ -551,8 +561,13 @@ mod tests {
551561
output_schema: None,
552562
};
553563

564+
let categorization = ToolCategorization {
565+
category: "messaging".to_string(),
566+
keywords: "send,message,chat".to_string(),
567+
short_description: "Send a message".to_string(),
568+
};
554569
let context = generator
555-
.create_tool_context("test-server", &tool, Some("messaging"))
570+
.create_tool_context("test-server", &tool, Some(&categorization))
556571
.unwrap();
557572

558573
assert_eq!(context.server_id, "test-server");
@@ -562,6 +577,11 @@ mod tests {
562577
assert_eq!(context.properties.len(), 1);
563578
assert_eq!(context.properties[0].name, "text");
564579
assert_eq!(context.category, Some("messaging".to_string()));
580+
assert_eq!(context.keywords, Some("send,message,chat".to_string()));
581+
assert_eq!(
582+
context.short_description,
583+
Some("Send a message".to_string())
584+
);
565585
}
566586

567587
#[test]

crates/mcp-codegen/src/progressive/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,5 +97,6 @@ pub mod types;
9797
// Re-export main types
9898
pub use generator::ProgressiveGenerator;
9999
pub use types::{
100-
BridgeContext, CategoryInfo, IndexContext, PropertyInfo, ToolContext, ToolSummary,
100+
BridgeContext, CategoryInfo, IndexContext, PropertyInfo, ToolCategorization, ToolContext,
101+
ToolSummary,
101102
};

crates/mcp-codegen/src/progressive/types.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ use serde::{Deserialize, Serialize};
2424
/// input_schema: json!({"type": "object"}),
2525
/// properties: vec![],
2626
/// category: Some("issues".to_string()),
27+
/// keywords: Some("create,issue,new,bug".to_string()),
28+
/// short_description: Some("Create a new issue".to_string()),
2729
/// };
2830
///
2931
/// assert_eq!(context.server_id, "github");
@@ -44,6 +46,10 @@ pub struct ToolContext {
4446
pub properties: Vec<PropertyInfo>,
4547
/// Optional category for tool grouping
4648
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>,
4753
}
4854

4955
/// Information about a single parameter property.
@@ -125,6 +131,8 @@ pub struct IndexContext {
125131
/// typescript_name: "createIssue".to_string(),
126132
/// description: "Creates a new issue".to_string(),
127133
/// category: Some("issues".to_string()),
134+
/// keywords: Some("create,issue,new".to_string()),
135+
/// short_description: Some("Create a new issue".to_string()),
128136
/// };
129137
///
130138
/// assert_eq!(summary.typescript_name, "createIssue");
@@ -137,6 +145,37 @@ pub struct ToolSummary {
137145
pub description: String,
138146
/// Optional category for tool grouping
139147
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_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)]
172+
pub 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,
140179
}
141180

142181
/// Category information for grouped tool display in index.
@@ -155,6 +194,8 @@ pub struct ToolSummary {
155194
/// typescript_name: "createIssue".to_string(),
156195
/// description: "Creates a new issue".to_string(),
157196
/// category: Some("issues".to_string()),
197+
/// keywords: Some("create,issue".to_string()),
198+
/// short_description: Some("Create issue".to_string()),
158199
/// },
159200
/// ],
160201
/// };
@@ -202,12 +243,15 @@ mod tests {
202243
input_schema: json!({"type": "object"}),
203244
properties: vec![],
204245
category: Some("issues".to_string()),
246+
keywords: Some("create,issue,new".to_string()),
247+
short_description: Some("Create a new issue".to_string()),
205248
};
206249

207250
assert_eq!(context.server_id, "github");
208251
assert_eq!(context.name, "create_issue");
209252
assert_eq!(context.typescript_name, "createIssue");
210253
assert_eq!(context.category, Some("issues".to_string()));
254+
assert_eq!(context.keywords, Some("create,issue,new".to_string()));
211255
}
212256

213257
#[test]
@@ -245,10 +289,13 @@ mod tests {
245289
typescript_name: "createIssue".to_string(),
246290
description: "Creates an issue".to_string(),
247291
category: Some("issues".to_string()),
292+
keywords: Some("create,issue".to_string()),
293+
short_description: Some("Create issue".to_string()),
248294
};
249295

250296
assert_eq!(summary.typescript_name, "createIssue");
251297
assert_eq!(summary.category, Some("issues".to_string()));
298+
assert_eq!(summary.keywords, Some("create,issue".to_string()));
252299
}
253300

254301
#[test]

crates/mcp-codegen/templates/progressive/index.ts.hbs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@
1212
*
1313
* ### {{name}}
1414
{{#each tools}}
15-
* - `{{typescript_name}}`: {{description}}
15+
* - `{{typescript_name}}`: {{#if short_description}}{{short_description}}{{else}}{{description}}{{/if}}{{#if keywords}} [{{keywords}}]{{/if}}
1616
{{/each}}
1717
{{/each}}
1818
{{else}}
1919
{{#each tools}}
20-
* - `{{typescript_name}}`: {{description}}
20+
* - `{{typescript_name}}`: {{#if short_description}}{{short_description}}{{else}}{{description}}{{/if}}{{#if keywords}} [{{keywords}}]{{/if}}
2121
{{/each}}
2222
{{/if}}
2323
*

crates/mcp-codegen/templates/progressive/tool.ts.hbs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,32 @@
44
* @server {{server_id}}
55
{{#if category}}
66
* @category {{category}}
7+
{{/if}}
8+
{{#if keywords}}
9+
* @keywords {{keywords}}
10+
{{/if}}
11+
{{#if short_description}}
12+
* @description {{short_description}}
713
{{/if}}
814
*/
915
import { callMCPTool } from './_runtime/mcp-bridge.ts';
1016

1117
/**
18+
{{#if short_description}}
19+
* {{short_description}}
20+
{{else}}
1221
* {{description}}
22+
{{/if}}
1323
{{#if input_schema.description}}
1424
*
1525
* {{input_schema.description}}
1626
{{/if}}
1727
{{#if category}}
1828
*
1929
* @category {{category}}
30+
{{/if}}
31+
{{#if keywords}}
32+
* @keywords {{keywords}}
2033
{{/if}}
2134
*
2235
* @param params - Tool parameters

crates/mcp-server/src/service.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -382,13 +382,24 @@ fn generate_with_categorization(
382382
server_info: &mcp_introspector::ServerInfo,
383383
categorization: &HashMap<String, &CategorizedTool>,
384384
) -> mcp_core::Result<mcp_codegen::GeneratedCode> {
385-
// Convert CategorizedTool map to simple tool_name -> category map
386-
let categories: HashMap<String, String> = categorization
385+
use mcp_codegen::progressive::ToolCategorization;
386+
387+
// Convert CategorizedTool map to ToolCategorization map
388+
let categorizations: HashMap<String, ToolCategorization> = categorization
387389
.iter()
388-
.map(|(tool_name, cat_tool)| (tool_name.clone(), cat_tool.category.clone()))
390+
.map(|(tool_name, cat_tool)| {
391+
(
392+
tool_name.clone(),
393+
ToolCategorization {
394+
category: cat_tool.category.clone(),
395+
keywords: cat_tool.keywords.clone(),
396+
short_description: cat_tool.short_description.clone(),
397+
},
398+
)
399+
})
389400
.collect();
390401

391-
generator.generate_with_categories(server_info, &categories)
402+
generator.generate_with_categories(server_info, &categorizations)
392403
}
393404

394405
#[cfg(test)]

0 commit comments

Comments
 (0)