Metadata-Version: 2.4
Name: healthcloud-cli
Version: 2.0.0
Summary: Command-line interface for the HealthCloud MCP gateway, generated from the shared MCP tool manifest.
Author: Healthcheck Systems Inc
License-Expression: MIT
Keywords: cli,health-cloud,healthcare,healthcloud,mcp
Requires-Python: >=3.10
Requires-Dist: click>=8.1
Requires-Dist: healthcloud-sdk>=0.9.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Description-Content-Type: text/markdown

# healthcloud-cli

`hc` is the HealthCloud MCP gateway CLI. It has exactly one subcommand per
MCP tool -- **996 commands across 12 audiences** (`patient`, `fieldagent`,
`ehr`, `vitals`, `diagnostics`, `appointments`, `graph`, `person`,
`provider`, `tenantadmin`, `connectors`, `telehealth`) -- generated
dynamically at startup from a shared manifest. Nothing here is hand-written
per tool: the same manifest also drives the sibling NPM CLI (`@healthcloud
CLI` in `packages/cli/npm`), so command names match exactly between the two.

## Install

```bash
pip install healthcloud-cli
```

This installs the `hc` console script plus its dependency, the
[`healthcloud-sdk`](../../pip) package, which does the actual HTTP/JSON-RPC
work against the MCP gateway.

## Local development

`healthcloud-sdk` is not yet published to PyPI, so for local development you
must install it in editable mode from this repo *before* installing this
package:

```bash
cd packages/cli/pip

# 1. Install the local SDK in editable mode (resolves `healthcloud-sdk` without PyPI)
pip install -e ../../pip

# 2. Install this CLI package (+ dev/test dependencies) in editable mode
pip install -e ".[dev]"

# 3. Run the test suite
python -m pytest

# 4. Try it
hc --help
hc mcp tools list --audience patient
hc mcp patient get-patient --help
```

The bundled tool manifest (`healthcloud_cli/contracts/mcp-tools.manifest.json`)
is a copy of `packages/cli/contracts/mcp-tools.manifest.json`. Re-sync it
after pulling manifest changes with:

```bash
python scripts/bundle_manifest.py
```

This also runs automatically as a hatchling build hook whenever you `python
-m build`, so a built wheel/sdist is always self-contained and never depends
on a relative path outside the installed package.

## Authentication & configuration

Every command accepts these global options (place them before the `mcp`
subcommand):

| Flag              | Env var           | Meaning                                      |
|-------------------|--------------------|-----------------------------------------------|
| `--access-token`  | `HC_ACCESS_TOKEN`  | Bearer token sent to the MCP gateway          |
| `--base-url`      | `HC_MCP_BASE_URL`  | Override the MCP gateway base URL             |
| `--profile`       | `HC_PROFILE`       | Named profile (see below)                     |
| `--output`        | --                  | `pretty` (default) \| `json` \| `raw`         |
| `--raw`           | --                  | Shortcut for `--output raw`                   |
| `--verbose`       | --                  | Print request diagnostics to stderr           |

Precedence, highest wins: **explicit CLI flag > environment variable >
profile file value > built-in default** (`environment` defaults to `"dev"`;
`accessToken`/`baseUrl` default to unset). The resolved access token is never
written to logs, generated files, or test snapshots.

### Optional profile file

`~/.healthcloud/cli/profiles.json`:

```json
{
  "dev-mia": {
    "baseUrl": "https://dev-api-mcp.health.cloud",
    "accessToken": "eyJ...",
    "environment": "dev"
  }
}
```

A missing profiles file is not an error -- profiles are entirely optional. If
`--profile`/`HC_PROFILE` names a profile and the file (or that entry) is
missing, `hc` exits with a clear usage error rather than silently ignoring
it.

```bash
# get-patient is self-service — the patient is resolved from the access
# token, so no --patient-id flag exists.
hc --profile dev-mia mcp patient get-patient
```

## Listing and inspecting tools

```bash
# Local (bundled manifest, no network call)
hc mcp tools list
hc mcp tools list --audience patient

# Live gateway (requires --audience)
hc mcp tools list --audience patient --remote

# Full manifest entry for one tool (description, inputSchema, method/path)
hc mcp tools show patient add_insurance --output json
```

## Calling a tool

Every manifest tool is its own subcommand, grouped by audience:

```bash
hc mcp patient get-patient
hc mcp appointments book-appointment --patient-id patient-123 --slot-id slot-456

# Every Diagnostics tool takes an explicit --patient-id — providers and
# field agents legitimately look up or record diagnostics for a patient
# other than themselves. When the caller holds a patient token, the backend
# enforces server-side that --patient-id must be their own (403 otherwise).
hc mcp diagnostics create-rapid-test --patient-id patient-123 --test-type covid-19 --result negative
```

Run `--help` on any generated command to see its real manifest description
and arguments:

```bash
hc mcp patient get-patient --help
```

### Argument flags

- **Primitive properties** (`string`/`number`/`integer`/`boolean`) become a
  normal flag in kebab-case, e.g. `patient_id` -> `--patient-id`.
  - `enum` values are validated; invalid values are rejected before any
    network call.
  - `minimum`/`maximum`/`exclusiveMinimum`/`exclusiveMaximum` are enforced
    the same way.
  - Boolean properties are a tri-state flag pair: `--flag/--no-flag`.
    Passing neither leaves the value unset (falls back to the manifest
    default or an `--input`/`--stdin` value, if any); passing one sets it
    explicitly to `true`/`false`.
- **Object/array properties are never flattened.** They get a single
  `--<prop>-json` flag that takes a raw JSON string, e.g.:

  ```bash
  # create-medication is self-service — no --patient-id flag; the patient is
  # resolved server-side from the access token.
  hc mcp patient create-medication \
    --name "Metformin" \
    --dosage-json '{"value":10,"unit":"mg","frequency":"once_daily"}'
  ```
- Path parameters (from `{placeholders}` in the tool's HTTP path) are always
  required, in addition to whatever `inputSchema.required` lists.

### Bulk input: `--input` / `--stdin`

```bash
hc mcp appointments book-appointment --input request.json
cat request.json | hc mcp appointments book-appointment --stdin
```

`--input <file>` and `--stdin` both supply a *base* JSON object of
arguments. `--input` and `--stdin` cannot be combined. Merge precedence
(highest wins): **manifest defaults < `--input`/`--stdin` object < explicit
flags** -- i.e. an explicit flag you actually pass always overrides the same
key coming from a file or stdin, which in turn overrides the schema default.

After merging, the final object is validated against the tool's
`inputSchema` (required fields present, correct types, enum membership,
numeric bounds). Any validation failure prints a clear message to stderr and
exits **2**, without making a network call.

### `hc mcp call` -- generic escape hatch

`hc mcp call` dispatches to any MCP tool by its exact snake_case tool name --
including one not yet present in the bundled manifest (the tool name itself
is never validated against the manifest, it's just forwarded):

```bash
# get_patient is self-service and takes no arguments — {} is a complete,
# valid call.
hc mcp call patient get_patient --input '{}'
hc mcp call patient get_patient --input-file request.json
cat request.json | hc mcp call patient get_patient --stdin
```

Note the deliberate inconsistency versus named commands: here `--input` takes
an inline JSON **value**, not a file path -- use `--input-file` for a file.
Exactly one of `--input` / `--input-file` / `--stdin` may be given (or none,
meaning `{}`).

## Output modes

The MCP tool-call result is `{ content: [{type:"text", text: "..."}],
is_error: bool }` from the JSON-RPC response's `.result`, or the JSON-RPC
`.error` object if the gateway returned one.

- **`pretty`** (default): if `content[0].text` parses as JSON, pretty-prints
  the parsed value; otherwise prints the raw text. On a JSON-RPC error,
  prints `error.message` (and `error.code` with `--verbose`) to stderr.
- **`json`**: prints the tool result's parsed JSON (or raw text if
  unparseable) to stdout, with no envelope.
- **`raw`**: prints the complete JSON-RPC envelope (`id`, `jsonrpc`,
  `result`, `error`) as returned by the gateway.

Diagnostics and `--verbose` logs always go to stderr -- stdout stays
pipeable in every mode.

## Exit codes

| Code | Meaning                                                              |
|------|-----------------------------------------------------------------------|
| `0`  | Success                                                                |
| `2`  | Local input validation failure (bad JSON, missing required field, invalid enum/number, `--input`+`--stdin` both given, unknown option) -- no network call made |
| `3`  | HTTP/network failure talking to the MCP gateway                       |
| `4`  | JSON-RPC error response (`.error` is not `None`)                      |
| `5`  | MCP tool result reported `is_error: true`                             |

## Building

```bash
pip install build
python -m build
python -m zipfile -l dist/*.whl   # confirm the bundled manifest is included
```
