The Honest Boundary
Affiant’s whole premise is that every AI-proposed write is sworn: a durable
Affidavit with field-level provenance, held on the
Docket until a human approves it. That promise only holds
for writes Affiant can actually see. This page states the boundary of what Affiant sees
precisely, explains why the boundary is where it is, and tells you what to do about the part it
can’t reach.
What Affiant intercepts
Section titled “What Affiant intercepts”Affiant intercepts locally-invoked tool calls — function calls that execute as code running
inside your own process, addressed and invoked by whichever backend object your host application
constructed: a Semantic Kernel Kernel, a Microsoft Agent Framework AIAgent, or a
Microsoft.Extensions.AI ChatOptions/IChatClient pairing. See
Interception Backends for how the three attach; this page’s
prose mostly speaks in Semantic Kernel’s vocabulary (Kernel, KernelFunction) since it’s the
framework’s original and still-reference backend, but the boundary described here is identical
on all three — each backend’s own hosted-tool audit enforces it structurally at wire-up.
Concretely, that means:
- Any
[KernelFunction]-decorated method in a plugin registered on yourKernel(or, on the other two backends, any method reflected into anAffiantToolCatalog), whether marked with[AffiantWriteTool]or left as a plain read tool. - Locally-invoked MCP tools — tools from an MCP (Model Context Protocol) server that you’ve
imported into your
Kernelas plugins, the standard Semantic Kernel pattern for consuming MCP tools. Once imported, an MCP-backed tool is an ordinaryKernelFunctionas far as theKernelis concerned. Nothing in Affiant’s interception layer needs to know, or care, that a function’s implementation happens to proxy to an MCP server running on your machine or your network — only that your process’sKernelis the one invoking it. That symmetry is why locally-invoked MCP tool writes get sworn Affidavits for free, with no MCP-specific code anywhere in the framework. The same holds for a locally-invoked MCP tool on the other two backends.
What Affiant cannot reach
Section titled “What Affiant cannot reach”Affiant cannot see hosted / server-side tool execution — tool calls that a model provider’s own runtime executes remotely, without ever handing control back to your process to make the call.
This covers hosted MCP tools (an MCP server the provider runs and invokes on your behalf, as distinct from one you run and invoke yourself), code interpreter, web search, and other provider-hosted toolboxes exposed by a given model API. If a write happens inside one of those, Affiant never sees the function name, the arguments, or the result — the call and its execution live entirely inside infrastructure Affiant’s host process never touches.
A concrete example: suppose your agent has access to a provider-hosted code-interpreter tool that
can, in principle, call out to an API and persist a result somewhere. If a user’s request causes
the model to route a mutation through that hosted tool rather than through one of your own
[KernelFunction] plugins, nothing about that mutation ever reaches your Kernel — no
IFunctionInvocationFilter fires, no Affidavit is produced, no Evidence Card is shown to a
reviewer. The write either already happened, or it didn’t; Affiant has no visibility into which,
and no opportunity to gate it either way.
Why this is architecturally true, not a missing feature
Section titled “Why this is architecturally true, not a missing feature”Affiant’s interception seam is Semantic Kernel’s function-invocation filter pipeline:
IFunctionInvocationFilter (wraps every function invocation) and IAutoFunctionInvocationFilter
(wraps each function invoked by SK’s automatic tool-calling loop). Both filter types attach
to your Kernel instance and fire only when that Kernel is the thing invoking the function —
whether through SK’s normal auto-invocation loop or through the manual-invocation fallback path SK
provides for connectors that don’t support the auto filter.
A hosted tool never enters that pipeline, because it was never invoked through your Kernel at
all. The model provider’s server decided to call the tool, executed it in its own runtime, and
returned only the final result to your process — often folded into the same response that would
otherwise contain a function-call request. There is no hook in Semantic Kernel’s filter pipeline
for a call that never passed through your Kernel, because the entire pipeline is defined as a
set of filters on that object. Affiant cannot swear to a write it structurally never observes.
This is not particular to Semantic Kernel, either. Microsoft’s own successor framework, Microsoft
Agent Framework (MAF), draws the identical line at the identical seam — its function-invocation
middleware (FunctionInvocationContext) is documented as working only with tools “the client
invokes locally,” while hosted tools are executed by the provider and carry a separate,
server-side approval mechanism instead. Microsoft.Extensions.AI (M.E.AI) — the lower-level
abstraction both SK and MAF sit on top of — draws it a third time at its own
FunctionInvokingChatClient seam: only client-invoked AIFunctions pass through it, and its own
hosted-tool markers (HostedWebSearchTool, HostedCodeInterpreterTool, HostedMcpServerTool,
and similar) are provider-executed with no client-side invocation to wrap. See the
FAQ for the sourcing on the MAF boundary, and
Interception Backends for the M.E.AI one. The boundary is a
property of how hosted-tool execution is architected industry-wide, not a gap specific to
Affiant’s current adapter — all three of Affiant’s bridges enforce it the same way, refusing at
wire-up time by default rather than staying silent about an uncovered hosted tool.
Guidance for architects
Section titled “Guidance for architects”If a value must be sworn — if you need to know, and prove, exactly where a written field came
from before it reaches your database — route that write through a locally-invoked tool.
That’s the only path Affiant’s filters can see, and it’s the path
Rule 3 (write tools never write directly; they produce a
WriteProposal for review) and Rule 7 (every Affidavit field
carries provenance, no exceptions) are built to govern.
Concretely, that leaves you with three honest options when a capability you’d otherwise want is only available as a hosted tool:
- Keep the hosted-tool path read-only. Let the provider’s hosted web search or code interpreter inform the model’s reasoning, but never let it be the thing that writes to your database. Route the actual mutation through a locally-invoked write tool that captures the fields with provenance, even if the values were suggested by something the hosted tool surfaced.
- Wrap the capability behind a local proxy. If you need code-interpreter-like execution or a
specific MCP server’s tools, run the equivalent locally and expose it to your
Kernelas an ordinary plugin instead of consuming the provider’s hosted version of it. The moment it’s invoked by yourKernel, it’s back inside Affiant’s interception surface. - If a hosted write path is unavoidable, treat it as explicitly unsworn. Don’t let a hosted-tool write reach the same downstream systems as reviewed writes without a separate, clearly-labeled reviewed path — a hosted write and a sworn write should never look the same to whoever reads the audit trail afterward.
The point of naming this boundary plainly is that a framework which claims coverage it doesn’t have is worse than no framework at all — an Evidence Card that looks complete but was silently bypassed by a hosted tool is exactly the kind of hollow assurance Affiant exists to prevent.