<!-- Source: https://docs.biosimulant.com/standards/model-compatibility/api-and-cli -->

# API and CLI

You can check compatibility on your own machine with the `biosimulant` command-line tool, or through the hosted Biosimulant API. Both use the same pinned release of the standard, so they give the same result for the same input.

## Install the CLI

```bash
pip install 'biosimulant[compatibility]'
```

The CLI reads schemas and profiles from the installed package. It never downloads definitions while it runs, so results only change when you upgrade.

## CLI commands

| Command | What it does |
|---|---|
| `biosimulant compatibility validate MODEL_YAML` | Checks the `compatibility` block and port contracts in a `model.yaml`. Exits with status 1 if it finds problems. |
| `biosimulant compatibility normalize MODEL_YAML [--output FILE]` | Prints the manifest as JSON, with profile imports and contracts in normalized order. |
| `biosimulant compatibility compare SOURCE TARGET [--ontology-snapshots FILE] [--mapping-snapshots FILE]` | Checks whether an output port can feed an input port, and prints a compatibility report. |
| `biosimulant compatibility profiles list [--domain DOMAIN]` | Lists profiles, including each one's `ref` and `sha256`. |
| `biosimulant compatibility profiles show PROFILE_REF` | Prints one profile's full definition. |
| `biosimulant compatibility lock MODEL_YAML [--output FILE]` | Writes a lock file (`compatibility.lock.json` by default) and prints its sha256. |
| `biosimulant compatibility plan LAB [--policy FILE] [--capabilities FILE] [--ontology-snapshots FILE] [--mapping-snapshots FILE] [--output FILE]` | Checks every wiring connection in a lab and prints a plan. `LAB` is a `lab.yaml` file or the folder that contains it. |
| `biosimulant compatibility conformance` | Runs the conformance tests. See [Conformance](/standards/model-compatibility/conformance). |

For `compare`, `SOURCE` is an output port and `TARGET` is an input port. Write each one as a path to a `model.yaml`, then `#outputs.` or `#inputs.` and the port name:

```bash
biosimulant compatibility compare \
  'upstream/model.yaml#outputs.expression' \
  'downstream/model.yaml#inputs.expression'
```

The report it prints has the same format as the API response [below](#example-compare-two-contracts).

For `plan`, `--policy` is a JSON file with a compatibility policy, and `--capabilities` is a JSON list of [adapter and inference capabilities](/standards/model-compatibility/adapters-and-inference). The two snapshot options take JSON arrays of exact, sha256-pinned snapshots. The same snapshot options work with `compare`.

If a command can't read a file, or a port doesn't exist, it prints an error and exits with a non-zero status.

When you build a model package with `biosimulant labs release build`, and the model has a `compatibility` block, the package includes `payload/compatibility.lock.json`. Your source `model.yaml` isn't changed.

## Public files

Schemas, profiles, contract items, packs, rules and test fixtures are public, and you don't need to sign in to get them. This site serves them under `/standards/model-compatibility/`, for example the [gene expression counts profile](/standards/model-compatibility/profiles/transcriptome/gene-expression-counts/v0.1.json). The Biosimulant API also serves the bundle manifest, catalogue, contract items, packs, schemas, fixtures and profiles at the same paths.

## MCP and Agent Skills

The authenticated Biosimulant Agent Gateway exposes the compatibility workflow as native MCP tools. Skills are optional guidance for an agent; they do not add permissions or substitute for the server's validation, policy checks, approval receipts, or digest verification.

| MCP tool | Scope | Purpose |
|---|---|---|
| `compatibility_standard_get` | `compatibility:read` | Returns the installed standard release, bundle digest, counts and canonical resources |
| `compatibility_profiles_search` | `compatibility:read` | Searches all bundled profiles by domain, review state or text |
| `compatibility_profile_get` | `compatibility:read` | Returns one exact immutable profile definition |
| `compatibility_validate` | `compatibility:compute` | Validates a manifest, contract or standard object |
| `compatibility_compare` | `compatibility:compute` | Compares source and target contracts without selecting transformations |
| `compatibility_resolve_prepare` | `compatibility:compute` | Produces and stores an expiring, optionally revision-bound resolution plan |
| `compatibility_report_get` | `compatibility:read` | Retrieves an owned immutable report |
| `compatibility_plan_get` | `compatibility:read` | Retrieves an owned immutable plan and its state |
| `compatibility_approval_prepare` | `compatibility:approve` | Prepares the exact approval operation for a lossy or inferred path |
| `compatibility_approval_create` | `compatibility:approve` | After explicit confirmation, records the digest-bound approval receipt |

For a strict managed run, resolve against the exact workspace and revision, obtain any required approval receipt, then pass both `compatibility_plan_id` and `compatibility_plan_digest` to `run_prepare`. The server rechecks the standard bundle, plan content, revision digest, expiry, workspace policy and active approvals when the run is prepared and again when it is created.

The [`biosimulant-model-compatibility` skill in the public skill catalogue](https://www.biosimulant.com/download/skills) guides profile selection, authoring, comparison, resolution and approval. The general `biosimulant` skill and the composition/model-building skills route to the same guidance. Agents must preserve `UNKNOWN` when evidence is absent and must never invent biological context merely to make a contract pass.

## Hosted API

**Info:**

  These endpoints are part of the backend alpha and may not be enabled on every deployment yet. The CLI works without them.

Send requests to `https://api.biosimulant.com` with a developer API key in the `Authorization: Bearer` header. The key needs the scope listed for each endpoint. See the [developer API quickstart](/developer-api/quickstart) to create a key.

| Endpoint | Scope | What it does |
|---|---|---|
| `POST /v1/compatibility/validate` | `compatibility:compute` | Validates a manifest, a port contract, or any other object in the standard |
| `POST /v1/compatibility/compare` | `compatibility:compute` | Compares two contracts and stores the report |
| `POST /v1/compatibility/resolve` | `compatibility:compute` | Compares two contracts and looks for a plan using active, reviewed capabilities |
| `GET /v1/compatibility/reports/{report_id}` | `compatibility:read` | Returns a stored report |
| `GET /v1/compatibility/plans/{plan_id}` | `compatibility:read` | Returns a stored plan |
| `POST /v1/compatibility/plans/{plan_id}/approvals` | `compatibility:approve` | Approves a plan that needs approval |

Reports, plans and approvals are private to the user who owns the API key that created them. Sending the same comparison again returns the same stored report rather than a new one.

### Example: compare two contracts

This request compares a source that outputs mouse data with a target that expects human data:

```bash
curl -X POST https://api.biosimulant.com/v1/compatibility/compare \
  -H "Authorization: Bearer $BIOSIMULANT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "source": { "biological_context": { "species": "NCBITaxon:10090" } },
    "target": { "biological_context": { "species": "NCBITaxon:9606" } }
  }'
```

The response is a compatibility report:

```json
{
  "schema_version": "0.1",
  "standard": "https://biosimulant.com/standards/model-compatibility/v0.1",
  "bundle_sha256": "sha256:…",
  "source": {
    "contract_digest": "sha256:b1f50cb7e650d56422826f8a8710572afd212aa2ca3bf3ba42e4372cfe16f00f",
    "profile_refs": []
  },
  "target": {
    "contract_digest": "sha256:5adc9c04c0ce2f7892e337c0486d43970cbefac63138eaab5e39f8ca72e38bfe",
    "profile_refs": []
  },
  "status": "INCOMPATIBLE",
  "policy_decision": "BLOCK",
  "findings": [
    {
      "dimension": "biological_context",
      "state": "INCOMPATIBLE",
      "severity": "error",
      "reason_code": "BMCS_VALUE_MISMATCH",
      "explanation": "/contract/biological_context/species: 'equal' check failed.",
      "evidence": { "source": "NCBITaxon:10090", "target": "NCBITaxon:9606" }
    }
  ],
  "digest": "sha256:…"
}
```

The `Location` response header gives the path of the stored report, `/v1/compatibility/reports/{report_id}`.

This example sends no profiles, so every field in the target contract is compared for equality, and units are compared with `unit-convertible`. To use a profile's rules instead, add `source_profile_refs` and `target_profile_refs`. If you send both, the target's profiles are used. Rules that need ontology or identifier data also accept `ontology_snapshots` and `mapping_snapshots`. Each snapshot must have an immutable `ref` and `sha256`.

### Validate

The request body has:

- `document`: the object to check
- `kind`: `contract` (the default), `manifest` or `object`
- `profile_refs`: profiles to check a contract against
- `schema_name`: which schema to use when `kind` is `object`, such as `resolution-plan.schema.json`

The response has `valid`, a list of `findings`, and the `standard` and `bundle_sha256` it used.

### Resolve

The request body has `source` and `target`. You can also send `capabilities`, `policy`, `ontology_snapshots`, `mapping_snapshots`, `expires_at`, and a `lab_revision_digest` that ties the plan to one revision of a lab.

If `capabilities` is empty, the resolver considers all active capabilities in the reviewed Biosimulant registry. If you send a list, it acts as an allowlist. Every item must exactly match an active registry entry. The server fills in the default policy, saves the final policy as an immutable record, and pins it in the plan.

The response always includes `report`, the direct comparison, and `report_id`. Its `resolution` field is one of:

- `RESOLVED`: the resolver found one best plan. The response includes `plan` and `plan_id`, and the plan is stored.
- `AMBIGUOUS`: several plans are equally good. They're listed in `candidate_plans` but not stored. Send fewer capabilities and resolve again to get a single plan.
- `UNRESOLVED`: no plan was found. `reason` is `UNKNOWN_CONTRACT`, `NO_CAPABILITY_PATH` or `SEARCH_LIMIT_EXCEEDED`.

### Approve a plan

If a plan's policy decision is `APPROVAL_REQUIRED`, approve it with `POST /v1/compatibility/plans/{plan_id}/approvals`. Send the exact `plan_digest`, `capability_refs` for the capabilities you approve (1 to 10, each used in the plan), and optionally `expires_at`.

The approval receipt records the plan's sha256 and the approved capabilities, so it can't be reused for a different plan. The API returns `409` if the plan doesn't need approval, and `422` if a capability isn't part of the plan.

Runs started from a released lab on the Biosimulant platform can be linked to a stored plan by its ID and sha256. The run's Passport then records the plan, its policy decision and any approvals.
