Docs Frameworks

Microsoft Agent Framework

Status: verified. The adapter ships in kiff-guard and passes the conformance suite. The seam was verified against the real agent-framework-core package.

Shape: middleware (async). A FunctionMiddleware wraps the tool call: await call_next() runs the tool, or set context.result and skip call_next to block. The guard decides first and only runs the tool when allowed. See how the guard works.

Install

pip install "git+https://github.com/kiffhq/kiff-guard.git#subdirectory=packages/python/kiff-guard"
# plus Microsoft Agent Framework, the framework you're guarding:
pip install agent-framework-core

Observe — audit with zero config

from agent_framework import Agent
from kiff_guard import Guard
from kiff_guard.adapters.microsoft_agent_framework import kiff_guard_middleware

guard = Guard(mode="observe")     # no client, no tenant needed

agent = Agent(
    client=...,
    name="assistant",
    tools=[refund_order, send_email],
    middleware=[kiff_guard_middleware(guard)],
)

Run the agent as usual, then read the trail:

for r in guard.receipts:
    print(r.state, r.tool, r.outcome)

Activate

Derive a starter domain from what you observed, review it, and activate it in the dashboard:

from kiff_guard import export_yaml
print(export_yaml("my-domain", guard.catalog))

Enforce — govern at runtime

Once you have a tenant and an active domain, point the guard at KIFF and switch to enforce. Only the guard changes:

from kiff_guard import Guard, HTTPClient, ToolMap
from agent_framework import Agent
from kiff_guard.adapters.microsoft_agent_framework import kiff_guard_middleware

client = HTTPClient(
    api_key="kiff_live_...",                       # mint in the dashboard
    tool_map=ToolMap().bind(
        "refund_order", action="REFUND_ORDER",
        entity_type="Order", entity_arg="order_id"),
)
guard = Guard(client=client, tenant="<tenant>", agent="support", mode="enforce")

agent = Agent(client=..., name="assistant", tools=[refund_order],
              middleware=[kiff_guard_middleware(guard)])

In enforce mode a withheld decision sets context.result to the reason and skips call_next, so the tool never runs. An allowed decision awaits call_next and the tool runs. On a transport error the guard fails closed (blocks) by default.

The seam (verified)

A FunctionMiddleware.process(self, context, call_next) fires before a tool runs; context.function.name + context.arguments carry the call. Awaiting call_next() runs the tool; setting context.result and skipping call_next blocks it. Attached via Agent(middleware=[...]).