Skip to content

ADR-0005: Authentication Gates as a Capability Skill

  • Date: 2026-05-06
  • Authors: Matteo Rizzo
  • Status: Accepted
  • Approval State: Approved (Approved by: Matteo Rizzo on 2026-05-06)
  • Implementation State: Completed

1. Context and Problem Statement

Many shared skills make outbound HTTP calls — AgentBrowserSkill, WeatherSkill, the BrowserWebSearchProvider for RAG, plus the long tail of future enterprise skills (Jira, Salesforce, etc.). Each call can fail with HTTP 401 (missing or expired credential) or 403 (credential lacks the required scope). When that happens, the agent has two unattractive options: bubble the error to the user (verbose, fails the turn), or silently retry without a credential (futile).

The right behavior is neither. The agent should pause the autonomous flow, hand off to a human (or to the calling system) with a structured request for the missing credential, and resume after the credential lands. This pattern is the same regardless of which skill encountered the 401 — the policy is "any skill, on auth failure, asks for help" — so the implementation should not be re-invented per skill.

mirai-agent-core already has a HITL primitive (agent-core ADR-0001) that suspends a workflow until a signal arrives. We need a way to bind the HTTP-auth-failure pattern to that primitive in a way that's reusable across every HTTP-using skill.

2. Decision Drivers (Forces)

  • Reuse: The credential-handoff payload, the 401/403 detection logic, and the resume contract should be authored once.
  • Discoverability: Agents should see "authentication-gates" in the skill catalog and know to route auth-failure events through it.
  • Composability: Other HTTP-using skills should not have to know about the gate's internals — they should call it as a tool.
  • Type safety: The handoff payload is a structured object (which credential, which scope, which provider), not free-text.
  • No runtime coupling between skills: Skills don't import each other; they all import mirai-agent-core types. Any cross-skill plumbing has to flow through the agent (via tool calls) or through agent-core types.

3. Considered Options

  1. Option 1: HTTP middleware injected into every skill. A shared httpx event hook that detects 401/403 and pauses.
  2. Option 2: A capability skill (AuthenticationGatesSkill) with explicit tools (chosen).
  3. Option 3: Per-skill catch blocks that each call a helper.
  4. Option 4: Bake auth-handoff into mirai-agent-core as a first-class primitive.

4. Decision Outcome

Chosen option: Option 2 (a capability skill with explicit tools), because it makes the auth-handoff pattern a first-class, agent-visible capability without coupling skills to each other. The pattern lives in the catalog as a peer of every other skill, and any skill that hits a 401 can route through it as a normal tool call.

AuthenticationGatesSkill exposes two tools:

  • inspect_status_code(status_code: int, response_body: str) — a pure function the agent calls when any HTTP-using skill returns a non-2xx status. Returns a structured classification: {auth_failure: bool, scope_required: str | None, hint: str}.
  • request_credential_handoff(provider: str, scope: str | None, context: dict) — emits a CredentialHandoff Pydantic model that the calling system (Chainlit, a custom UI, a Slack bot) receives via the AG-UI stream. The skill itself doesn't suspend; it relies on the downstream SecureSkill wrapper (with REQUIRES_HITL for request_credential_handoff) to do the suspending via agent-core's HITL primitive.

This composes cleanly with SecureSkill:

from mirai_core.core.types import SecureSkill, SecurityLevel
from mirai_shared_skills import AuthenticationGatesSkill

gated = SecureSkill(
    AuthenticationGatesSkill(),
    policy={
        "inspect_status_code": SecurityLevel.SAFE,         # pure function, autonomous
        "request_credential_handoff": SecurityLevel.REQUIRES_HITL,  # pauses turn
    },
)

The agent prompt (the skill's instructional payload) tells the LLM: "if you receive an HTTP error from another skill, call inspect_status_code; if it returns auth_failure: true, call request_credential_handoff with the provider and scope". The reasoning happens in the LLM, the gating happens in SecureSkill, the credential delivery happens in the calling system.

4.1. Validation / Compliance

  • The CredentialHandoff Pydantic model has unit tests asserting required fields.
  • inspect_status_code has table-driven tests for 401, 403, 5xx, and 2xx.
  • End-to-end test: an httpx 401 → inspect_status_coderequest_credential_handoff → simulated HITL signal → resume.

5. Pros and Cons of the Options

Option 1: HTTP middleware in every skill

  • Pros: Automatic — skills don't have to opt in.
  • Cons: Couples every skill to the gate's implementation. Cross-cuts vendor SDKs that don't use shared httpx clients. The agent can't reason about "should I retry?" because the middleware fired before the LLM saw the failure.

Option 2 (chosen): Capability skill

  • Pros:
  • One catalog entry; one set of tests; one set of docs.
  • The LLM is in the loop: it can decide whether to escalate or to try a different skill first.
  • Composable with SecureSkill for HITL gating.
  • Other skills don't know AuthenticationGatesSkill exists — coupling is via the agent, not the code.
  • Cons:
  • Requires the agent's system prompt to mention the gate (handled by the gate's instructional payload — see ADR-0008).

Option 3: Per-skill catch blocks

  • Pros: Localized.
  • Cons: Re-invented per skill; drift risk; no shared discoverability.

Option 4: Bake into agent-core

  • Pros: Available everywhere by default.
  • Cons: agent-core is provider-agnostic — auth handoff is an enterprise pattern, not a core agent runtime concern. Bloats agent-core's surface.

6. Consequences

  • Positive Consequences:
  • The pattern is auditable: anyone reading mirai_shared_skills/auth_gates/skill.py sees the entire auth-failure surface.
  • New HTTP-using skills inherit the pattern by being deployed alongside AuthenticationGatesSkill in the agent's skill list — no code coupling.
  • The CredentialHandoff Pydantic model is the contract between the skill and any UI; UIs can render it however they like.
  • Negative Consequences / Trade-offs:
  • Requires the LLM to actively call inspect_status_code after HTTP failures. If the LLM doesn't, the failure surfaces as a normal tool error. The agent's system prompt must reinforce this expectation.
  • Risks & Mitigations:
  • Risk: A new HTTP-using skill ships before its prompt mentions the auth-gates pattern. Mitigation: the "Adding New Skills" guide enumerates this as a checklist item; integration tests for HTTP-using skills include a 401 case that the gate must handle.

7. Implementation Plan & Status Updates

  • Target Milestone/Release: v0.1.0 (current).
  • Implementation Notes:
  • 2026-05-06: ADR formalizes the existing skill in mirai_shared_skills/auth_gates/. No code changes.