Skip to main content

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-mcp", "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.

Metadata

Release files for dhis2w-mcp-bridge 1.30.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dhis2w-mcp-bridge 1.30.0
File Size Uploaded
dhis2w_mcp_bridge-1.30.0.tar.gz 12.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for dhis2w-mcp-bridge 1.30.0
File Interpreter ABI Platform
dhis2w_mcp_bridge-1.30.0-py3-none-any.whl Python 3 none any Details

Total release size: 27.5 kB

Release files / dhis2w_mcp_bridge-1.30.0.tar.gz

Download URL dhis2w_mcp_bridge-1.30.0.tar.gz
Size 12.6 kB
Tags Source
SHA-256 checksum
How to use checksums
aa49bd8155653f7f73dfa9083e34d0794c50742b5a4af71d79714ff7250a740e
BLAKE2b-256 checksum
How to use checksums
07ad23a5ce9eff18a2c9759bff13500165db869d1b933837680b8c3f01587c7c
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 29, 2026.

Transparency log

Release files / dhis2w_mcp_bridge-1.30.0-py3-none-any.whl

Download URL dhis2w_mcp_bridge-1.30.0-py3-none-any.whl
Size 14.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
47e0754aab80ed44f2fce590736ed394f5aa9c6d296c588737ef54130dc0f34e
BLAKE2b-256 checksum
How to use checksums
3d30b9e792e2aea782b439c0bf8d07b1ebc24714e8fe8f5e123988875a52ce69
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 29, 2026.

Transparency log

Release history Release notifications | RSS feed

1.30.1

2 release files

This release

1.30.0 This release

2 release files

1.29.0

2 release files

1.28.1

2 release files

1.28.0

2 release files

1.27.0

2 release files

1.26.0

2 release files

1.25.0

2 release files

1.24.2

2 release files

1.24.1

2 release files

1.24.0

2 release files

1.23.0

2 release files

1.22.0

2 release files

1.21.0

2 release files

1.20.0

2 release files

1.19.0

2 release files

1.18.0

2 release files

1.17.0

2 release files

1.16.0

2 release files

1.15.0

2 release files

1.14.0

2 release files

1.9.0

2 release files

1.8.3

2 release files

1.8.2

2 release files

1.8.1

2 release files

1.8.0

2 release files

1.7.0

2 release files

1.6.0

2 release files

1.5.0

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.0

2 release files

0.99.0

2 release files

0.23.0

2 release files

0.22.0

2 release files

0.21.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page