Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Enterprise Pack Metadata and Capability Snapshots

The v0.6 scope deliberately adds only the part of enterprise packs that the current server needs: tools may declare an optional pack and requiredModules, and the server maintains a per-instance snapshot of installed Odoo modules. Instance-scoped tools/list responses hide tools whose pack is disabled or whose required modules are absent. Standard unscoped tools/list responses advertise a gated tool only when every configured instance can use it. The call path repeats the per-instance check so a stale client catalog cannot bypass it.

Snapshots are stored as module-snapshots.json beside the active tools.json. ODOO_MODULE_SNAPSHOT_TTL_SECS controls the refresh interval and defaults to 300 seconds. odoo_refresh_capabilities forces a refresh. A failed refresh marks the snapshot stale and records the error while preserving the last successful module list.

The Config UI reads the same snapshot through GET /api/config/instances/{name}/capabilities (add ?refresh=true to force a live re-scan). Each instance card shows module count, edition, staleness, and last refresh, and disabledPacks is editable per instance from the instance form. Tool cards surface each tool’s pack and requiredModules.

Per-instance pack suppression reuses toolConfig:

{
  "toolConfig": {
    "disabledPacks": ["inventory"]
  }
}

Policy denials emit a metadata-only tool_policy_decision event with the instance, tool, pack, and reason code. Arguments and results are not logged in that event.

Behavior notes

The unscoped rule is intentionally conservative. An unscoped tools/call (no instance argument) runs against the default instance and does not re-run the per-instance capability check, so advertising a gated tool only when every instance supports it keeps that default-instance call safe. Clients that want a tool available on just one instance should pass that instance to both tools/list and tools/call.

A failed refresh advances the snapshot’s checkedAt, so an unreachable instance is treated as fresh for one TTL window rather than re-scanned on every tools/list. The last successful module list is retained, so gating stays stable while an instance is briefly down.

Deliberate deferrals

This is metadata and filtering, not a pack framework. There are no YAML manifests, resolver traits, exposure profiles, workflow runtime, compatibility adapter matrix, new confirmation-token service, or generated domain tool suites. Existing declarative tools, environment guards, read-only instances, execute allowlists, and the controlled mutation capability remain the enforcement primitives.

The larger .plan/odoo-rust-mcp-enterprise-packs-dev-docs tree is a non-canonical historical brainstorm. Add any deferred subsystem only after a concrete use case demonstrates that these small primitives are insufficient.

Live smoke check

The regular suite tests filtering and stale preservation without Odoo. A live cell can be checked explicitly:

ODOO_INSTANCES_JSON=/absolute/path/instances.json \
ODOO_E2E_INSTANCE=odoo18ce \
cargo test --manifest-path rust-mcp/Cargo.toml \
  --test module_capabilities_live -- --ignored --nocapture

The smoke test refreshes the selected instance and proves that the stock-gated tool is visible exactly when stock is installed. For an isolated inline definition, pass the same JSON through ODOO_E2E_INSTANCES; the test writes it only to its temporary directory and gives it precedence over the operator’s default runtime config.