Skip to content
This repository was archived by the owner on Mar 12, 2026. It is now read-only.

Commit c395ecf

Browse files
tylerslatonranst91
andauthored
feat: add A2UI, MCP apps and other optimization (#13)
Signed-off-by: Tyler Slaton <tyler@copilotkit.ai> Co-authored-by: ran <ran@copilotkit.ai>
1 parent dbd32ae commit c395ecf

10 files changed

Lines changed: 909 additions & 324 deletions

File tree

apps/agent/main.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@
99

1010
from src.query import query_data
1111
from src.todos import AgentState, todo_tools
12+
from src.form import generate_form
1213

1314
agent = create_agent(
1415
model=ChatOpenAI(model="gpt-5-mini", reasoning={"effort": "low", "summary": "concise"}),
15-
tools=[query_data, *todo_tools],
16+
tools=[query_data, *todo_tools, generate_form],
1617
middleware=[CopilotKitMiddleware()],
1718
state_schema=AgentState,
1819
system_prompt="""

apps/agent/src/form.py

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
import json
2+
from langchain.tools import tool
3+
4+
@tool
5+
def generate_form() -> str:
6+
"""
7+
Generates a login form for the user to sign in.
8+
"""
9+
components = [
10+
{
11+
"id": "root",
12+
"component": {
13+
"Card": {
14+
"child": "main-column"
15+
}
16+
}
17+
},
18+
{
19+
"id": "main-column",
20+
"component": {
21+
"Column": {
22+
"children": {
23+
"explicitList": [
24+
"header",
25+
"email-field",
26+
"password-field",
27+
"login-btn",
28+
"divider",
29+
"signup-text"
30+
]
31+
},
32+
"gap": "medium"
33+
}
34+
}
35+
},
36+
{
37+
"id": "header",
38+
"component": {
39+
"Column": {
40+
"children": {
41+
"explicitList": [
42+
"title",
43+
"subtitle"
44+
]
45+
},
46+
"alignment": "center"
47+
}
48+
}
49+
},
50+
{
51+
"id": "title",
52+
"component": {
53+
"Text": {
54+
"text": {
55+
"literalString": "Welcome back"
56+
},
57+
"usageHint": "h2"
58+
}
59+
}
60+
},
61+
{
62+
"id": "subtitle",
63+
"component": {
64+
"Text": {
65+
"text": {
66+
"literalString": "Sign in to your account"
67+
},
68+
"usageHint": "caption"
69+
}
70+
}
71+
},
72+
{
73+
"id": "email-field",
74+
"component": {
75+
"TextField": {
76+
"value": {
77+
"path": "/email"
78+
},
79+
"placeholder": {
80+
"literalString": "Email address"
81+
},
82+
"label": {
83+
"literalString": "Email"
84+
},
85+
"action": "updateEmail"
86+
}
87+
}
88+
},
89+
{
90+
"id": "password-field",
91+
"component": {
92+
"TextField": {
93+
"value": {
94+
"path": "/password"
95+
},
96+
"placeholder": {
97+
"literalString": "Password"
98+
},
99+
"label": {
100+
"literalString": "Password"
101+
},
102+
"action": "updatePassword"
103+
}
104+
}
105+
},
106+
{
107+
"id": "login-btn-text",
108+
"component": {
109+
"Text": {
110+
"text": {
111+
"literalString": "Sign in"
112+
}
113+
}
114+
}
115+
},
116+
{
117+
"id": "login-btn",
118+
"component": {
119+
"Button": {
120+
"child": "login-btn-text",
121+
"action": "login"
122+
}
123+
}
124+
},
125+
{
126+
"id": "divider",
127+
"component": {
128+
"Divider": {}
129+
}
130+
},
131+
{
132+
"id": "signup-text",
133+
"component": {
134+
"Row": {
135+
"children": {
136+
"explicitList": [
137+
"no-account",
138+
"signup-link"
139+
]
140+
},
141+
"distribution": "center",
142+
"gap": "small"
143+
}
144+
}
145+
},
146+
{
147+
"id": "no-account",
148+
"component": {
149+
"Text": {
150+
"text": {
151+
"literalString": "Don't have an account?"
152+
},
153+
"usageHint": "caption"
154+
}
155+
}
156+
},
157+
{
158+
"id": "signup-link-text",
159+
"component": {
160+
"Text": {
161+
"text": {
162+
"literalString": "Sign up"
163+
}
164+
}
165+
}
166+
},
167+
{
168+
"id": "signup-link",
169+
"component": {
170+
"Button": {
171+
"child": "signup-link-text",
172+
"action": "signup"
173+
}
174+
}
175+
}
176+
]
177+
178+
return json.dumps([
179+
{
180+
"surfaceUpdate": {
181+
"surfaceId": "login-form",
182+
"components": components
183+
}
184+
},
185+
{
186+
"beginRendering": {
187+
"surfaceId": "login-form",
188+
"root": "root"
189+
}
190+
}
191+
])

apps/app/package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
"lint": "eslint ."
1010
},
1111
"dependencies": {
12+
"@ag-ui/a2ui-middleware": "^0.0.2",
1213
"@ag-ui/mcp-apps-middleware": "^0.0.3",
13-
"@copilotkit/react-core": "1.52.0-next.6",
14-
"@copilotkit/react-ui": "1.52.0-next.6",
15-
"@copilotkit/runtime": "1.52.0-next.6",
14+
"@copilotkit/react-core": "next",
15+
"@copilotkit/react-ui": "next",
16+
"@copilotkit/runtime": "next",
17+
"@copilotkitnext/shared": "next",
1618
"next": "16.1.6",
1719
"react": "^19.2.4",
1820
"react-dom": "^19.2.4",

apps/app/src/app/api/copilotkit/ag-ui-middleware.ts

Lines changed: 0 additions & 14 deletions
This file was deleted.

apps/app/src/app/api/copilotkit/route.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import {
55
} from "@copilotkit/runtime";
66
import { LangGraphAgent } from "@copilotkit/runtime/langgraph";
77
import { NextRequest } from "next/server";
8-
import { aguiMiddleware } from "@/app/api/copilotkit/ag-ui-middleware";
98

109
// 1. Define the agent connection to LangGraph
1110
const defaultAgent = new LangGraphAgent({
@@ -14,17 +13,20 @@ const defaultAgent = new LangGraphAgent({
1413
langsmithApiKey: process.env.LANGSMITH_API_KEY || "",
1514
});
1615

17-
// 2. Bind in middleware to the agent. For A2UI and MCP Apps.
18-
defaultAgent.use(...aguiMiddleware);
19-
2016
// 3. Define the route and CopilotRuntime for the agent
2117
export const POST = async (req: NextRequest) => {
2218
const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({
2319
endpoint: "/api/copilotkit",
2420
serviceAdapter: new ExperimentalEmptyAdapter(),
2521
runtime: new CopilotRuntime({
26-
agents: {
27-
default: defaultAgent,
22+
agents: { default: defaultAgent, },
23+
a2ui: { injectA2UITool: true },
24+
mcpApps: {
25+
servers: [{
26+
type: "http",
27+
url: process.env.MCP_SERVER_URL || "https://mcp.excalidraw.com",
28+
serverId: "example_mcp_app",
29+
}],
2830
},
2931
}),
3032
});

apps/app/src/app/layout.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
"use client";
22

33
import "./globals.css";
4+
import "@copilotkit/react-core/v2/styles.css";
45

56
import { CopilotKit } from "@copilotkit/react-core";
6-
import "@copilotkit/react-core/v2/styles.css";
77
import { ThemeProvider } from "@/hooks/use-theme";
88

9-
export default function RootLayout({
10-
children,
11-
}: Readonly<{
12-
children: React.ReactNode;
13-
}>) {
9+
export default function RootLayout({children}: Readonly<{ children: React.ReactNode }>) {
1410
return (
1511
<html lang="en">
1612
<body className={`antialiased`}>
1713
<ThemeProvider>
18-
<CopilotKit runtimeUrl="/api/copilotkit">{children}</CopilotKit>
14+
<CopilotKit
15+
runtimeUrl="/api/copilotkit"
16+
// a2ui={ theme } // Custom theme for A2UI, check @/lib/a2ui-theme.css
17+
>
18+
{children}
19+
</CopilotKit>
1920
</ThemeProvider>
2021
</body>
2122
</html>

apps/app/src/hooks/use-example-suggestions.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ export const useExampleSuggestions = () => {
55
suggestions: [
66
{ title: "Pie chart ( Controlled Generative UI )", message: "Please show me the distribution of our revenue by category in a pie chart." },
77
{ title: "Bar chart ( Controlled Generative UI )", message: "Please show me the distribution of our expenses by category in a bar chart." },
8-
{ title: "MCP apps ( Open Generative UI )", message: "Please create a simple network diagram of a router and two switches." },
8+
{ title: "MCP apps ( Open Generative UI )", message: "Please create a simple network diagram of a router and two switches using Excalidraw." },
99
{ title: "Change theme ( Frontend Tools )", message: "Switch the app to dark mode." },
1010
{ title: "Scheduling ( Human In The Loop )", message: "Please schedule a meeting with me to learn about CopilotKit." },
11-
{ title: "Canvas ( Shared State )", message: "Please demonstrate shared state, open the canvas, and then add some todos to it about learning about CopilotKit." },
11+
{ title: "Canvas ( Shared State )", message: "Please enable app mode and then add some todos about learning about CopilotKit." },
12+
{ title: "A2UI ( Declarative UI )", message: "Please generate a simple form." },
1213
],
1314
available: "always", // Optional: when to show suggestions
1415
});

apps/app/src/hooks/use-generative-ui-examples.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,12 @@ export const useGenerativeUIExamples = () => {
5050
// --------------------------
5151
// 🪁 Default Tool Rendering: https://docs.copilotkit.ai/langgraph/generative-ui/backend-tools
5252
// --------------------------
53+
const ignoredTools = ["generate_form"]
5354
useDefaultRenderTool({
54-
render: ({ name, status, parameters }) => (
55-
<ToolReasoning name={name} status={status} args={parameters} />
56-
),
55+
render: ({ name, status, parameters }) => {
56+
if(ignoredTools.includes(name)) return <></>;
57+
return <ToolReasoning name={name} status={status} args={parameters} />;
58+
},
5759
});
5860

5961
// -------------------------------------

0 commit comments

Comments
 (0)