dhis2w-mcp-bridge
A FastMCP server that exposes the entire d2w CLI as one MCP tool, dhis2_cli.
Where dhis2w-mcp registers ~304 typed tools (≈50-65k tokens of schema loaded into the
model's context up front), this server registers a single tool that shells out to the local
d2w binary. A small, context-limited local model discovers the command surface
progressively with --help and runs commands with --json — and nothing leaves the host.
Why this exists
For sensitive data that must stay on-box, you run a local model (LM Studio, Ollama,
llama.cpp). Such models can't spare ~53k tokens for tool schemas, and many degrade when
choosing among hundreds of tools. One tool + on-demand --help fits an 8k-context model and
keeps the full DHIS2 surface reachable. Same code as the CLI — the bridge just runs it.
Use dhis2w-mcp (the full typed server) for hosts that do progressive tool disclosure
themselves (e.g. Claude Code handles all 304 tools fine). Use this bridge for small local
models.
The tool
dhis2_cli(args: list[str], profile: str | None = None) -> CliResult
CliResult = { exit_code: int, stdout: str, stderr: str }
The model is expected to discover, then act:
dhis2_cli(["--help"]) # list command groups
dhis2_cli(["metadata", "--help"]) # drill into a group
dhis2_cli(["metadata", "list", "dataElements",
"--filter", "name:ilike:malaria"]) # run a command
Contract: --json is injected automatically, so on success (exit_code == 0) stdout is
JSON. --help/--version exit 0 with human text. Any non-zero exit is a failure and the
message is on stderr (never JSON). profile is injected as -p <profile>.
Install & run
The bridge depends on dhis2w-cli, so installing it provides the d2w binary.
# From a workspace checkout (development)
uv run dhis2w-mcp-bridge
# From PyPI
uv tool install dhis2w-mcp-bridge
dhis2w-mcp-bridge
Configure a client (LM Studio shown; any MCP host works)
~/.lmstudio/mcp.json:
{
"mcpServers": {
"dhis2": {
"command": "uv",
"args": ["run", "--directory", "/ABS/PATH/TO/dhis2w", "dhis2w-mcp-bridge"],
"env": {
"DHIS2_PROFILE": "local_basic",
"DHIS2_MCP_READONLY": "1"
}
}
}
}
The server speaks MCP over stdio and reads its DHIS2 connection from a profile
(.dhis2/profiles.toml / ~/.config/dhis2/profiles.toml) or env vars (DHIS2_URL +
DHIS2_PAT / DHIS2_USERNAME+DHIS2_PASSWORD), exactly like the CLI.
Environment variables
| Variable | Default | Effect |
|---|---|---|
DHIS2_MCP_READONLY |
unset | When truthy (1/true/yes/on), only read commands and --help are allowed; writes are refused (exit 126). |
DHIS2_CLI_BIN |
auto | Path to the d2w executable. Auto-discovered next to the running interpreter, then on PATH. |
DHIS2_MCP_CLI_TIMEOUT |
120 |
Per-command timeout in seconds (exit 124 on timeout). |
DHIS2_PROFILE |
profile default | Selects the DHIS2 profile, passed through to the CLI. |
Exit-code conventions added by the bridge: 124 timeout, 126 refused by read-only mode,
127 CLI not found. Otherwise the result carries the CLI's own exit code (0 success / JSON,
1 domain error, 2 usage error).
Read-only mode
DHIS2_MCP_READONLY=1 is the safe default for handing a local model a query-only surface. It
is fail-closed: only commands on an allowlist of read-only command paths (and --help)
are permitted; everything else is refused before any subprocess runs. The allowlist is
generated by introspecting the Typer command tree and verified against the live tree by the
test suite, so it cannot silently drift, and ambiguous verbs default to denied.
This is convenience, not the security boundary — the authoritative control is the DHIS2 authorities of the profile's credentials. For a hard guarantee, point the profile at a read-scoped PAT or user.
How it works
build_server() creates a FastMCP instance and registers the single dhis2_cli tool
(cli_bridge.register). The tool runs d2w --json [-p <profile>] <args> via
asyncio.create_subprocess_exec (exec form — no shell, no injection), bounded by a timeout,
and returns a typed CliResult. There is no version resolution or plugin discovery: the CLI
subprocess auto-detects the DHIS2 version itself on connect.
Release files for dhis2w-mcp-bridge 1.24.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| dhis2w_mcp_bridge-1.24.0.tar.gz | 12.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| dhis2w_mcp_bridge-1.24.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 27.6 kB
Release files / dhis2w_mcp_bridge-1.24.0.tar.gz
| Download URL | dhis2w_mcp_bridge-1.24.0.tar.gz |
|---|---|
| Size | 12.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8ce3e58401f88af1a7d0ffa5f5d610d64344014d8a3e4107ab2d5e7db1aa4234
|
|
BLAKE2b-256 checksum How to use checksums |
4aecf2764629ec4e90d39c33ee3d7bbfffebe6c76c842a56e3ef041a6aa07f38
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.
Transparency logRelease files / dhis2w_mcp_bridge-1.24.0-py3-none-any.whl
| Download URL | dhis2w_mcp_bridge-1.24.0-py3-none-any.whl |
|---|---|
| Size | 14.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c6c7443de3b754676f74a28699cbab89f2665b650eff43ab32557f3f27ba669b
|
|
BLAKE2b-256 checksum How to use checksums |
942192970474fcbf3acc618868b1ae14934dfd01f227b26c058cc7dbfa9ff8da
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.
Transparency log