Skip to content

vmcp core: add ListBackends / LookupBackend to the VMCP interface (THV-0076 extension) #5741

Description

@ChrisJBurns

Summary

Add backend-level enumeration to the pkg/vmcp/core VMCP interface, serving two consumer surfaces from one method:

  • a non-admin user discovery API ("which backends can I enable?") that must be authorization-filtered, and
  • an admin UI API that lists all backends without authz filtering.
// filterUnauthorized=true  → authorized view (Cedar-filtered for identity)
// filterUnauthorized=false → admin view (all group backends, no authz filtering)
ListBackends(ctx context.Context, identity *auth.Identity, filterUnauthorized bool) ([]vmcp.Backend, error)

// authorized single-backend resolve
LookupBackend(ctx context.Context, identity *auth.Identity, backendID string) (*vmcp.Backend, error)

Motivation

Embedders consuming vmcp through the core interface need a backend-level view. Today the only way to approximate it is to reverse-engineer the backend set from ListTools, which is:

  • Lossy — a backend advertising zero visible tools (tool-less, or all-filtered) would not appear at all.
  • Wrong shape — the surface is about backends, not tools.

ListBackends semantics

filterUnauthorized == true (authorized / user-discovery path). There is no backend-level Cedar entity: admission is per-capability (AllowToolCall / AllowResourceRead / AllowPromptGet), parented to the vMCP server. So the authorized view is a derived rule:

A backend appears iff the identity is Cedar-allowed at least one of that backend's discovered capabilities.

Implementation: run the aggregated capability set through the existing admission, then group the surviving capabilities by BackendID. identity is required in this mode.

filterUnauthorized == false (admin path). Returns all group-scoped backends with no per-identity authz filtering. identity is unused in this mode (may be nil). Caller-authorization for the admin surface is enforced at the API layer (admin-gated endpoint), not inside core.

Common to both modes:

  • groupRef scoping always applies (a backend must be in the gateway's group).
  • Health is a status, not a visibility filter. ListBackends does NOT apply filterHealthyBackends; a degraded backend still appears with HealthStatus set so the UI can badge it (dropping it would make a transient blip remove a connector from the catalog).
  • Corner case: in the authorized mode, a backend with genuinely zero capabilities is vacuously hidden by the ≥1 rule. Flagged to decide explicitly.

Layering / not in scope

  • Per-user enable/disable state is a decorator on top of the core (directory-service-driven), NOT a parameter of core ListBackends. Core stays groupRef + Cedar; per-user (and future admin-config) filtering wrap as decorators around the core.
  • vmcp.Backend.Metadata annotation-carry (title/description/icon) is tracked separately.

Notes

  • This is the interface extension anticipated by the THV-0076 vMCP Core Interface RFC.
  • API shape decided: a single ListBackends with a filterUnauthorized bool (vs. two methods / functional option), since the implementations are largely shared.

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-triageIssue needs initial triage by a maintainer

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions