Research patterns
Most work with the current server falls into one of three patterns. Whichever you pick, the same discipline applies: establish which statistical variable is appropriate, and what coverage and provenance it has, before pulling observations.
A recommended first exploration¶
A simple first exercise is to search for an indicator such as population:
Read the general research playbook,
skill://data-commons-researcher/SKILL.md.search_indicatorswith the concept and the place (query="population",places=["Rwanda"], never the place inside the query text): the result’sdcidNameMappingsis where you learn the place DCID — there is no separate resolve tool.Inspect the returned indicator candidates and their DCIDs.
get_variable_metadatafor the candidates that look right, withentity_dcidsset to the place DCID(s) so the coverage shown is for your place.Select the variable — and, where there are several facets, the facet — from those candidates.
get_observationsfor the chosen variable and place.
This sequence is preferable to starting directly with get_observations, because it
first establishes which variable is appropriate and what data coverage it has.
Reading a candidate list¶
A search usually returns several closely related variables, and the search result carries only their DCIDs and names. Use the names to narrow the set; use the structured metadata to make the semantic decision:
Triage visually from the names. A name reads
series label [Dimension=Value, …], so at a glance the ten candidates of Walkthrough A are one family, UNICEF’s Total population, with and without age and sex splits. That is a quick reading of a summary, not yet a decision.Group candidates by series. In the names, members of one series share the text before the bracket; in metadata they share a
populationType, which is the authoritative grouping. A search shows the members it ranked; to list every governed slice of the family from the identifier you already hold, rather than by widening the search, see Inspecting the graph with the REST API.Identify the likely total and the likely breakdowns. The total is the member whose metadata lists no
constraintProperties:undata/unicef/DM_POP, notundata/unicef/DM_POP.AGE--Y0T4. The others fix one or more dimensions.Inspect the variable’s constraints to tell slices apart. For the candidates that matter, call
get_variable_metadataand readconstraintPropertiestogether with the per-dimension properties. Variables in one family fix different dimensions to different values:undataAgeUnder 18 years old alone, andundataAgeUnder 18 years old withundataSexFemale, are two variables because their constraint sets differ, not because their names carry different bracketed text. The bracket is a readable summary of the same constraints; do not parse it, or theDIM--CODEsegments of a DCID, for meaning the metadata gives you directly. Metadata, not another search, is also how to separate near neighbours that read alike: rewording the query mostly reorders the same candidates.Check the facet before retrieval. Its
unit(a count, a percentage, a rate and a true/false response are different measurements), itsobservationPeriod(monthly, quarterly and annual facets can coexist for one variable) and itsobsCountand date range for the place you need. A variable can exist with no observations for your place; the facet table says so before any retrieval call.
This is the assessment step of the playbook read from the candidates’ side; the sequence discover → assess → retrieve is unchanged. The terms are defined on Concepts.
When the user asks for a breakdown or comparison¶
Show total population by sex in Rwanda. What breakdowns of the unemployment rate are there for Kenya? Questions like these ask for a set of variables that belong together, and a search result usually mixes such a set with near neighbours from other series. The graph holds a governed answer. A terminal topic names one analytical view and stands for one peer group, the set of statistical variables the platform intends to be considered together for that view (both terms are on Concepts). The preferred workflow:
natural-language question
→ search_indicators(
query=<statistical concept + breakdown>, "total population by sex"
places=[<place name>], ["Rwanda"]
include_topics=true
)
→ identify a matching terminal topic in `topics`
→ use its memberVariables as the place-available governed comparison set
→ get_variable_metadata for those members, with the place
→ select compatible facet(s)
→ get_observations for each member
→ compare / chart / explainA terminal topic gives the agent a governed answer to “which variables belong together?” rather than requiring it to infer a comparison set from independent search hits.
Example. For total population by sex with places=["Rwanda"], the variable
candidates on 2026-09-11 put eight UNFPA sex-ratio slices next to UNICEF’s female and
male population counts, and topics included undata/topic/unicef/DM_POP.004, Total
population, by Sex, whose memberVariables are exactly undata/unicef/DM_POP.SEX--F
and undata/unicef/DM_POP.SEX--M. The terminal topic identifies the governed pair; the
sex-ratio slices are another series and another measurement, and reading the topic
settles that without reasoning over labels. Continue as in pattern A, once per member:
metadata with the place DCID, then observations. Nothing in the pattern depends on
what the values are today.
The member list is place-aware¶
Two things that look alike are different, and the difference is current MCP behaviour, not a property of the graph:
peer group membership
= the full governed comparison set
terminal topic memberVariables in a place-aware search result
= the members that currently have data for the searched placeThe search filters a terminal topic’s members to those with data for the place you
named, and this workflow needs a place: on the current server a search without
places can return nothing at all. The underlying peer group can hold further members
with no observations for that place — a UNHCR group with one variable per country of
origin lists 208 members in the graph and 30 for Uganda — and the filtered result does
not change the group. When you need the full set, the structural lookup in
Inspecting the graph lists it.
Check compatibility before you compare¶
Membership answers “which variables belong together semantically?”. It does not by itself answer “can I put every returned value on one chart right now?”. Before retrieval:
call
get_variable_metadatafor the members withentity_dcidsset to the place;drop members that return no usable facet for that place (ILO’s Sex=Other unemployment rate is a member of the by-sex group with no observations for Kenya);
choose a common facet across the remaining members, above all a common
observationPeriod: the facet the server selects by default is not necessarily the annual one, and the members of one group can each carry monthly, quarterly and annual facets;pass that facet’s id as
source_overrideon each observation call where the default would differ, and when you bound the window, setdate="range"with the bounds: bounds without it are ignored on the current server;verify the unit on the facet you chose is the same for every member (members of a UN peer group share a series, so it should be; read it anyway);
consider the common date coverage: members can start in different years even under one facet.
Comparison, not addition¶
Peer group membership means comparison, not addition. Do not assume members partition a total, are mutually exclusive, sum to 100 percent or can be stacked: UNICEF’s population age group holds Under 18, Under 5, 10 to 19 and 15 to 24, which overlap; ILO’s education groups carry a total and an Unknown beside the levels; sex groups can carry Other. Compare peer group members; only aggregate or stack them if the semantics of the classification independently establish that this is valid.
When a peer group is not the answer¶
Peer groups encode the analytical views the platform governs; they are not exhaustive of every reasonable comparison. A requested comparison may cut across different peer groups or fixed contexts (the female and the male youth unemployment rates sit in two groups, one under each sex), or run along a dimension the current presentation model did not choose to vary (UNFPA’s sex ratio holds age fixed, so its age bands are separate single-member groups rather than one group). In those cases keep the series and family inspection of Reading a candidate list and REST recipe 1 as the fallback:
populationType / family
→ every governed statistical variable in the source series
peer group
→ one governed analytical or presentation projection of that familyDo not force a question into a peer group when no appropriate group exists: say so, and compose the comparison from the family with the same compatibility checks. Either way, choose the presentation from the coverage, the time span and the question, not from membership alone.
The three patterns¶
A. Indicator for a specific place¶
search_indicators → get_variable_metadata → get_observationsExample question: What population indicators are available for Rwanda? — run for real in Walkthrough A.
B. Indicator across the countries within a parent geography¶
search_child_indicators → get_variable_metadata → get_child_observationsExample question: How does access to safely managed drinking water compare across the countries of Sub-Saharan Africa? — the parent can be the world, a continent, a UN region or another geographic grouping; in the current workflows the children are countries (see Geographic scope). Run for real, for the countries of the world and then for Sub-Saharan Africa, in Walkthrough B.
C. Relationships between multiple entities¶
search_indicators → get_variable_metadata → get_multi_entity_observationsafter discovery has found a variable whose candidates carry observation_properties
(entity roles such as donor and recipient) and metadata has confirmed them.
Example question: How much aid flows from one donor to one recipient? — run for real in Walkthrough C, which finds that this instance currently has no multi-entity variables (every variable is observed about one place), and shows how a relational question — refugees from one origin, by country of asylum — is answered with patterns A and B instead.
Keep research within the UN System Data Commons corpus¶
A useful Inspector workflow¶
When exploring the server interactively, this order works well:
Resources → read the relevant SKILL.md → Tools → search →
inspect returned DCIDs → metadata → observationsThe Inspector is particularly useful during development because it shows each tool’s input schema.
Moving beyond the Inspector¶
The Inspector is primarily a development and exploration tool. Once the server’s behaviour is understood, the same endpoint can be configured in an MCP-capable AI client or development environment (see the Claude Code and VS Code tabs on Getting started). The client can then:
discover the available tools;
read the relevant research playbook;
translate a natural-language question into tool calls;
inspect candidate variables and metadata;
retrieve observations;
use the returned structured data in its answer or workflow.
Minimal first-session checklist¶
For a new user, the shortest useful path is:
Confirm Node.js 22.19 or later and
npxare availableStart the MCP Inspector
Add the UN System Data Commons server using Streamable HTTP
Confirm Tools are visible
Open Resources
Read
skill://data-commons-researcher/SKILL.mdRun
search_indicatorswith a simple queryInspect one returned variable
Retrieve its metadata
Retrieve a small set of observations
If all of these steps work, you have exercised the main MCP path from discovery through data retrieval.