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
JavaScript
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