ValueSet Expansion Mechanics
Shared reference used by snomed-semantic, clinical-search, and
terminology-crossmap. Not triggered directly by user queries.
Before Expanding — Always Include version
Every compose.include block must specify "version". Call this first if
you don’t already have the version string:
list_available_codesystem_versions()
Omitting version causes the expand to fail silently or use an unexpected
default.
valueset_expand Failures — Symptom and Fix
In some agent harnesses the valueset parameter — typed as a $ref schema
object — is serialised as a plain string before the tool call is dispatched.
The MCP server receives a string instead of an object and responds with:
valueset: must be object / must be null / must match a schema in anyOf
This is a known issue in pi (XML <parameter> tag serialisation) and
may surface in other harnesses that do not handle nested JSON objects in
tool parameters. The symptom is the same regardless of harness.
This error cannot be fixed by:
- Reformatting the JSON (pretty-print vs compact)
- Removing optional parameters like
countoroffset - Different quoting or escaping
- Retrying the same call
Do not retry. Do not offer alternatives. Immediately run
reasonhub-skills expand instead.
reasonhub-skills expand — Universal CLI Fallback
This is the recommended fallback whenever valueset_expand fails or
times out, regardless of which agent harness is in use.
echo '{ ... paste ValueSet JSON here ... }' | reasonhub-skills expand
If this fails with
stdin is closedor prints help text, the CLI is outdated. Upgrade it:curl -fsSL https://raw.githubusercontent.com/reason-healthcare/reasonhub-skills/main/bin/reasonhub-skills \ -o ~/.local/bin/reasonhub-skills && chmod +x ~/.local/bin/reasonhub-skillsIf you cannot upgrade immediately, add
--count 100as a workaround:echo '...' | reasonhub-skills expand --count 100Do not run both the
echo |form and themktempform in parallel. They are alternatives. Pick one, run it, use the output.
If the CLI is not installed at all:
curl -fsSL https://raw.githubusercontent.com/reason-healthcare/reasonhub-skills/main/bin/reasonhub-skills \
-o ~/.local/bin/reasonhub-skills && chmod +x ~/.local/bin/reasonhub-skills
⛔ Do not read credentials yourself. Do not read
~/.reasonhub/config.toml,.reasonhub/config.toml,~/.pi/agent/mcp.json, or any env var to extract a token and hand-roll a curl command. Thereasonhub-skillsCLI handles credentials internally. If you find yourself writingcurl ... -H "Authorization: Bearer ..."with a token you read from a file, stop and use the CLI instead.
Truncated Expansions
The MCP transport layer truncates returned rows regardless of the count
parameter. After a successful expansion, check total in the response.
If rows returned are fewer than total, label the output and stop:
⚠️ Partial result — {n} of {total} codes shown. The full set is defined by the ValueSet JSON above; run it against any FHIR terminology server for the complete expansion.
Do not retry with different count or offset values — this will not
retrieve additional rows.
Python Scripting — Bulk Expansions
When expanding many ValueSets in a loop, use subprocess.run with
input=. Do not use a heredoc (<< 'EOF') inside a subprocess call
— it closes stdin and causes write_stdin failed: stdin is closed.
import json, subprocess
def expand(filter_list, system, version, count=100):
vs = {
"resourceType": "ValueSet",
"compose": {"include": [{
"system": system,
"version": version,
"filter": filter_list
}]}
}
p = subprocess.run(
["reasonhub-skills", "expand", f"--count={count}"],
input=json.dumps(vs),
text=True, capture_output=True, timeout=60
)
return json.loads(p.stdout)
Sequential calls are fine for small sets; for 20+ analytes use
ThreadPoolExecutor.
Deduplication: LOINC expansions can return the same code twice with different display names (canonical vs. short name). Deduplicate by code before processing:
seen = {}
for c in result["expansion"].get("contains", []):
seen.setdefault(c["code"], c["display"])
Debugging Checklist
If valueset_expand or reasonhub-skills expand returns no results or an error:
- Check the version — run
list_available_codesystem_versions()and confirm the version string matches exactly. - Check the property name — run
codesystem_filter_properties(system=...)to verify the property is spelled correctly for the target system. - Verify the value concept — run
codesystem_lookupon the filter value to confirm it exists and is active in the right system. - Test a simpler filter first — remove all but one filter condition to isolate which is causing empty results.
- For SNOMED attribute filters — look up a representative concept you
expect to match and confirm it actually carries the attribute you’re
filtering on. Primitive concepts (
sufficientlyDefined = false) may have no attributes at all.