.NET: Make server-side tool calls private by default
Pre-flight Checklist
- I have searched existing issues and this hasn't been requested yet.
Problem or Motivation
The current AG-UI design exposes all server-side tool calls in the client-visible event stream. When an agent invokes a server tool, its name and arguments are sent over the wire using TOOL_CALL_START, TOOL_CALL_ARGS, and TOOL_CALL_END.
This makes sense when a server tool intentionally participates in the UI, but many server tools are implementation details: database lookups, internal service calls, routing operations, retries, and agent-to-agent orchestration.
The existing public-by-default behavior has several consequences:
- Internal implementation details unintentionally become part of the application's public protocol contract.
- Tool arguments may contain data that should not cross the client trust boundary.
- Refactoring or renaming an internal tool can affect clients observing the stream.
- Tool arguments can be duplicated when the call is also projected into state, activity, or custom events.
- Applications need custom filtering simply to preserve a conventional server/client boundary.
Client-side filtering is insufficient because the information has already crossed the wire. Suppressing calls in the underlying agent framework is also undesirable because it may need the complete tool lifecycle for execution, model continuation, persistence, and tracing.
This issue proposes changing the existing design: server-side tool calls should be private by default and exposed only through explicit server configuration.
Proposed Solution
AG-UI server adapters should apply an explicit visibility policy when converting server-side agent events into AG-UI events.
The proposed default is:
| Server tool classification | Wire visibility |
|---|---|
| Explicitly exposed or observable | Public |
| Not explicitly exposed | Private |
A private server tool should continue to execute normally:
model requests server tool
-> server executes tool
-> result returned to model
-> model continuesBut the AG-UI adapter should not emit its client-visible lifecycle:
TOOL_CALL_STARTTOOL_CALL_ARGSTOOL_CALL_END- Associated tool-result content
Applications should explicitly expose server tools whose lifecycle is intentionally part of the UI:
options.ExposeTool("web_search");
options.ExposeTool("generate_report");For applications that intentionally present most server tool activity, SDKs should also provide a convenience API that exposes all tools while allowing specific internal tools to be excluded:
options
.ExposeAllTools()
.HideTool("lookup_customer_record")
.HideTool("internal_route");An equivalent overload could accept exclusions directly:
options.ExposeAllTools(
except:
[
"lookup_customer_record",
"internal_route",
]);SDKs could additionally support predicate-based configuration where idiomatic:
options.ExposeTools(tool =>
!tool.Name.StartsWith("internal_", StringComparison.Ordinal));The important semantics are:
- Server tools remain private unless explicitly exposed.
ExposeToolexposes an individual tool.ExposeAllToolsis an explicit opt-in to the current public behavior.- A specific exclusion always overrides the expose-all setting.
- Hiding a tool suppresses its call arguments and associated result content consistently.
Applications should also be able to project a private operation into a safer public abstraction without exposing the underlying tool:
private lookup_customer_record tool
-> server executes tool
-> tool call and result remain private
-> sanitized STATE_SNAPSHOT or activity event emittedThe protocol may not require a new wire-level field because private calls are never transmitted. However, the specification should define this visibility boundary, and server SDKs should provide an idiomatic per-tool configuration mechanism.
Existing Behavior and Compatibility
This is a proposed change from the current public-by-default behavior for server tools.
Existing applications may depend on observing all server tool calls for status displays, debugging, or generative UI. A transition strategy may therefore be necessary:
- Introduce private-by-default behavior as an opt-in policy.
- Provide migration guidance for explicitly exposing UI-relevant server tools.
- Change the default in the next major protocol or SDK version.
- Retain
ExposeAllTools()as a compatibility option for the current behavior.
Existing applications could preserve today's behavior with:
options.ExposeAllTools();They could then selectively hide sensitive or implementation-specific tools:
options
.ExposeAllTools()
.HideTool("lookup_customer_record");The long-term default should follow least disclosure: registering a server tool for model use should not implicitly publish that tool to every AG-UI client.
Alternatives Considered
Continue exposing every server tool and let the client ignore internal ones. This does not establish a meaningful trust boundary because the information still crosses the wire.
Filter calls in the client. This is too late to prevent information disclosure or accidental client dependencies.
Suppress calls in the underlying agent framework. This conflates internal execution with public protocol projection and may interfere with execution, continuation, tracing, persistence, or other consumers.
Require custom server-side wrappers. This is difficult to implement correctly across streamed arguments, parallel calls, call/result correlation, and continuation runs. Visibility should be a first-class AG-UI server policy.
Additional Context
The intended responsibility split is:
Agent framework
-> retains the complete internal execution stream
AG-UI server adapter
-> keeps server tools private by default
-> exposes only explicitly public server tools
-> may project internal operations into sanitized public events
AG-UI client
-> receives only server operations intended to be part of the UI contractThis preserves AG-UI's transparent tool experiences where they are intentional without automatically making every server-side implementation detail part of the public protocol stream.
Source: ag-ui-protocol/ag-ui