Discovery, search & filter
RoadmapThis capability is on the Roadmap; the API shown here is the planned design.
donkey.tools.discover(...) is the one entry point for finding governed tools
to bind. It narrows the catalog by governance, domain and tags, so an agent
binds only the tools it needs rather than the entire catalog. It returns a
ToolSet whose per-framework methods hand back native
tool objects. Name search, asset type and environment are filters on the
lower-level donkey.registry.search().
The two most common calls
# 1. Everything governed in a domain:
tools = await donkey.tools.discover(domain="hr", governed=True)
# 2. Governed tools in a domain that carry a tag:
tools = await donkey.tools.discover(domain="hr", tags=["approved"], governed=True)Filter reference
Every argument is keyword-only and optional, and filters combine with AND
semantics:
tools = await donkey.tools.discover(
domain="hr", # catalog domain
tags=["approved"], # all tags must be present
governed=True, # True = default criteria; or an explicit GovernanceCriteria
locked=False, # True = resolve only what donkey.lock pins
)| Argument | Type | Meaning |
|---|---|---|
domain | str | None | Catalog domain. |
tags | list[str] | None | All listed tags must be present. |
governed | bool | GovernanceCriteria | None | True applies the default criteria; pass a GovernanceCriteria (e.g. STRICT) for explicit rules; None = unfiltered (the default). |
governance | Any | None | Reserved in the signature; no behaviour is defined for it yet. |
locked | bool | True resolves only the versions pinned in donkey.lock (see Pinning & lockfile). Default False. |
Registry search
discover(...) is the high-level facade over the registry. When you need name
search, asset types, an explicit environment or a result limit, call
donkey.registry.search() directly. It returns AssetRef handles rather than a
bindable ToolSet:
refs = await donkey.registry.search(
query="*accounts*", # glob over asset name + description; None = no text filter
asset_types=["mcp"], # restrict to MCP servers, agents, etc.
tags=["approved"],
domain="hr",
environment="Production", # environment-scoped governance
governed=True,
limit=50, # max results (default 50)
)“Governed” is a computed predicate
Publication to Exchange says nothing about whether an asset is fronted by a
gateway, has policies applied, or passes the org’s rulesets — there is no single
boolean to query. “Governed” is computed by joining state across systems,
and it is environment-scoped: an asset governed in Production may be
ungoverned in Sandbox. GovernanceCriteria makes every condition explicit:
from donkey_kit.registry.governance import GovernanceCriteria, STRICT
@dataclass(frozen=True)
class GovernanceCriteria:
require_api_instance: bool = True # an API Manager instance exists in this env
require_deployed: bool = True # deployed to a gateway, not just configured
require_any_policy: bool = True # at least one policy applied
required_policies: list[str] = ... # e.g. ["client-id-enforcement"]
forbidden_policies: list[str] = ...
require_governance_pass: bool = False # passes org rulesets with no `error` findings
require_gateways: list[str] = ... # only assets behind these named gateways
require_tags: list[str] = ...
require_lifecycle: list[str] = ... # e.g. ["published", "approved"]
allow_unknown: bool = False # if a check can't be evaluated, does it pass?
# A ready-made strict preset:
STRICT = GovernanceCriteria(
require_governance_pass=True,
required_policies=["client-id-enforcement"],
allow_unknown=False,
)Pass it straight through:
tools = await donkey.tools.discover(domain="hr", governed=STRICT)allow_unknown matters more than it looks. If the platform doesn’t expose,
say, ruleset results, then require_governance_pass=True with
allow_unknown=False filters the whole catalog to zero — so every
filtered-out asset carries a reason, surfaced by explain().
explain() — why a tool was included or excluded
Without it, governed=True returning an empty list is indistinguishable
from a broken credential. explain() reports every check:
report = await donkey.registry.explain(ref, criteria=STRICT)
# GovernanceReport(governed=False, checks=[
# Check("api_instance_exists", True, "instance 19283 in Sandbox"),
# Check("deployed", True, "gateway managed-omni-eu-1"),
# Check("required_policies", False, "missing: client-id-enforcement"),
# Check("governance_pass", None, "UNKNOWN: rulesets API returned 403"),
# ])Each check is True (passed), False (failed, with the reason), or None
(couldn’t be evaluated — resolved via allow_unknown). The empty-result warning
message points you to explain().
Defaults and performance
- Unfiltered by default.
governeddefaults toNone, and a startup log line states that discovery is unfiltered. Changing the default toTrueis reserved for a future major version. - Warm the index. The governance join is a bulk operation, not one API call
per asset. A long-running agent can build the index at startup with
donkey.registry.warm(environment=...)so the first discovery is fast.
Related
- Framework binding — turn a
ToolSetinto native tools. - Pinning & lockfile — pin resolved versions for production.