A barebones Next.js demo showcasing how to build a chat interface with Corti's agent capabilities using A2A (Agent-to-Agent) streaming and the Vercel AI SDK.
This example shows how to:
- Initialize a Corti agent using the
@corti/sdkpackage - Use the singleton pattern to create an agent once on server startup and reuse it
- Stream agent responses in real-time using
@a2a-js/sdk - Integrate with Vercel AI SDK using
@corti/ai-sdk-adapterto bridge A2A streams with AI SDK - Build a chat UI with React using AI SDK's
useChathook for seamless streaming - Handle errors gracefully with user-facing error messages
┌─────────────────────────────────────────────────────────────────┐
│ Frontend (React) │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ app/page.tsx │ │
│ │ • Uses AI SDK's useChat hook for state management │ │
│ │ • Displays messages in real-time as they stream │ │
│ │ • Handles user input and error states │ │
│ │ • Leverages DefaultChatTransport for streaming │ │
│ └────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
↓ HTTP POST
↓ /api/chat
┌─────────────────────────────────────────────────────────────────┐
│ Backend API Route (Next.js) │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ app/api/chat/route.ts │ │
│ │ • Receives messages from frontend │ │
│ │ • Gets singleton agent instance from lib/agent.ts │ │
│ │ • Uses buildParams from @corti/ai-sdk-adapter │ │
│ │ • Calls sendMessageStream on A2A client │ │
│ │ • Uses toUIMessageStream to convert A2A to UI format │ │
│ │ • Returns streaming response via createUIMessageStream │ │
│ └────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
↓
↓ Uses
┌─────────────────────────────────────────────────────────────────┐
│ Agent Singleton Module │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ lib/agent.ts │ │
│ │ • Initializes CortiClient with credentials │ │
│ │ • Creates agent once on server startup │ │
│ │ • Uses ClientFactory to create A2A client │ │
│ │ • Exports client for reuse across all requests │ │
│ └────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
↓
↓ Communicates with
┌─────────────────────────────────────────────────────────────────┐
│ Corti Platform │
│ • Agent execution and processing │
│ • A2A protocol for real-time streaming │
│ • Experts and capabilities integration │
└─────────────────────────────────────────────────────────────────┘
- Node.js 20.9 or higher
- Corti account with API credentials
- Get credentials from https://console.corti.app
- You'll need: Tenant Name, Client ID, and Client Secret
-
Clone or navigate to this directory
cd agents/react/next-agent-chat -
Install dependencies
npm install
-
Configure environment variables
Copy the environment file and fill in your Corti credentials:
cp .env .env.local
Edit
.env.localand add your credentials:TENANT=your-tenant-name CLIENT_ID=your-client-id CLIENT_SECRET=your-client-secret ENVIRONMENT=eu # or "us" for US region
⚠️ Security Note: Never commit your.env.localfile to version control. The.envfile is committed as a template with placeholder values. -
Run the development server
npm run dev
-
Open your browser
Navigate to http://localhost:3000
-
Start chatting!
Type a message in the input field and press Enter. The agent will respond in real-time with streaming text.
next-agent-chat/
├── app/
│ ├── api/
│ │ └── chat/
│ │ └── route.ts # Chat API endpoint (handles streaming)
│ ├── layout.tsx # Root layout with metadata
│ ├── page.tsx # Main chat UI (uses useChat hook)
│ └── globals.css # Tailwind CSS imports
├── lib/
│ └── agent.ts # Agent singleton initialization
├── .env # Environment variables template (committed)
├── package.json # Dependencies and scripts
├── tsconfig.json # TypeScript configuration
├── next.config.ts # Next.js configuration
├── biome.json # Linting and formatting config
├── postcss.config.mjs # PostCSS configuration for Tailwind
└── README.md # This file
This module creates a Corti agent once when the server starts and exports it for reuse. Using a singleton pattern is more efficient than creating a new agent for each request.
Key concepts:
- Initializes
CortiClientwith OAuth credentials (client credentials flow) - Creates an ephemeral agent that's automatically cleaned up
- Uses
ClientFactoryfrom@a2a-js/sdk/clientto create the A2A client - Calls
createFromUrl()to initialize the client from the agent's base URL - Caches the agent promise to prevent multiple initializations
This Next.js API route handles incoming chat messages and streams responses back to the frontend.
Key concepts:
- Receives messages from the frontend via POST request
- Gets the singleton agent instance (not creating a new one)
- Uses
buildParams()from@corti/ai-sdk-adapterto convert UI messages to A2A format - Calls
sendMessageStream()on the A2A client to get an async generator - Uses
toUIMessageStream()to convert A2A events to UI message chunks - Returns streaming responses via
createUIMessageStreamResponse()
The main chat interface built with React using the AI SDK's useChat hook.
Key concepts:
- Uses
useChathook from@ai-sdk/reactfor complete state management - Calls
sendMessage()to send user input to/api/chatendpoint - Receives
UIMessageobjects with apartsarray structure - Extracts text content from message parts for display
- Leverages
statusfromuseChatfor loading states ('ready', 'streaming', etc.) - Automatic error handling via the
errorproperty fromuseChat - Styled with Tailwind CSS.
A2A is Corti's protocol for real-time communication with agents. It enables:
- Streaming responses - Text appears in real-time as the agent generates it
- Tool usage - Agents can call tools and return results
- Context management - Maintain conversation state across multiple turns
The @corti/ai-sdk-adapter package acts as a bridge:
- Corti side: Uses A2A protocol for agent communication
- AI SDK side: Converts A2A events to
UIMessageformat - Benefit: Seamlessly use AI SDK's powerful
useChathook with Corti's agent capabilities - Developer experience: Write less code - AI SDK handles streaming, state management, and error handling
Creating an agent on every request is inefficient. This demo uses a singleton pattern:
- First request: Agent is created and cached
- Subsequent requests: Same agent is reused
- Benefits: Faster response times, lower resource usage, more cost-effective
npm run dev- Start development server on http://localhost:3000npm run build- Build the application for productionnpm start- Start the production server (requiresnpm run buildfirst)npm run lint- Run Biome linter and formatter checksnpm run lint:fix- Fix linting and formatting issues automaticallynpm run format- Format code with Biome
Error: Missing required environment variable: TENANT
Solution: Make sure you've copied .env to .env.local and filled in all required values.
Error: 401 Unauthorized or 422 Unprocessable Content authentication failures
Solution:
- Verify your
CLIENT_IDandCLIENT_SECRETare correct - Check that your credentials have the necessary permissions to create agents
- Ensure the
ENVIRONMENTmatches your account region (eu or us)
Problem: Messages send but no response appears
Solution:
- Check the browser console and server logs for errors
- Verify the agent was created successfully (check server logs on startup)
- Corti SDK: https://docs.corti.ai/sdk/overview
- Corti SDK NPM: https://www.npmjs.com/package/@corti/sdk
- A2A JS SDK: https://www.npmjs.com/package/@a2a-js/sdk
- Corti AI SDK Adapter: https://github.com/corti-ai/corti-ai-sdk-adapter
- Vercel AI SDK: https://sdk.vercel.ai/docs
- Next.js Documentation: https://nextjs.org/docs
This example is part of the Corti examples repository. See the main repository README for license information.