Files
mcp-tool/create-missing-tools.js
nasir@endelospay.com 8c74b0e23f first
2025-07-11 20:22:12 +05:00

327 lines
11 KiB
JavaScript

/**
* @fileoverview Create new tools for missing API endpoints
* Generate new MCP tools for any API endpoints that don't have corresponding tools,
* following naming conventions and including all parameters with exact specifications.
*/
import fs from 'fs';
import path from 'path';
/**
* Create new tools for missing API endpoints
*/
function createMissingTools() {
try {
console.log('=== CREATING NEW TOOLS FOR MISSING API ENDPOINTS ===');
console.log('');
// Read the audit results
const auditResultsPath = path.join(process.cwd(), 'mcp-tools-audit-results.json');
const auditResultsContent = fs.readFileSync(auditResultsPath, 'utf8');
const auditResults = JSON.parse(auditResultsContent);
// Read the complete API parameters
const apiParametersPath = path.join(process.cwd(), 'complete-api-parameters.json');
const apiParametersContent = fs.readFileSync(apiParametersPath, 'utf8');
const apiEndpoints = JSON.parse(apiParametersContent);
// Read the endpoints configuration
const endpointsConfigPath = path.join(process.cwd(), 'src/config/endpoints.js');
const endpointsConfigContent = fs.readFileSync(endpointsConfigPath, 'utf8');
console.log(`🔍 Found ${auditResults.missingTools.length} missing tools to create`);
console.log('');
// Group missing tools by authentication type
const missingToolsByAuth = groupMissingToolsByAuth(auditResults.missingTools, apiEndpoints);
// Generate new tool definitions
const newToolDefinitions = generateNewToolDefinitions(missingToolsByAuth, apiEndpoints);
// Update the endpoints configuration
const updatedContent = addNewToolsToConfig(endpointsConfigContent, newToolDefinitions);
// Save the updated configuration
if (Object.keys(newToolDefinitions).length > 0) {
// Create backup
const backupPath = path.join(process.cwd(), 'src/config/endpoints_backup_missing_' + Date.now() + '.js');
fs.writeFileSync(backupPath, endpointsConfigContent);
console.log(`📁 Backup created: ${backupPath}`);
// Save updated file
fs.writeFileSync(endpointsConfigPath, updatedContent);
console.log(`💾 Updated endpoints configuration saved`);
}
// Generate summary
const totalNewTools = Object.values(newToolDefinitions).reduce((sum, tools) => sum + tools.length, 0);
console.log('');
console.log('=== NEW TOOLS CREATION SUMMARY ===');
Object.keys(newToolDefinitions).forEach(authType => {
console.log(`${authType.toUpperCase()}: ${newToolDefinitions[authType].length} new tools`);
});
console.log(`Total new tools created: ${totalNewTools}`);
console.log(`Backup created: ${totalNewTools > 0 ? 'Yes' : 'No'}`);
return {
newToolsByAuth: newToolDefinitions,
totalNewTools,
backupCreated: totalNewTools > 0
};
} catch (error) {
console.error('Error creating missing tools:', error);
throw error;
}
}
/**
* Group missing tools by authentication type
*/
function groupMissingToolsByAuth(missingTools, apiEndpoints) {
const grouped = {
public: [],
provider: [],
patient: [],
partner: [],
affiliate: [],
network: []
};
missingTools.forEach(missingTool => {
// Find the corresponding API endpoint
const apiEndpoint = apiEndpoints.find(ep =>
ep.path === missingTool.path &&
ep.method === missingTool.method
);
if (apiEndpoint) {
const authType = determineAuthType(apiEndpoint);
if (grouped[authType]) {
grouped[authType].push({
missingTool,
apiEndpoint
});
}
}
});
return grouped;
}
/**
* Determine authentication type for an API endpoint
*/
function determineAuthType(apiEndpoint) {
// Check if endpoint requires authentication
if (!apiEndpoint.requiresAuth || !apiEndpoint.security || apiEndpoint.security.length === 0) {
return 'public';
}
// Determine specific auth type based on path patterns
const path = apiEndpoint.path;
if (path.includes('/api/frontend/patient') || path.includes('/patient/')) {
return 'patient';
} else if (path.includes('/partner/')) {
return 'partner';
} else if (path.includes('/affiliate/')) {
return 'affiliate';
} else if (path.includes('/network/')) {
return 'network';
} else {
// Default authenticated endpoints to provider
return 'provider';
}
}
/**
* Generate new tool definitions
*/
function generateNewToolDefinitions(missingToolsByAuth, apiEndpoints) {
const newToolDefinitions = {};
Object.keys(missingToolsByAuth).forEach(authType => {
const tools = missingToolsByAuth[authType];
newToolDefinitions[authType] = [];
tools.forEach(({ missingTool, apiEndpoint }) => {
const toolDefinition = generateToolDefinition(apiEndpoint, authType);
if (toolDefinition) {
newToolDefinitions[authType].push(toolDefinition);
console.log(`📝 Generated ${authType} tool: ${toolDefinition.path} (${toolDefinition.method})`);
}
});
});
return newToolDefinitions;
}
/**
* Generate a single tool definition
*/
function generateToolDefinition(apiEndpoint, authType) {
const parameters = {};
// Add path parameters
if (apiEndpoint.parameters?.path) {
apiEndpoint.parameters.path.forEach(param => {
parameters[param.name] = {
type: param.type || 'string',
required: param.required || true, // Path parameters are usually required
description: param.description || `${param.name} parameter`
};
});
}
// Add query parameters
if (apiEndpoint.parameters?.query) {
apiEndpoint.parameters.query.forEach(param => {
parameters[param.name] = {
type: param.type || 'string',
required: param.required || false,
description: param.description || `${param.name} parameter`
};
});
}
// Add body parameters
if (apiEndpoint.parameters?.body) {
apiEndpoint.parameters.body.forEach(param => {
parameters[param.name] = {
type: param.type || 'string',
required: param.required || false,
description: param.description || `${param.name} parameter`
};
});
}
return {
path: apiEndpoint.path,
method: apiEndpoint.method,
controller: generateControllerName(apiEndpoint),
category: determineCategoryFromTags(apiEndpoint.tags),
description: apiEndpoint.summary || apiEndpoint.description || `${apiEndpoint.method} ${apiEndpoint.path}`,
parameters: Object.keys(parameters).length > 0 ? parameters : undefined
};
}
/**
* Generate controller name from endpoint
*/
function generateControllerName(apiEndpoint) {
const operationId = apiEndpoint.operationId;
if (operationId) {
return `ApiController@${operationId}`;
}
// Generate from path and method
const pathParts = apiEndpoint.path.split('/').filter(part => part && !part.startsWith('{'));
const resource = pathParts[pathParts.length - 1] || 'api';
const action = apiEndpoint.method.toLowerCase();
return `ApiController@${action}${resource.charAt(0).toUpperCase() + resource.slice(1)}`;
}
/**
* Determine category from tags
*/
function determineCategoryFromTags(tags) {
if (!tags || tags.length === 0) return 'GENERAL';
const tagMap = {
'Appointments': 'APPOINTMENT_SCHEDULING',
'Appointment': 'APPOINTMENT_SCHEDULING',
'Patients': 'PATIENT_MANAGEMENT',
'Patient': 'PATIENT_MANAGEMENT',
'Forms': 'FORMS_QUESTIONNAIRES',
'Documents': 'DOCUMENT_MANAGEMENT',
'User Management': 'USER_MANAGEMENT',
'Authentication': 'USER_MANAGEMENT',
'Locations': 'LOCATION_MANAGEMENT',
'Inventory': 'INVENTORY',
'Tasks': 'USER_MANAGEMENT',
'Emails': 'MESSAGING',
'Phone Logs': 'MESSAGING',
'Vitals': 'MEDICAL_RECORDS',
'Medical Problems': 'MEDICAL_RECORDS',
'Insurance': 'PATIENT_MANAGEMENT',
'Products': 'BUSINESS_OPERATIONS',
'Payments': 'BILLING_ORDERS',
'Meetings': 'AI_INTEGRATION',
'Provider': 'PROVIDER_MANAGEMENT'
};
const primaryTag = tags[0];
return tagMap[primaryTag] || 'GENERAL';
}
/**
* Add new tools to configuration
*/
function addNewToolsToConfig(configContent, newToolDefinitions) {
let updatedContent = configContent;
Object.keys(newToolDefinitions).forEach(authType => {
const tools = newToolDefinitions[authType];
if (tools.length === 0) return;
const sectionName = `${authType.toUpperCase()}_ENDPOINTS`;
const toolsCode = tools.map(tool => generateToolCode(tool)).join(',\n\n');
// Find the section and add tools
const sectionRegex = new RegExp(`(export const ${sectionName}\\s*=\\s*\\[)([\\s\\S]*?)(\\];)`, 'g');
const match = sectionRegex.exec(updatedContent);
if (match) {
const beforeSection = match[1];
const existingContent = match[2];
const afterSection = match[3];
// Add new tools at the end of the section
const newContent = existingContent.trim() ?
`${existingContent.trimEnd()},\n\n // ===== NEW TOOLS FROM API DOCUMENTATION =====\n ${toolsCode}\n` :
`\n // ===== NEW TOOLS FROM API DOCUMENTATION =====\n ${toolsCode}\n`;
updatedContent = updatedContent.replace(
sectionRegex,
`${beforeSection}${newContent}${afterSection}`
);
}
});
return updatedContent;
}
/**
* Generate code for a single tool
*/
function generateToolCode(tool) {
let code = ` {\n`;
code += ` path: "${tool.path}",\n`;
code += ` method: "${tool.method}",\n`;
code += ` controller: "${tool.controller}",\n`;
code += ` category: ENDPOINT_CATEGORIES.${tool.category},\n`;
code += ` description: "${tool.description}",\n`;
if (tool.parameters && Object.keys(tool.parameters).length > 0) {
code += ` parameters: {\n`;
Object.keys(tool.parameters).forEach(paramName => {
const param = tool.parameters[paramName];
code += ` ${paramName}: { type: "${param.type}", required: ${param.required}, description: "${param.description}" },\n`;
});
code += ` },\n`;
}
code += ` }`;
return code;
}
// Run the creation
if (import.meta.url === `file://${process.argv[1]}`) {
createMissingTools();
}
export { createMissingTools };