AttestoMCP.Anubis (AttestoMCP v0.9.0)

Copy Markdown View Source

Bridge a verified resource-server identity into the Anubis MCP frame.

AttestoMCP.Plug.Authenticate (or AttestoPhoenix.Plug.Authenticate) verifies the access token and assigns a protocol-shaped context onto the conn under :attesto_context. The Anubis HTTP transport copies conn.assigns into frame.assigns, so that context is reachable as frame.assigns[:attesto_context] inside the server.

Anubis's own authorization machinery - the tools/list visibility filter, Frame.scopes/1, Frame.has_scope?/2, any per-tool scope gate - reads the identity from frame.context.auth (an Anubis.Server.Context field), which is nil until a host populates it. put_auth/1 performs that one projection: frame.assigns[:attesto_context] -> frame.context.auth, in the auth-claims map shape Anubis.Server.Context documents.

The projection is purely mechanical. It carries no application policy - no scope-superset/admin expansion, no role mapping, no tenant or audit logic, no "which tools are visible" rules. Those belong in the host app; this only surfaces the verified claims in the place Anubis reads them.

Host wiring

Call it once at the top of the server's handle_request/2 (or any callback that needs the identity):

def handle_request(request, frame) do
  frame = AttestoMCP.Anubis.put_auth(frame)
  # ... Anubis authorization now sees the verified identity ...
end

The module is compile-guarded on Anubis.Server.Frame: a resource server that does not depend on anubis_mcp never compiles it (mirroring how attesto core gates its optional Plug surface on Plug.Conn).

Summary

Functions

Project the verified :attesto_context into frame.context.auth.

Functions

put_auth(frame)

Project the verified :attesto_context into frame.context.auth.

Idempotent and non-destructive:

  • if frame.context.auth is already set, the frame is returned unchanged (a host or an earlier call owns it);
  • if frame.assigns[:attesto_context] is absent (unauthenticated request, STDIO transport, or the auth plug did not run), the frame is returned unchanged.

Otherwise frame.context.auth is set to the Anubis.Server.Context auth-claims map projected from the context.