334 lines
14 KiB
JavaScript
334 lines
14 KiB
JavaScript
/**
|
|
* @fileoverview Categorize API endpoints by authentication type and functional category
|
|
* Maps endpoints from api-docs.json to the Laravel Healthcare MCP Server structure
|
|
*/
|
|
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
|
|
/**
|
|
* Authentication types for endpoint categorization
|
|
*/
|
|
const AUTH_TYPES = {
|
|
PUBLIC: "public",
|
|
PROVIDER: "provider",
|
|
PATIENT: "patient",
|
|
PARTNER: "partner",
|
|
AFFILIATE: "affiliate",
|
|
NETWORK: "network"
|
|
};
|
|
|
|
/**
|
|
* Functional categories for endpoint organization
|
|
*/
|
|
const CATEGORIES = {
|
|
MEETINGS: "meetings",
|
|
APPOINTMENTS: "appointments",
|
|
PATIENTS: "patients",
|
|
DOCTORS: "doctors",
|
|
LABS: "labs",
|
|
NOTES: "notes",
|
|
FORMS: "forms",
|
|
DOCUMENTS: "documents",
|
|
AUTHENTICATION: "authentication",
|
|
USER_MANAGEMENT: "user_management",
|
|
MEDICAL_RECORDS: "medical_records",
|
|
PRESCRIPTIONS: "prescriptions",
|
|
INVENTORY: "inventory",
|
|
LOCATIONS: "locations",
|
|
INSURANCE: "insurance",
|
|
PAYMENTS: "payments",
|
|
VITALS: "vitals",
|
|
TASKS: "tasks",
|
|
TAGS: "tags",
|
|
PHONE_LOGS: "phone_logs",
|
|
PRODUCTS: "products",
|
|
COMPANY: "company",
|
|
TOKENS: "tokens",
|
|
EMAILS: "emails",
|
|
ASSISTANT: "assistant",
|
|
LIVEKIT: "livekit"
|
|
};
|
|
|
|
/**
|
|
* Categorize endpoints by authentication type and functional category
|
|
*/
|
|
function categorizeEndpoints() {
|
|
try {
|
|
// Read the analysis file
|
|
const analysisPath = path.join(process.cwd(), 'api-docs-analysis.json');
|
|
const analysisContent = fs.readFileSync(analysisPath, 'utf8');
|
|
const analysis = JSON.parse(analysisContent);
|
|
|
|
console.log('=== CATEGORIZING 184 ENDPOINTS ===');
|
|
console.log('');
|
|
|
|
const categorizedEndpoints = {
|
|
[AUTH_TYPES.PUBLIC]: [],
|
|
[AUTH_TYPES.PROVIDER]: [],
|
|
[AUTH_TYPES.PATIENT]: [],
|
|
[AUTH_TYPES.PARTNER]: [],
|
|
[AUTH_TYPES.AFFILIATE]: [],
|
|
[AUTH_TYPES.NETWORK]: []
|
|
};
|
|
|
|
// Process each endpoint
|
|
analysis.allEndpoints.forEach(endpoint => {
|
|
const authType = determineAuthType(endpoint);
|
|
const category = determineCategory(endpoint);
|
|
|
|
const categorizedEndpoint = {
|
|
...endpoint,
|
|
authType,
|
|
category,
|
|
toolName: generateToolName(endpoint, authType)
|
|
};
|
|
|
|
categorizedEndpoints[authType].push(categorizedEndpoint);
|
|
});
|
|
|
|
// Display categorization summary
|
|
console.log('=== CATEGORIZATION SUMMARY ===');
|
|
Object.keys(categorizedEndpoints).forEach(authType => {
|
|
const count = categorizedEndpoints[authType].length;
|
|
console.log(`${authType.toUpperCase()}: ${count} endpoints`);
|
|
});
|
|
console.log('');
|
|
|
|
// Display by functional categories
|
|
const byCategoryCount = {};
|
|
Object.values(categorizedEndpoints).flat().forEach(endpoint => {
|
|
byCategoryCount[endpoint.category] = (byCategoryCount[endpoint.category] || 0) + 1;
|
|
});
|
|
|
|
console.log('=== BY FUNCTIONAL CATEGORY ===');
|
|
Object.keys(byCategoryCount).sort().forEach(category => {
|
|
console.log(`${category}: ${byCategoryCount[category]} endpoints`);
|
|
});
|
|
console.log('');
|
|
|
|
// Save categorized endpoints
|
|
const outputPath = path.join(process.cwd(), 'categorized-endpoints.json');
|
|
fs.writeFileSync(outputPath, JSON.stringify(categorizedEndpoints, null, 2));
|
|
console.log(`Categorized endpoints saved to: ${outputPath}`);
|
|
|
|
// Display detailed categorization
|
|
console.log('');
|
|
console.log('=== DETAILED CATEGORIZATION ===');
|
|
Object.keys(categorizedEndpoints).forEach(authType => {
|
|
console.log(`\n--- ${authType.toUpperCase()} ENDPOINTS (${categorizedEndpoints[authType].length}) ---`);
|
|
categorizedEndpoints[authType].forEach((endpoint, index) => {
|
|
console.log(`${index + 1}. ${endpoint.toolName}`);
|
|
console.log(` ${endpoint.method} ${endpoint.path}`);
|
|
console.log(` Category: ${endpoint.category}`);
|
|
console.log(` Summary: ${endpoint.summary}`);
|
|
console.log('');
|
|
});
|
|
});
|
|
|
|
return categorizedEndpoints;
|
|
|
|
} catch (error) {
|
|
console.error('Error categorizing endpoints:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Determine authentication type based on endpoint characteristics
|
|
*/
|
|
function determineAuthType(endpoint) {
|
|
const path = endpoint.path.toLowerCase();
|
|
const tags = endpoint.tags.map(tag => tag.toLowerCase());
|
|
const requiresAuth = endpoint.requiresAuth;
|
|
|
|
// Public endpoints (no authentication required)
|
|
if (!requiresAuth) {
|
|
// Check for specific public patterns
|
|
if (path.includes('/login') || path.includes('/register') ||
|
|
path.includes('/forgot-password') || path.includes('/reset-password') ||
|
|
path.includes('/set-password') || path.includes('/check-user') ||
|
|
path.includes('/patient-order-create') || path.includes('/patient-book-appointment') ||
|
|
path.includes('/get-signed-patient-data') || path.includes('/get/document') ||
|
|
path.includes('/generate-permanent-token') || path.includes('/redirect-with-auth') ||
|
|
path.includes('/get-pdf-url') || path.includes('/download/pdf') ||
|
|
path.includes('/get-form-without-auth') || path.includes('/store-intake-form-data') ||
|
|
path.includes('/update-intake-form-data') || path.includes('/room-joined/event') ||
|
|
path.includes('/get-patient-summary') || path.includes('/update-patient-summary') ||
|
|
path.includes('/generate-patient-summary') || path.includes('/get-patient-full-details') ||
|
|
path.includes('/get-patient-forms-list') || path.includes('/user-list-profile') ||
|
|
path.includes('/available-slots') || path.includes('/check-email') ||
|
|
tags.includes('patient authentication') && !requiresAuth) {
|
|
return AUTH_TYPES.PUBLIC;
|
|
}
|
|
return AUTH_TYPES.PUBLIC;
|
|
}
|
|
|
|
// Provider endpoints (clinical/EMR data)
|
|
if (path.includes('/emr/') || path.includes('/api/emr/') || path.includes('/emr-api/') ||
|
|
path.includes('/provider') || path.includes('/practitioners') ||
|
|
path.includes('/assistant') || path.includes('/company') ||
|
|
tags.includes('provider') || tags.includes('assistant') || tags.includes('company') ||
|
|
// Clinical data endpoints
|
|
path.includes('/appointment') || path.includes('/patient') || path.includes('/medical') ||
|
|
path.includes('/prescription') || path.includes('/document') || path.includes('/form') ||
|
|
path.includes('/lab') || path.includes('/vital') || path.includes('/note') ||
|
|
path.includes('/task') || path.includes('/tag') || path.includes('/phone-log') ||
|
|
path.includes('/inventory') || path.includes('/location') || path.includes('/insurance') ||
|
|
path.includes('/email') || path.includes('/user') ||
|
|
// Meeting/call endpoints
|
|
path.includes('/meeting') || path.includes('/call') || path.includes('/token') ||
|
|
tags.includes('meetings') || tags.includes('appointments') || tags.includes('patients') ||
|
|
tags.includes('doctors') || tags.includes('labs') || tags.includes('forms') ||
|
|
tags.includes('documents') || tags.includes('notes') || tags.includes('vitals') ||
|
|
tags.includes('tasks') || tags.includes('tags') || tags.includes('phone logs') ||
|
|
tags.includes('inventory') || tags.includes('locations') || tags.includes('insurance') ||
|
|
tags.includes('emails') || tags.includes('user management') || tags.includes('medical problems') ||
|
|
tags.includes('forms management') || tags.includes('intake forms') || tags.includes('consent forms') ||
|
|
tags.includes('patient forms') || tags.includes('patient data') || tags.includes('patient medical') ||
|
|
tags.includes('patient profile') || tags.includes('patient subscription') || tags.includes('patient payment') ||
|
|
tags.includes('products') || tags.includes('product sync') || tags.includes('payments') ||
|
|
tags.includes('token management') || tags.includes('appointment reports')) {
|
|
return AUTH_TYPES.PROVIDER;
|
|
}
|
|
|
|
// Patient endpoints (patient portal)
|
|
if (path.includes('/patient/') && !path.includes('/api/patient/register-patient') ||
|
|
tags.includes('patient authentication') && requiresAuth) {
|
|
return AUTH_TYPES.PATIENT;
|
|
}
|
|
|
|
// Partner endpoints
|
|
if (path.includes('/partner/') || tags.includes('partner')) {
|
|
return AUTH_TYPES.PARTNER;
|
|
}
|
|
|
|
// Affiliate endpoints
|
|
if (path.includes('/affiliate/') || tags.includes('affiliate')) {
|
|
return AUTH_TYPES.AFFILIATE;
|
|
}
|
|
|
|
// Network endpoints
|
|
if (path.includes('/network/') || tags.includes('network')) {
|
|
return AUTH_TYPES.NETWORK;
|
|
}
|
|
|
|
// Default to provider for authenticated endpoints
|
|
return AUTH_TYPES.PROVIDER;
|
|
}
|
|
|
|
/**
|
|
* Determine functional category based on endpoint characteristics
|
|
*/
|
|
function determineCategory(endpoint) {
|
|
const path = endpoint.path.toLowerCase();
|
|
const tags = endpoint.tags.map(tag => tag.toLowerCase());
|
|
|
|
// Map based on tags first
|
|
if (tags.includes('meetings')) return CATEGORIES.MEETINGS;
|
|
if (tags.includes('appointments') || tags.includes('appointment') || tags.includes('appointment reports')) return CATEGORIES.APPOINTMENTS;
|
|
if (tags.includes('patients')) return CATEGORIES.PATIENTS;
|
|
if (tags.includes('doctors')) return CATEGORIES.DOCTORS;
|
|
if (tags.includes('labs')) return CATEGORIES.LABS;
|
|
if (tags.includes('notes')) return CATEGORIES.NOTES;
|
|
if (tags.includes('forms') || tags.includes('forms management') || tags.includes('intake forms') ||
|
|
tags.includes('consent forms') || tags.includes('patient forms')) return CATEGORIES.FORMS;
|
|
if (tags.includes('documents')) return CATEGORIES.DOCUMENTS;
|
|
if (tags.includes('authentication') || tags.includes('patient authentication')) return CATEGORIES.AUTHENTICATION;
|
|
if (tags.includes('user management')) return CATEGORIES.USER_MANAGEMENT;
|
|
if (tags.includes('medical problems') || tags.includes('patient medical')) return CATEGORIES.MEDICAL_RECORDS;
|
|
if (tags.includes('inventory')) return CATEGORIES.INVENTORY;
|
|
if (tags.includes('locations')) return CATEGORIES.LOCATIONS;
|
|
if (tags.includes('insurance')) return CATEGORIES.INSURANCE;
|
|
if (tags.includes('payments') || tags.includes('patient payment')) return CATEGORIES.PAYMENTS;
|
|
if (tags.includes('vitals')) return CATEGORIES.VITALS;
|
|
if (tags.includes('tasks')) return CATEGORIES.TASKS;
|
|
if (tags.includes('tags')) return CATEGORIES.TAGS;
|
|
if (tags.includes('phone logs')) return CATEGORIES.PHONE_LOGS;
|
|
if (tags.includes('products') || tags.includes('product sync')) return CATEGORIES.PRODUCTS;
|
|
if (tags.includes('company')) return CATEGORIES.COMPANY;
|
|
if (tags.includes('token management')) return CATEGORIES.TOKENS;
|
|
if (tags.includes('emails')) return CATEGORIES.EMAILS;
|
|
if (tags.includes('assistant')) return CATEGORIES.ASSISTANT;
|
|
if (tags.includes('livekit')) return CATEGORIES.LIVEKIT;
|
|
if (tags.includes('patient data') || tags.includes('patient profile') ||
|
|
tags.includes('patient subscription') || tags.includes('patient summary')) return CATEGORIES.PATIENTS;
|
|
if (tags.includes('provider')) return CATEGORIES.USER_MANAGEMENT;
|
|
|
|
// Map based on path patterns
|
|
if (path.includes('/meeting') || path.includes('/call')) return CATEGORIES.MEETINGS;
|
|
if (path.includes('/appointment')) return CATEGORIES.APPOINTMENTS;
|
|
if (path.includes('/patient')) return CATEGORIES.PATIENTS;
|
|
if (path.includes('/doctor')) return CATEGORIES.DOCTORS;
|
|
if (path.includes('/lab')) return CATEGORIES.LABS;
|
|
if (path.includes('/note')) return CATEGORIES.NOTES;
|
|
if (path.includes('/form')) return CATEGORIES.FORMS;
|
|
if (path.includes('/document')) return CATEGORIES.DOCUMENTS;
|
|
if (path.includes('/login') || path.includes('/register') || path.includes('/password') || path.includes('/token')) return CATEGORIES.AUTHENTICATION;
|
|
if (path.includes('/user')) return CATEGORIES.USER_MANAGEMENT;
|
|
if (path.includes('/medical') || path.includes('/prescription')) return CATEGORIES.MEDICAL_RECORDS;
|
|
if (path.includes('/inventory')) return CATEGORIES.INVENTORY;
|
|
if (path.includes('/location')) return CATEGORIES.LOCATIONS;
|
|
if (path.includes('/insurance')) return CATEGORIES.INSURANCE;
|
|
if (path.includes('/payment')) return CATEGORIES.PAYMENTS;
|
|
if (path.includes('/vital')) return CATEGORIES.VITALS;
|
|
if (path.includes('/task')) return CATEGORIES.TASKS;
|
|
if (path.includes('/tag')) return CATEGORIES.TAGS;
|
|
if (path.includes('/phone')) return CATEGORIES.PHONE_LOGS;
|
|
if (path.includes('/product')) return CATEGORIES.PRODUCTS;
|
|
if (path.includes('/company')) return CATEGORIES.COMPANY;
|
|
if (path.includes('/email')) return CATEGORIES.EMAILS;
|
|
if (path.includes('/assistant')) return CATEGORIES.ASSISTANT;
|
|
|
|
// Default category
|
|
return CATEGORIES.USER_MANAGEMENT;
|
|
}
|
|
|
|
/**
|
|
* Generate MCP tool name following the naming convention
|
|
*/
|
|
function generateToolName(endpoint, authType) {
|
|
const method = endpoint.method.toLowerCase();
|
|
const path = endpoint.path.toLowerCase();
|
|
|
|
// Extract meaningful parts from the path
|
|
let pathParts = path.split('/').filter(part => part && !part.startsWith('{') && !part.endsWith('}'));
|
|
|
|
// Remove common prefixes
|
|
pathParts = pathParts.filter(part => !['api', 'emr', 'emr-api'].includes(part));
|
|
|
|
// Create action from method and path
|
|
let action = method;
|
|
if (method === 'post' && (path.includes('/login') || path.includes('/register'))) {
|
|
action = 'create';
|
|
} else if (method === 'get') {
|
|
action = 'get';
|
|
} else if (method === 'put') {
|
|
action = 'update';
|
|
} else if (method === 'delete') {
|
|
action = 'delete';
|
|
} else if (method === 'post') {
|
|
action = 'create';
|
|
}
|
|
|
|
// Create resource name from path parts
|
|
let resource = pathParts.join('_').replace(/-/g, '_');
|
|
|
|
// Clean up resource name
|
|
resource = resource.replace(/[^a-z0-9_]/g, '');
|
|
|
|
// Ensure we have a resource name
|
|
if (!resource) {
|
|
resource = endpoint.operationId || 'unknown';
|
|
}
|
|
|
|
return `${authType}_${action}_${resource}`;
|
|
}
|
|
|
|
// Run the categorization
|
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
categorizeEndpoints();
|
|
}
|
|
|
|
export { categorizeEndpoints };
|