Microsoft Agent Framework agents invoke tools through a function middleware pipeline. Paybond hooks that boundary — spend verify before side-effecting tools execute, then auto-evidence after success. Model inference stays on your Foundry or other chat client.
Python only — install paybond-kit[microsoft-agent-framework] for the native adapter (depends on agent-framework-core).
Adapter reference: /docs/kit/microsoft-agent-framework.
spend verify in function middleware
MAF function middleware authorizes operation and amount before call_next runs the tool.
Auto-evidence
Successful side-effecting calls finalize spend and submit signed evidence.
Policy budgets
Versioned YAML caps per-call and intent spend for Azure / Foundry agents.
Python native
Install paybond-kit[microsoft-agent-framework] — TypeScript stacks use agent-agnostic or another adapter.
Why Paybond (not just MAF tool approval)?
MAF @tool(approval_mode=...) and host HITL do not enforce a spend limit, a per-operation permission check (capability token), or a signed completion receipt tied to a spend agreement (intent). With approval_mode="never_require", Paybond middleware is the sole spend authority — do not compose MAF always_require with Paybond holds in the same sample.
Model / input guardrails
- MAF tool approval alone
- Yes — SDK or host checks and approvals
- With Paybond
- Yes — plus Harbor authorize at the tool boundary
Spend boundary
- MAF tool approval alone
- No per-tool Harbor budget or capability token
- With Paybond
- Per-call and intent budgets enforced before invoke
Signed evidence
- MAF tool approval alone
- SDK traces / logs only
- With Paybond
- Signed completion digests bound to the intent
Intent binding
- MAF tool approval alone
- No Harbor intent or settlement receipt
- With Paybond
- Capability token + intentId from authenticated bind
Paid tool deny / HITL
- MAF tool approval alone
- Host or SDK approvals only
- With Paybond
- spend verify, deny, or HITL hold before side effects
| Capability | MAF tool approval alone | With Paybond |
|---|---|---|
| Model / input guardrails | Yes — SDK or host checks and approvals | Yes — plus Harbor authorize at the tool boundary |
| Spend boundary | No per-tool Harbor budget or capability token | Per-call and intent budgets enforced before invoke |
| Signed evidence | SDK traces / logs only | Signed completion digests bound to the intent |
| Intent binding | No Harbor intent or settlement receipt | Capability token + intentId from authenticated bind |
| Paid tool deny / HITL | Host or SDK approvals only | spend verify, deny, or HITL hold before side effects |
How it works
Adapter flow
Function invoke
MAF agent invokes a side-effecting tool
Paybond middleware
Harbor authorize before call_next
- Verify spend and operation
- Deny or HITL hold
- Issue / check capability
Tool runs
call_next executes your tool body
Evidence
Middleware finalizes spend + auto-evidence
Paybond function middleware gates MAF tools: Harbor authorize before call_next, then auto-evidence after success.
3-minute quickstart
Smoke the microsoft-agent-framework adapter sandbox contract:
terminal
paybond login
paybond agent demo microsoft-agent-framework smoke \
--operation paid-tool \
--requested-spend-cents 100 \
--evidence-preset cost_and_completion \
--format tableInstall the extra first:
terminal
pip install "paybond-kit[microsoft-agent-framework]"When the smoke succeeds you should see:
- ✓ Spend approved
- ✓ Tool completed
- ✓ Evidence verified (
cost_and_completion)
What success looks like
What success looks like
Authorized tool call · illustrative
- Operation
- paid-tool
- Status
- Approved
- Requested
- $1.00
- Evidence
- Verified
- Preset
- cost_and_completion
Scaffold
terminal
paybond init agent-middleware --framework microsoft-agent-framework --out paybond_microsoft_agent_framework.py
paybond policy init --preset saas --out paybond.policy.yamlOr the vertical template:
terminal
paybond init --template microsoft-agent-framework-procurement-agentClone-and-run GitHub starter:
terminal
git clone https://github.com/nonameuserd/paybond-microsoft-agent-framework-procurement-agent.gitWire middleware
Recommended wiring
Bind a run with a catalog-backed spend resolver, then attach function middleware on the MAF Agent.
paybond_session.py
from agent_framework import Agent, tool
from paybond_kit import Paybond
from paybond_kit.agent.registry import create_paybond_tool_registry
from paybond_kit.microsoft_agent_framework import (
create_paybond_microsoft_agent_framework_config,
)
# Price lives in your catalog — not in an LLM-invented amount_cents.
FARES = {"SFO-SEA": 18_900}
@tool(name="book_flight", approval_mode="never_require")
def book_flight(route: str, seats: int = 1) -> dict:
"""Book a flight. Fare comes from the fare table, not the model."""
cost_cents = FARES[route] * seats
return {"status": "completed", "route": route, "cost_cents": cost_cents}
paybond = await Paybond.open(api_key=os.environ["PAYBOND_API_KEY"])
run = await paybond.agent_run.bind(
{
"bootstrap": {
"kind": "sandbox",
"operation": "book_flight",
"requested_spend_cents": 18_900,
"completion_preset": "cost_and_completion",
},
"registry": create_paybond_tool_registry(
{
"default_deny": True,
"side_effecting": {
"book_flight": {
"operation": "book_flight",
"evidence_preset": "cost_and_completion",
"spend_cents": lambda args: FARES[args["route"]]
* int(args.get("seats", 1)),
}
},
}
),
}
)
maf = create_paybond_microsoft_agent_framework_config(run, [book_flight])
agent = Agent(
client=...,
tools=maf.tools,
middleware=maf.middleware,
)Manual hook wiring
When you already bound a PaybondAgentRun:
paybond_session.py
from paybond_kit.microsoft_agent_framework import (
create_paybond_microsoft_agent_framework_config,
)
config = create_paybond_microsoft_agent_framework_config(run, tools)
# Agent(..., tools=config.tools, middleware=config.middleware)Deny / hold: middleware sets context.result to a Paybond message and does not call call_next() — the model sees a tool result without stopping the function-calling loop.
Bypass classes: direct tool invocation bypassing the agent, host plugins outside FunctionInvokingChatClient, and any non-middleware path are unguarded unless separately wrapped.
Production checklist
Production checklist
- Scaffold middleware with paybond init agent-middleware --framework microsoft-agent-framework
- Init a policy preset (e.g. saas) and validate tools
- Wire paybond.instrument(framework="microsoft-agent-framework") and attach middleware
- Bind intentId and capabilityToken per session in production
- Smoke with paybond agent demo microsoft-agent-framework smoke before ship
Works with
Works with
- Microsoft Agent Framework
- OpenAI
- MCP
- Agent-agnostic
Ready to test?
Related
- Microsoft Agent Framework adapter docs
- Agent middleware
- Agent-agnostic spend controls
- Support matrix
Public template: paybond-microsoft-agent-framework-procurement-agent.