@cyanheads/who-gho-mcp-server 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CLAUDE.md +391 -0
  2. package/Dockerfile +98 -0
  3. package/LICENSE +201 -0
  4. package/README.md +294 -0
  5. package/changelog/0.1.x/0.1.0.md +20 -0
  6. package/changelog/0.1.x/0.1.1.md +23 -0
  7. package/changelog/template.md +119 -0
  8. package/dist/config/server-config.d.ts +9 -0
  9. package/dist/config/server-config.d.ts.map +1 -0
  10. package/dist/config/server-config.js +28 -0
  11. package/dist/config/server-config.js.map +1 -0
  12. package/dist/index.d.ts +7 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +36 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/mcp-server/resources/definitions/who-dimension-values.resource.d.ts +18 -0
  17. package/dist/mcp-server/resources/definitions/who-dimension-values.resource.d.ts.map +1 -0
  18. package/dist/mcp-server/resources/definitions/who-dimension-values.resource.js +41 -0
  19. package/dist/mcp-server/resources/definitions/who-dimension-values.resource.js.map +1 -0
  20. package/dist/mcp-server/resources/definitions/who-indicator-metadata.resource.d.ts +16 -0
  21. package/dist/mcp-server/resources/definitions/who-indicator-metadata.resource.d.ts.map +1 -0
  22. package/dist/mcp-server/resources/definitions/who-indicator-metadata.resource.js +44 -0
  23. package/dist/mcp-server/resources/definitions/who-indicator-metadata.resource.js.map +1 -0
  24. package/dist/mcp-server/tools/definitions/who-get-indicator-metadata.tool.d.ts +25 -0
  25. package/dist/mcp-server/tools/definitions/who-get-indicator-metadata.tool.d.ts.map +1 -0
  26. package/dist/mcp-server/tools/definitions/who-get-indicator-metadata.tool.js +112 -0
  27. package/dist/mcp-server/tools/definitions/who-get-indicator-metadata.tool.js.map +1 -0
  28. package/dist/mcp-server/tools/definitions/who-list-dimension-values.tool.d.ts +24 -0
  29. package/dist/mcp-server/tools/definitions/who-list-dimension-values.tool.d.ts.map +1 -0
  30. package/dist/mcp-server/tools/definitions/who-list-dimension-values.tool.js +76 -0
  31. package/dist/mcp-server/tools/definitions/who-list-dimension-values.tool.js.map +1 -0
  32. package/dist/mcp-server/tools/definitions/who-list-dimensions.tool.d.ts +12 -0
  33. package/dist/mcp-server/tools/definitions/who-list-dimensions.tool.d.ts.map +1 -0
  34. package/dist/mcp-server/tools/definitions/who-list-dimensions.tool.js +38 -0
  35. package/dist/mcp-server/tools/definitions/who-list-dimensions.tool.js.map +1 -0
  36. package/dist/mcp-server/tools/definitions/who-list-indicators.tool.d.ts +17 -0
  37. package/dist/mcp-server/tools/definitions/who-list-indicators.tool.d.ts.map +1 -0
  38. package/dist/mcp-server/tools/definitions/who-list-indicators.tool.js +64 -0
  39. package/dist/mcp-server/tools/definitions/who-list-indicators.tool.js.map +1 -0
  40. package/dist/mcp-server/tools/definitions/who-query-indicator-data.tool.d.ts +58 -0
  41. package/dist/mcp-server/tools/definitions/who-query-indicator-data.tool.d.ts.map +1 -0
  42. package/dist/mcp-server/tools/definitions/who-query-indicator-data.tool.js +215 -0
  43. package/dist/mcp-server/tools/definitions/who-query-indicator-data.tool.js.map +1 -0
  44. package/dist/mcp-server/tools/definitions/who-search-indicators.tool.d.ts +23 -0
  45. package/dist/mcp-server/tools/definitions/who-search-indicators.tool.d.ts.map +1 -0
  46. package/dist/mcp-server/tools/definitions/who-search-indicators.tool.js +89 -0
  47. package/dist/mcp-server/tools/definitions/who-search-indicators.tool.js.map +1 -0
  48. package/dist/services/gho/gho-service.d.ts +50 -0
  49. package/dist/services/gho/gho-service.d.ts.map +1 -0
  50. package/dist/services/gho/gho-service.js +263 -0
  51. package/dist/services/gho/gho-service.js.map +1 -0
  52. package/dist/services/gho/types.d.ts +113 -0
  53. package/dist/services/gho/types.d.ts.map +1 -0
  54. package/dist/services/gho/types.js +6 -0
  55. package/dist/services/gho/types.js.map +1 -0
  56. package/package.json +79 -0
  57. package/server.json +99 -0
@@ -0,0 +1,64 @@
1
+ /**
2
+ * @fileoverview Browse the WHO GHO indicator catalog with pagination.
3
+ * @module mcp-server/tools/definitions/who-list-indicators
4
+ */
5
+ import { tool, z } from '@cyanheads/mcp-ts-core';
6
+ import { getGhoService } from '../../../services/gho/gho-service.js';
7
+ export const whoListIndicators = tool('who_list_indicators', {
8
+ title: 'List WHO GHO Indicators',
9
+ description: 'Browse the WHO Global Health Observatory indicator catalog with pagination. ' +
10
+ 'Use when you want to explore indicators without a keyword, or to page through the full catalog. ' +
11
+ 'Use who_search_indicators when you have a keyword to narrow the results.',
12
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
13
+ input: z.object({
14
+ limit: z
15
+ .number()
16
+ .int()
17
+ .min(1)
18
+ .max(500)
19
+ .default(50)
20
+ .describe('Number of indicators to return per page. Default 50, max 500.'),
21
+ offset: z
22
+ .number()
23
+ .int()
24
+ .min(0)
25
+ .default(0)
26
+ .describe('Zero-based offset for pagination. Default 0.'),
27
+ }),
28
+ output: z.object({
29
+ indicators: z
30
+ .array(z
31
+ .object({
32
+ indicatorCode: z
33
+ .string()
34
+ .describe('Unique indicator code used in who_query_indicator_data, e.g. "WHOSIS_000001".'),
35
+ indicatorName: z
36
+ .string()
37
+ .describe('Full indicator name, e.g. "Life expectancy at birth (years)".'),
38
+ })
39
+ .describe('An indicator entry with its code and name.'))
40
+ .describe('Indicators for the requested page.'),
41
+ total: z.number().describe('Total number of indicators in the GHO catalog.'),
42
+ hasMore: z.boolean().describe('True when more indicators exist beyond the current page.'),
43
+ }),
44
+ async handler(input, ctx) {
45
+ ctx.log.info('Listing indicators', { limit: input.limit, offset: input.offset });
46
+ const { indicators, total } = await getGhoService().listIndicators({ limit: input.limit, offset: input.offset }, ctx);
47
+ return {
48
+ indicators,
49
+ total,
50
+ hasMore: input.offset + indicators.length < total,
51
+ };
52
+ },
53
+ format: (result) => {
54
+ const lines = [
55
+ `**Indicators (${result.indicators.length} shown, ${result.total} total, hasMore: ${result.hasMore}):**`,
56
+ '',
57
+ ];
58
+ for (const ind of result.indicators) {
59
+ lines.push(`- **${ind.indicatorCode}**: ${ind.indicatorName}`);
60
+ }
61
+ return [{ type: 'text', text: lines.join('\n') }];
62
+ },
63
+ });
64
+ //# sourceMappingURL=who-list-indicators.tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"who-list-indicators.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/who-list-indicators.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAE9D,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,CAAC,qBAAqB,EAAE;IAC3D,KAAK,EAAE,yBAAyB;IAChC,WAAW,EACT,8EAA8E;QAC9E,kGAAkG;QAClG,0EAA0E;IAC5E,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE;IAC/E,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC;QACd,KAAK,EAAE,CAAC;aACL,MAAM,EAAE;aACR,GAAG,EAAE;aACL,GAAG,CAAC,CAAC,CAAC;aACN,GAAG,CAAC,GAAG,CAAC;aACR,OAAO,CAAC,EAAE,CAAC;aACX,QAAQ,CAAC,+DAA+D,CAAC;QAC5E,MAAM,EAAE,CAAC;aACN,MAAM,EAAE;aACR,GAAG,EAAE;aACL,GAAG,CAAC,CAAC,CAAC;aACN,OAAO,CAAC,CAAC,CAAC;aACV,QAAQ,CAAC,8CAA8C,CAAC;KAC5D,CAAC;IACF,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,UAAU,EAAE,CAAC;aACV,KAAK,CACJ,CAAC;aACE,MAAM,CAAC;YACN,aAAa,EAAE,CAAC;iBACb,MAAM,EAAE;iBACR,QAAQ,CACP,+EAA+E,CAChF;YACH,aAAa,EAAE,CAAC;iBACb,MAAM,EAAE;iBACR,QAAQ,CAAC,+DAA+D,CAAC;SAC7E,CAAC;aACD,QAAQ,CAAC,4CAA4C,CAAC,CAC1D;aACA,QAAQ,CAAC,oCAAoC,CAAC;QACjD,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gDAAgD,CAAC;QAC5E,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,CAAC,0DAA0D,CAAC;KAC1F,CAAC;IAEF,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG;QACtB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QACjF,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,MAAM,aAAa,EAAE,CAAC,cAAc,CAChE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,EAC5C,GAAG,CACJ,CAAC;QACF,OAAO;YACL,UAAU;YACV,KAAK;YACL,OAAO,EAAE,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM,GAAG,KAAK;SAClD,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE;QACjB,MAAM,KAAK,GAAG;YACZ,iBAAiB,MAAM,CAAC,UAAU,CAAC,MAAM,WAAW,MAAM,CAAC,KAAK,oBAAoB,MAAM,CAAC,OAAO,MAAM;YACxG,EAAE;SACH,CAAC;QACF,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YACpC,KAAK,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,aAAa,OAAO,GAAG,CAAC,aAAa,EAAE,CAAC,CAAC;QACjE,CAAC;QACD,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACpD,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @fileoverview Query data rows for a WHO GHO indicator with spatial, temporal, and dimension filters.
3
+ * @module mcp-server/tools/definitions/who-query-indicator-data
4
+ */
5
+ import { z } from '@cyanheads/mcp-ts-core';
6
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
7
+ export declare const whoQueryIndicatorData: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
8
+ indicator_code: z.ZodString;
9
+ country_codes: z.ZodOptional<z.ZodArray<z.ZodString>>;
10
+ region_codes: z.ZodOptional<z.ZodArray<z.ZodString>>;
11
+ income_group_codes: z.ZodOptional<z.ZodArray<z.ZodString>>;
12
+ year_from: z.ZodOptional<z.ZodNumber>;
13
+ year_to: z.ZodOptional<z.ZodNumber>;
14
+ sex: z.ZodOptional<z.ZodEnum<{
15
+ SEX_BTSX: "SEX_BTSX";
16
+ SEX_FMLE: "SEX_FMLE";
17
+ SEX_MLE: "SEX_MLE";
18
+ }>>;
19
+ dim1_value: z.ZodOptional<z.ZodString>;
20
+ include_uncertainty: z.ZodDefault<z.ZodBoolean>;
21
+ limit: z.ZodDefault<z.ZodNumber>;
22
+ }, z.core.$strip>, z.ZodObject<{
23
+ rows: z.ZodArray<z.ZodObject<{
24
+ indicatorCode: z.ZodString;
25
+ spatialDimType: z.ZodOptional<z.ZodString>;
26
+ spatialDim: z.ZodOptional<z.ZodString>;
27
+ spatialLabel: z.ZodOptional<z.ZodString>;
28
+ year: z.ZodNumber;
29
+ dim1Type: z.ZodOptional<z.ZodString>;
30
+ dim1: z.ZodOptional<z.ZodString>;
31
+ dim2Type: z.ZodOptional<z.ZodString>;
32
+ dim2: z.ZodOptional<z.ZodString>;
33
+ numericValue: z.ZodOptional<z.ZodNumber>;
34
+ low: z.ZodOptional<z.ZodNumber>;
35
+ high: z.ZodOptional<z.ZodNumber>;
36
+ displayValue: z.ZodOptional<z.ZodString>;
37
+ comments: z.ZodOptional<z.ZodString>;
38
+ }, z.core.$strip>>;
39
+ totalRows: z.ZodNumber;
40
+ truncated: z.ZodBoolean;
41
+ truncatedNote: z.ZodOptional<z.ZodString>;
42
+ }, z.core.$strip>, readonly [{
43
+ readonly reason: "indicator_not_found";
44
+ readonly code: JsonRpcErrorCode.NotFound;
45
+ readonly when: "The indicator code returned HTTP 404 from the GHO API.";
46
+ readonly recovery: "Use who_search_indicators to find the correct indicator code and retry.";
47
+ }, {
48
+ readonly reason: "no_data";
49
+ readonly code: JsonRpcErrorCode.NotFound;
50
+ readonly when: "The indicator exists but no data rows matched the applied filters.";
51
+ readonly recovery: string;
52
+ }, {
53
+ readonly reason: "ambiguous_spatial_filter";
54
+ readonly code: JsonRpcErrorCode.InvalidParams;
55
+ readonly when: "More than one of country_codes, region_codes, or income_group_codes were provided.";
56
+ readonly recovery: "Provide only one spatial filter type per call. To compare countries and regions, make separate calls.";
57
+ }]>;
58
+ //# sourceMappingURL=who-query-indicator-data.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"who-query-indicator-data.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/who-query-indicator-data.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAKjE,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0PhC,CAAC"}
@@ -0,0 +1,215 @@
1
+ /**
2
+ * @fileoverview Query data rows for a WHO GHO indicator with spatial, temporal, and dimension filters.
3
+ * @module mcp-server/tools/definitions/who-query-indicator-data
4
+ */
5
+ import { tool, z } from '@cyanheads/mcp-ts-core';
6
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
7
+ import { getGhoService } from '../../../services/gho/gho-service.js';
8
+ const SEX_VALUES = ['SEX_BTSX', 'SEX_FMLE', 'SEX_MLE'];
9
+ export const whoQueryIndicatorData = tool('who_query_indicator_data', {
10
+ title: 'Query WHO GHO Indicator Data',
11
+ description: 'Query data rows for a single WHO GHO indicator with optional spatial, temporal, and dimension filters. ' +
12
+ 'Returns rows with numeric values, uncertainty intervals (Low/High), and spatial/time metadata. ' +
13
+ 'This is the primary data-fetching tool in the find-then-query workflow: ' +
14
+ 'use who_search_indicators to find the indicator code, optionally call who_get_indicator_metadata ' +
15
+ 'to confirm which filter dimensions are valid, then call this tool. ' +
16
+ 'Spatial filters are mutually exclusive per call: provide only one of country_codes, region_codes, ' +
17
+ 'or income_group_codes — mixing them triggers an error. ' +
18
+ 'Omitting all spatial filters returns all geographies (may be large; use limit to cap). ' +
19
+ 'The sex filter applies as Dim1Type=SEX — if the indicator does not use SEX as Dim1, the filter ' +
20
+ 'returns empty rows; check who_get_indicator_metadata first if uncertain.',
21
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
22
+ input: z.object({
23
+ indicator_code: z
24
+ .string()
25
+ .min(1)
26
+ .describe('Indicator code to query, e.g. "WHOSIS_000001". Use who_search_indicators to find codes.'),
27
+ country_codes: z
28
+ .array(z.string().min(1))
29
+ .optional()
30
+ .describe('ISO 3166-1 alpha-3 country codes to filter on, e.g. ["JPN","USA","BRA"]. ' +
31
+ 'Mutually exclusive with region_codes and income_group_codes.'),
32
+ region_codes: z
33
+ .array(z.string().min(1))
34
+ .optional()
35
+ .describe('WHO region codes to filter on, e.g. ["AFR","EUR","AMR","EMR","SEAR","WPR"]. ' +
36
+ 'Use who_list_dimension_values with dimension="REGION" to see all valid codes. ' +
37
+ 'Mutually exclusive with country_codes and income_group_codes.'),
38
+ income_group_codes: z
39
+ .array(z.string().min(1))
40
+ .optional()
41
+ .describe('World Bank income group codes, e.g. ["WB_HI","WB_LMI","WB_LI","WB_UMI"]. ' +
42
+ 'Use who_list_dimension_values with dimension="WORLDBANKINCOMEGROUP" to see all valid codes. ' +
43
+ 'Mutually exclusive with country_codes and region_codes.'),
44
+ year_from: z
45
+ .number()
46
+ .int()
47
+ .optional()
48
+ .describe('Start year (inclusive) for the time range filter, e.g. 2015.'),
49
+ year_to: z
50
+ .number()
51
+ .int()
52
+ .optional()
53
+ .describe('End year (inclusive) for the time range filter, e.g. 2023.'),
54
+ sex: z
55
+ .enum(SEX_VALUES)
56
+ .optional()
57
+ .describe('Filter on sex dimension: SEX_BTSX (both sexes), SEX_FMLE (female), SEX_MLE (male). ' +
58
+ 'Only applies when the indicator uses SEX as its first cross-cutting dimension.'),
59
+ dim1_value: z
60
+ .string()
61
+ .optional()
62
+ .describe('Arbitrary Dim1 value filter for indicators whose first cross-cutting dimension is not SEX ' +
63
+ '(e.g. an AGEGROUP code). Ignored when sex is also provided.'),
64
+ include_uncertainty: z
65
+ .boolean()
66
+ .default(true)
67
+ .describe('Include Low and High uncertainty interval bounds in output. Default true.'),
68
+ limit: z
69
+ .number()
70
+ .int()
71
+ .min(1)
72
+ .max(1000)
73
+ .default(200)
74
+ .describe('Maximum number of data rows to return. Default 200, max 1000.'),
75
+ }),
76
+ output: z.object({
77
+ rows: z
78
+ .array(z
79
+ .object({
80
+ indicatorCode: z.string().describe('Indicator code for this row.'),
81
+ spatialDimType: z
82
+ .string()
83
+ .optional()
84
+ .describe('Spatial dimension type: COUNTRY, REGION, WORLDBANKINCOMEGROUP, GLOBAL, etc.'),
85
+ spatialDim: z
86
+ .string()
87
+ .optional()
88
+ .describe('Spatial dimension value, e.g. "JPN", "AFR", "WB_HI".'),
89
+ spatialLabel: z
90
+ .string()
91
+ .optional()
92
+ .describe('Human-readable spatial label, e.g. WHO region name for a country row.'),
93
+ year: z.number().describe('Year of the data point.'),
94
+ dim1Type: z
95
+ .string()
96
+ .optional()
97
+ .describe('First cross-cutting dimension type, e.g. "SEX".'),
98
+ dim1: z
99
+ .string()
100
+ .optional()
101
+ .describe('First cross-cutting dimension value, e.g. "SEX_BTSX".'),
102
+ dim2Type: z
103
+ .string()
104
+ .optional()
105
+ .describe('Second cross-cutting dimension type, e.g. "AGEGROUP".'),
106
+ dim2: z.string().optional().describe('Second cross-cutting dimension value.'),
107
+ numericValue: z.number().optional().describe('Machine-readable numeric value.'),
108
+ low: z
109
+ .number()
110
+ .optional()
111
+ .describe('Lower bound of the uncertainty interval. Present when include_uncertainty is true and the upstream provides it.'),
112
+ high: z
113
+ .number()
114
+ .optional()
115
+ .describe('Upper bound of the uncertainty interval. Present when include_uncertainty is true and the upstream provides it.'),
116
+ displayValue: z
117
+ .string()
118
+ .optional()
119
+ .describe('Formatted display string as provided by WHO, e.g. "84.5 [84.5-84.5]".'),
120
+ comments: z
121
+ .string()
122
+ .optional()
123
+ .describe('Data source notes or comments attached to this row.'),
124
+ })
125
+ .describe('A single data row from the GHO indicator dataset.'))
126
+ .describe('Data rows matching the query.'),
127
+ totalRows: z
128
+ .number()
129
+ .describe('Total row count matching the query on the server (before the limit is applied).'),
130
+ truncated: z
131
+ .boolean()
132
+ .describe('True when totalRows exceeds the limit and not all rows were returned.'),
133
+ truncatedNote: z
134
+ .string()
135
+ .optional()
136
+ .describe('Present when truncated is true. Explains how to retrieve additional rows, e.g. by narrowing filters or increasing the limit.'),
137
+ }),
138
+ errors: [
139
+ {
140
+ reason: 'indicator_not_found',
141
+ code: JsonRpcErrorCode.NotFound,
142
+ when: 'The indicator code returned HTTP 404 from the GHO API.',
143
+ recovery: 'Use who_search_indicators to find the correct indicator code and retry.',
144
+ },
145
+ {
146
+ reason: 'no_data',
147
+ code: JsonRpcErrorCode.NotFound,
148
+ when: 'The indicator exists but no data rows matched the applied filters.',
149
+ recovery: 'Broaden the filters: remove year range, try different spatial codes, or remove the sex filter. ' +
150
+ 'Use who_get_indicator_metadata to confirm which dimensions this indicator supports.',
151
+ },
152
+ {
153
+ reason: 'ambiguous_spatial_filter',
154
+ code: JsonRpcErrorCode.InvalidParams,
155
+ when: 'More than one of country_codes, region_codes, or income_group_codes were provided.',
156
+ recovery: 'Provide only one spatial filter type per call. To compare countries and regions, make separate calls.',
157
+ },
158
+ ],
159
+ async handler(input, ctx) {
160
+ // Validate mutual exclusion of spatial filters
161
+ const spatialCount = [input.country_codes, input.region_codes, input.income_group_codes].filter((arr) => arr?.length).length;
162
+ if (spatialCount > 1) {
163
+ throw ctx.fail('ambiguous_spatial_filter', 'Provide only one of country_codes, region_codes, or income_group_codes per call.', { ...ctx.recoveryFor('ambiguous_spatial_filter') });
164
+ }
165
+ ctx.log.info('Querying indicator data', {
166
+ indicatorCode: input.indicator_code,
167
+ spatialCount,
168
+ yearFrom: input.year_from,
169
+ yearTo: input.year_to,
170
+ });
171
+ const queryParams = {
172
+ indicatorCode: input.indicator_code,
173
+ includeUncertainty: input.include_uncertainty,
174
+ limit: input.limit,
175
+ ...(input.country_codes?.length && { countryCodes: input.country_codes }),
176
+ ...(input.region_codes?.length && { regionCodes: input.region_codes }),
177
+ ...(input.income_group_codes?.length && { incomeGroupCodes: input.income_group_codes }),
178
+ ...(input.year_from != null && { yearFrom: input.year_from }),
179
+ ...(input.year_to != null && { yearTo: input.year_to }),
180
+ ...(input.sex && { sex: input.sex }),
181
+ ...(input.dim1_value && { dim1Value: input.dim1_value }),
182
+ };
183
+ const { rows, totalRows, truncated } = await getGhoService().queryData(queryParams, ctx);
184
+ if (rows.length === 0) {
185
+ throw ctx.fail('no_data', `Indicator "${input.indicator_code}" returned no data for the applied filters.`, { indicatorCode: input.indicator_code, ...ctx.recoveryFor('no_data') });
186
+ }
187
+ const truncatedNote = truncated
188
+ ? `Showing ${input.limit} of ${totalRows} rows. Narrow the filters (year range, spatial codes) or increase the limit (max 1000) to retrieve more.`
189
+ : undefined;
190
+ return { rows, totalRows, truncated, ...(truncatedNote && { truncatedNote }) };
191
+ },
192
+ format: (result) => {
193
+ const lines = [
194
+ `**Rows: ${result.rows.length} shown${result.truncated ? ` of ${result.totalRows} total (truncated)` : ` of ${result.totalRows} total`}**`,
195
+ '',
196
+ ];
197
+ if (result.truncatedNote) {
198
+ lines.push(`> ${result.truncatedNote}`, '');
199
+ }
200
+ for (const row of result.rows) {
201
+ const spatial = [row.spatialDimType, row.spatialDim, row.spatialLabel]
202
+ .filter(Boolean)
203
+ .join(' / ');
204
+ const dim1 = row.dim1Type ? ` | ${row.dim1Type}: ${row.dim1}` : '';
205
+ const dim2 = row.dim2Type ? ` | ${row.dim2Type}: ${row.dim2}` : '';
206
+ const displayPart = row.displayValue ? ` display=${row.displayValue}` : '';
207
+ const numericPart = row.numericValue != null ? ` numeric=${row.numericValue}` : '';
208
+ const uncertainty = row.low != null && row.high != null ? ` [${row.low}–${row.high}]` : '';
209
+ const comments = row.comments ? ` — ${row.comments}` : '';
210
+ lines.push(`- [${row.indicatorCode}] **${row.year}** | ${spatial}${dim1}${dim2}${displayPart}${numericPart}${uncertainty}${comments}`);
211
+ }
212
+ return [{ type: 'text', text: lines.join('\n') }];
213
+ },
214
+ });
215
+ //# sourceMappingURL=who-query-indicator-data.tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"who-query-indicator-data.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/who-query-indicator-data.tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @fileoverview Search the WHO GHO indicator catalog by keyword.
3
+ * @module mcp-server/tools/definitions/who-search-indicators
4
+ */
5
+ import { z } from '@cyanheads/mcp-ts-core';
6
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
7
+ export declare const whoSearchIndicators: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
8
+ query: z.ZodString;
9
+ limit: z.ZodDefault<z.ZodNumber>;
10
+ }, z.core.$strip>, z.ZodObject<{
11
+ indicators: z.ZodArray<z.ZodObject<{
12
+ indicatorCode: z.ZodString;
13
+ indicatorName: z.ZodString;
14
+ }, z.core.$strip>>;
15
+ totalMatches: z.ZodNumber;
16
+ note: z.ZodOptional<z.ZodString>;
17
+ }, z.core.$strip>, readonly [{
18
+ readonly reason: "no_results";
19
+ readonly code: JsonRpcErrorCode.NotFound;
20
+ readonly when: "No indicators matched the query keyword.";
21
+ readonly recovery: "Try a different keyword or use who_list_indicators to browse the full catalog without a filter.";
22
+ }]>;
23
+ //# sourceMappingURL=who-search-indicators.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"who-search-indicators.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/who-search-indicators.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAGjE,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;GA8F9B,CAAC"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * @fileoverview Search the WHO GHO indicator catalog by keyword.
3
+ * @module mcp-server/tools/definitions/who-search-indicators
4
+ */
5
+ import { tool, z } from '@cyanheads/mcp-ts-core';
6
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
7
+ import { getGhoService } from '../../../services/gho/gho-service.js';
8
+ export const whoSearchIndicators = tool('who_search_indicators', {
9
+ title: 'Search WHO GHO Indicators',
10
+ description: 'Search the WHO Global Health Observatory indicator catalog by keyword in the indicator name. ' +
11
+ 'Returns indicator codes and names for use with who_query_indicator_data. ' +
12
+ 'The search uses a substring match on indicator names — try terms like "life expectancy", ' +
13
+ '"immunization", "mortality", "diabetes", or "HIV". ' +
14
+ 'If results are truncated, refine the query or use who_list_indicators to browse by offset.',
15
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
16
+ input: z.object({
17
+ query: z
18
+ .string()
19
+ .min(1)
20
+ .describe('Keyword to search in indicator names, e.g. "life expectancy" or "tuberculosis".'),
21
+ limit: z
22
+ .number()
23
+ .int()
24
+ .min(1)
25
+ .max(100)
26
+ .default(20)
27
+ .describe('Maximum number of indicators to return. Default 20, max 100.'),
28
+ }),
29
+ output: z.object({
30
+ indicators: z
31
+ .array(z
32
+ .object({
33
+ indicatorCode: z
34
+ .string()
35
+ .describe('Unique indicator code used in who_query_indicator_data, e.g. "WHOSIS_000001".'),
36
+ indicatorName: z
37
+ .string()
38
+ .describe('Full indicator name, e.g. "Life expectancy at birth (years)".'),
39
+ })
40
+ .describe('An indicator entry with its code and name.'))
41
+ .describe('Matching indicators up to the requested limit.'),
42
+ totalMatches: z
43
+ .number()
44
+ .describe('Total count of indicators matching the query, before the limit is applied.'),
45
+ note: z
46
+ .string()
47
+ .optional()
48
+ .describe('Present when the limit was reached and more results exist. Suggests how to get additional results.'),
49
+ }),
50
+ errors: [
51
+ {
52
+ reason: 'no_results',
53
+ code: JsonRpcErrorCode.NotFound,
54
+ when: 'No indicators matched the query keyword.',
55
+ recovery: 'Try a different keyword or use who_list_indicators to browse the full catalog without a filter.',
56
+ },
57
+ ],
58
+ async handler(input, ctx) {
59
+ ctx.log.info('Searching indicators', { query: input.query, limit: input.limit });
60
+ const { indicators, total } = await getGhoService().listIndicators({ query: input.query, limit: input.limit, offset: 0 }, ctx);
61
+ if (indicators.length === 0) {
62
+ throw ctx.fail('no_results', `No indicators matched "${input.query}".`, {
63
+ ...ctx.recoveryFor('no_results'),
64
+ });
65
+ }
66
+ const truncated = total > input.limit;
67
+ return {
68
+ indicators,
69
+ totalMatches: total,
70
+ ...(truncated && {
71
+ note: `Showing ${input.limit} of ${total} matches. Refine the query or increase the limit (max 100) to get more targeted results.`,
72
+ }),
73
+ };
74
+ },
75
+ format: (result) => {
76
+ const lines = [
77
+ `**Found ${result.totalMatches} indicator${result.totalMatches === 1 ? '' : 's'} (showing ${result.indicators.length}):**`,
78
+ '',
79
+ ];
80
+ for (const ind of result.indicators) {
81
+ lines.push(`- **${ind.indicatorCode}**: ${ind.indicatorName}`);
82
+ }
83
+ if (result.note) {
84
+ lines.push('', `> ${result.note}`);
85
+ }
86
+ return [{ type: 'text', text: lines.join('\n') }];
87
+ },
88
+ });
89
+ //# sourceMappingURL=who-search-indicators.tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"who-search-indicators.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/who-search-indicators.tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * @fileoverview GHO OData API client for the WHO Global Health Observatory.
3
+ * @module services/gho/gho-service
4
+ */
5
+ import type { Context } from '@cyanheads/mcp-ts-core';
6
+ import type { AppConfig } from '@cyanheads/mcp-ts-core/config';
7
+ import type { StorageService } from '@cyanheads/mcp-ts-core/storage';
8
+ import type { DataQueryParams, DataRow, Dimension, DimensionValue, Indicator, IndicatorDimensionEntry } from './types.js';
9
+ export declare class GhoService {
10
+ private readonly baseUrl;
11
+ private readonly timeoutMs;
12
+ constructor(_config: AppConfig, _storage: StorageService);
13
+ /** Fetch the indicator catalog with optional keyword search. */
14
+ listIndicators(params: {
15
+ query?: string;
16
+ limit: number;
17
+ offset: number;
18
+ }, ctx: Context): Promise<{
19
+ indicators: Indicator[];
20
+ total: number;
21
+ }>;
22
+ /** Fetch all dimension type codes and titles. */
23
+ listDimensions(ctx: Context): Promise<Dimension[]>;
24
+ /** Fetch valid values for a dimension type. Returns empty array if unknown code. */
25
+ listDimensionValues(dimensionCode: string, ctx: Context): Promise<DimensionValue[]>;
26
+ /**
27
+ * Fetch dimension metadata for multiple indicator codes in parallel.
28
+ * Returns a map of code → dimensions. Codes with empty metadata are absent from the map.
29
+ */
30
+ getIndicatorDimensions(indicatorCodes: string[], ctx: Context): Promise<Map<string, IndicatorDimensionEntry[]>>;
31
+ /** Query data rows for an indicator with optional OData filters. */
32
+ queryData(params: DataQueryParams, ctx: Context): Promise<{
33
+ rows: DataRow[];
34
+ totalRows: number;
35
+ truncated: boolean;
36
+ }>;
37
+ private normalizeRow;
38
+ /**
39
+ * Raw fetch with composed timeout + cancellation signal.
40
+ * Does not throw on non-2xx — callers inspect `response.status` as needed.
41
+ */
42
+ private fetchRaw;
43
+ /** Internal: fetch JSON from a URL and parse the OData envelope. Throws ServiceUnavailable on non-2xx. */
44
+ private getJson;
45
+ /** Escape single quotes in OData string values. */
46
+ private escapeODataString;
47
+ }
48
+ export declare function initGhoService(config: AppConfig, storage: StorageService): void;
49
+ export declare function getGhoService(): GhoService;
50
+ //# sourceMappingURL=gho-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gho-service.d.ts","sourceRoot":"","sources":["../../../src/services/gho/gho-service.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAE/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAGrE,OAAO,KAAK,EACV,eAAe,EACf,OAAO,EACP,SAAS,EACT,cAAc,EACd,SAAS,EACT,uBAAuB,EAOxB,MAAM,YAAY,CAAC;AAKpB,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,cAAc;IAOxD,gEAAgE;IAChE,cAAc,CACZ,MAAM,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EACzD,GAAG,EAAE,OAAO,GACX,OAAO,CAAC;QAAE,UAAU,EAAE,SAAS,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAyBtD,iDAAiD;IACjD,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAWlD,oFAAoF;IACpF,mBAAmB,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAiBnF;;;OAGG;IACG,sBAAsB,CAC1B,cAAc,EAAE,MAAM,EAAE,EACxB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,uBAAuB,EAAE,CAAC,CAAC;IA6BlD,oEAAoE;IACpE,SAAS,CACP,MAAM,EAAE,eAAe,EACvB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC;QAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;IA0FtE,OAAO,CAAC,YAAY;IAsBpB;;;OAGG;YACW,QAAQ;IAkBtB,0GAA0G;YAC5F,OAAO;IAkBrB,mDAAmD;IACnD,OAAO,CAAC,iBAAiB;CAG1B;AAMD,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI,CAE/E;AAED,wBAAgB,aAAa,IAAI,UAAU,CAK1C"}