Adapter for integrating Corti's A2A (Agent-to-Agent) API with Vercel AI SDK. This package provides utilities to convert between AI SDK's UI message format and Corti's A2A format.
npm install @corti/ai-sdk-adapter @corti/sdk @a2a-js/sdk aiThis adapter provides three main functions:
convertToParams()- ConvertsCortiUIMessage[]to A2AMessageSendParamstoUIMessageStream()- Converts A2A stream to UI message streamcreateA2AClientFactory()- Creates an A2A client factory configured with Corti authentication
import { convertToParams, toUIMessageStream, createA2AClientFactory } from '@corti/ai-sdk-adapter';
import type { CortiUIMessage, ExpertCredential } from '@corti/ai-sdk-adapter';
import { CortiClient } from '@corti/lib';
import { createUIMessageStreamResponse } from 'ai';
export async function POST(req: Request) {
const { messages }: { messages: CortiUIMessage[] } = await req.json();
// Optional: Define credentials for MCP servers
const credentials: ExpertCredential[] = [
{
mcp_name: 'my-server',
token: process.env.MCP_TOKEN,
type: 'bearer' as const,
},
];
// Build A2A params from UI messages
const params = convertToParams(messages, credentials);
// Create A2A client factory and send message stream
const corti = new CortiClient({ /* ... */ });
const factory = createA2AClientFactory(corti);
const agentUrl = await corti.agents.getCardUrl("your-agent-id");
const client = factory.createFromUrl(agentUrl.toString(), '');
const a2aStream = client.sendMessageStream(params);
// Convert to UI stream with optional callbacks
const uiStream = toUIMessageStream(a2aStream, {
callbacks: {
onStart: () => console.log('Stream started'),
onEvent: (event) => console.log('Event:', event),
onFinish: (state) => console.log('Final state:', state),
onError: (error) => console.error('Error:', error),
},
});
return createUIMessageStreamResponse({ stream: uiStream });
}import { useState } from 'react';
import { useChat } from 'ai/react';
import type { CortiUIMessage } from '@corti/ai-sdk-adapter';
export default function Chat() {
const [input, setInput] = useState('');
const { messages, sendMessage, status } = useChat<CortiUIMessage>({
api: '/api/chat',
});
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!input.trim() || status === "streaming" || status === "submitted") return;
sendMessage({ message: input });
setInput('');
};
return (
<div>
{messages.map((m) => (
<div key={m.id}>
<strong>{m.role}:</strong>{' '}
{m.parts.map(p => p.type === 'text' ? p.text : '').join(' ')}
</div>
))}
<form onSubmit={handleSubmit}>
<input value={input} onChange={(e) => setInput(e.target.value)} />
<button type="submit">Send</button>
</form>
</div>
);
}Custom UI message type that extends the standard UIMessage with A2A-specific metadata and data parts:
import type { CortiUIMessage } from '@corti/ai-sdk-adapter';
// CortiUIMessage includes:
// - metadata: contextId, taskId, history, credits, state
// - custom data parts: text with name, json with name/content, status-updateCredentials for authenticating with MCP servers:
type ExpertCredential =
| {
mcp_name: string;
token: string;
type: 'bearer';
}
| {
mcp_name: string;
client_id: string;
client_secret: string;
type: 'oauth2.0';
};The adapter automatically manages conversation context and task continuity:
contextId: Maintains conversation context across multiple messages. Automatically inferred from the last assistant message.taskId: Continues an existing task when the agent requires more input. Only included when the last assistant message hasstate: 'input-required'.- Credentials: Only sent on the first message (when no
taskIdis present).
The convertToParams() function handles all of this automatically - you just pass the messages array from useChat.
The toUIMessageStream() function accepts a StreamConversionOptions object with optional callbacks to monitor stream progress:
const uiStream = toUIMessageStream(a2aStream, {
callbacks: {
onStart: () => {
// Called when streaming begins
},
onEvent: (event) => {
// Called on each new event from the stream
},
onFinish: (state) => {
// Called when stream completes with the final task status
},
onError: (error: Error) => {
// Called if an error occurs during streaming
},
},
});This package supports:
- Node.js (18+)
- Edge runtimes (Vercel Edge, Cloudflare Workers)
- No tool support: This adapter does not currently support AI SDK tools/function calling
- A2A-specific: Only works with Corti's A2A agents, not general LLM providers
MIT