UNPKG

dhis2-mcp-server

Version:

A Model Context Protocol server providing 108 tools for DHIS2 development, including code generators, debugging helpers, and documentation access for web and Android app development.

830 lines 31.8 kB
import axios from 'axios'; export class DHIS2Client { client; baseUrl; constructor(baseUrl, username, password) { this.baseUrl = baseUrl.replace(/\/$/, ''); this.client = axios.create({ baseURL: `${this.baseUrl}/api`, auth: { username, password, }, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, timeout: 30000, }); } get baseURL() { return this.baseUrl; } async testConnection() { try { const response = await this.client.get('/me'); return response.status === 200; } catch (error) { throw new Error(`Failed to connect to DHIS2: ${error}`); } } async getCurrentUser() { const response = await this.client.get('/me', { params: { fields: 'id,displayName,username,userCredentials[username,userRoles[id,name,authorities]],userGroups[id,name],organisationUnits[id,name,level],authorities' } }); return response.data; } async getSystemInfo() { try { const response = await this.client.get('/system/info'); return response.data; } catch (error) { throw new Error(`Failed to get system info: ${error}`); } } async getDataElements(params) { try { const queryParams = { fields: params?.fields || 'id,name,displayName,shortName,code,valueType,domainType', filter: params?.filter, paging: params?.paging ?? false, pageSize: params?.pageSize || 50, }; const response = await this.client.get('/dataElements', { params: queryParams }); return response.data; } catch (error) { throw new Error(`Failed to get data elements: ${error}`); } } async getOrganisationUnits(params) { try { const queryParams = { fields: params?.fields || 'id,name,displayName,shortName,code,level,path', filter: params?.filter, paging: params?.paging ?? false, pageSize: params?.pageSize || 50, }; const response = await this.client.get('/organisationUnits', { params: queryParams }); return response.data; } catch (error) { throw new Error(`Failed to get organisation units: ${error}`); } } async getAnalytics(query) { try { const params = {}; if (query.dimension) params.dimension = query.dimension; if (query.filter) params.filter = query.filter; if (query.startDate) params.startDate = query.startDate; if (query.endDate) params.endDate = query.endDate; const response = await this.client.get('/analytics', { params }); return response.data; } catch (error) { throw new Error(`Failed to get analytics: ${error}`); } } async createDataElement(dataElement) { try { const response = await this.client.post('/dataElements', dataElement); return response.data; } catch (error) { throw new Error(`Failed to create data element: ${error}`); } } async updateDataElement(id, dataElement) { try { const response = await this.client.put(`/dataElements/${id}`, dataElement); return response.data; } catch (error) { throw new Error(`Failed to update data element: ${error}`); } } async deleteDataElement(id) { try { const response = await this.client.delete(`/dataElements/${id}`); return response.data; } catch (error) { throw new Error(`Failed to delete data element: ${error}`); } } async createDataValue(dataValue) { try { const response = await this.client.post('/dataValues', dataValue); return response.data; } catch (error) { throw new Error(`Failed to create data value: ${error}`); } } async getDataSets(params) { try { const queryParams = { fields: params?.fields || 'id,name,displayName,shortName,code,description,periodType,categoryCombo[id,name],dataSetElements[dataElement[id,name,valueType]],organisationUnits[id,name],sections[id,name,sortOrder],expiryDays,timelyDays,openFuturePeriods', filter: params?.filter, paging: params?.paging ?? false, pageSize: params?.pageSize || 50, }; const response = await this.client.get('/dataSets', { params: queryParams }); return response.data; } catch (error) { throw new Error(`Failed to get data sets: ${error}`); } } async createDataSet(dataSet) { try { const response = await this.client.post('/dataSets', dataSet); return response.data; } catch (error) { throw new Error(`Failed to create data set: ${error}`); } } async updateDataSet(id, dataSet) { try { const response = await this.client.put(`/dataSets/${id}`, dataSet); return response.data; } catch (error) { throw new Error(`Failed to update data set: ${error}`); } } async getCategories(params) { try { const queryParams = { fields: params?.fields || 'id,name,displayName,shortName,code,dataDimension,dataDimensionType,categoryOptions[id,name,displayName,code]', filter: params?.filter, paging: params?.paging ?? false, pageSize: params?.pageSize || 50, }; const response = await this.client.get('/categories', { params: queryParams }); return response.data; } catch (error) { throw new Error(`Failed to get categories: ${error}`); } } async createCategory(category) { try { const response = await this.client.post('/categories', category); return response.data; } catch (error) { throw new Error(`Failed to create category: ${error}`); } } async getCategoryOptions(params) { try { const queryParams = { fields: params?.fields || 'id,name,displayName,shortName,code,startDate,endDate', filter: params?.filter, paging: params?.paging ?? false, pageSize: params?.pageSize || 50, }; const response = await this.client.get('/categoryOptions', { params: queryParams }); return response.data; } catch (error) { throw new Error(`Failed to get category options: ${error}`); } } async createCategoryOption(categoryOption) { try { const response = await this.client.post('/categoryOptions', categoryOption); return response.data; } catch (error) { throw new Error(`Failed to create category option: ${error}`); } } async getCategoryCombos(params) { try { const queryParams = { fields: params?.fields || 'id,name,displayName,shortName,code,dataDimensionType,categories[id,name],categoryOptionCombos[id,name,displayName]', filter: params?.filter, paging: params?.paging ?? false, pageSize: params?.pageSize || 50, }; const response = await this.client.get('/categoryCombos', { params: queryParams }); return response.data; } catch (error) { throw new Error(`Failed to get category combos: ${error}`); } } async createCategoryCombo(categoryCombo) { try { const response = await this.client.post('/categoryCombos', categoryCombo); return response.data; } catch (error) { throw new Error(`Failed to create category combo: ${error}`); } } async getCategoryOptionCombos(params) { try { const queryParams = { fields: params?.fields || 'id,name,displayName,code,categoryOptions[id,name],categoryCombo[id,name]', filter: params?.filter, paging: params?.paging ?? false, pageSize: params?.pageSize || 50, }; const response = await this.client.get('/categoryOptionCombos', { params: queryParams }); return response.data; } catch (error) { throw new Error(`Failed to get category option combos: ${error}`); } } async getOrganisationUnitGroups(params) { try { const queryParams = { fields: params?.fields || 'id,name,displayName,shortName,code,symbol,organisationUnits[id,name]', filter: params?.filter, paging: params?.paging ?? false, pageSize: params?.pageSize || 50, }; const response = await this.client.get('/organisationUnitGroups', { params: queryParams }); return response.data; } catch (error) { throw new Error(`Failed to get organisation unit groups: ${error}`); } } async createOrganisationUnitGroup(orgUnitGroup) { try { const response = await this.client.post('/organisationUnitGroups', orgUnitGroup); return response.data; } catch (error) { throw new Error(`Failed to create organisation unit group: ${error}`); } } async getValidationRules(params) { try { const queryParams = { fields: params?.fields || 'id,name,displayName,description,instruction,importance,operator,leftSide[expression,description],rightSide[expression,description],periodType', filter: params?.filter, paging: params?.paging ?? false, pageSize: params?.pageSize || 50, }; const response = await this.client.get('/validationRules', { params: queryParams }); return response.data; } catch (error) { throw new Error(`Failed to get validation rules: ${error}`); } } async createValidationRule(validationRule) { try { const response = await this.client.post('/validationRules', validationRule); return response.data; } catch (error) { throw new Error(`Failed to create validation rule: ${error}`); } } async runValidation(params) { try { const response = await this.client.post('/validation', params); return response.data; } catch (error) { throw new Error(`Failed to run validation: ${error}`); } } async getDataValues(params) { try { const queryParams = {}; Object.entries(params).forEach(([key, value]) => { if (value !== undefined) { if (Array.isArray(value)) { queryParams[key] = value.join(','); } else { queryParams[key] = value; } } }); const response = await this.client.get('/dataValues', { params: queryParams }); return response.data; } catch (error) { throw new Error(`Failed to get data values: ${error}`); } } async bulkImportDataValues(dataValues) { try { const payload = { dataValues, }; const response = await this.client.post('/dataValues', payload); return response.data; } catch (error) { throw new Error(`Failed to bulk import data values: ${error}`); } } async deleteDataValue(params) { try { const response = await this.client.delete('/dataValues', { params }); return response.data; } catch (error) { throw new Error(`Failed to delete data value: ${error}`); } } async getPrograms(params) { try { const queryParams = { fields: params?.fields || 'id,name,displayName,shortName,code,description,programType,trackedEntityType[id,name],programStages[id,name,sortOrder],organisationUnits[id,name]', filter: params?.filter, paging: params?.paging ?? false, pageSize: params?.pageSize || 50, }; const response = await this.client.get('/programs', { params: queryParams }); return response.data; } catch (error) { throw new Error(`Failed to get programs: ${error}`); } } async createProgram(program) { try { const response = await this.client.post('/programs', program); return response.data; } catch (error) { throw new Error(`Failed to create program: ${error}`); } } async updateProgram(id, program) { try { const response = await this.client.put(`/programs/${id}`, program); return response.data; } catch (error) { throw new Error(`Failed to update program: ${error}`); } } async getTrackedEntityTypes(params) { try { const queryParams = { fields: params?.fields || 'id,name,displayName,shortName,code,description,trackedEntityTypeAttributes[id,trackedEntityAttribute[id,name,valueType,unique],displayInList,mandatory,searchable,sortOrder]', filter: params?.filter, paging: params?.paging ?? false, pageSize: params?.pageSize || 50, }; const response = await this.client.get('/trackedEntityTypes', { params: queryParams }); return response.data; } catch (error) { throw new Error(`Failed to get tracked entity types: ${error}`); } } async createTrackedEntityType(trackedEntityType) { try { const response = await this.client.post('/trackedEntityTypes', trackedEntityType); return response.data; } catch (error) { throw new Error(`Failed to create tracked entity type: ${error}`); } } async getTrackedEntityAttributes(params) { try { const queryParams = { fields: params?.fields || 'id,name,displayName,shortName,code,description,valueType,unique,inherit,pattern,confidential,optionSet[id,name,options[id,name,code]]', filter: params?.filter, paging: params?.paging ?? false, pageSize: params?.pageSize || 50, }; const response = await this.client.get('/trackedEntityAttributes', { params: queryParams }); return response.data; } catch (error) { throw new Error(`Failed to get tracked entity attributes: ${error}`); } } async createTrackedEntityAttribute(attribute) { try { const response = await this.client.post('/trackedEntityAttributes', attribute); return response.data; } catch (error) { throw new Error(`Failed to create tracked entity attribute: ${error}`); } } async getProgramStages(params) { try { const queryParams = { fields: params?.fields || 'id,name,displayName,shortName,code,description,program[id,name],sortOrder,repeatable,minDaysFromStart,programStageDataElements[id,dataElement[id,name],compulsory,sortOrder]', filter: params?.filter, paging: params?.paging ?? false, pageSize: params?.pageSize || 50, }; const response = await this.client.get('/programStages', { params: queryParams }); return response.data; } catch (error) { throw new Error(`Failed to get program stages: ${error}`); } } async createProgramStage(programStage) { try { const response = await this.client.post('/programStages', programStage); return response.data; } catch (error) { throw new Error(`Failed to create program stage: ${error}`); } } async getProgramRules(params) { try { const queryParams = { fields: params?.fields || 'id,name,displayName,description,program[id,name],programStage[id,name],condition,priority,programRuleActions[id,programRuleActionType,dataElement[id,name],data,content]', filter: params?.filter, paging: params?.paging ?? false, pageSize: params?.pageSize || 50, }; const response = await this.client.get('/programRules', { params: queryParams }); return response.data; } catch (error) { throw new Error(`Failed to get program rules: ${error}`); } } async createProgramRule(programRule) { try { const response = await this.client.post('/programRules', programRule); return response.data; } catch (error) { throw new Error(`Failed to create program rule: ${error}`); } } async getTrackedEntityInstances(params) { try { const queryParams = {}; Object.entries(params).forEach(([key, value]) => { if (value !== undefined) { if (Array.isArray(value)) { queryParams[key] = value.join(','); } else { queryParams[key] = value; } } }); if (!queryParams.fields) { queryParams.fields = 'id,trackedEntityType,orgUnit,attributes[attribute,value,displayValue],enrollments[id,program,enrollmentDate,status,events[id,programStage,eventDate,status,dataValues[dataElement,value]]]'; } const response = await this.client.get('/trackedEntityInstances', { params: queryParams }); return response.data; } catch (error) { throw new Error(`Failed to get tracked entity instances: ${error}`); } } async createTrackedEntityInstance(tei) { try { const response = await this.client.post('/trackedEntityInstances', tei); return response.data; } catch (error) { throw new Error(`Failed to create tracked entity instance: ${error}`); } } async updateTrackedEntityInstance(id, tei) { try { const response = await this.client.put(`/trackedEntityInstances/${id}`, tei); return response.data; } catch (error) { throw new Error(`Failed to update tracked entity instance: ${error}`); } } async getEnrollments(params) { try { const queryParams = {}; Object.entries(params).forEach(([key, value]) => { if (value !== undefined) { queryParams[key] = value; } }); if (!queryParams.fields) { queryParams.fields = 'id,trackedEntityInstance,program,orgUnit,enrollmentDate,incidentDate,status,followup,events[id,programStage,eventDate,status,dataValues[dataElement,value]]'; } const response = await this.client.get('/enrollments', { params: queryParams }); return response.data; } catch (error) { throw new Error(`Failed to get enrollments: ${error}`); } } async createEnrollment(enrollment) { try { const response = await this.client.post('/enrollments', enrollment); return response.data; } catch (error) { throw new Error(`Failed to create enrollment: ${error}`); } } async updateEnrollment(id, enrollment) { try { const response = await this.client.put(`/enrollments/${id}`, enrollment); return response.data; } catch (error) { throw new Error(`Failed to update enrollment: ${error}`); } } async getEvents(params) { try { const queryParams = {}; Object.entries(params).forEach(([key, value]) => { if (value !== undefined) { queryParams[key] = value; } }); if (!queryParams.fields) { queryParams.fields = 'id,enrollment,program,programStage,orgUnit,trackedEntityInstance,status,eventDate,dueDate,coordinate,dataValues[dataElement,value],created,lastUpdated'; } const response = await this.client.get('/events', { params: queryParams }); return response.data; } catch (error) { throw new Error(`Failed to get events: ${error}`); } } async createEvent(event) { try { const response = await this.client.post('/events', event); return response.data; } catch (error) { throw new Error(`Failed to create event: ${error}`); } } async updateEvent(id, event) { try { const response = await this.client.put(`/events/${id}`, event); return response.data; } catch (error) { throw new Error(`Failed to update event: ${error}`); } } async bulkImportEvents(events) { try { const payload = { events, }; const response = await this.client.post('/events', payload); return response.data; } catch (error) { throw new Error(`Failed to bulk import events: ${error}`); } } async getEventAnalytics(params) { try { const queryParams = { program: params.program, startDate: params.startDate, endDate: params.endDate, orgUnit: params.orgUnit, outputType: params.outputType || 'EVENT', }; if (params.stage) queryParams.stage = params.stage; if (params.dimension) queryParams.dimension = params.dimension.join(','); if (params.filter) queryParams.filter = params.filter.join(','); if (params.value) queryParams.value = params.value; if (params.coordinatesOnly) queryParams.coordinatesOnly = params.coordinatesOnly; if (params.page) queryParams.page = params.page; if (params.pageSize) queryParams.pageSize = params.pageSize; const response = await this.client.get('/analytics/events/query', { params: queryParams }); return response.data; } catch (error) { throw new Error(`Failed to get event analytics: ${error}`); } } async getEnrollmentAnalytics(params) { try { const queryParams = { program: params.program, startDate: params.startDate, endDate: params.endDate, orgUnit: params.orgUnit, }; if (params.dimension) queryParams.dimension = params.dimension.join(','); if (params.filter) queryParams.filter = params.filter.join(','); if (params.page) queryParams.page = params.page; if (params.pageSize) queryParams.pageSize = params.pageSize; const response = await this.client.get('/analytics/enrollments/query', { params: queryParams }); return response.data; } catch (error) { throw new Error(`Failed to get enrollment analytics: ${error}`); } } async getDataStatistics() { try { const response = await this.client.get('/dataStatistics'); return response.data; } catch (error) { throw new Error(`Failed to get data statistics: ${error}`); } } async getDashboards(params) { try { const queryParams = { fields: params?.fields || 'id,name,displayName,dashboardItems[id,type,visualization[id,name],map[id,name],reportTable[id,name],chart[id,name]]', filter: params?.filter, paging: params?.paging ?? false, pageSize: params?.pageSize || 50, }; const response = await this.client.get('/dashboards', { params: queryParams }); return response.data; } catch (error) { throw new Error(`Failed to get dashboards: ${error}`); } } async createDashboard(dashboard) { try { const response = await this.client.post('/dashboards', dashboard); return response.data; } catch (error) { throw new Error(`Failed to create dashboard: ${error}`); } } async getVisualizations(params) { try { const queryParams = { fields: params?.fields || 'id,name,displayName,type,dataDimensionItems,columns,rows,filters,organisationUnits,periods', filter: params?.filter, paging: params?.paging ?? false, pageSize: params?.pageSize || 50, }; const response = await this.client.get('/visualizations', { params: queryParams }); return response.data; } catch (error) { throw new Error(`Failed to get visualizations: ${error}`); } } async createVisualization(visualization) { try { const response = await this.client.post('/visualizations', visualization); return response.data; } catch (error) { throw new Error(`Failed to create visualization: ${error}`); } } async getReports(params) { try { const queryParams = { fields: params?.fields || 'id,name,displayName,type,designContent', filter: params?.filter, paging: params?.paging ?? false, pageSize: params?.pageSize || 50, }; const response = await this.client.get('/reports', { params: queryParams }); return response.data; } catch (error) { throw new Error(`Failed to get reports: ${error}`); } } async generateReport(reportId, params) { try { const queryParams = {}; if (params?.organisationUnit) queryParams.organisationUnit = params.organisationUnit; if (params?.period) queryParams.period = params.period; if (params?.date) queryParams.date = params.date; const response = await this.client.get(`/reports/${reportId}/data.pdf`, { params: queryParams, responseType: 'blob', }); return response.data; } catch (error) { throw new Error(`Failed to generate report: ${error}`); } } // DataStore operations for Web App Platform integration async createDataStoreNamespace(namespace, options) { try { // Create namespace metadata (if supported) if (options?.description || options?.sharing) { const namespaceConfig = { description: options.description, sharing: options.sharing }; try { await this.client.put(`/dataStore/${namespace}/__metadata__`, namespaceConfig); } catch (error) { // Metadata creation is optional, continue with key creation console.warn('Could not create namespace metadata:', error); } } // Create initial keys if provided if (options?.initialKeys) { const results = []; for (const { key, value } of options.initialKeys) { try { const response = await this.client.post(`/dataStore/${namespace}/${key}`, value); results.push({ key, status: 'created', data: response.data }); } catch (error) { results.push({ key, status: 'error', error: error }); } } return { namespace, results }; } return { namespace, status: 'created' }; } catch (error) { throw new Error(`Failed to create DataStore namespace: ${error}`); } } async manageDataStoreKey(params) { try { const { operation, namespace, key, value, encrypt } = params; switch (operation) { case 'list': const response = await this.client.get(`/dataStore/${namespace}`); return response.data; case 'read': if (!key) throw new Error('Key is required for read operation'); const readResponse = await this.client.get(`/dataStore/${namespace}/${key}`); return readResponse.data; case 'create': case 'update': if (!key || value === undefined) { throw new Error('Key and value are required for create/update operations'); } const data = encrypt ? this.encryptValue(value) : value; const method = operation === 'create' ? 'post' : 'put'; const writeResponse = await this.client[method](`/dataStore/${namespace}/${key}`, data); return writeResponse.data; case 'delete': if (!key) throw new Error('Key is required for delete operation'); await this.client.delete(`/dataStore/${namespace}/${key}`); return { status: 'deleted', namespace, key }; default: throw new Error(`Unsupported operation: ${operation}`); } } catch (error) { throw new Error(`Failed to manage DataStore key: ${error}`); } } encryptValue(value) { // Simple base64 encoding for demonstration // In production, use proper encryption const jsonString = JSON.stringify(value); return { __encrypted__: true, data: Buffer.from(jsonString).toString('base64') }; } } //# sourceMappingURL=dhis2-client.js.map