registerRenderToolCall
Angular function for registering a renderer component for a tool call with CopilotKit, with access to streaming arguments, status, and result
Overview
registerRenderToolCall registers a renderer for a tool call. It does not run a handler. It tells CopilotKit which standalone Angular component to render whenever a tool call with a matching name appears in the conversation. Use it to visualize tool calls that execute somewhere other than the browser, for example a server-side tool or an agentic (long-running) tool, where you only want to show progress and results in the chat.
You call registerRenderToolCall inside an Angular injection context (a component or service constructor, or a field initializer). The renderer registers immediately, and CopilotKit removes it automatically when the owning component or service is destroyed.
This is the Angular equivalent of React's useRenderTool() / useRenderToolCall(). If you want to both run a browser handler and render UI, use registerFrontendTool with its component field instead.
Import from the package root, @copilotkit/angular. There is no /v2
subpath. registerRenderToolCall must run in an injection context that has
provideCopilotKit in
scope.
Signature
import { registerRenderToolCall } from "@copilotkit/angular";
function registerRenderToolCall<Args extends Record<string, unknown>>(
renderToolCall: RenderToolCallConfig<Args>,
): void;Parameters
Prop
Type
Return Value
registerRenderToolCall returns void. It registers the renderer as a side effect and wires up automatic cleanup.
The renderer component
Your component implements the ToolRenderer<Args> interface. CopilotKit binds the inputs by name, so declare them with Angular's input():
import { Signal } from "@angular/core";
import { AbstractAgent } from "@ag-ui/client";
interface ToolRenderer<Args extends Record<string, unknown>> {
// Required: the current state of the tool call.
toolCall: Signal<AngularToolCall<Args>>;
// Bound only when the config sets passAgent: true.
agent?: Signal<AbstractAgent | undefined>;
}AngularToolCall
toolCall() returns a discriminated union keyed on status. The shape of args and result depends on the status:
type AngularToolCall<Args extends Record<string, unknown>> =
| {
name?: string;
args: Partial<Args>; // streaming, may be incomplete
status: "in-progress";
result: undefined;
}
| {
name?: string;
args: Args; // fully parsed
status: "executing";
result: undefined;
}
| {
name?: string;
args: Args; // fully parsed
status: "complete";
result: string; // the tool result, as a string
};in-progressmeans the arguments are still streaming in, soargsisPartial<Args>andresultisundefined.executingmeans the arguments have fully arrived and the tool is running, soargsis the completeArgsandresultis stillundefined.completemeans the tool finished, soargsis complete andresultholds the string result.
Usage
A renderer component for a server-side tool
This renderer shows a spinner while the tool runs and then renders the result. It narrows on status so the template only reads result once it is available.
import { Component, input } from "@angular/core";
import { AngularToolCall, ToolRenderer } from "@copilotkit/angular";
type SearchArgs = { query: string };
@Component({
selector: "app-search-tool-view",
standalone: true,
template: `
@let call = toolCall();
@switch (call.status) {
@case ("in-progress") {
<div class="text-sm opacity-70">Preparing search...</div>
}
@case ("executing") {
<div class="text-sm opacity-70">Searching for "{{ call.args.query }}"...</div>
}
@case ("complete") {
<pre class="rounded border p-3 text-sm">{{ call.result }}</pre>
}
}
`,
})
export class SearchToolViewComponent implements ToolRenderer<SearchArgs> {
readonly toolCall = input.required<AngularToolCall<SearchArgs>>();
}Register it for the tool name your agent emits:
import { Component } from "@angular/core";
import { z } from "zod";
import { registerRenderToolCall } from "@copilotkit/angular";
import { SearchToolViewComponent } from "./search-tool-view.component";
@Component({
selector: "app-chat",
standalone: true,
template: ``,
})
export class ChatComponent {
constructor() {
registerRenderToolCall({
name: "searchDocuments",
args: z.object({ query: z.string() }),
component: SearchToolViewComponent,
});
}
}Receiving the agent instance
Set passAgent: true to have CopilotKit bind the agent signal input as well. Declare it on the component as an optional input().
import { Component, input } from "@angular/core";
import { AbstractAgent } from "@ag-ui/client";
import { AngularToolCall, ToolRenderer } from "@copilotkit/angular";
type HandoffArgs = { target: string };
@Component({
selector: "app-handoff-tool-view",
standalone: true,
template: `<div>Handing off to {{ toolCall().args.target }}</div>`,
})
export class HandoffToolViewComponent implements ToolRenderer<HandoffArgs> {
readonly toolCall = input.required<AngularToolCall<HandoffArgs>>();
readonly agent = input<AbstractAgent | undefined>();
}registerRenderToolCall({
name: "handoff",
args: z.object({ target: z.string() }),
component: HandoffToolViewComponent,
passAgent: true,
});Related
registerFrontendTool
Register a client-side tool with an async handler and an optional renderer component.
registerHumanInTheLoop
Register a tool that pauses the agent and waits for the user to respond from a rendered component.
CopilotKit service
The central service that holds agents, tools, and the runtime connection.