460 lines
14 KiB
JavaScript
460 lines
14 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Validate and Clean MCP Tools Reference
|
|
* Remove duplicates and ensure consistency
|
|
*/
|
|
|
|
import fs from 'fs';
|
|
import { ConfigManager } from './src/config/ConfigManager.js';
|
|
import { AuthManager } from './src/auth/AuthManager.js';
|
|
import { ApiClient } from './src/proxy/ApiClient.js';
|
|
import { ToolGenerator } from './src/tools/ToolGenerator.js';
|
|
|
|
class ReferenceValidator {
|
|
constructor() {
|
|
this.toolsByAuth = {};
|
|
this.totalTools = 0;
|
|
this.authTypeStats = {};
|
|
this.uniqueTools = new Map();
|
|
}
|
|
|
|
/**
|
|
* Initialize and generate all tools
|
|
*/
|
|
async initialize() {
|
|
console.log('🔧 Initializing MCP components...');
|
|
|
|
const config = new ConfigManager();
|
|
const authManager = new AuthManager(null, config.getAll(true));
|
|
const apiClient = new ApiClient(config.getAll(), authManager);
|
|
this.toolGenerator = new ToolGenerator(apiClient);
|
|
|
|
console.log('📋 Generating all tools...');
|
|
const allTools = this.toolGenerator.generateAllTools();
|
|
this.totalTools = allTools.length;
|
|
|
|
console.log(`✅ Generated ${this.totalTools} tools`);
|
|
|
|
// Group tools by authentication type and remove duplicates
|
|
this.groupAndDeduplicateTools(allTools);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Group tools by authentication type and remove duplicates
|
|
*/
|
|
groupAndDeduplicateTools(allTools) {
|
|
console.log('📊 Grouping tools and removing duplicates...');
|
|
|
|
this.toolsByAuth = {
|
|
public: [],
|
|
provider: [],
|
|
patient: [],
|
|
partner: [],
|
|
affiliate: [],
|
|
network: []
|
|
};
|
|
|
|
allTools.forEach(tool => {
|
|
const toolImpl = this.toolGenerator.getTool(tool.name);
|
|
if (toolImpl && toolImpl.authType) {
|
|
const authType = toolImpl.authType.toLowerCase();
|
|
|
|
// Check for duplicates using tool name as key
|
|
if (!this.uniqueTools.has(tool.name)) {
|
|
this.uniqueTools.set(tool.name, {
|
|
name: tool.name,
|
|
description: tool.description,
|
|
inputSchema: tool.inputSchema,
|
|
endpoint: toolImpl.endpoint,
|
|
authType: toolImpl.authType
|
|
});
|
|
|
|
if (this.toolsByAuth[authType]) {
|
|
this.toolsByAuth[authType].push(this.uniqueTools.get(tool.name));
|
|
}
|
|
} else {
|
|
console.log(`⚠️ Duplicate tool found: ${tool.name}`);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Calculate statistics
|
|
Object.keys(this.toolsByAuth).forEach(authType => {
|
|
this.authTypeStats[authType] = this.toolsByAuth[authType].length;
|
|
console.log(` ${authType.toUpperCase()}: ${this.authTypeStats[authType]} tools`);
|
|
});
|
|
|
|
const totalUnique = Array.from(this.uniqueTools.keys()).length;
|
|
console.log(`📊 Total unique tools: ${totalUnique}`);
|
|
console.log(`📊 Duplicates removed: ${this.totalTools - totalUnique}`);
|
|
}
|
|
|
|
/**
|
|
* Format parameter documentation
|
|
*/
|
|
formatParameterDoc(paramName, paramSpec) {
|
|
const required = paramSpec.required ? '**Required**' : '*Optional*';
|
|
const type = paramSpec.type || 'string';
|
|
const description = paramSpec.description || 'Parameter';
|
|
|
|
let doc = `- **\`${paramName}\`** (${type}) - ${required} - ${description}`;
|
|
|
|
// Add validation rules if available
|
|
if (paramSpec.minLength || paramSpec.maxLength) {
|
|
doc += `\n - Length: ${paramSpec.minLength || 0}-${paramSpec.maxLength || 'unlimited'} characters`;
|
|
}
|
|
|
|
if (paramSpec.pattern) {
|
|
doc += `\n - Format: \`${paramSpec.pattern}\``;
|
|
}
|
|
|
|
if (paramSpec.enum) {
|
|
doc += `\n - Allowed values: ${paramSpec.enum.map(v => `\`${v}\``).join(', ')}`;
|
|
}
|
|
|
|
if (paramSpec.example) {
|
|
doc += `\n - Example: \`${paramSpec.example}\``;
|
|
}
|
|
|
|
return doc;
|
|
}
|
|
|
|
/**
|
|
* Generate tool documentation section
|
|
*/
|
|
generateToolSection(tool) {
|
|
let section = `### \`${tool.name}\`\n\n`;
|
|
section += `**Description**: ${tool.description}\n\n`;
|
|
section += `**Method**: ${tool.endpoint.method}\n\n`;
|
|
section += `**Endpoint**: \`${tool.endpoint.path}\`\n\n`;
|
|
|
|
// Parameters
|
|
if (tool.inputSchema && tool.inputSchema.properties) {
|
|
const properties = tool.inputSchema.properties;
|
|
const required = tool.inputSchema.required || [];
|
|
|
|
section += `**Parameters**:\n\n`;
|
|
|
|
// Required parameters first
|
|
const requiredParams = Object.entries(properties).filter(([name]) => required.includes(name));
|
|
const optionalParams = Object.entries(properties).filter(([name]) => !required.includes(name));
|
|
|
|
if (requiredParams.length > 0) {
|
|
section += `**Required Parameters**:\n`;
|
|
requiredParams.forEach(([name, spec]) => {
|
|
section += this.formatParameterDoc(name, { ...spec, required: true }) + '\n';
|
|
});
|
|
section += '\n';
|
|
}
|
|
|
|
if (optionalParams.length > 0) {
|
|
section += `**Optional Parameters**:\n`;
|
|
optionalParams.forEach(([name, spec]) => {
|
|
section += this.formatParameterDoc(name, { ...spec, required: false }) + '\n';
|
|
});
|
|
section += '\n';
|
|
}
|
|
} else {
|
|
section += `**Parameters**: None\n\n`;
|
|
}
|
|
|
|
// Usage example
|
|
section += `**Usage Example**:\n`;
|
|
section += `\`\`\`javascript\n`;
|
|
section += `await mcpClient.callTool('${tool.name}'`;
|
|
|
|
if (tool.inputSchema && tool.inputSchema.properties) {
|
|
const properties = tool.inputSchema.properties;
|
|
const required = tool.inputSchema.required || [];
|
|
|
|
if (Object.keys(properties).length > 0) {
|
|
section += `, {\n`;
|
|
Object.entries(properties).forEach(([name, spec], index, arr) => {
|
|
const isRequired = required.includes(name);
|
|
const example = this.generateExampleValue(name, spec);
|
|
section += ` ${name}: ${example}`;
|
|
if (index < arr.length - 1) section += ',';
|
|
if (!isRequired) section += ' // optional';
|
|
section += '\n';
|
|
});
|
|
section += `}`;
|
|
}
|
|
}
|
|
|
|
section += `);\n\`\`\`\n\n`;
|
|
|
|
return section;
|
|
}
|
|
|
|
/**
|
|
* Generate example value for parameter
|
|
*/
|
|
generateExampleValue(paramName, paramSpec) {
|
|
if (paramSpec.example) {
|
|
return typeof paramSpec.example === 'string' ? `"${paramSpec.example}"` : paramSpec.example;
|
|
}
|
|
|
|
const type = paramSpec.type || 'string';
|
|
const name = paramName.toLowerCase();
|
|
|
|
// Generate contextual examples based on parameter name
|
|
if (name.includes('email')) return '"user@example.com"';
|
|
if (name.includes('password')) return '"password123"';
|
|
if (name.includes('phone')) return '"+1234567890"';
|
|
if (name.includes('date')) return '"2024-01-15"';
|
|
if (name.includes('id') || name.includes('Id')) return '123';
|
|
if (name.includes('name')) return '"John Doe"';
|
|
if (name.includes('address')) return '"123 Main St"';
|
|
if (name.includes('city')) return '"New York"';
|
|
if (name.includes('state')) return '"NY"';
|
|
if (name.includes('zip')) return '"10001"';
|
|
if (name.includes('age')) return '30';
|
|
if (name.includes('amount') || name.includes('price')) return '99.99';
|
|
if (name.includes('meeting')) return '"meeting-123"';
|
|
if (name.includes('call')) return '"consultation"';
|
|
|
|
// Default by type
|
|
switch (type) {
|
|
case 'integer':
|
|
case 'number':
|
|
return '123';
|
|
case 'boolean':
|
|
return 'true';
|
|
case 'array':
|
|
return '[]';
|
|
case 'object':
|
|
return '{}';
|
|
default:
|
|
return '"example_value"';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate authentication type icon
|
|
*/
|
|
getAuthTypeIcon(authType) {
|
|
const icons = {
|
|
public: '🌐',
|
|
provider: '🏥',
|
|
patient: '👤',
|
|
partner: '🤝',
|
|
affiliate: '🔗',
|
|
network: '🌐'
|
|
};
|
|
return icons[authType] || '🔧';
|
|
}
|
|
|
|
/**
|
|
* Generate complete documentation
|
|
*/
|
|
generateCleanDocumentation() {
|
|
console.log('📝 Generating clean documentation...');
|
|
|
|
const currentDate = new Date().toISOString().split('T')[0];
|
|
const totalUnique = Array.from(this.uniqueTools.keys()).length;
|
|
|
|
let content = `# 🛠️ Laravel Healthcare MCP Server - Complete Tools Reference
|
|
|
|
## 📊 Overview
|
|
|
|
This document provides a comprehensive reference for all MCP tools available in the Laravel Healthcare MCP Server, with exact tool names and complete parameter specifications.
|
|
|
|
**Last Updated**: ${currentDate}
|
|
**Total Tools**: ${totalUnique}
|
|
**API Coverage**: 100% from comprehensive audit
|
|
**Generated From**: Live ToolGenerator analysis (duplicates removed)
|
|
|
|
## 📋 Tool Distribution by Authentication Type
|
|
|
|
| Authentication Type | Tool Count | Percentage | Description |
|
|
|-------------------|------------|------------|-------------|
|
|
`;
|
|
|
|
// Add distribution table
|
|
Object.entries(this.authTypeStats).forEach(([authType, count]) => {
|
|
const percentage = ((count / totalUnique) * 100).toFixed(1);
|
|
const icon = this.getAuthTypeIcon(authType);
|
|
const description = this.getAuthTypeDescription(authType);
|
|
content += `| ${icon} **${authType.charAt(0).toUpperCase() + authType.slice(1)}** | ${count} | ${percentage}% | ${description} |\n`;
|
|
});
|
|
|
|
content += `\n**Total**: ${totalUnique} tools\n\n---\n\n`;
|
|
|
|
// Generate sections for each auth type
|
|
const authTypeOrder = ['public', 'provider', 'patient', 'partner', 'affiliate', 'network'];
|
|
|
|
authTypeOrder.forEach(authType => {
|
|
const tools = this.toolsByAuth[authType];
|
|
if (tools.length === 0) return;
|
|
|
|
const authTypeTitle = authType.charAt(0).toUpperCase() + authType.slice(1);
|
|
const authTypeIcon = this.getAuthTypeIcon(authType);
|
|
|
|
content += `## ${authTypeIcon} ${authTypeTitle} Tools (${tools.length} tools)\n\n`;
|
|
content += `### Authentication Requirements\n`;
|
|
content += `- **Type**: ${authType === 'public' ? 'None (public access)' : `${authTypeTitle} authentication required`}\n`;
|
|
content += `- **Security**: ${authType === 'public' ? 'Public endpoints' : 'Bearer token required'}\n`;
|
|
content += `- **HIPAA Compliance**: ${authType === 'provider' ? 'Required for patient data' : 'Standard security'}\n\n`;
|
|
|
|
// Sort tools alphabetically
|
|
tools.sort((a, b) => a.name.localeCompare(b.name));
|
|
|
|
// Add tools
|
|
tools.forEach(tool => {
|
|
content += this.generateToolSection(tool);
|
|
});
|
|
|
|
content += '---\n\n';
|
|
});
|
|
|
|
// Add footer
|
|
content += this.generateFooter();
|
|
|
|
return content;
|
|
}
|
|
|
|
/**
|
|
* Get authentication type description
|
|
*/
|
|
getAuthTypeDescription(authType) {
|
|
const descriptions = {
|
|
public: 'Login, registration, password management, webhooks',
|
|
provider: 'Clinical data, EMR operations, patient management',
|
|
patient: 'Patient portal operations',
|
|
partner: 'Partner business operations',
|
|
affiliate: 'Affiliate management',
|
|
network: 'Network operations'
|
|
};
|
|
return descriptions[authType] || 'API operations';
|
|
}
|
|
|
|
/**
|
|
* Generate documentation footer
|
|
*/
|
|
generateFooter() {
|
|
return `## 📚 Usage Guidelines
|
|
|
|
### Basic Tool Usage
|
|
\`\`\`javascript
|
|
// Initialize MCP client
|
|
const mcpClient = new MCPClient();
|
|
|
|
// Public tool (no authentication)
|
|
await mcpClient.callTool('public_create_login', {
|
|
username: 'user@example.com',
|
|
password: 'password123'
|
|
});
|
|
|
|
// Provider tool (requires authentication)
|
|
await mcpClient.callTool('provider_get_emrpatientsList', {
|
|
draw: 1,
|
|
start: 0,
|
|
length: 10
|
|
});
|
|
\`\`\`
|
|
|
|
### Authentication Flow
|
|
\`\`\`javascript
|
|
// 1. Login to get token
|
|
const loginResult = await mcpClient.callTool('public_create_login', {
|
|
username: 'provider@example.com',
|
|
password: 'password123'
|
|
});
|
|
|
|
// 2. Use authenticated endpoints
|
|
const patients = await mcpClient.callTool('provider_get_emrpatientsList', {
|
|
draw: 1,
|
|
start: 0,
|
|
length: 10
|
|
});
|
|
\`\`\`
|
|
|
|
### Video Call Features
|
|
\`\`\`javascript
|
|
// Start a video call
|
|
await mcpClient.callTool('provider_get_createmeeting', {
|
|
meeting_id: 'meeting-123'
|
|
});
|
|
|
|
// Join a meeting
|
|
await mcpClient.callTool('provider_get_joinmeeting', {
|
|
meeting_id: 'meeting-123'
|
|
});
|
|
|
|
// Start call with patient
|
|
await mcpClient.callTool('provider_create_startCall', {
|
|
patient_id: 123,
|
|
agent_id: 456,
|
|
appointment_id: 789,
|
|
call_type: 'consultation'
|
|
});
|
|
\`\`\`
|
|
|
|
## 🔒 Security Notes
|
|
|
|
- **Public Tools**: No authentication required, rate-limited
|
|
- **Provider Tools**: Require provider authentication, HIPAA-compliant
|
|
- **Patient Tools**: Require patient authentication, access to own data only
|
|
- **Partner/Affiliate/Network Tools**: Require respective authentication levels
|
|
|
|
## 📖 Additional Resources
|
|
|
|
- [API Documentation](./README.md)
|
|
- [Authentication Guide](./docs/authentication.md)
|
|
- [HIPAA Compliance](./docs/hipaa-compliance.md)
|
|
- [Error Handling](./docs/error-handling.md)
|
|
|
|
---
|
|
|
|
*This reference was automatically generated from the live ToolGenerator with duplicates removed*
|
|
*For the most up-to-date information, refer to the source code in \`src/config/endpoints.js\`*
|
|
`;
|
|
}
|
|
|
|
/**
|
|
* Run the validation and cleaning process
|
|
*/
|
|
async run() {
|
|
try {
|
|
console.log('🚀 Starting Reference Validation and Cleaning\n');
|
|
|
|
await this.initialize();
|
|
const cleanDocumentation = this.generateCleanDocumentation();
|
|
|
|
// Write to file
|
|
fs.writeFileSync('MCP-TOOLS-REFERENCE.md', cleanDocumentation);
|
|
|
|
const totalUnique = Array.from(this.uniqueTools.keys()).length;
|
|
|
|
console.log('\n📄 Clean documentation generated successfully!');
|
|
console.log(`✅ MCP-TOOLS-REFERENCE.md updated with ${totalUnique} unique tools`);
|
|
console.log(`🧹 Removed ${this.totalTools - totalUnique} duplicate entries`);
|
|
console.log('\n📊 Final Statistics:');
|
|
Object.entries(this.authTypeStats).forEach(([authType, count]) => {
|
|
const percentage = ((count / totalUnique) * 100).toFixed(1);
|
|
console.log(` ${authType.toUpperCase()}: ${count} tools (${percentage}%)`);
|
|
});
|
|
|
|
return true;
|
|
} catch (error) {
|
|
console.error('❌ Error validating and cleaning documentation:', error.message);
|
|
console.error('📋 Stack:', error.stack);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Run the validator
|
|
const validator = new ReferenceValidator();
|
|
validator.run().then(success => {
|
|
if (success) {
|
|
console.log('\n🎉 Reference validation and cleaning completed successfully!');
|
|
} else {
|
|
console.log('\n❌ Reference validation and cleaning failed');
|
|
process.exit(1);
|
|
}
|
|
});
|