first
This commit is contained in:
326
create-missing-tools.js
Normal file
326
create-missing-tools.js
Normal file
@@ -0,0 +1,326 @@
|
||||
/**
|
||||
* @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 };
|
Reference in New Issue
Block a user