327 lines
11 KiB
JavaScript
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 };
|