App context

Tools usually need to know who they are acting for. Mistri.agent(context:) takes any object you pass and hands it to every tool handler and hook as context.app, untouched. No globals, and no closure capturing the current user into each tool.

agent = Mistri.agent("claude-opus-4-8", tools: tools,
context: { user: current_user, tenant: current_tenant })

Handlers take the run context as an optional second argument and read the slot from it:

Mistri::Tool.define("open_tickets", "Lists the caller's open tickets.") do |args, context|
Ticket.for(context.app[:tenant]).open.limit(args["limit"] || 20)
end

A tool that does not need the context just omits the second parameter; nothing changes for it.

Hooks see the same context

before_tool and after_tool receive the identical context, so authorization and audit read the acting user from one place:

authorize = lambda do |call, context|
"not permitted for this tenant" unless context.app[:user].can?(call.name)
end
agent = Mistri.agent("claude-opus-4-8", tools: tools,
context: { user: current_user, tenant: current_tenant },
before_tool: authorize)

Returning a string from before_tool blocks the call and answers the model in band, so a tenant that lacks a capability gets a real explanation rather than a silent drop.

What the slot holds

Whatever you pass, exactly as you passed it, and the same object rather than a copy. It can be an ActiveRecord user, a plain struct, a tenant id, a request, a permissions object, or a hash of several of these. When you pass nothing, context.app is nil.

Why a slot, not a convention

The alternative is a closure per tool that captures the current user, which means rebuilding every tool on every request and leaking request state into tool definitions. A context slot lets you define tools once, at boot, and keep them authorization-aware: the acting identity arrives with the run, not baked into the tool.

The context also carries the run’s session, signal, and emit, which is how sub-agents and progress-streaming tools are built. app is the part that is yours. It pairs naturally with agent definitions, where a file declares the tools and the context supplies the user they act for.