Noizu.MCP.Auth.ProtectedResourceMetadataPlug (Noizu MCP v0.1.6)

Copy Markdown View Source

Serves the RFC 9728 protected-resource metadata document MCP clients use to discover your authorization server:

# Phoenix router (path is fixed by RFC 9728)
forward "/.well-known/oauth-protected-resource",
        Noizu.MCP.Auth.ProtectedResourceMetadataPlug,
        resource: "https://api.example.com/mcp",
        authorization_servers: ["https://auth.example.com"]

Options: :resource (required), :authorization_servers (required), :scopes_supported, :bearer_methods_supported, :resource_name, :extra (map merged in).

Several mounts behind one forward

A host with more than one MCP mount needs a distinct resource value per mount, and RFC 9728 path-insertion puts the mount path in the well-known URL: /.well-known/oauth-protected-resource/mcp/learning describes https://host/mcp/learning. Pass :resources — a map of path suffix to per-resource options — and one forward answers them all:

forward "/.well-known/oauth-protected-resource",
        Noizu.MCP.Auth.ProtectedResourceMetadataPlug,
        authorization_servers: ["https://app.example.com"],
        scopes_supported: ["mcp"],
        default_resource: "/mcp",
        resources: %{
          "/mcp" => [resource: "https://app.example.com/mcp"],
          "/mcp/learning" => [resource: "https://app.example.com/mcp/learning"],
          "/mcp/workspace" => [resource: "https://app.example.com/mcp/workspace"]
        }

Top-level options are defaults every entry inherits; entry options win. :default_resource names the suffix answered at the bare forward path (what a client that skipped path-insertion asks for). An unknown suffix is a 404 — never a document describing some other resource, which would hand a client the wrong audience.

CORS

claude.ai performs discovery from a browser context, so the document is served with Access-Control-Allow-Origin (default * — it is public, unauthenticated metadata) and OPTIONS answers 204. Pass allow_origin: nil to omit the header.