Skip to content

Commit 2cf3753

Browse files
committed
Improvements and optimizations
1 parent 1dd9eac commit 2cf3753

2 files changed

Lines changed: 85 additions & 7 deletions

File tree

src/generator/McpGenerator.ts

Lines changed: 84 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -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-zA-Z0-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
/**

src/server/routes/deployments.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ router.post('/:serverId/deploy', async (req, res) => {
326326
if (startPhase === 'pending' || startPhase === 'installing' || startPhase === 'installed' || startPhase === 'building') {
327327
console.log(`[${server.name}] Building server...`);
328328
deploymentsDb.update(deploymentId, { status: 'deploying', phase: 'building' });
329-
try{
329+
try {
330330
await spawnWithTimeout(
331331
'npm',
332332
['run', 'build'],

0 commit comments

Comments
 (0)