Skip to main content
Glama

DHIS2 MCP Server

by Dradebo
index.ts•56.1 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, CompleteRequestSchema, Tool, } from '@modelcontextprotocol/sdk/types.js'; import { DHIS2Client } from './dhis2-client.js'; import { createDevelopmentTools, createAPITools } from './tools/index.js'; import { auditLogger } from './audit-logger.js'; import { ConfirmationSystem } from './confirmation-system.js'; import { PermissionSystem, UserPermissions } from './permission-system.js'; import { ParameterCompletion } from './parameter-completion.js'; import { MultiServerComposition } from './multi-server-composition.js'; import { generateWebAppInitInstructions, generateManifestContent, generateBuildSystemConfig, generateDevEnvironmentConfig, generateAppRuntimeConfig, generateAuthenticationPatterns, generateUIComponents, generateUIFormPatterns, generateUIDataDisplayPatterns, generateUINavigationLayout, generateDesignSystemConfig, generateAndroidMaterialForm, generateAndroidListAdapter, generateAndroidNavigationDrawer, generateAndroidBottomSheet, generateTestSetup } from './webapp-generators.js'; import { diagnoseCORSIssues, generateCORSConfiguration, debugAuthentication, generateProxyConfiguration, resolveBuildIssues, generatePerformanceOptimizations, validateEnvironment, generateMigrationGuide } from './debugging-helpers.js'; import { generateAndroidProjectInit, generateGradleBuildConfig, generateSyncConfiguration, generateLocationServicesConfig, generateStorageConfiguration, generateCameraConfiguration, generateAndroidAuthenticationConfig, generateDataModelsConfiguration, generateAndroidTestingConfiguration, generateAndroidUIConfiguration, generateOfflineAnalyticsConfiguration, generateNotificationsConfiguration, generatePerformanceOptimizationConfiguration } from './android-generators.js'; function filterUndefinedValues<T extends Record<string, any>>(obj: T): Partial<T> { const filtered: Partial<T> = {}; for (const [key, value] of Object.entries(obj)) { if (value !== undefined) { filtered[key as keyof T] = value; } } return filtered; } // Helper function to add audit logging to successful operations function logSuccessfulOperation(toolName: string, params: Record<string, any>, result: any, startTime: number, resourcesAffected?: string[]) { auditLogger.log({ toolName, parameters: params, outcome: 'success', dhis2Instance: dhis2Client?.baseURL, userId: currentUser?.username, executionTime: Date.now() - startTime, ...(resourcesAffected && { resourcesAffected }) }); } const server = new Server( { name: 'dhis2-mcp', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); let dhis2Client: DHIS2Client | null = null; let tools: Tool[] = createDevelopmentTools(); // Initialize with development tools immediately let userPermissions: UserPermissions = PermissionSystem.createDefaultPermissions(); let currentUser: any = null; let parameterCompletion: ParameterCompletion = new ParameterCompletion(null); let multiServerComposition: MultiServerComposition = new MultiServerComposition(); server.setRequestHandler(ListToolsRequestSchema, async () => { // Filter tools based on user permissions const filteredTools = PermissionSystem.filterToolsByPermissions(tools, userPermissions); return { tools: filteredTools, }; }); server.setRequestHandler(CompleteRequestSchema, async (request) => { const { ref, argument } = request.params; try { // Extract current argument values from context for tool-specific completions const context = argument?.name ? { [argument.name]: argument.value } : {}; const toolName = typeof ref === 'object' && 'name' in ref ? (ref as any).name : ''; const argumentName = argument?.name || ''; const partialValue = typeof argument?.value === 'string' ? argument.value : undefined; const completion = await parameterCompletion.getCompletion(toolName, argumentName, partialValue); // Also try tool-specific completions if (toolName && argumentName) { const toolSpecific = await parameterCompletion.getToolSpecificCompletion( toolName, argumentName, context ); if (toolSpecific.values.length > 0) { completion.values = [...toolSpecific.values, ...completion.values]; } } return { completion: { values: completion.values.slice(0, 50), // Limit to 50 suggestions hasMore: completion.values.length > 50 } }; } catch (error) { console.error('Error in completion handler:', error); return { completion: { values: [], hasMore: false } }; } }); server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; const startTime = Date.now(); // Check if tool requires confirmation and isn't already confirmed const argsWithConfirmation = args as any; if (ConfirmationSystem.requiresConfirmation(name) && !argsWithConfirmation.confirmed) { const confirmationRequest = ConfirmationSystem.getConfirmationRequest(name, argsWithConfirmation); if (confirmationRequest) { const confirmationMessage = ConfirmationSystem.generateConfirmationMessage(confirmationRequest); // Log the confirmation request auditLogger.log({ toolName: name, parameters: args as Record<string, any>, outcome: 'error', error: 'Confirmation required', dhis2Instance: dhis2Client?.baseURL }); return { content: [{ type: 'text', text: confirmationMessage }], isError: true }; } } try { switch (name) { case 'dhis2_configure': const { baseUrl, username, password } = args as { baseUrl: string; username: string; password: string; }; dhis2Client = new DHIS2Client(baseUrl, username, password); await dhis2Client!.testConnection(); // Update parameter completion with the new client parameterCompletion = new ParameterCompletion(dhis2Client); // Fetch user information and permissions try { currentUser = await dhis2Client!.getCurrentUser(); userPermissions = PermissionSystem.extractPermissionsFromDHIS2User(currentUser); } catch (error) { console.warn('Could not fetch user permissions, using defaults:', error); userPermissions = PermissionSystem.createDefaultPermissions(); } tools = [...createDevelopmentTools(), ...createAPITools()]; // Log successful connection auditLogger.log({ toolName: name, parameters: { baseUrl, username: '***REDACTED***' }, outcome: 'success', dhis2Instance: baseUrl, userId: currentUser?.username || username, executionTime: Date.now() - startTime }); const permissionSummary = PermissionSystem.getPermissionSummary(userPermissions); return { content: [ { type: 'text', text: `āœ… Successfully connected to DHIS2 instance at ${baseUrl} šŸ‘¤ User: ${currentUser?.displayName || username} šŸ” Permission Level: ${permissionSummary.level} šŸ“ ${permissionSummary.description} šŸ› ļø Available Tools: ${PermissionSystem.filterToolsByPermissions(tools, userPermissions).length} of ${tools.length} total tools • Development tools: Available without connection āœ… • API tools: Now available with connection āœ… āœ… Allowed Operations: ${permissionSummary.allowedOperations.map(op => ` • ${op}`).join('\n')} ${permissionSummary.restrictedOperations.length > 0 ? `ā›” Restricted Operations: ${permissionSummary.restrictedOperations.map(op => ` • ${op}`).join('\n')}` : ''} šŸ” All tools starting with 'dhis2_' are now available for development and API access.`, }, ], }; default: // Check if this tool requires DHIS2 connection const apiTools = createAPITools().map(t => t.name); if (apiTools.includes(name) && !dhis2Client) { return { content: [ { type: 'text', text: `āŒ **Connection Required** The tool \`${name}\` requires a DHIS2 instance connection to function. šŸš€ **Quick Setup - Choose Your Option:** **Option 1: Public Demo (Fastest)** Use the \`dhis2_configure\` tool with these demo credentials: \`\`\`json { "baseUrl": "https://play.dhis2.org/2.40.4", "username": "admin", "password": "district" } \`\`\` **Option 2: Local Docker Instance** \`\`\`bash # Run DHIS2 locally docker run -d --name dhis2 -p 8080:8080 dhis2/core:2.40.4 # Then configure with: { "baseUrl": "http://localhost:8080", "username": "admin", "password": "district" } \`\`\` **Option 3: Organization Instance** Contact your DHIS2 administrator for: - Instance URL (e.g., https://yourorg.dhis2.org) - Username and password - Required authorities for your workflow šŸ’” **Meanwhile, try these tools without connection:** • \`dhis2_init_webapp\` - Create new DHIS2 apps • \`dhis2_configure_build_system\` - Setup build tools • \`dhis2_generate_test_setup\` - Testing configuration • \`dhis2_create_ui_components\` - UI code generation šŸ”„ **After connecting, you'll have access to ${name} and all API tools!**`, }, ], isError: true, }; } break; } // At this point, if we reach here for API tools, dhis2Client must be initialized // because the earlier check would have returned an error switch (name) { case 'dhis2_get_system_info': const systemInfo = await dhis2Client!.getSystemInfo(); logSuccessfulOperation(name, {}, systemInfo, startTime); return { content: [ { type: 'text', text: JSON.stringify(systemInfo, null, 2), }, ], }; case 'dhis2_list_data_elements': const { filter: deFilter, pageSize: dePageSize } = args as { filter?: string; pageSize?: number; }; const dataElements = await dhis2Client!.getDataElements(filterUndefinedValues({ filter: deFilter, pageSize: dePageSize })); logSuccessfulOperation(name, { filter: deFilter, pageSize: dePageSize }, dataElements, startTime); return { content: [ { type: 'text', text: JSON.stringify(dataElements, null, 2), }, ], }; case 'dhis2_create_data_element': const newDataElement = args as any; const createdDE = await dhis2Client!.createDataElement(newDataElement); logSuccessfulOperation(name, newDataElement, createdDE, startTime, [createdDE.response?.uid || 'unknown']); return { content: [ { type: 'text', text: JSON.stringify(createdDE, null, 2), }, ], }; case 'dhis2_update_data_element': const { id: deId, ...deUpdateData } = args as any; const updatedDE = await dhis2Client!.updateDataElement(deId, deUpdateData); return { content: [ { type: 'text', text: JSON.stringify(updatedDE, null, 2), }, ], }; case 'dhis2_delete_data_element': const { id: deDeleteId } = args as { id: string }; const deletedDE = await dhis2Client!.deleteDataElement(deDeleteId); return { content: [ { type: 'text', text: JSON.stringify(deletedDE, null, 2), }, ], }; case 'dhis2_list_data_sets': const { filter: dsFilter, pageSize: dsPageSize } = args as { filter?: string; pageSize?: number; }; const dataSets = await dhis2Client!.getDataSets(filterUndefinedValues({ filter: dsFilter, pageSize: dsPageSize })); return { content: [ { type: 'text', text: JSON.stringify(dataSets, null, 2), }, ], }; case 'dhis2_create_data_set': const newDataSet = args as any; const createdDS = await dhis2Client!.createDataSet(newDataSet); return { content: [ { type: 'text', text: JSON.stringify(createdDS, null, 2), }, ], }; case 'dhis2_list_categories': const { filter: catFilter, pageSize: catPageSize } = args as { filter?: string; pageSize?: number; }; const categories = await dhis2Client!.getCategories(filterUndefinedValues({ filter: catFilter, pageSize: catPageSize })); return { content: [ { type: 'text', text: JSON.stringify(categories, null, 2), }, ], }; case 'dhis2_create_category': const newCategory = args as any; const createdCat = await dhis2Client!.createCategory(newCategory); return { content: [ { type: 'text', text: JSON.stringify(createdCat, null, 2), }, ], }; case 'dhis2_list_category_options': const { filter: coFilter, pageSize: coPageSize } = args as { filter?: string; pageSize?: number; }; const categoryOptions = await dhis2Client!.getCategoryOptions(filterUndefinedValues({ filter: coFilter, pageSize: coPageSize })); return { content: [ { type: 'text', text: JSON.stringify(categoryOptions, null, 2), }, ], }; case 'dhis2_create_category_option': const newCategoryOption = args as any; const createdCO = await dhis2Client!.createCategoryOption(newCategoryOption); return { content: [ { type: 'text', text: JSON.stringify(createdCO, null, 2), }, ], }; case 'dhis2_list_category_combos': const { filter: ccFilter, pageSize: ccPageSize } = args as { filter?: string; pageSize?: number; }; const categoryCombos = await dhis2Client!.getCategoryCombos(filterUndefinedValues({ filter: ccFilter, pageSize: ccPageSize })); return { content: [ { type: 'text', text: JSON.stringify(categoryCombos, null, 2), }, ], }; case 'dhis2_create_category_combo': const newCategoryCombo = args as any; const createdCC = await dhis2Client!.createCategoryCombo(newCategoryCombo); return { content: [ { type: 'text', text: JSON.stringify(createdCC, null, 2), }, ], }; case 'dhis2_list_category_option_combos': const { filter: cocFilter, pageSize: cocPageSize } = args as { filter?: string; pageSize?: number; }; const categoryOptionCombos = await dhis2Client!.getCategoryOptionCombos(filterUndefinedValues({ filter: cocFilter, pageSize: cocPageSize })); return { content: [ { type: 'text', text: JSON.stringify(categoryOptionCombos, null, 2), }, ], }; case 'dhis2_list_org_units': const { filter: ouFilter, pageSize: ouPageSize } = args as { filter?: string; pageSize?: number; }; const orgUnits = await dhis2Client!.getOrganisationUnits(filterUndefinedValues({ filter: ouFilter, pageSize: ouPageSize })); return { content: [ { type: 'text', text: JSON.stringify(orgUnits, null, 2), }, ], }; case 'dhis2_list_org_unit_groups': const { filter: ougFilter, pageSize: ougPageSize } = args as { filter?: string; pageSize?: number; }; const orgUnitGroups = await dhis2Client!.getOrganisationUnitGroups(filterUndefinedValues({ filter: ougFilter, pageSize: ougPageSize })); return { content: [ { type: 'text', text: JSON.stringify(orgUnitGroups, null, 2), }, ], }; case 'dhis2_create_org_unit_group': const newOrgUnitGroup = args as any; const createdOUG = await dhis2Client!.createOrganisationUnitGroup(newOrgUnitGroup); return { content: [ { type: 'text', text: JSON.stringify(createdOUG, null, 2), }, ], }; case 'dhis2_list_validation_rules': const { filter: vrFilter, pageSize: vrPageSize } = args as { filter?: string; pageSize?: number; }; const validationRules = await dhis2Client!.getValidationRules(filterUndefinedValues({ filter: vrFilter, pageSize: vrPageSize })); return { content: [ { type: 'text', text: JSON.stringify(validationRules, null, 2), }, ], }; case 'dhis2_create_validation_rule': const { leftSideExpression, rightSideExpression, ...vrData } = args as any; const validationRule = { ...vrData, leftSide: { expression: leftSideExpression, missingValueStrategy: 'SKIP_IF_ANY_VALUE_MISSING', }, rightSide: { expression: rightSideExpression, missingValueStrategy: 'SKIP_IF_ANY_VALUE_MISSING', }, }; const createdVR = await dhis2Client!.createValidationRule(validationRule); return { content: [ { type: 'text', text: JSON.stringify(createdVR, null, 2), }, ], }; case 'dhis2_run_validation': const validationParams = args as any; const validationResults = await dhis2Client!.runValidation(validationParams); return { content: [ { type: 'text', text: JSON.stringify(validationResults, null, 2), }, ], }; case 'dhis2_get_data_values': const dataValueParams = args as any; const dataValues = await dhis2Client!.getDataValues(dataValueParams); return { content: [ { type: 'text', text: JSON.stringify(dataValues, null, 2), }, ], }; case 'dhis2_bulk_import_data_values': const { dataValues: valuesToImport } = args as { dataValues: any[] }; const importResult = await dhis2Client!.bulkImportDataValues(valuesToImport); return { content: [ { type: 'text', text: JSON.stringify(importResult, null, 2), }, ], }; case 'dhis2_get_analytics': const analyticsParams = args as any; const analytics = await dhis2Client!.getAnalytics(analyticsParams); return { content: [ { type: 'text', text: JSON.stringify(analytics, null, 2), }, ], }; case 'dhis2_list_programs': const { filter: progFilter, pageSize: progPageSize } = args as { filter?: string; pageSize?: number; }; const programs = await dhis2Client!.getPrograms(filterUndefinedValues({ filter: progFilter, pageSize: progPageSize })); return { content: [ { type: 'text', text: JSON.stringify(programs, null, 2), }, ], }; case 'dhis2_create_program': const newProgram = args as any; const createdProg = await dhis2Client!.createProgram(newProgram); return { content: [ { type: 'text', text: JSON.stringify(createdProg, null, 2), }, ], }; case 'dhis2_list_tracked_entity_types': const { filter: tetFilter, pageSize: tetPageSize } = args as { filter?: string; pageSize?: number; }; const trackedEntityTypes = await dhis2Client!.getTrackedEntityTypes(filterUndefinedValues({ filter: tetFilter, pageSize: tetPageSize })); return { content: [ { type: 'text', text: JSON.stringify(trackedEntityTypes, null, 2), }, ], }; case 'dhis2_create_tracked_entity_type': const newTET = args as any; const createdTET = await dhis2Client!.createTrackedEntityType(newTET); return { content: [ { type: 'text', text: JSON.stringify(createdTET, null, 2), }, ], }; case 'dhis2_list_tracked_entity_attributes': const { filter: teaFilter, pageSize: teaPageSize } = args as { filter?: string; pageSize?: number; }; const trackedEntityAttributes = await dhis2Client!.getTrackedEntityAttributes(filterUndefinedValues({ filter: teaFilter, pageSize: teaPageSize })); return { content: [ { type: 'text', text: JSON.stringify(trackedEntityAttributes, null, 2), }, ], }; case 'dhis2_create_tracked_entity_attribute': const newTEA = args as any; const createdTEA = await dhis2Client!.createTrackedEntityAttribute(newTEA); return { content: [ { type: 'text', text: JSON.stringify(createdTEA, null, 2), }, ], }; case 'dhis2_list_program_stages': const { filter: psFilter, pageSize: psPageSize } = args as { filter?: string; pageSize?: number; }; const programStages = await dhis2Client!.getProgramStages(filterUndefinedValues({ filter: psFilter, pageSize: psPageSize })); return { content: [ { type: 'text', text: JSON.stringify(programStages, null, 2), }, ], }; case 'dhis2_create_program_stage': const newPS = args as any; const createdPS = await dhis2Client!.createProgramStage(newPS); return { content: [ { type: 'text', text: JSON.stringify(createdPS, null, 2), }, ], }; case 'dhis2_list_program_rules': const { filter: prFilter, pageSize: prPageSize } = args as { filter?: string; pageSize?: number; }; const programRules = await dhis2Client!.getProgramRules(filterUndefinedValues({ filter: prFilter, pageSize: prPageSize })); return { content: [ { type: 'text', text: JSON.stringify(programRules, null, 2), }, ], }; case 'dhis2_create_program_rule': const newPR = args as any; const createdPR = await dhis2Client!.createProgramRule(newPR); return { content: [ { type: 'text', text: JSON.stringify(createdPR, null, 2), }, ], }; case 'dhis2_list_tracked_entity_instances': const teiParams = args as any; const trackedEntityInstances = await dhis2Client!.getTrackedEntityInstances(teiParams); return { content: [ { type: 'text', text: JSON.stringify(trackedEntityInstances, null, 2), }, ], }; case 'dhis2_create_tracked_entity_instance': const newTEI = args as any; const createdTEI = await dhis2Client!.createTrackedEntityInstance(newTEI); return { content: [ { type: 'text', text: JSON.stringify(createdTEI, null, 2), }, ], }; case 'dhis2_list_enrollments': const enrollmentParams = args as any; const enrollments = await dhis2Client!.getEnrollments(enrollmentParams); return { content: [ { type: 'text', text: JSON.stringify(enrollments, null, 2), }, ], }; case 'dhis2_create_enrollment': const newEnrollment = args as any; const createdEnrollment = await dhis2Client!.createEnrollment(newEnrollment); return { content: [ { type: 'text', text: JSON.stringify(createdEnrollment, null, 2), }, ], }; case 'dhis2_list_events': const eventParams = args as any; const events = await dhis2Client!.getEvents(eventParams); return { content: [ { type: 'text', text: JSON.stringify(events, null, 2), }, ], }; case 'dhis2_create_event': const newEvent = args as any; const createdEvent = await dhis2Client!.createEvent(newEvent); return { content: [ { type: 'text', text: JSON.stringify(createdEvent, null, 2), }, ], }; case 'dhis2_bulk_import_events': const { events: eventsToImport } = args as { events: any[] }; const eventImportResult = await dhis2Client!.bulkImportEvents(eventsToImport); return { content: [ { type: 'text', text: JSON.stringify(eventImportResult, null, 2), }, ], }; case 'dhis2_get_event_analytics': const eventAnalyticsParams = args as any; const eventAnalytics = await dhis2Client!.getEventAnalytics(eventAnalyticsParams); return { content: [ { type: 'text', text: JSON.stringify(eventAnalytics, null, 2), }, ], }; case 'dhis2_get_enrollment_analytics': const enrollmentAnalyticsParams = args as any; const enrollmentAnalytics = await dhis2Client!.getEnrollmentAnalytics(enrollmentAnalyticsParams); return { content: [ { type: 'text', text: JSON.stringify(enrollmentAnalytics, null, 2), }, ], }; case 'dhis2_get_data_statistics': const dataStatistics = await dhis2Client!.getDataStatistics(); return { content: [ { type: 'text', text: JSON.stringify(dataStatistics, null, 2), }, ], }; case 'dhis2_list_dashboards': const { filter: dbFilter, pageSize: dbPageSize } = args as { filter?: string; pageSize?: number; }; const dashboards = await dhis2Client!.getDashboards(filterUndefinedValues({ filter: dbFilter, pageSize: dbPageSize })); return { content: [ { type: 'text', text: JSON.stringify(dashboards, null, 2), }, ], }; case 'dhis2_create_dashboard': const newDashboard = args as any; const createdDashboard = await dhis2Client!.createDashboard(newDashboard); return { content: [ { type: 'text', text: JSON.stringify(createdDashboard, null, 2), }, ], }; case 'dhis2_list_visualizations': const { filter: vizFilter, pageSize: vizPageSize } = args as { filter?: string; pageSize?: number; }; const visualizations = await dhis2Client!.getVisualizations(filterUndefinedValues({ filter: vizFilter, pageSize: vizPageSize })); return { content: [ { type: 'text', text: JSON.stringify(visualizations, null, 2), }, ], }; case 'dhis2_create_visualization': const newVisualization = args as any; const createdVisualization = await dhis2Client!.createVisualization(newVisualization); return { content: [ { type: 'text', text: JSON.stringify(createdVisualization, null, 2), }, ], }; case 'dhis2_list_reports': const { filter: reportFilter, pageSize: reportPageSize } = args as { filter?: string; pageSize?: number; }; const reports = await dhis2Client!.getReports(filterUndefinedValues({ filter: reportFilter, pageSize: reportPageSize })); return { content: [ { type: 'text', text: JSON.stringify(reports, null, 2), }, ], }; case 'dhis2_generate_report': const { reportId, ...reportParams } = args as any; const generatedReport = await dhis2Client!.generateReport(reportId, reportParams); return { content: [ { type: 'text', text: 'Report generated successfully (binary data)', }, ], }; // Web App Platform Integration Tools case 'dhis2_init_webapp': const { appName, appTitle, appDescription, namespace, appType, template, typescript, pwa, outputPath } = args as any; const initInstructions = generateWebAppInitInstructions(appName, appTitle, appDescription, { namespace, appType, template, typescript, pwa, outputPath }); return { content: [ { type: 'text', text: initInstructions, }, ], }; case 'dhis2_configure_app_manifest': const manifestArgs = args as any; const manifestContent = generateManifestContent(manifestArgs); return { content: [ { type: 'text', text: manifestContent, }, ], }; case 'dhis2_configure_build_system': const buildSystemArgs = args as any; const buildConfig = generateBuildSystemConfig(buildSystemArgs); return { content: [ { type: 'text', text: buildConfig, }, ], }; case 'dhis2_setup_dev_environment': const devEnvArgs = args as any; const devEnvConfig = generateDevEnvironmentConfig(devEnvArgs); return { content: [ { type: 'text', text: devEnvConfig, }, ], }; case 'dhis2_generate_app_runtime_config': const runtimeConfigArgs = args as any; const runtimeConfig = generateAppRuntimeConfig(runtimeConfigArgs); return { content: [ { type: 'text', text: runtimeConfig, }, ], }; case 'dhis2_create_datastore_namespace': const datastoreNsArgs = args as any; const result = await dhis2Client!.createDataStoreNamespace(datastoreNsArgs.namespace, datastoreNsArgs); return { content: [ { type: 'text', text: `DataStore namespace '${datastoreNsArgs.namespace}' configured successfully`, }, ], }; case 'dhis2_manage_datastore_key': const datastoreKeyArgs = args as any; const keyResult = await dhis2Client!.manageDataStoreKey(datastoreKeyArgs); return { content: [ { type: 'text', text: JSON.stringify(keyResult, null, 2), }, ], }; case 'dhis2_setup_authentication_patterns': const authArgs = args as any; const authPatterns = generateAuthenticationPatterns(authArgs); return { content: [ { type: 'text', text: authPatterns, }, ], }; case 'dhis2_create_ui_components': const uiArgs = args as any; const uiComponents = generateUIComponents(uiArgs); return { content: [ { type: 'text', text: uiComponents, }, ], }; // Phase 4: UI Library Integration case 'dhis2_generate_ui_form_patterns': const formArgs = args as any; const formCode = generateUIFormPatterns(formArgs); return { content: [ { type: 'text', text: formCode } ] }; case 'dhis2_generate_ui_data_display': const displayArgs = args as any; const displayCode = generateUIDataDisplayPatterns(displayArgs); return { content: [ { type: 'text', text: displayCode } ] }; case 'dhis2_generate_ui_navigation_layout': const navArgs = args as any; const navCode = generateUINavigationLayout(navArgs); return { content: [ { type: 'text', text: navCode } ] }; case 'dhis2_generate_design_system': const dsArgs = args as any; const dsConfig = generateDesignSystemConfig(dsArgs); return { content: [ { type: 'text', text: dsConfig } ] }; // Android UI pattern tools (Phase 4) case 'android_generate_material_form': const aFormArgs = args as any; const aForm = generateAndroidMaterialForm(aFormArgs); return { content: [{ type: 'text', text: aForm }] }; case 'android_generate_list_adapter': const aListArgs = args as any; const aList = generateAndroidListAdapter(aListArgs); return { content: [{ type: 'text', text: aList }] }; case 'android_generate_navigation_drawer': const aNavArgs = args as any; const aNav = generateAndroidNavigationDrawer(aNavArgs); return { content: [{ type: 'text', text: aNav }] }; case 'android_generate_bottom_sheet': const aSheetArgs = args as any; const aSheet = generateAndroidBottomSheet(aSheetArgs); return { content: [{ type: 'text', text: aSheet }] }; case 'dhis2_generate_test_setup': const testArgs = args as any; const testSetup = generateTestSetup(testArgs); return { content: [ { type: 'text', text: testSetup, }, ], }; // DHIS2 Debugging and Troubleshooting Tools case 'dhis2_diagnose_cors_issues': const corsArgs = args as any; const corsAnalysis = diagnoseCORSIssues(corsArgs); return { content: [ { type: 'text', text: corsAnalysis, }, ], }; case 'dhis2_configure_cors_allowlist': const corsAllowlistArgs = args as any; const corsConfig = generateCORSConfiguration(corsAllowlistArgs); return { content: [ { type: 'text', text: corsConfig, }, ], }; case 'dhis2_debug_authentication': const authDebugArgs = args as any; const authAnalysis = debugAuthentication(authDebugArgs); return { content: [ { type: 'text', text: authAnalysis, }, ], }; case 'dhis2_fix_proxy_configuration': const proxyArgs = args as any; const proxyConfig = generateProxyConfiguration(proxyArgs); return { content: [ { type: 'text', text: proxyConfig, }, ], }; case 'dhis2_resolve_build_issues': const buildIssueArgs = args as any; const buildSolution = resolveBuildIssues(buildIssueArgs); return { content: [ { type: 'text', text: buildSolution, }, ], }; case 'dhis2_optimize_performance': const perfArgs = args as any; const perfOptimizations = generatePerformanceOptimizations(perfArgs); return { content: [ { type: 'text', text: perfOptimizations, }, ], }; case 'dhis2_validate_environment': const envArgs = args as any; const envValidation = validateEnvironment(envArgs); return { content: [ { type: 'text', text: envValidation, }, ], }; case 'dhis2_migration_assistant': const migrationArgs = args as any; const migrationGuide = generateMigrationGuide(migrationArgs); return { content: [ { type: 'text', text: migrationGuide, }, ], }; // Phase 3: Android SDK Integration Tools case 'dhis2_android_init_project': const androidProjectArgs = args as any; const androidProjectInstructions = generateAndroidProjectInit(androidProjectArgs); return { content: [ { type: 'text', text: androidProjectInstructions, }, ], }; case 'dhis2_android_configure_gradle': const gradleArgs = args as any; const gradleConfig = generateGradleBuildConfig(gradleArgs); return { content: [ { type: 'text', text: gradleConfig, }, ], }; case 'dhis2_android_setup_sync': const syncArgs = args as any; const syncConfig = generateSyncConfiguration(syncArgs); return { content: [ { type: 'text', text: syncConfig, }, ], }; case 'dhis2_android_configure_storage': const storageArgs = args as any; const storageConfig = generateStorageConfiguration(storageArgs); return { content: [ { type: 'text', text: storageConfig, }, ], }; case 'dhis2_android_setup_location_services': const locationArgs = args as any; const locationConfig = generateLocationServicesConfig(locationArgs); return { content: [ { type: 'text', text: locationConfig, }, ], }; case 'dhis2_android_configure_camera': const cameraArgs = args as any; const cameraConfig = generateCameraConfiguration(cameraArgs); return { content: [ { type: 'text', text: cameraConfig, }, ], }; case 'dhis2_android_setup_authentication': const androidAuthArgs = args as any; const authConfig = generateAndroidAuthenticationConfig(androidAuthArgs); return { content: [ { type: 'text', text: authConfig, }, ], }; case 'dhis2_android_generate_data_models': const dataModelsArgs = args as any; const dataModelsConfig = generateDataModelsConfiguration(dataModelsArgs); return { content: [ { type: 'text', text: dataModelsConfig, }, ], }; case 'dhis2_android_setup_testing': const testingArgs = args as any; const testingConfig = generateAndroidTestingConfiguration(testingArgs); return { content: [ { type: 'text', text: testingConfig, }, ], }; case 'dhis2_android_configure_ui_patterns': const androidUIArgs = args as any; const uiConfig = generateAndroidUIConfiguration(androidUIArgs); return { content: [ { type: 'text', text: uiConfig, }, ], }; case 'dhis2_android_setup_offline_analytics': const analyticsArgs = args as any; const analyticsConfig = generateOfflineAnalyticsConfiguration(analyticsArgs); return { content: [ { type: 'text', text: analyticsConfig, }, ], }; case 'dhis2_android_configure_notifications': const notificationsArgs = args as any; const notificationsConfig = generateNotificationsConfiguration(notificationsArgs); return { content: [ { type: 'text', text: notificationsConfig, }, ], }; case 'dhis2_android_performance_optimization': const androidPerfArgs = args as any; const perfConfig = generatePerformanceOptimizationConfiguration(androidPerfArgs); return { content: [ { type: 'text', text: perfConfig, }, ], }; // System Management and Audit Tools case 'dhis2_get_audit_log': const { limit = 50 } = args as { limit?: number }; const auditEntries = auditLogger.getAuditTrail(Math.min(limit, 1000)); // Log successful audit retrieval auditLogger.log({ toolName: name, parameters: { limit }, outcome: 'success', dhis2Instance: dhis2Client?.baseURL, userId: currentUser?.username, executionTime: Date.now() - startTime }); return { content: [{ type: 'text', text: `šŸ“‹ Audit Log (${auditEntries.length} entries)\n\n` + auditEntries.map(entry => `šŸ• ${entry.timestamp}\n` + `šŸ› ļø Tool: ${entry.toolName}\n` + `šŸ‘¤ User: ${entry.userId || 'unknown'}\n` + `${entry.outcome === 'success' ? 'āœ…' : 'āŒ'} Result: ${entry.outcome}${entry.error ? ` - ${entry.error}` : ''}\n` + `ā±ļø Duration: ${entry.executionTime || 0}ms\n` + `šŸ“ Instance: ${entry.dhis2Instance || 'N/A'}\n` ).join('\n') }] }; case 'dhis2_get_audit_summary': const summary = auditLogger.getAuditSummary(); const permissionSummary = PermissionSystem.getPermissionSummary(userPermissions); auditLogger.log({ toolName: name, parameters: {}, outcome: 'success', dhis2Instance: dhis2Client?.baseURL, userId: currentUser?.username, executionTime: Date.now() - startTime }); return { content: [{ type: 'text', text: `šŸ“Š DHIS2 MCP Server Audit Summary šŸ”¢ **Usage Statistics:** • Total Operations: ${summary.totalOperations} • Successful: ${summary.successCount} (${Math.round((summary.successCount / summary.totalOperations) * 100) || 0}%) • Errors: ${summary.errorCount} (${Math.round((summary.errorCount / summary.totalOperations) * 100) || 0}%) šŸ‘¤ **Current Session:** • User: ${currentUser?.displayName || 'Unknown'} • Permission Level: ${permissionSummary.level} • Available Tools: ${PermissionSystem.filterToolsByPermissions(tools, userPermissions).length} of ${tools.length} • Connected to: ${dhis2Client?.baseURL || 'Not connected'} šŸ› ļø **Most Used Tools:** ${summary.mostUsedTools.slice(0, 5).map(tool => ` • ${tool.tool}: ${tool.count} times`).join('\n') || ' • No operations yet'} ${summary.recentErrors.length > 0 ? `āš ļø **Recent Errors:** ${summary.recentErrors.slice(0, 3).map(error => ` • ${error.toolName}: ${error.error}`).join('\n')}` : 'āœ… No recent errors'}` }] }; case 'dhis2_export_audit_log': const exportData = auditLogger.exportAuditLog(); auditLogger.log({ toolName: name, parameters: {}, outcome: 'success', dhis2Instance: dhis2Client?.baseURL, userId: currentUser?.username, executionTime: Date.now() - startTime }); return { content: [{ type: 'text', text: `šŸ“¤ Audit Log Export\n\n${exportData}` }] }; case 'dhis2_clear_audit_log': auditLogger.clear(); return { content: [{ type: 'text', text: `šŸ—‘ļø Audit log cleared successfully.` }] }; case 'dhis2_get_permission_info': const filteredTools = PermissionSystem.filterToolsByPermissions(tools, userPermissions); const permInfo = PermissionSystem.getPermissionSummary(userPermissions); auditLogger.log({ toolName: name, parameters: {}, outcome: 'success', dhis2Instance: dhis2Client?.baseURL, userId: currentUser?.username, executionTime: Date.now() - startTime }); return { content: [{ type: 'text', text: `šŸ” Permission Information šŸ‘¤ **User Details:** • Name: ${currentUser?.displayName || 'Unknown'} • Username: ${currentUser?.username || 'Unknown'} • User Groups: ${currentUser?.userGroups?.map((g: any) => g.name).join(', ') || 'None'} šŸŽÆ **Permission Level:** ${permInfo.level} šŸ“ **Description:** ${permInfo.description} āœ… **Allowed Operations:** ${permInfo.allowedOperations.map(op => ` • ${op}`).join('\n')} ${permInfo.restrictedOperations.length > 0 ? `ā›” **Restricted Operations:** ${permInfo.restrictedOperations.map(op => ` • ${op}`).join('\n')}` : ''} šŸ› ļø **Available Tools:** ${filteredTools.length} of ${tools.length} total • Configuration: ${filteredTools.filter(t => t.name.includes('configure')).length} • Data Management: ${filteredTools.filter(t => t.name.includes('list') || t.name.includes('get')).length} • Creation Tools: ${filteredTools.filter(t => t.name.includes('create')).length} • Analytics: ${filteredTools.filter(t => t.name.includes('analytics')).length} • Development: ${filteredTools.filter(t => t.name.includes('init') || t.name.includes('generate')).length} šŸ”‘ **DHIS2 Authorities:** ${userPermissions.authorities.length} authorities assigned` }] }; // Multi-Server Composition Tools case 'dhis2_get_server_info': const serverInfo = multiServerComposition.getServerInfo(); auditLogger.log({ toolName: name, parameters: {}, outcome: 'success', dhis2Instance: dhis2Client?.baseURL, userId: currentUser?.username, executionTime: Date.now() - startTime }); return { content: [{ type: 'text', text: `šŸ–„ļø DHIS2 MCP Server Information **Server Details:** • Name: ${serverInfo.name} • Version: ${serverInfo.version} • Composition Mode: ${serverInfo.compositionMode ? 'Enabled' : 'Disabled'} **Description:** ${serverInfo.description} **Capabilities:** ${serverInfo.capabilities.map(cap => ` • **${cap.domain}** (v${cap.version}): ${cap.operations.join(', ')}` ).join('\n')} **Compatible MCP Servers:** ${serverInfo.compatibleWith.map(server => ` • ${server}`).join('\n')} **Currently Registered Servers:** ${multiServerComposition.getCompatibleServers().length} ${multiServerComposition.getCompatibleServers().map(server => ` • ${server.name} v${server.version}: ${server.description}` ).join('\n') || ' • No servers registered yet'} šŸ”— **Integration Status:** ${multiServerComposition.getCompatibleServers().length > 0 ? `āœ… Ready for multi-server workflows with ${multiServerComposition.getCompatibleServers().length} registered server(s)` : 'āš ļø No compatible servers registered. Use dhis2_register_compatible_server to enable workflows.' }` }] }; case 'dhis2_get_composition_examples': const examples = multiServerComposition.generateIntegrationExamples(); auditLogger.log({ toolName: name, parameters: {}, outcome: 'success', dhis2Instance: dhis2Client?.baseURL, userId: currentUser?.username, executionTime: Date.now() - startTime }); return { content: [{ type: 'text', text: examples }] }; case 'dhis2_register_compatible_server': const serverRegistrationArgs = args as { name: string; version: string; capabilities: Array<{ domain: string; operations: string[]; version: string }>; description: string; }; multiServerComposition.registerCompatibleServer({ name: serverRegistrationArgs.name, version: serverRegistrationArgs.version, capabilities: serverRegistrationArgs.capabilities, description: serverRegistrationArgs.description, compatibleWith: ['dhis2-mcp'], compositionMode: true }); auditLogger.log({ toolName: name, parameters: serverRegistrationArgs, outcome: 'success', dhis2Instance: dhis2Client?.baseURL, userId: currentUser?.username, executionTime: Date.now() - startTime }); return { content: [{ type: 'text', text: `āœ… Successfully registered MCP server: ${serverRegistrationArgs.name} v${serverRegistrationArgs.version} **Capabilities Added:** ${serverRegistrationArgs.capabilities.map(cap => ` • ${cap.domain}: ${cap.operations.join(', ')}` ).join('\n')} šŸ”— **Multi-Server Workflows:** Now enabled with ${multiServerComposition.getCompatibleServers().length} registered server(s) šŸ’” **Next Steps:** • Use dhis2_get_composition_recommendations to see integration suggestions • Use dhis2_export_for_composition to share data with other servers • Check dhis2_get_composition_examples for workflow ideas` }] }; case 'dhis2_get_composition_recommendations': const { lastTool } = args as { lastTool?: string }; const recommendations = multiServerComposition.getCompositionRecommendations( lastTool || 'dhis2_general', {} ); auditLogger.log({ toolName: name, parameters: { lastTool }, outcome: 'success', dhis2Instance: dhis2Client?.baseURL, userId: currentUser?.username, executionTime: Date.now() - startTime }); return { content: [{ type: 'text', text: `šŸ’” Multi-Server Integration Recommendations ${lastTool ? `**Based on your last operation:** ${lastTool}` : '**General Recommendations:**'} **Suggested Next Steps:** ${recommendations.length > 0 ? recommendations.map(rec => ` • ${rec}`).join('\n') : ' • No specific recommendations for this operation' } **Available Compatible Servers:** ${multiServerComposition.getCompatibleServers().length} ${multiServerComposition.getCompatibleServers().map(server => ` • **${server.name}**: ${server.capabilities.map(c => c.domain).join(', ')}` ).join('\n') || ' • Register servers with dhis2_register_compatible_server'} **Common Integration Patterns:** • **Data Quality**: DHIS2 validation → GitHub issues → Slack notifications • **Development**: DHIS2 app generation → Git commits → Pull requests • **Analytics**: DHIS2 reports → Database storage → Email distribution • **Monitoring**: DHIS2 system info → Log aggregation → Alert systems šŸ”— Use **dhis2_export_for_composition** to prepare data for other servers.` }] }; case 'dhis2_export_for_composition': const exportArgs = args as { toolName: string; data: any; targetServer?: string; metadata?: Record<string, any>; }; const exportContext = multiServerComposition.exportDataForComposition( exportArgs.toolName, exportArgs.data, { targetServer: exportArgs.targetServer, ...exportArgs.metadata } ); auditLogger.log({ toolName: name, parameters: exportArgs, outcome: 'success', dhis2Instance: dhis2Client?.baseURL, userId: currentUser?.username, executionTime: Date.now() - startTime, resourcesAffected: [exportArgs.toolName] }); return { content: [{ type: 'text', text: `šŸ“¤ Data Exported for Multi-Server Composition **Export Details:** • Source Tool: ${exportArgs.toolName} • Timestamp: ${exportContext.timestamp} • Target Server: ${exportArgs.targetServer || 'Any compatible server'} • Operation Type: ${exportContext.operationType} **Standardized Export Format:** \`\`\`json ${JSON.stringify(exportContext, null, 2)} \`\`\` **Compatible Servers:** ${multiServerComposition.getCompatibleServers().map(s => s.name).join(', ') || 'None registered'} **Next Steps:** • Share this exported data with other MCP servers • Use the standardized format for workflow automation • Check server documentation for import procedures šŸ’” **Integration Tip:** This format is designed to work seamlessly with GitHub, Slack, Database, and other MCP servers.` }] }; default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { // Log the error auditLogger.log({ toolName: name, parameters: args as Record<string, any>, outcome: 'error', error: error instanceof Error ? error.message : String(error), dhis2Instance: dhis2Client?.baseURL, userId: currentUser?.username, executionTime: Date.now() - startTime }); return { content: [ { type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }); async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error('šŸš€ DHIS2 MCP Server running on stdio'); console.error(''); console.error('šŸ“‹ Available Modes:'); console.error(' šŸ› ļø Development Mode: Ready! (App building, code generation, testing)'); console.error(' šŸ”§ API Mode: Use dhis2_configure to connect to DHIS2 instance'); console.error(''); console.error('šŸ’” Start with development tools - no DHIS2 connection required!'); } main().catch((error) => { console.error('Fatal error in main():', error); process.exit(1); });

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/Dradebo/dhis2-mcp'

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