Skip to main content
Glama

Smart EHR MCP Server

by jmandel
build-ehretriever.ts9.22 kB
#!/usr/bin/env bun import fs from 'fs/promises'; import path from 'path'; import { spawn } from 'bun'; import { Command } from 'commander'; async function main() { const program = new Command(); program .name('build-ehretriever') .description('Builds the ehretriever.ts bundle, injecting configuration.') .option('-c, --config <path>', 'Path to the base configuration JSON file.') .option('--extra-endpoints <json_string>', 'JSON string of additional delivery endpoints to merge.') .allowUnknownOption() // Allow other args to pass through to bun build .parse(process.argv); const options = program.opts(); const configPath = options.config ? path.resolve(options.config) : null; const extraEndpointsJson = options.extraEndpoints; let defines: Record<string, string> = {}; let config: any = {}; // Start with empty config if (configPath) { console.log(`Loading base config from: ${configPath}`); try { const configContent = await fs.readFile(configPath, 'utf-8'); config = JSON.parse(configContent); console.log(`Successfully loaded base config.`); } catch (error: any) { console.error(`Warning: Failed to load or parse config file ${configPath}: ${error.message}. Proceeding with empty config.`); config = {}; // Reset to empty on error } } else { console.log('No base config file specified, starting with empty config.'); } // Ensure retrieverConfig and its sub-objects exist if (!config.retrieverConfig || typeof config.retrieverConfig !== 'object') { config.retrieverConfig = {}; } if (!config.retrieverConfig.deliveryEndpoints || typeof config.retrieverConfig.deliveryEndpoints !== 'object') { config.retrieverConfig.deliveryEndpoints = {}; } if (!config.retrieverConfig.vendorConfig || typeof config.retrieverConfig.vendorConfig !== 'object') { config.retrieverConfig.vendorConfig = {}; } // Parse and merge extra endpoints if provided if (extraEndpointsJson) { console.log(`Parsing extra endpoints: ${extraEndpointsJson}`); try { const extraEndpoints = JSON.parse(extraEndpointsJson); if (typeof extraEndpoints === 'object' && extraEndpoints !== null) { // Merge extra endpoints into the config config.retrieverConfig.deliveryEndpoints = { ...config.retrieverConfig.deliveryEndpoints, ...extraEndpoints }; console.log('Successfully merged extra endpoints.'); } else { console.warn('Warning: --extra-endpoints value did not parse to a valid object. Ignoring.'); } } catch (error: any) { console.warn(`Warning: Failed to parse --extra-endpoints JSON: ${error.message}. Ignoring.`); } } // --- Now extract defines from the final merged config --- console.log('Extracting defines from final configuration...'); // --- Inject Brand File Index --- type BrandFileEntry = { url: string; tags: string[]; vendorConfig: { clientId: string; scopes: string; redirectUrl?: string }; }; const finalBrandIndex: BrandFileEntry[] = []; if (!Array.isArray(config.retrieverConfig.brandFiles)) { console.error('Error: retrieverConfig.brandFiles must be an array.'); process.exit(1); } for (const entry of config.retrieverConfig.brandFiles) { const valid = entry && typeof entry === 'object' && typeof entry.url === 'string' && Array.isArray(entry.tags) && entry.tags.every((t: any) => typeof t === 'string') && entry.vendorConfig && typeof entry.vendorConfig === 'object' && typeof entry.vendorConfig.clientId === 'string' && typeof entry.vendorConfig.scopes === 'string' && (typeof entry.vendorConfig.redirectUrl === 'undefined' || typeof entry.vendorConfig.redirectUrl === 'string') && (typeof entry.vendorConfig.note === 'undefined' || typeof entry.vendorConfig.note === 'string'); if (!valid) { console.error('Error: Invalid brandFiles entry detected:', entry); process.exit(1); } finalBrandIndex.push({ url: entry.url, tags: entry.tags, vendorConfig: { clientId: entry.vendorConfig.clientId, scopes: entry.vendorConfig.scopes, ...(entry.vendorConfig.redirectUrl && { redirectUrl: entry.vendorConfig.redirectUrl }), ...(entry.vendorConfig.note && { note: entry.vendorConfig.note }) } }); } if (finalBrandIndex.length === 0) { console.error('Error: No valid brandFiles configured.'); process.exit(1); } defines['__BRAND_FILE_INDEX__'] = JSON.stringify(finalBrandIndex); // --- Inject Delivery Endpoints --- // Validate structure before stringifying const finalValidEndpoints: Record<string, { postUrl: string }> = {}; if (config.retrieverConfig.deliveryEndpoints && typeof config.retrieverConfig.deliveryEndpoints === 'object') { for (const key in config.retrieverConfig.deliveryEndpoints) { const endpointConfig = config.retrieverConfig.deliveryEndpoints[key]; if (endpointConfig && typeof endpointConfig === 'object' && typeof endpointConfig.postUrl === 'string') { finalValidEndpoints[key] = { postUrl: endpointConfig.postUrl }; } else { console.warn(`Warning: Invalid structure or missing string property (postUrl) for final deliveryEndpoint '${key}'. Skipping.`); } } } if (Object.keys(finalValidEndpoints).length > 0) { defines['__DELIVERY_ENDPOINTS__'] = JSON.stringify(finalValidEndpoints); } // --- Inject Vendor Config --- // Validate structure before stringifying const finalValidVendorConfig: Record<string, { clientId: string, scopes: string, redirectUrl?: string }> = {}; if (config.retrieverConfig.vendorConfig && typeof config.retrieverConfig.vendorConfig === 'object') { for (const key in config.retrieverConfig.vendorConfig) { const vendorConf = config.retrieverConfig.vendorConfig[key]; if (vendorConf && typeof vendorConf === 'object' && typeof vendorConf.clientId === 'string' && typeof vendorConf.scopes === 'string' && (typeof vendorConf.redirectUrl === 'undefined' || typeof vendorConf.redirectUrl === 'string')) { finalValidVendorConfig[key] = { clientId: vendorConf.clientId, scopes: vendorConf.scopes, ...(vendorConf.redirectUrl && { redirectUrl: vendorConf.redirectUrl }) // Include redirectUrl only if it exists }; } else { console.warn(`Warning: Invalid structure or missing string properties (clientId, scopes) for vendorConfig '${key}'. Skipping.`); } } } if (Object.keys(finalValidVendorConfig).length > 0) { defines['__VENDOR_CONFIG__'] = JSON.stringify(finalValidVendorConfig); } else { // Still define it as an empty object if nothing valid was found or provided defines['__VENDOR_CONFIG__'] = JSON.stringify({}); console.warn('Warning: No valid vendor configurations found in config. Injecting empty __VENDOR_CONFIG__ = {}.'); } console.log('Injecting defines for build:', defines); // --- Define source and output paths --- const staticDir = path.resolve(process.cwd(), 'static'); const outputDir = path.resolve(staticDir, 'dist'); const sourceTs = path.resolve(process.cwd(), 'ehretriever.ts'); // Assuming TS source is in project root // const sourceHtml = path.resolve(staticDir, 'ehretriever.html'); // Assuming HTML source is in static/ const outputJs = path.resolve(outputDir, 'ehretriever.bundle.js'); // const outputHtml = path.resolve(outputDir, 'ehretriever.html'); // --- Ensure output directory exists --- console.log(`Ensuring output directory exists: ${outputDir}`); await fs.mkdir(outputDir, { recursive: true }); // --- Build the TypeScript file --- const buildArgs = [ 'build', sourceTs, '--outfile', outputJs, '--target', 'browser' ]; // Add defines to the build arguments for (const key in defines) { // Pass KEY='VALUE' (note the single quotes around the JSON stringified value) buildArgs.push('--define', `${key}=${defines[key]}`); } console.log(`Running: bun ${buildArgs.join(' ')}`); const proc = spawn(['bun', ...buildArgs], { stdout: 'inherit', stderr: 'inherit' }); const bunBuildExitCode = await proc.exited; if (bunBuildExitCode !== 0) { console.error('Bun build process failed.'); process.exit(bunBuildExitCode); } console.log('Bun build completed successfully.'); process.exit(0); // Explicit success exit } main();

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jmandel/health-record-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server