Add a `doctor` command for read-only environment and connectivity diagnostics
- Dominant language
- C#
- Stars
- 3
- Forks
- 7
- Avg merge
- 1d 12h
- Merged PRs (30d)
- 14
Description
## Problem
Just an idea - cosmos/azure is very complex and problems tend to jump out of the box from nowehere. Would maybe be helpful to let the shell check problems for you before they occur.
After GA, the most common support traffic for a CLI is usually not feature requests — it is "it does not connect", "I get 403", or "it hangs". Diagnosing those today requires a round trip to collect shell version, install method, platform, auth mode, connection mode, endpoint, and RBAC state.
There is no single command that collects this and renders a verdict. `info` reports resource configuration and usage, and `connect` reports the current connection, but neither answers *what is broken and what to do about it*.
## Proposed solution
Add a read-only `doctor` command that runs a fixed set of checks and reports `PASS` / `WARN` / `FAIL` / `SKIP` with actionable remediation text.
```text
doctor
doctor --database MyDb --container Items
doctor --format json
```
Example output:
```text
PASS Shell version 1.0.213-preview (dotnet tool)
PASS Authentication Azure CLI credential acquired
PASS Account endpoint Reachable in 84 ms (Direct)
WARN Control plane No ARM context (account-key auth)
mkdb/mkcon/rmdb/rmcon/indexpolicy may fail under strict data-plane RBAC.
Reconnect with Entra ID, or pass --subscription and --resource-group.
FAIL Container access 403 Forbidden
Required data-plane role: Cosmos DB Built-in Data Reader
```
## Scope: three tiers
ARM is **not** required, and must stay optional.
| Tier | Requires | Checks |
| --- | --- | --- |
| 0 | nothing | shell version, OS/arch, install method, .NET runtime, relevant env vars, DNS, proxy, TLS, clock skew |
| 1 | data plane | credential acquisition, endpoint reachability, connection mode, database/container existence, read + query permission |
| 2 | ARM (opt-in) | account resource resolution, control-plane reachability, role assignments |
Tier 2 runs **only** when an ARM context already exists on the state, or when the user passes `--arm` explicitly. A missing ARM context is reported as `SKIP`, never `FAIL`.
### Why ARM must stay optional
- `CosmosArmResourceProvider.TryCreateContextAsync` returns `null` immediately when `credential == null`, so account-key and connection-string auth have no ARM context at all.
- The emulator has no ARM.
- `CosmosArmResourceProvider.DiscoverContextAsync` enumerates every subscription and every Cosmos account until the endpoint matches. That is far too slow to sit on a diagnostic command's default path.
- CI and workload-identity principals frequently have no ARM permission by design.
## Notable checks
1. **ARM-context gap (highest value).** The comment above `CosmosArmResourceProvider.TryCreateContextAsync` already documents this: key auth means no ARM context, so control-plane commands (`mkdb`, `mkcon`, `rmdb`, `rmcon`, `indexpolicy`) fall back to the data plane and then fail on accounts with strict data-plane RBAC. `doctor` should name this explicitly instead of leaving users to discover it through a failed command.
2. **RBAC parsing reuse.** `InfoCommand.PrincipalIdRegex` already parses `does not have required RBAC permissions to perform action`. Extract and reuse it rather than writing a second parser.
3. **Direct-mode port probe.** Direct mode needs outbound TCP 10250-10256. On corporate networks this presents as a hang rather than an error; the check should recommend `--connect-mode gateway`.
4. **Clock skew.** Drift beyond roughly 5 minutes breaks token validation with unhelpful errors.
5. **Emulator certificate.** Certificate validation is bypassed only for emulator endpoints, so a self-signed certificate on a non-emulator endpoint should be flagged rather than silently failing.
## Non-goals for v1
- No `--fix` and no mutating behavior. `doctor` stays read-only so it is safe to run and safe to paste into an issue.
- No duplication of `info` or `connect` output beyond what a verdict requires.
## Acceptance criteria
- [ ] `doctor` runs with no connection and still produces Tier 0 results
- [ ] Tier 1 checks run against the current or specified database/container
- [ ] Tier 2 is skipped cleanly when no ARM context is present; `--arm` opts in
- [ ] `--database` / `--container` target explicitly, consistent with other commands
- [ ] Text output plus stable `--format json` output
- [ ] Non-zero exit code when a required check fails (aligns with the non-interactive output contract in #155)
- [ ] Output never contains keys, tokens, connection strings, or document data
- [ ] Golden-file redaction test: run against a connection carrying a known key and token, assert neither appears in text or JSON output
- [ ] Unit tests for severity resolution and remediation mapping
- [ ] Exposed over MCP as a read-only tool so agents can self-diagnose connectivity
- [ ] Help strings added to `lang/en.ftl`
- [ ] Documented in `docs/commands.md` plus a troubleshooting section
Contributor guide
Assessment
This issue has not been assessed yet.