# `AttestoMCP.Anubis`
[🔗](https://github.com/XukuLLC/attesto_mcp/blob/v1.0.5/lib/attesto_mcp/anubis.ex#L2)

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`).

# `put_auth`

```elixir
@spec put_auth(Anubis.Server.Frame.t()) :: Anubis.Server.Frame.t()
```

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.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
