@@ -306,12 +306,15 @@ export class McpGenerator {
306306 const endpoint = 'endpoint' in tool ? tool . endpoint : tool . path ;
307307
308308 // Generate tool name from operationId (preferred) or path+method
309- const toolName = this . generateToolName (
309+ const originalToolName = this . generateToolName (
310310 endpoint ,
311311 tool . method ,
312312 tool . operationId
313313 ) ;
314314
315+ // Sanitize and potentially abbreviate the tool name
316+ const toolName = this . sanitizeToolName ( originalToolName ) ;
317+
315318 const inputSchema : any = {
316319 type : 'object' ,
317320 properties : { } ,
@@ -341,9 +344,15 @@ export class McpGenerator {
341344 }
342345 }
343346
347+ // Build description, appending original name if abbreviated
348+ let description = tool . description || `${ tool . method } ${ endpoint } ` ;
349+ if ( toolName !== originalToolName && tool . operationId ) {
350+ description += ` (Original operationId: ${ tool . operationId } )` ;
351+ }
352+
344353 tools . push ( {
345354 name : toolName ,
346- description : tool . description || ` ${ tool . method } ${ endpoint } ` ,
355+ description : description ,
347356 inputSchema,
348357 endpoint : endpoint ,
349358 method : tool . method ,
@@ -356,6 +365,7 @@ export class McpGenerator {
356365 /**
357366 * Generate a tool name from endpoint information
358367 * Prefers operationId if available, otherwise falls back to path+method
368+ * Note: This returns the raw tool name without sanitization/abbreviation
359369 */
360370 private generateToolName ( path : string , method : string , operationId ?: string ) : string {
361371 // If operationId exists, use it (convert to snake_case)
@@ -382,17 +392,17 @@ export class McpGenerator {
382392 . map ( ( p , i ) => ( i === 0 ? p : p . charAt ( 0 ) . toUpperCase ( ) + p . slice ( 1 ) ) ) ;
383393
384394 const methodPrefix = method . toLowerCase ( ) ;
385- const toolName = methodPrefix + pathParts . join ( '' ) ;
386-
387- return this . sanitizeToolName ( toolName ) ;
395+ return methodPrefix + pathParts . join ( '' ) ;
388396 }
389397
390398 /**
391399 * Sanitize tool name to match MCP requirements
392400 * MCP tool names must contain only alphanumeric characters, underscores, and hyphens
401+ * Tool names must be <= 64 characters to comply with Claude Desktop MCP spec
393402 */
394403 private sanitizeToolName ( name : string ) : string {
395- return name
404+ // First, apply basic sanitization
405+ let sanitized = name
396406 // Remove any invalid characters (keep only alphanumeric, underscore, hyphen)
397407 . replace ( / [ ^ a - z A - Z 0 - 9 _ - ] / g, '_' )
398408 // Replace multiple consecutive underscores/hyphens with single underscore
@@ -401,6 +411,74 @@ export class McpGenerator {
401411 . replace ( / ^ _ + | _ + $ / g, '' )
402412 // Ensure it's not empty
403413 || 'tool' ;
414+
415+ // If already under 64 char limit (must be < 64, not <= 64), return as-is
416+ if ( sanitized . length < 64 ) {
417+ return sanitized ;
418+ }
419+
420+ // Only if >= 64 chars, apply abbreviations
421+ return this . abbreviateToolName ( sanitized ) ;
422+ }
423+
424+ /**
425+ * Abbreviate tool name to fit within 64 character limit
426+ * Only called when tool name exceeds 64 characters
427+ */
428+ private abbreviateToolName ( name : string ) : string {
429+ // Common abbreviations for Microsoft Graph and other enterprise APIs
430+ const ABBREVIATIONS : Record < string , string > = {
431+ 'management' : 'mgmt' ,
432+ 'device' : 'dev' ,
433+ 'virtual' : 'virt' ,
434+ 'endpoint' : 'ep' ,
435+ 'provisioning' : 'prov' ,
436+ 'policy' : 'pol' ,
437+ 'assignment' : 'assign' ,
438+ 'assigned' : 'asgn' ,
439+ 'certificate' : 'cert' ,
440+ 'notification' : 'notif' ,
441+ 'apple_push_notification' : 'apn' ,
442+ 'service' : 'svc' ,
443+ 'error' : 'err' ,
444+ 'request' : 'req' ,
445+ 'response' : 'resp' ,
446+ 'configuration' : 'config' ,
447+ 'application' : 'app' ,
448+ 'information' : 'info' ,
449+ 'directory' : 'dir' ,
450+ 'authentication' : 'auth' ,
451+ 'authorization' : 'authz' ,
452+ 'administrator' : 'admin' ,
453+ 'organization' : 'org' ,
454+ 'department' : 'dept' ,
455+ 'environment' : 'env' ,
456+ 'deployment' : 'deploy' ,
457+ 'development' : 'dev' ,
458+ 'production' : 'prod' ,
459+ 'acceptance' : 'accept' ,
460+ 'connection' : 'conn' ,
461+ } ;
462+
463+ // Split by underscores, abbreviate each word, then rejoin
464+ let parts = name . split ( '_' ) ;
465+
466+ // Apply abbreviations to each part
467+ parts = parts . map ( part => {
468+ // Check if this part has an abbreviation
469+ const lowerPart = part . toLowerCase ( ) ;
470+ return ABBREVIATIONS [ lowerPart ] || part ;
471+ } ) ;
472+
473+ let abbreviated = parts . join ( '_' ) ;
474+
475+ // If still too long after abbreviations, truncate intelligently
476+ // Keep first 30 chars + '_' + last 30 chars to preserve both prefix and action verb
477+ if ( abbreviated . length > 64 ) {
478+ abbreviated = abbreviated . substring ( 0 , 30 ) + '_' + abbreviated . substring ( abbreviated . length - 30 ) ;
479+ }
480+
481+ return abbreviated ;
404482 }
405483
406484 /**
0 commit comments