# CLI

> [!NOTE]
> **Coming soon.** We will release the open-source OSD CLI soon. This reference previews its commands and behavior; source code and installation instructions will be shared with the release.

The Open Service Directory (OSD) CLI searches registries, verifies published support entries, and connects to support agents through MCP. By default, it uses the [registry directory](/0.3.0/docs/reference/osd-directories/) to find registries to query. For a walkthrough, see [Use the OSD CLI](/0.3.0/docs/guides/use-the-osd-cli/).

The CLI requires Node.js 22.19 or later. Run `osd --help`, or add `--help` to a command, for its options.

## Contract versions

Use OSD contract version `0.3.0` with `search` and `verify`; it is the CLI default and is currently a draft. Select it explicitly with `--contract-version 0.3.0`. An unsupported version exits with status `64`.

## Search

`osd search <query>` refreshes the registry directory and searches its `active` registries. It sends the same query, filters, and exact contract version to each registry and combines their candidates.

```bash
osd search "Example Air cancelled flight rebooking" \
  --contract-version 0.3.0
```

Suspended registries are excluded. If the directory cannot be refreshed or contains no active registries, the search fails.

### Choose a registry

To search one registry, select its base URL. The CLI uses the first of:

1. `--target <url>`
2. the `OSD_REGISTRY_URL` environment variable

With neither configured, it searches the directory registries. An explicitly empty `OSD_REGISTRY_URL` is a usage error unless overridden by `--target`. A selected registry can be outside the directory. The CLI appends `search` to its base URL. Directory records already contain complete search endpoints.

```bash
osd search "Example Air cancelled flight rebooking" \
  --target https://registry.example/
```

### Search options

| Option | Effect |
| --- | --- |
| `--contract-version <version>` | Exact OSD contract version to request |
| `--filter key=value` | Structured filter. Repeatable. Repeated keys become an array |
| `--page-size <n>`, `--page-token <token>` | Result page size and continuation token. See [Pagination](#pagination) |
| `--federation auto\|referrals\|none` | ARD federation mode when searching one registry. Directory search uses `none` |
| `--allow-local-registry` | Allows an HTTP or HTTPS loopback target when one registry is explicitly selected |
| `--verify` | Checks each candidate against its brand's published manifest |
| `--json` | Prints combined CLI output for directory search, or the registry response when searching one registry |

Directory search sends `federation: "none"` and does not follow referrals. Explicit `auto` or `referrals` requires a registry selected through `--target` or `OSD_REGISTRY_URL`. For that registry, the default is `auto`, following the [registry's federation behavior](/0.3.0/docs/reference/registry/#federation).

The CLI cannot supply `query.@context`. For portable filters on namespaced OSD terms, send the HTTP request shown in [Discover support](/0.3.0/docs/guides/discover-support/).

### Response validation

Directory search validates each response against the requested contract and requires a matching `OSD-Contract-Version` header before using it.

When you select one registry, the CLI validates its response body but does not reject a missing or mismatched version header. Its JSON output contains the body, not the headers, so it cannot confirm the declared version.

## Search results

Directory search takes one candidate from each registry in turn, preserving each registry's ordering. It does not compare relevance scores across registries or use them to establish endorsement.

Identical complete entries become one candidate with all their source registries recorded. Different entries with the same resource identifier remain separate candidates and are reported as conflicts. The CLI preserves each entry. The caller chooses which candidate to use.

In directory search, `--json` returns the `osd-cli-federation` format, initially version `0.1.0`. The `results` array contains candidate records:

| Field | Meaning |
| --- | --- |
| `candidateId` | CLI identifier for this candidate |
| `entry` | The complete OSD entry to pass to `verify` or use for connection |
| `sourceRegistries` | The registries that returned this entry |
| `verification` | Optional brand endorsement check, added by `--verify` |

The CLI also preserves the `score` and `source` reported by each registry.

The response also reports the directory revision, requested OSD contract version, registry identities, `coverage`, `warnings`, `conflicts`, `updates`, and an optional `pageToken`. `formatVersion` identifies the combined output format. It is separate from the OSD contract version and the directory's `schemaVersion`.

A search can succeed even when some registries fail or reach a limit. Inspect `coverage` and `warnings` before treating the candidates as a complete search. Without `--json`, the summary shows sources, conflicts, verification, and coverage warnings. Diagnostics go to stderr.

When you select one registry, `--json` returns the [registry response format](/0.3.0/docs/reference/registry/#results-and-pagination), with complete entries directly in `results`. Scripts using default directory search read `.results[].entry`; scripts that need the registry response select a target explicitly.

## Pagination

Directory search defaults to 20 new candidates per page. `--page-size` accepts 1 to 100. Pass the returned `pageToken` back with `--page-token` and the same query, filters, page size, and contract version. Tokens are opaque, expire after one hour, and become unusable when the directory changes. An expired token or changed directory requires a fresh search.

Later pages can discover another source for an earlier candidate or a new conflicting variant. These changes appear as complete candidate records in `updates`. JSON callers replace their earlier records by `candidateId`. Updates do not count toward the page's new-candidate limit. Without `--json`, the summary lists updates separately. A page can contain only updates, so use coverage and the continuation token to determine whether more results remain.

When you select one registry, its page-size defaults and opaque page tokens apply. Tokens cannot be moved between directory search and a search of one registry.

## Verify

`osd verify --candidate <file>` checks one complete entry, not a whole search response or directory candidate record. Save the selected directory candidate's `entry` as the input. A selected registry result object is also accepted. Without `--candidate`, the command reads standard input. Don't use both. `--json` prints structured outcomes.

For contract version `0.3.0`, the CLI retrieves the brand's manifest from `https://{osd:brandDomain}/.well-known/ard.json`. It compares the complete candidate, excluding search-only annotations, with the published entry that has the same identifier.

`search --verify` runs the same check on every candidate and adds a separate `verification` annotation. Verdicts are `verified`, `mismatch`, `no_match`, `unreachable`, `invalid`, and `malformed`. Directory search also reports `not_checked` when a candidate was not checked before the verification deadline. These annotations do not change the entry, resolve conflicts, or choose a candidate.

For standalone verification, invalid candidate input and malformed manifests are reported on stderr with a nonzero exit status and may not produce a JSON verdict.

A match shows the entry is published on the stated brand domain. It does not establish business legitimacy, and it does not verify signatures or attestations. See [Trust model](/0.3.0/docs/concepts/trust-model/).

## Connect through MCP

`osd mcp <target>` accepts a Server Card URL or a Streamable HTTP endpoint URL. Given a card, it selects a supported endpoint, connects, and lists the agent's tools.

| Option | Effect |
| --- | --- |
| `--call <tool>` | Calls the named tool instead of listing tools |
| `--args <json>` | Tool arguments as a JSON object. Requires `--call`, defaults to `{}` |
| `--json` | Formats tool listings as JSON. Tool-call results are always JSON |
| `--no-auth` | Never starts a browser sign-in. Cached credentials are still used |
| `--login` | Allows browser sign-in from a non-interactive shell |

Tool names and arguments come from the agent's tool schemas. If authorization is needed, an interactive run opens a browser and caches credentials.

> [!CAUTION]
> `osd mcp` accepts URLs directly. It does not check brand endorsement or obtain customer confirmation. Verify the entry and obtain any required approvals first.

## Updates

On macOS and Linux, a global npm installation checks for new versions in the background and updates itself. `osd update` upgrades immediately. `osd update --check` only reports whether an update is available, and `--json` prints that result as JSON.

| Variable | Effect |
| --- | --- |
| `OSD_NO_AUTO_UPDATE=1` | Keeps checks and notices. Disables automatic installation |
| `OSD_NO_UPDATE_CHECK=1` or `CI` set | Disables update checks and automatic updates |
| `OSD_UPDATE_TAG=<tag>` | Follows another npm dist-tag instead of `latest` |

## Exit status

Directory search uses these statuses:

| Status | Meaning |
| --- | --- |
| `0` | At least one valid registry response, including empty or partial results |
| `1` | Invalid or unsupported directory data, no active registries, mixed or other failures from every registry, a directory change during continuation, or required session persistence failure |
| `3` | Every registry explicitly rejected the requested contract version |
| `4` | Directory transport or HTTP failure, or every registry failed through connectivity, timeout, or network-safety refusal |
| `64` | Invalid command arguments, an unsupported contract version, or an unusable pagination token |

Searching one registry and running the other commands use these statuses:

| Status | Meaning |
| --- | --- |
| `0` | Success, including a search with no results |
| `1` | General failure, including invalid response data or a malformed manifest |
| `2` | Invalid input (candidate, target URL, tool arguments) or a registry `INVALID_ARGUMENT` response |
| `3` | The target returned `UNSUPPORTED_CONTRACT_VERSION` |
| `4` | The target could not be reached, or no acceptable resource was obtained |
| `5` | Standalone verification found no matching identifier |
| `6` | Standalone verification found the identifier with differing fields |
| `64` | Invalid command arguments or an unsupported contract version |

`search --verify` keeps the search's exit status even when individual checks fail. Status `0` does not mean every candidate verified or that an MCP tool succeeded: inspect verdicts and tool results, including `isError`. A search of one registry can print a schema-invalid response to stdout, so check the status before using output.