Skip to content

Choose plugins from a catalog

0.4 development — not released

These commands require a wheel built from the development source, not the published 0.3.0 package. Follow the development installation and record its source commit.

A plugin catalog describes available plugin artifacts. It is separate from apizr scan's catalog of Python capabilities. A profile selects exact plugin versions; it contains no scripts, secrets, activation or execution permissions.

Obtain a development catalog

The Linux/macOS MCP server plugin CI artifacts contain catalog/catalogue.json, wheels/ (including requirements/) and the exported plans. Choose the artifact for your exact Python/platform target and the source commit you intend to use. These are development builds, not published releases or a hosted marketplace. The three official plugins still declare 0.0.0; use the source commit and SHA-256 values to distinguish builds. The core's 0.3.0 version string does not establish compatibility with the published stable package.

To prepare and qualify these artifacts yourself, from a development checkout with uv and an installed Python, use a new directory outside the checkout:

uv run --locked python scripts/smoke_mcp_server.py --output /tmp/apizr-catalog-proof

Preparation explicitly builds the core and three plugins and obtains dependency wheels. It then generates the catalog from those inspected bytes, exports the profiles and tests an installed MCP server with a real client. Preparation may use the network; catalog commands do not. The wheels are retained and are not rebuilt between catalog generation and installation.

Consult, choose and lock

Extract the matching mcp-stdio-OS-python-VERSION artifact and enter its _temp/mcp-proof/ directory. It contains catalog/catalogue.json and wheels/; test/coverage reports occupy another subtree. Use the development apizr executable:

apizr plugins catalog list --catalog catalog/catalogue.json --json
apizr plugins catalog show apizr-mcp --version 0.0.0 \
  --catalog catalog/catalogue.json --json
apizr plugins catalog resolve --profile mcp \
  --catalog catalog/catalogue.json --wheelhouse ./wheels \
  --output-dir ./plugin-plan --json
apizr plugins lock check --project plugin-plan/apizr.toml \
  --lock plugin-plan/apizr.plugins.lock.json --wheelhouse ./wheels --json
Profile Selected installations Purpose
mcp apizr-mcp Persistent analysis MCP server
oci apizr-oci Build and publish service images
delivery apizr-oci, apizr-attest Images and delivery receipts

list reads metadata only. show requires an unambiguous entry for the exact version and current target. Neither reads the wheels or invokes an external tool. resolve checks the profile, target, hashes, manifests and archive protections, then writes apizr.toml, unchanged requirements bytes and apizr.plugins.lock.json using the existing lock creator. A successful result says compatibility: declared, artifacts: statically_verified and installation: not_performed.

Choose a new output directory: even an existing empty directory is refused. The private staging directory is removed on error or interruption; only a complete plan is published. Identical inputs and target produce identical files, without machine-specific absolute paths or timestamps. The exported project has root = "."; set your analysis root separately when using your own project.

Install and activate explicitly

Review and trust the selected code before installing it. Hashes establish integrity, not author trust. A catalog cannot grant execution authority.

apizr plugins sync --project plugin-plan/apizr.toml \
  --lock plugin-plan/apizr.plugins.lock.json --wheelhouse ./wheels \
  --plugins-dir ./trusted-plugins --json
apizr plugins list --active --plugins-dir ./trusted-plugins --json
apizr plugins enable apizr-mcp --version 0.0.0 --plugins-dir ./trusted-plugins

sync uses installed uv offline, validates constraints and installs into separate environments. It leaves the plugin inactive; the active list remains empty in a new store until enable. Continue with the analysis MCP client setup, or the OCI and Attest operation guides. Activation is not a sandbox. External tools and credentials remain explicit operation inputs, never profile contents.

Each plugin has its own dependency closure. Attest's Python dependency on apizr-oci is inside Attest's environment. It does not register or activate a separate OCI installation. delivery explicitly selects both installations.

Document contract and Python API

The JSON schema describes apizr.plugin-catalog/v1. Unknown fields, duplicate JSON keys, invalid types, unknown protocols, duplicate entries/profiles/selections and missing profile references are rejected. The file is limited to 1 MiB, 128 entries and 32 profiles; a profile selects at most 32 plugins, each with at most 127 dependency wheels.

Each entry reuses the existing manifest and locked wheel identity. It adds a short description, compressed wheel byte sizes, an exact target (Python patch, implementation, ABI, platform and machine), a declared core version/channel, operation-specific external prerequisites and source repository/commit/status. Optional publication references are inert metadata. Requirements use the existing locked requirements subset, not a second policy language. Their canonical path is requirements/PLUGIN-NAME.lock relative to the supplied wheelhouse. Wheel filenames are single portable basenames. Symlinks and special files, including through parent paths, are refused.

Resolve retains verified bytes in a private snapshot and applies existing wheel, expanded archive and aggregate size limits. It compares the generated lock with the complete catalog identity; altered or substituted manifests/dependencies are refused. The original wheelhouse must still pass lock check and sync afterwards. No automatic version fallback, download, subprocess, plugin import, store access or project mutation occurs during resolution.

from pathlib import Path
from apizr.plugin_catalog import load_catalog, resolve_profile, select_entry

catalog = load_catalog(Path("catalog/catalogue.json"))
entry = select_entry(catalog, "apizr-mcp", "0.0.0")
result = resolve_profile(catalog, "mcp", Path("wheels"), Path("plugin-plan"))
assert result.installation == "not_performed"

generate_entries(project, wheelhouse, descriptions=..., provenance=..., prerequisites=...) generates typed entries from declarations and real inspected wheels. Place the original requirements at their canonical artifact paths before calling it. Catalog, Profile, Identity, Provenance and Prerequisite are available from apizr.plugin_catalog.models; serialize produces canonical JSON. The proof driver scripts/catalog_plugin_plan.py is a worked generation example.

Compatibility and qualification limits

The target is exact; export on the same Python/platform as the catalog and lock. Static wheel tag admission supports pure Python, the current native ABI, CPython stable ABI on non-free-threaded builds, macOS arm64/x86_64/universal2 and modern manylinux_x_y on known glibc x86_64/aarch64 hosts. For universal2 Python builds, macOS checks use the running OS and active CPU slice rather than the interpreter build deployment minimum. Other tags fail closed. This bounded check follows PyPA's wheel tag specification and does not replace uv's dependency/platform checks at installation.

The declared development core version is necessary but insufficient: use the recorded source commit and its qualification evidence. Catalog metadata does not certify publisher identity, dependency constraints, external-tool availability or successful operation. CI qualifies MCP through its real SDK client and OCI/Attest through their existing disposable Docker/HTTPS-registry proofs. No registry or release is published by preparing a catalog.