stacklok / stacklok/toolhive

Generalize vMCP runtime authz to support non-Cedar backends via MCPAuthzConfig

Open
#5,582 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
2.2k
Forks
300
Avg merge
1d 15h
Merged PRs (30d)
184

Description

Background

MCPServer and MCPRemoteProxy already support any registered authz backend (cedarv1, httpv1, etc.) via authzConfigRef — the runner's authz middleware dispatches on type through a pluggable registry.

VirtualMCPServer does not. Its runtime (vmcp binary) has its own incoming-auth pipeline that was only ever built for Cedar. Even after #5580 wires authzConfigRef into the VirtualMCPServer controller, the controller explicitly rejects non-Cedar refs with a clear error — because passing them through would be inert and fail later with an opaque message.

This issue tracks making VirtualMCPServer's runtime authz pluggable so it reaches parity with the other two workload types.

Simple explanation

Think of MCPServer and MCPRemoteProxy as dumb proxies — they hand any authz config blob to a pluggable system that looks up the right enforcer by type. VirtualMCPServer runs its own binary (vmcp) which has its own separate auth pipeline hard-coded to Cedar. The Cedar-specific fields are baked into the vMCP config struct, and the factory that creates auth middleware just checks if type == "cedar" with no way to plug in anything else.

The result is:

Workload cedarv1 httpv1
MCPServer
MCPRemoteProxy
VirtualMCPServer ✗ (fails fast, clear error)

This issue closes the gap for VirtualMCPServer.

Technical detail
What's hard-coded today

pkg/vmcp/config.AuthzConfig has Cedar-specific fields baked in:

type AuthzConfig struct {
    Type            string   // only "cedar" or "none" meaningful today
    Policies        []string // Cedar-specific
    EntitiesJSON    string   // Cedar-specific
    GroupClaimName  string   // Cedar-specific
    RoleClaimName   string   // Cedar-specific
    GroupEntityType string   // Cedar-specific
}

There is no way to represent an HTTP PDP authorizer in this struct.

pkg/vmcp/auth/factory/incoming.go:93 hard-codes the Cedar check:

if cfg.Authz != nil && cfg.Authz.Type == "cedar" && len(cfg.Authz.Policies) > 0 {
    authzMiddleware, err = newCedarAuthzMiddleware(cfg.Authz, serverName, passThroughTools)
}

No dispatch, no registry, no fallback.

What needs to change
  1. pkg/vmcp/config.AuthzConfig — replace the Cedar-specific fields with a generic backend blob, mirroring how pkg/authz.Config works for the runner (a Type string + a RawExtension-style opaque config). The existing Cedar fields can be retained inside a Cedar-specific sub-struct or migrated to the blob representation.

  2. pkg/vmcp/auth/factory/incoming.go — replace the if type == "cedar" check with a dispatch through the authorizer registry (pkg/authz/authorizers), so any registered backend can be resolved at runtime.

  3. pkg/vmcp/config validator — update validateAuthz to accept the new generic shape and validate the backend-specific blob against the registered factory.

  4. cmd/thv-operator/pkg/vmcpconfig/converter.go — once the runtime is pluggable, resolveAuthzConfigRef can resolve any registered backend (remove the cedarv1-only gate). The controller's handleAuthzConfig cedarv1 check in virtualmcpserver_controller.go can also be removed.

  5. cmd/thv-operator/pkg/vmcpconfig validator — align with the new config shape.

References
  • Design doc: design-mcpauthzconfig-workload-wiring.md (Follow-up section)
  • PR that adds the cedarv1-only gate with the clear error: #5580
  • Foundation PR that added MCPAuthzConfig CRD and helpers: #5559
  • Original issue tracking full authz wiring: #4778

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with design-mcpauthzconfig-workload-wiring.md and inspect pkg/vmcp/config.AuthzConfig, pkg/vmcp/auth/factory/incoming.go, and the authorizer registry. Then trace validation and conversion in cmd/thv-operator/pkg/vmcpconfig and virtualmcpserver_controller.go. Done means VirtualMCPServer accepts registered non-Cedar backends and dispatches them through the runtime authz path.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
backend, security
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.