Skip to content

@ToolInterceptor

@ToolInterceptor ("@agboss/core") declares a reusable pre-call and/or post-call interceptor around @Tool and @McpTool invocations. While @InputGuardrail and @OutputGuardrail guard the outer text boundary of an @Agent or @Router, @ToolInterceptor guards every individual tool call inside the agent’s tool loop (app.execute / app.stream) as well as direct app.tool(ToolClass).invoke(input) calls.

A @ToolInterceptor class implements BeforeToolCall, AfterToolCall, or both ([tool-interceptor.missing-handler] if neither is implemented), and cascades across @Application $\to$ @Workflow $\to$ @Agent $\to$ @Tool / @McpTool via toolInterceptors: [...] and excludeToolInterceptors: [...].

@ToolInterceptor options & handler interfaces

Section titled “@ToolInterceptor options & handler interfaces”
Option / Interface Type Required Description
name string Yes Unique kebab-case identifier ending with -tool-interceptor; the class name must end with ToolInterceptor ([name.kind-suffix]).
description string Yes Non-empty summary of the interceptor’s responsibility.
deps readonly Token[] No Constructor dependencies (TenantContext, ExecutionContext, @Injectable() services) injected per invocation.
BeforeToolCall beforeToolCall(call: ToolCallContext) At least one of BeforeToolCall or AfterToolCall ([tool-interceptor.missing-handler]) Inspects { tool, toolName, callId, input } after DTO validation and returns a ToolCallDecision.
AfterToolCall afterToolCall(call: ToolCallResultContext) At least one of BeforeToolCall or AfterToolCall Inspects { tool, toolName, callId, input, output } after tool.run(input) and returns the (optionally transformed) output validated against the tool’s output DTO before maxResultSize truncation and PII masking.
Factory Method Runtime Behavior
ToolCallDecision.allow() Proceeds to the next BeforeToolCall interceptor (or tool.run(input) once all interceptors allow).
ToolCallDecision.replyToModel({ message }) Self-correcting tool feedback: tool.run() and channel approval do not execute; inside an @Agent loop, message is returned to the model as the tool call’s observation so the agent can self-correct its arguments. On direct app.tool(T).invoke(), throws ToolBlockedError.
ToolCallDecision.block({ reason }) Hard abort: tool.run() does not execute and the step fails immediately with ToolBlockedError (a subclass of StepFailedError, catchable by .onError() in workflow flows).

How the cascading interceptor hierarchy works

Section titled “How the cascading interceptor hierarchy works”

Interceptors attach via toolInterceptors: [...] on @Application, @Workflow, @Agent, @Tool, and @McpTool, and can be opted out at any inner scope via excludeToolInterceptors: [...]:

  1. beforeToolCall runs outer-to-inner: @Application $\to$ @Workflow $\to$ @Agent $\to$ @Tool / @McpTool, before any human approval @Channel on the tool is triggered (so unauthorized or malformed calls never wake a human reviewer).
  2. afterToolCall runs inner-to-outer (onion order): @Tool / @McpTool $\to$ @Agent $\to$ @Workflow $\to$ @Application, before output DTO re-validation, maxResultSize truncation, and @PiiPolicy masking.