Hinweis: Dieses Video wurden mithilfe von KI erstellt auf Grundlage der ursprünglichen Inhalten sowie den technischen Erkenntnissen des Autors des Blogartikels.
CopilotKit für Angular [1] wurde kürzlich als Open-Source-Projekt veröffentlicht. Dieser Artikel zeigt anhand eines Beispiels, wie sich diese populäre Bibliothek zusammen mit offenen Standards nutzen lässt. Die verwendete Demoanwendung findet sich unter [2]. Die Readme zeigt, wie sich das Beispiel starten lässt.
Gesucht: eine Bibliothek zum Konsumieren von Agenten
Mit einem Agenten zu sprechen, scheint in der Theorie einfach zu sein: Frage senden, Antwort empfangen. In der Praxis aber muss der Client einen Strom von Events verarbeiten, beispielsweise Textfragmente, Tool-Aufrufe samt Argumenten und Ergebnissen, Freigaben durch den Benutzer und Fehlersignale.
Hier geht es um Infrastrukturcode – und Infrastrukturcode hat im Anwendungscode nichts verloren. Was wir brauchen, ist eine Bibliothek, die die genannten Details übernimmt, damit sich der Anwendungscode auf das konzentrieren kann, was das Produkt besonders macht.
AG-UI: kein Lock-in beim Serverstack
Die zweite Anforderung ist Unabhängigkeit. Ob das Backend-Team seine Agenten mit LangGraph oder LangChain, mit Mastra, Spring AI oder dem Microsoft Agent Framework baut, sollte für das Frontend keine Rolle spielen.
Genau das liefert AG-UI [3]: ein offenes, leichtgewichtiges Protokoll zwischen Agenten und Benutzeroberflächen. Der Client bekommt einen standardisierten Strom an Events und muss nicht wissen, welches Framework diese Events erzeugt. Diese Events weisen unter anderem darauf hin, dass nun der Agent eine neue Aufgabe startet bzw. beendet (RUN_STARTED, RUN_FINISHED), dass er Text sendet (TEXT_MESSAGE_CONTENT) oder einen Tool-Call anfordert (TOOL_CALL_START). Der Client greift diese Events auf, ohne wissen zu müssen, welches konkrete serverseitige Agent-Framework im Einsatz ist.
Drei Standards, ein Frontend
AG-UI ist nicht der einzige Standard, um den sich das Frontend kümmern muss. Daneben entstehen zwei weitere, die jeweils ein komplementäres Anliegen abdecken:
- A2UI lässt einen Agenten Benutzeroberflächen deklarativ beschreiben, sodass er seine Antwort aus echten UI-Komponenten statt aus Prosa zusammensetzen kann.
- MCP-Apps erweitern das Model Context Protocol, damit Drittanbieter-Tools eigene Visualisierungen zusammen mit ihren Fähigkeiten mitliefern können.
Diese Standards widersprechen einander nicht, sondern lassen sich kombinieren: ein über AG-UI gestreamter Agent, der mit A2UI-Oberflächen antwortet und MCP-Apps von Dritten einbettet. Jemand muss diese Teile in ein kohärentes Programmiermodell bringen – und dieser Jemand sollte nicht jedes Projektteam für sich sein.
Standards allein genügen nicht
Es gibt eine zweite Lücke: Ease of Use und Developer Experience (DX). Die offiziellen SDKs zu den genannten Standards sind ein solider erster Schritt, aber bewusst low-level. Sie liefern zwar Konzepte wie Event-Streams und Renderer, die Verdrahtung bleibt aber bei uns. Das würde Infrastrukturcode in der Anwendung nötig machen – und eben das wollen wir vermeiden.
Genau hier setzt CopilotKit an. Entwickelt vom selben Team, das AG-UI initiiert hat, verpackt es die genannten Standards in ein Frontend-SDK mit zwei Betriebsmodi: eine ausgereifte Chatkomponente für schnelle Erfolge sowie einen Headless-Modus, der direkten Zugriff auf die empfangenen Events gewährt und somit mehr Freiheiten bietet. Mit @copilotkit/angular steht all das als Angular-natives Paket zur Verfügung: Provider, Signals und Komponenten, die sich in der Angular-Welt ganz natürlich anfühlen.
Kurz gesagt: AG-UI, A2UI und MCP-Apps standardisieren die Kommunikation mit dem Agenten; CopilotKit liefert eine bequeme Möglichkeit, Agenten über diese Protokolle einzubinden, und stellt somit den Missing Link für Agentic UI mit Angular dar.
Eine Demo erstellen
Nun soll eine Demo entstehen, die aus einer regulären Angular-Anwendung und einem Mastra-basierten Agenten besteht. Mastra [4] ist ein beliebtes TypeScript-Framework zum Bauen von Agenten.
Wir verwenden Mastra, weil es von Grund auf für TypeScript entwickelt wurde und sehr leichtgewichtig ist. Für unsere Zwecke spielt die Wahl allerdings kaum eine Rolle, da das Frontend immer nur AG-UI sehen wird. Jeder Agent, der AG-UI spricht – unabhängig davon, ob er mit LangGraph, CrewAI, Pydantic AI, LlamaIndex oder dem Microsoft Agent Framework erstellt wurde –, ließe sich auf exakt dieselbe Weise anbinden.
Die Mastra CLI erlaubt das Hinzufügen einer Mastra-Anwendung zu einer bestehenden Lösung. Mit der Anweisung init fügt Mastra einen Ordner src/mastra mit einem funktionierenden Beispielagenten hinzu und fragt unterwegs nach LLM-Provider und API-Key, den er in einer .env-Datei ablegt:
ng new angular-copilot-demo
cd angular-copilot-demo
npx mastra@latest init
Der serverseitige Agent
Automatisch erstellt wird das Mastra-Agent-Grundgerüst eines Wetterassistenten mit einem einzigen Tool, das das Wetter für eine Stadt nachschlägt – gewissermaßen das „Hello World“ der Agentic AI. Die Datei weather-agent.ts enthält alles, was ein Agent benötigt – Prompt, Modell, Tools und Memory (Listing 1).
Generative AI für Angular-Entwickler:innen
Erlebe das neue Bootcamp vom 15. – 16. Oktober 2026
Listing 1
import { Agent } from ‘@mastra/core/agent’;
import { Memory } from ‘@mastra/memory’;
import { weatherTool } from ‘../tools/weather-tool’;
export const weatherAgent = new Agent({
id: ‘weather-agent’,
name: ‘Weather Agent’,
instructions: `You are a helpful weather assistant […]`,
model: ‘openai/gpt-5.6-terra’,
tools: { weatherTool },
memory: new Memory(),
});
In den instructions ist der System-Prompt enthalten, der die Rolle und das Verhalten des Agenten festlegt. Beim model genügt ein einziger Bezeichner aus Provider und Modellname – darum kümmert sich Mastras Model-Router, der den zugehörigen API-Key als Umgebungsvariable erwartet.
Über die konfigurierte memory-Instanz bleibt der Gesprächsverlauf auf dem Server, sodass der Client immer nur die neueste Nachricht senden muss. Das von mastra init generierte Grundgerüst nutzt dafür eine lokale SQLite-Datenbank. Andere Datenbanken lassen sich leicht konfigurieren.
Mit Schemata, die auf der populären Bibliothek Zod basieren, beschreibt das registrierte weatherTool seine Parameter und Ergebnisse (Listing 2).
Listing 2
// src/mastra/tools/weather-tool.ts
import { createTool } from ‘@mastra/core/tools’;
import { z } from ‘zod’;
export const weatherTool = createTool({
id: ‘get-weather’,
description: ‘Get current weather for a location’,
inputSchema: z.object({
location: z.string().describe(‘City name’),
}),
outputSchema: z.object({
temperature: z.number(),
conditions: z.string(),
location: z.string(),
[…]
}),
execute: async (inputData) => {
return await getWeather(inputData.location);
},
});
Aus diesen Schemata ergeben sich zum einen die TypeScript-Typen für den Programmcode. Zum anderen leitet Mastra zur Laufzeit aus dem inputSchema ein JSON-Schema ab und schickt es zusammen mit dem Namen und der description an das Modell. Anhand dieser Angaben entscheidet das Modell selbst, wann es das Tool aufruft und welche Argumente es übergibt. Das outputSchema bleibt hingegen auf dem Server. Es validiert und typisiert das Tool-Ergebnis.
Die Methode execute enthält die Tool-Implementierung, die im betrachteten Fall lediglich mit getWeather an ein öffentliches Wetter-API delegiert.
Den Agenten via AG-UI veröffentlichen
Bisher nutzt der Agent nur die Konventionen von Mastra. Die Brücke zu AG-UI bildet der Adapter MastraAgent aus @ag-ui/mastra: Er führt den Mastra-Agenten aus und übersetzt dessen Streaming-Events in AG-UI-Events. Eine eigene Route in der Datei ag-ui-route.ts macht das per HTTP verfügbar – der leicht vereinfachte Handler in Listing 3 veröffentlicht jeden registrierten Agenten unter /ag-ui/:agentId und streamt die AG-UI-Events als Server-sent-Events (SSE) zurück.
Listing 3
import { registerApiRoute } from ‘@mastra/core/server’;
import { MastraAgent } from ‘@ag-ui/mastra’;
import type { RunAgentInput } from ‘@ag-ui/core’;
import { streamSSE } from ‘hono/streaming’;
import { concatMap, lastValueFrom } from ‘rxjs’;
export const agUiRoute = registerApiRoute(‘/ag-ui/:agentId’, {
method: ‘POST’,
handler: async (c) => {
const mastra = c.get(‘mastra’);
const agent = mastra.listAgents()[c.req.param(‘agentId’)];
const input = (await c.req.json()) as RunAgentInput;
const aguiAgent = new MastraAgent({ agent, resourceId: ‘anonymous’ });
return streamSSE(c, async (sse) => {
const send = (data: unknown): Promise<void> =>
sse.writeSSE({ data: JSON.stringify(data) });
await lastValueFrom(aguiAgent.run(input).pipe(concatMap(send)), {
defaultValue: undefined,
});
});
},
});
Der Handler liest den RunAgentInput – also die Payload, die AG-UI-Clients senden, inklusive Thread-ID und neuer Nachrichten – und übergibt ihn dem Adapter, dessen run-Methode ein Observable mit AG-UI-Events zurückgibt. Die Thread-ID repräsentiert die gesamte Konversation, die aus mehreren Runs bestehen kann.
Der Rest ist unspektakulär: Da Mastras Server auf Hono [5] aufbaut, kümmert sich dessen Helfer streamSSE um SSE-Header und -Framing. Der Operator concatMap leitet die Events der Reihe nach durch send weiter, und lastValueFrom hält den Callback – und damit den Stream – offen, bis das Observable abgeschlossen ist.
Die zentrale Mastra-Instanz in index.ts registriert den Agenten sowie die Route und aktiviert CORS per Middleware (Listing 4).
Praktische Fullstack-Architektur: von der API bis zur Benutzeroberfläche
Erlebe das neue Bootcamp vom 15. – 16. Oktober 2026
Listing 4
import { Mastra } from ‘@mastra/core/mastra’;
import { weatherAgent } from ‘./agents/weather-agent’;
import { agUiRoute } from ‘./server/ag-ui-route’;
export const mastra = new Mastra({
agents: { weatherAgent },
server: {
apiRoutes: [agUiRoute],
cors: { origin: ‘*’ },
},
[…]
});
Für die Demo ist das großzügige origin: ‘*’ in Ordnung; bei einer echten Anwendung wären die erlaubten Origins jedoch eingeschränkt. Das ist die gesamte für uns relevante Serverseite: ein gescaffoldeter Agent mit Tool, eine Route und ein Konfigurationseintrag.
Noch kürzer: die CopilotKit-Runtime
Der Vollständigkeit halber sei erwähnt, dass das Ganze auf der Serverseite sogar noch kürzer geht. CopilotKit bringt eine eigene Runtime mit, die das Veröffentlichen von Agenten übernimmt – somit muss man die AG-UI-Route gar nicht mehr selbst schreiben. Den Mastra-Agenten zu registrieren und auszuliefern reduziert sich damit auf wenige Zeilen (Listing 5).
Listing 5
const runtime = new CopilotRuntime({
agents: { weatherAgent: new MastraAgent({ agent: weatherAgent }) },
});
const app = createCopilotHonoHandler({ runtime, basePath: ‘/’ });
serve({ fetch: app.fetch, port: 4555 }, (info) => {
console.log(`CopilotKit runtime listening on http://localhost:${info.port}`);
});
Diese Abkürzung habe ich für unsere Demo bewusst nicht gewählt: Mir ist wichtig, zu zeigen, dass unser Angular-Client mit jedem beliebigen Server zusammenarbeitet, der AG-UI spricht – ganz gleich, ob auf der Serverseite CopilotKit im Spiel ist oder nicht. Die handgeschriebene Route macht genau das deutlich.
Der Client
Zeit für den Missing Link: CopilotKit für Angular. Installiert wird das Paket per npm i @copilotkit/angular. Die Funktion provideCopilotKit in app.config.ts verbindet die Anwendung mit unserem Agenten (Listing 6).
Listing 6
import { ApplicationConfig, provideBrowserGlobalErrorListeners } from ‘@angular/core’;
import { provideCopilotKit } from ‘@copilotkit/angular’;
import { HttpAgent } from ‘@ag-ui/client’;
const AG_UI_URL = ‘http://localhost:4111/ag-ui/weatherAgent’;
export const appConfig: ApplicationConfig = {
providers: [
provideBrowserGlobalErrorListeners(),
[…]
provideCopilotKit({
agents: {
default: new HttpAgent({ url: AG_UI_URL }),
},
}),
],
};
Bleib informiert
Brancheninsights im Newsletter erhalten:
CopilotKit erwartet lediglich einen AG-UI-Agenten. Der HttpAgent aus dem offiziellen AG-UI-Client-SDK zeigt auf unseren Endpunkt /ag-ui/weatherAgent – keine CopilotKit-Runtime, keine zusätzliche Middleware dazwischen und keine einzige Zeile Mastra-spezifischer Code im Browser. Der Schlüssel default ist der clientseitige Name, über den Komponenten auf diesen Agenten verweisen.
Die Benutzeroberfläche ist ein Einzeiler (Listing 8). Die Komponente CopilotChat bringt Nachrichtenliste, Eingabefeld und Streaming-Anzeige mit (Listing 7).
Listing 7
// src/app/app.ts
import { CopilotChat } from ‘@copilotkit/angular’;
@Component({
selector: ‘app-root’,
imports: [CopilotChat],
templateUrl: ‘./app.html’,
styleUrl: ‘./app.css’,
})
export class App {}
Listing 8
<!– src/app/app.html –>
<main class=”chat-host”>
<copilot-chat agentId=”default” />
</main>
Das war’s. Ergebnis ist ein vollständiger, streamender Chat über einen echten Agenten, ohne eine einzige Zeile Event-Handling-Code in der Anwendung.
Ausprobieren
Für den Start der Demo werden zwei Terminals im Projektstamm benötigt. Die vom Mastra-Wizard erzeugte .env-Datei muss den LLM Key enthalten – für OpenAI ist das OPENAI_API_KEY. Das erste Terminal startet mit npm run dev den Mastra-Dev-Server, der auf Port 4111 lauscht. Das zweite startet mit ng serve den Angular-Dev-Server auf Port 4200.
Nun fragen wir nach dem Wetter in Wien. Die Antwort wird Wort für Wort hereingestreamt. Hinter den Kulissen hat der Agent die Stadt bereits geokodiert und sein Wetter-Tool aufgerufen (Abb. 1).
Abb. 1: Die CopilotKit-Chatkomponente beantwortet eine Wetterfrage mit dem Mastra-Agenten im Rücken
Dank serverseitigem Memory funktionieren auch Anschlussfragen wie „Is it warmer than in Paris?“: Beide Fragen gehören zum selben Thread. Der Agent weiß also, worauf sich das Wort „it“ bezieht.
Headless-Modus
Die fertige Chatkomponente ist der schnellste Weg zu einem lauffähigen Ergebnis. Doch echte Anwendungen wachsen schnell darüber hinaus: Der Chat muss zum Designsystem passen, eigene Widgets sollen in der Konversation erscheinen – und nicht alles ist ein Chat. Ein Agent kann genauso gut ein Formular, ein Dashboard oder einen ganzen Workflow steuern. Für diese Flexibilität gibt es den Headless-Modus von CopilotKit. Hier kümmert sich CopilotKit um die Kommunikation mit dem Agenten und die Zustandsverwaltung und überlässt uns das Rendering.
Um das zu demonstrieren, erhält unser Demoprojekt eine selbstgebaute Chatkomponente, die denselben Agenten über das Headless-API nutzt (Listing 9).
Listing 9
// src/app/headless-chat/headless-chat.ts
import { Component, computed, inject } from ‘@angular/core’;
import { CopilotKit, injectAgentStore } from ‘@copilotkit/angular’;
import { randomUUID } from ‘@copilotkit/shared’;
@Component({
selector: ‘app-headless-chat’,
[…]
})
export class HeadlessChat {
private readonly copilotKit = inject(CopilotKit);
private readonly store = injectAgentStore(‘default’);
protected readonly isRunning = computed(() => this.store().isRunning());
protected readonly visibleMessages = computed(() =>
this.store()
.messages()
.filter((m) => m.role === ‘user’ || m.role === ‘assistant’)
.map((m) => […])
);
protected async send(content: string) {
const agent = this.store().agent;
agent.addMessage({ id: randomUUID(), role: ‘user’, content });
await this.copilotKit.core.runAgent({ agent });
}
}
Die Funktion injectAgentStore liefert einen Signal-basierten Store für den registrierten Agenten: messages hält die Konversation und isRunning den Ausführungszustand. Das Senden einer Nachricht ist ebenso explizit: Der Code fügt die Benutzernachricht hinzu und startet anschließend einen Run mit runAgent. Der Store aktualisiert sich, während die Events hereinströmen, sodass das UI kontinuierlich neu rendert. Das Template gibt lediglich die Konversation in einer Liste aus (Listing 10).
Listing 10
<!– src/app/headless-chat/headless-chat.html –>
<ol class=”log” aria-live=”polite”>
@for (message of visibleMessages(); track message.id) {
<li [attr.data-role]=”message.role”>
<span>{{ message.role === ‘user’ ? ‘Sie’ : ‘Agent’ }}</span>
<p>{{ message.text }}</p>
</li>
}
</ol>
<form (submit)=”[…]”>
[…]
</form>
Fazit und Ausblick
In wenigen Schritten ist eine vollständige Strecke für Agentic UI entstanden: ein Mastra-Agent mit Tool und Memory, als AG-UI-Endpunkt veröffentlicht, und eine Angular-Anwendung, die ihn einmal über die fertige Chatkomponente und einmal headless konsumiert – ohne Event-Parsing, ohne Streaming-Handling und ohne eine Zeile Mastra-spezifischen Code im Browser.
Die Entkopplung leistet AG-UI: Weil das Protokoll den Dialog mit dem Agenten auf einen standardisierten Event-Strom reduziert, bleibt der Serverstack eine reine Backend-Entscheidung. So könnte statt Mastra auch LangGraph oder Spring AI zum Einsatz kommen, ohne dass das Frontend davon etwas mitbekommt. CopilotKit macht daraus ein Angular-natives Programmiermodell und lässt uns entscheiden, wie viel Kontrolle wir selbst übernehmen möchten. Wir können wählen, ob wir die fertige Chatkomponente oder den Headless-Modus nutzen möchten, bei dem jedes Pixel dem eigenen Designsystem überlassen wird.
Damit ist allerdings erst der Einstieg geschafft. Ausgewachsene Agentic UIs gehen weiter: Clientseitige Tools geben dem Agenten den aktuellen Benutzerkontext und erlauben ihm, Aufgaben innerhalb der Anwendung zu automatisieren. Anstatt einer Textwüste kann er dynamisch aus den Komponenten der Anwendung wählen. Mit A2UI komponiert er seine Antwort on the fly als deklarative Oberfläche und über MCP-Apps können Drittanbieter-Tools eigene Visualisierungen mitbringen. Human-in-the-Loop-Muster jenseits einfacher Freigaben sorgen dafür, dass die Anwender die Kontrolle darüber behalten, was der Agent tut.
Abb. 2: Ein dynamisches, vom Agenten gesteuertes Dashboard, das in der Blogserie [6] Schritt für Schritt entsteht
Ein Beispiel dafür, wohin die Reise geht, zeigt Abbildung 2: ein dynamisches Dashboard, dessen Inhalt vom Agenten gesteuert wird. Auch dieses Terrain deckt CopilotKit ab. Mehr Informationen dazu finden sich unter [6].
Links & Literatur
[1] https://www.copilotkit.ai
[2] https://github.com/manfredsteyer/copilotkit-intro
[3] https://ag-ui.com
[4] https://mastra.ai
[5] https://hono.dev
[6] https://www.angulararchitects.io/blog/ag-ui-verstehen-der-standard-fuer-agentic-user-interfaces/
Author
🔍 Frequently Asked Questions (FAQ)
1. Was ist CopilotKit für Angular?
CopilotKit für Angular ist ein Open-Source-Frontend-SDK, mit dem sich Agenten in Angular-Anwendungen integrieren lassen. Es stellt dafür unter anderem eine Chatkomponente und einen Headless-Modus bereit.
2. Welche Rolle spielt AG-UI?
AG-UI ist ein offenes Protokoll zwischen Agenten und Benutzeroberflächen. Es standardisiert den Event-Strom und entkoppelt das Frontend vom verwendeten serverseitigen Agent-Framework.
3. Welche weiteren Standards werden im Artikel genannt?
A2UI ermöglicht es Agenten, Benutzeroberflächen deklarativ zu beschreiben. MCP-Apps erweitern das Model Context Protocol um Visualisierungen, die Drittanbieter-Tools zusammen mit ihren Fähigkeiten bereitstellen können.
4. Wie wird der Agent im Beispiel umgesetzt?
Die Demo verwendet einen Mastra-Agenten mit einem Wetter-Tool und serverseitigem Memory. Über einen AG-UI-Adapter werden seine Streaming-Events als AG-UI-Events bereitgestellt.
5. Wie bindet die Angular-Anwendung den Agenten ein?
Das Paket @copilotkit/angular wird installiert und über provideCopilotKit mit dem AG-UI-Agenten verbunden. Die fertige CopilotChat-Komponente ermöglicht anschließend einen streamenden Chat ohne eigenes Event-Handling.
6. Was bietet der Headless-Modus?
Im Headless-Modus übernimmt CopilotKit die Kommunikation mit dem Agenten und die Zustandsverwaltung, während die Anwendung selbst für das Rendering zuständig ist. Dadurch lassen sich beispielsweise eigene Designs und Widgets integrieren.
7. Welche Vorteile bietet die Entkopplung durch AG-UI?
Das Frontend muss das serverseitige Agent-Framework nicht kennen. Dadurch kann beispielsweise Mastra gegen LangGraph oder Spring AI ausgetauscht werden, ohne dass sich die Anbindung im Angular-Frontend ändern muss.
8. Welche Möglichkeiten gehen über den einfachen Chat hinaus?
Der Artikel nennt clientseitige Tools, dynamische Benutzeroberflächen mit A2UI, MCP-Apps und Human-in-the-Loop-Muster. Damit können Agenten innerhalb einer Anwendung auch Aufgaben ausführen oder dynamische UIs und Dashboards steuern.







