Dynamic action discovery

Every connected service exposes two action surfaces: a small curated set that ships in tools/list on every turn, and a much larger long-tail the model reaches on demand through search. Dynamic discovery is the layer that keeps the long-tail out of context until it is needed.

Why it exists

A gateway with a few dozen connected services and a read/write tool split would put well over 50 tool definitions — on the order of 50,000 tokens — into the model's context on every turn, before the user has typed anything. That overruns the 30–50-tool selection ceiling at which tool-selection accuracy degrades, and it bills for tool prose nobody reads.

The catalog also has a long tail. Auto-importing a full OpenAPI spec (Microsoft Graph, BigQuery, and others) yields thousands of endpoints — the catalog currently exceeds 17,000 long-tail actions. Shipping all of that in tools/list is not viable. Dynamic discovery makes the full surface reachable without paying for it on every turn.

Curated vs long-tail

  • Curated actions are hand-picked, carry written descriptions and parameter prose, and appear in the default <service>_read_actions / <service>_write_actions enums. These load on every turn. The action counts on the Services page are the curated surface.
  • Long-tail actions are auto-generated from the upstream OpenAPI spec, marked curated: false in YAML. They are indexed for search and never enter tools/list — once discovered, the model calls them directly through the service's existing <service>_read_actions / <service>_write_actions tool.

How the model reaches the long-tail

The flow is two steps — search, then call:

  1. Search. gateway_search_actions(query, service?, limit) runs BM25 over the long-tail index and returns ranked candidates with their service, HTTP method, endpoint, and risk tier. If nothing clears the relevance floor, it returns an empty result and a hint rather than a fabricated top match.
  2. Call. A discovered action runs directly — there is no separate activation step. The model invokes it through the matching <service>_read_actions / <service>_write_actions tool by name, exactly as it would a curated action. The same read/write split and destructive-confirmation gate apply, and the call is audited.

The default tool list stays compact: only curated actions are enumerated in tools/list. Calling a long-tail action does not add it to anyone's tool list and does not persist across sessions — the search index is how it is reached, every time.

The gateway also says so wherever a model could conclude otherwise. An enum reads as a closed world — an agent that doesn't see an action in the list tends to decide the capability is missing and fall back to raw API calls. So each service tool's description marks its action enum as a curated subset with a coarse count of the searchable rest, and a rejected unknown action points at gateway_search_actions instead of a dead end. An enum miss is a discovery cue, not a missing capability.

Search visibility is not execution

This is the distinction that makes the default safe. Being discoverable in search is separate from being callable:

  • Search visibility is what the per-service toggle controls — whether a service's curated: false actions show up in gateway_search_actions.
  • Execution goes through the same read/write split and destructive-confirmation gate as any curated action. A long-tail action surfacing in search does not make it fire on its own — a write or destructive call still requires user_confirmed=true, and every call is audited.

So turning a service's long-tail on widens what the model can find, not what can fire unattended.

Risk tiers

Each long-tail action carries a risk tier derived from its HTTP method plus a path-keyword scan, with maintainer YAML overrides on top:

  • read — GET, no body. The model can call autonomously.
  • write — POST/PUT/PATCH. Carries a body, mutates state. The call requires the model to pass user_confirmed=true and is recorded in the audit log.
  • destructive — DELETE, or a POST whose path matches a keyword list (/delete, /revoke, /terminate, /wipe). The same user_confirmed=true gate as write, and the model is expected to surface the consequence in plain language first. Catastrophic actions (permanent delete, container destroy, bulk delete, generic API passthrough) sit behind an additional admin-approval gate the AI cannot self-grant — see destructive-action governance.

Retrieving an action correctly is not the same as executing it safely — the risk tier keeps the two as separate decisions.

The same protections apply

Long-tail actions are not a side door. The PII pseudonymization-and-rehydration pipeline and the two-layer policy hooks that govern curated actions apply identically to an auto-imported endpoint. Hooks see the call and can deny it before the gateway dispatches; every call lands in the same audit log.

Turning it on and off

As of May 2026, long-tail discovery defaults to ON for every connected service. The control is opt-out:

  • Per service, from the dashboard. Each service's page in the dashboard carries a "Find more by tool search" toggle — the authoritative control. Switch it off to exclude that service's long-tail from search.
  • For IaC-managed deployments. Set DYNAMIC_DISCOVERY_DISABLED_SERVICES to a comma-separated denylist of service ids. Empty (the default) means long-tail is visible for every service. The value hot-reloads across containers; no restart required.

What is still open

  • Published retrieval benchmark. BM25 is fast and predictable at the current catalog size, but a public recall number (scoped and unscoped) is owed.
  • Semantic retrieval. Pure BM25 has a known ceiling versus hybrid BM25-plus-semantic. The plan is to revisit as the catalog grows or once ground-truth labels exist.
  • Swarm promotion. When many tenants call the same long-tail action, it should graduate to a curated first-class tool with hand-written prose. The telemetry exists; the promotion pipeline does not yet.

Related