npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kuntur/a2a-carbon-chat-adapter

v1.0.0

Published

Production-ready A2A protocol adapter for Carbon AI Chat - connect any Agent2Agent protocol agent to IBM Carbon Design System chat UI

Readme

@kuntur/a2a-carbon-chat-adapter

npm version License: MIT

Production-ready A2A protocol adapter for Carbon AI Chat - Connect any Agent2Agent protocol agent to IBM Carbon Design System chat UI with full streaming support, multi-agent management, and extensible rendering.


📋 Table of Contents


Overview

@kuntur/a2a-carbon-chat-adapter is a TypeScript library that bridges the Agent2Agent (A2A) protocol with IBM's Carbon AI Chat component. It provides:

  • Protocol Translation: Converts A2A streaming responses to Carbon AI Chat's message format
  • Multi-Agent Support: Built-in context provider for managing multiple agents
  • Extension System: Handles A2A UI extensions (citations, forms, errors)
  • Server Utilities: Ready-to-use API handlers for Next.js and other frameworks
  • Type Safety: Full TypeScript support with comprehensive type definitions

This library is designed for production use in enterprise applications requiring AI agent integration with Carbon Design System.


Why This Library?

The Problem

Integrating A2A protocol agents with Carbon AI Chat requires:

  1. Protocol Translation: A2A uses Server-Sent Events (SSE) streaming; Carbon expects a specific message format
  2. CORS Handling: Browser security prevents direct agent connections
  3. Extension Parsing: A2A UI extensions need custom rendering logic
  4. State Management: Multi-agent scenarios require context management

The Solution

This library provides:

  • Drop-in Components: <A2AChat> component with full A2A support
  • Server Proxy: Pre-built API handlers for streaming proxy setup
  • Extension Renderers: Built-in components for citations, forms, and errors
  • Multi-Agent Context: AgentProvider for seamless agent switching
  • Programmatic API: Hooks for custom implementations

Benefits

  • Faster Development: Skip protocol implementation, focus on your application
  • Production Ready: Battle-tested streaming, error handling, and state management
  • Type Safe: Full TypeScript support prevents runtime errors
  • Extensible: Custom renderers and hooks for advanced use cases
  • Carbon Native: Seamless integration with Carbon Design System

Quick Start

Installation

npm install @kuntur/a2a-carbon-chat-adapter @carbon/ai-chat react react-dom

Basic Usage

import { A2AChat } from '@kuntur/a2a-carbon-chat-adapter';
import '@kuntur/a2a-carbon-chat-adapter/styles';
import '@carbon/ai-chat/dist/styles.css';

function App() {
  return (
    <A2AChat
      agentUrl="https://your-agent.example.com"
      agentName="My Agent"
      layout="sidebar"
    />
  );
}

⚠️ Important: This requires a server proxy to work. See Server Proxy Setup below.


⚠️ Server Proxy Setup (Required)

Why You Need a Server Proxy

Direct browser connections to A2A agents are blocked by CORS (Cross-Origin Resource Sharing) security policies. Browsers prevent JavaScript from making requests to different domains unless the server explicitly allows it.

Most A2A agents:

  • Run on different domains than your frontend
  • Don't configure CORS headers for browser access
  • Use streaming protocols that require special handling

Solution: Route agent requests through your backend server, which:

  1. Receives requests from your frontend (same origin = no CORS)
  2. Forwards them to the A2A agent (server-to-server = no CORS restrictions)
  3. Streams responses back to the frontend

Next.js Setup (Recommended)

Create an API route that proxies requests to your A2A agent:

// app/api/agent/chat/route.ts
import { createA2AHandler } from '@kuntur/a2a-carbon-chat-adapter/server';

export const POST = createA2AHandler({
  // Security: Only allow requests to trusted agent URLs
  allowedAgentUrls: [
    'https://your-agent.example.com',
    'https://another-agent.example.com',
  ],
  
  // Optional: Set request timeout (default: 120000ms)
  timeout: 120000,
});

// Required for streaming responses
export const runtime = 'nodejs';
export const dynamic = 'force-dynamic';

Then configure your frontend to use this proxy:

import { A2AChat } from '@kuntur/a2a-carbon-chat-adapter';

function App() {
  return (
    <A2AChat
      agentUrl="https://your-agent.example.com"
      agentName="My Agent"
      // Point to your proxy endpoint
      proxyUrl="/api/agent/chat"
    />
  );
}

Express.js Setup

import express from 'express';
import { createA2AHandler } from '@kuntur/a2a-carbon-chat-adapter/server';

const app = express();

// Create the handler
const handler = createA2AHandler({
  allowedAgentUrls: ['https://your-agent.example.com'],
});

// Mount as Express middleware
app.post('/api/agent/chat', async (req, res) => {
  const response = await handler(req);
  
  // Copy headers
  response.headers.forEach((value, key) => {
    res.setHeader(key, value);
  });
  
  // Stream response
  const reader = response.body?.getReader();
  if (reader) {
    while (true) {
      const { done, value } = await reader.read();
      if (done) break;
      res.write(value);
    }
  }
  res.end();
});

Other Frameworks

The createA2AHandler function returns a standard Web API Response object, making it compatible with:

  • Remix: Use in action functions
  • SvelteKit: Use in +server.ts endpoints
  • Astro: Use in API routes
  • Cloudflare Workers: Direct compatibility
  • Vercel Edge Functions: Direct compatibility

Example for any framework:

import { createA2AHandler } from '@kuntur/a2a-carbon-chat-adapter/server';

const handler = createA2AHandler({
  allowedAgentUrls: ['https://your-agent.example.com'],
});

// In your framework's request handler:
export async function POST(request: Request) {
  return handler(request);
}

Security Considerations

Always use allowedAgentUrls to prevent your proxy from being used to access arbitrary URLs:

createA2AHandler({
  // ✅ Good: Whitelist specific agents
  allowedAgentUrls: [
    'https://trusted-agent.example.com',
    'https://another-trusted-agent.example.com',
  ],
  
  // ❌ Bad: Allows any URL (security risk!)
  // allowedAgentUrls: undefined,
});

Core Concepts

A2A Protocol

The Agent2Agent (A2A) protocol is a standard for AI agent communication. Key features:

  • Streaming: Real-time response delivery via Server-Sent Events (SSE)
  • Context Management: Maintains conversation state across requests
  • UI Extensions: Agents can request UI components (forms, citations, etc.)
  • Task Management: Supports long-running tasks with progress updates

Extension System

A2A agents can send UI extension requests using special URIs:

agentstack://extension/{type}/{id}

This library automatically handles these extensions:

  • Citations: agentstack://extension/citation/* → Rendered inline with text
  • Forms: agentstack://extension/form/* → Interactive form components
  • Errors: agentstack://extension/error/* → Formatted error displays

Streaming Architecture

┌─────────────┐      ┌──────────────┐      ┌─────────────┐
│   Browser   │─────▶│ Your Server  │─────▶│  A2A Agent  │
│  (Frontend) │      │   (Proxy)    │      │             │
└─────────────┘      └──────────────┘      └─────────────┘
       │                     │                      │
       │  1. POST request    │                      │
       │────────────────────▶│                      │
       │                     │  2. Forward request  │
       │                     │─────────────────────▶│
       │                     │                      │
       │                     │  3. SSE stream       │
       │                     │◀─────────────────────│
       │  4. SSE stream      │                      │
       │◀────────────────────│                      │
       │                     │                      │

The library handles:

  1. Converting Carbon messages to A2A format
  2. Streaming A2A responses back to Carbon
  3. Parsing and rendering UI extensions
  4. Managing conversation context

API Reference

Components

<A2AChat>

Main chat component that connects to A2A agents.

import { A2AChat } from '@kuntur/a2a-carbon-chat-adapter';

<A2AChat
  // Agent Configuration (choose one)
  agent={{ id: 'agent', name: 'Agent', url: 'https://...' }}
  // OR
  agentId="agent" // Requires AgentProvider
  // OR
  agentUrl="https://..." // Simple URL-only mode
  agentName="Agent Name" // Optional display name

  // Server Proxy (required for CORS)
  proxyUrl="/api/agent/chat"

  // Display Options
  layout="fullscreen" // 'fullscreen' | 'sidebar' | 'float'
  className="my-chat"

  // Behavior
  showThinking={true}
  showChainOfThought={true}
  allowCancel={true}

  // Callbacks
  onSend={(message) => console.log('Sent:', message)}
  onResponse={(response) => console.log('Received:', response)}
  onError={(error) => console.error('Error:', error)}

  // Custom Renderers
  renderCitations={(citations, text) => <CustomCitations />}
  renderError={(error) => <CustomError />}
  renderForm={(form) => <CustomForm />}
/>

Props:

| Prop | Type | Required | Description | |------|------|----------|-------------| | agent | AgentConfig | No* | Full agent configuration object | | agentId | string | No* | Agent ID (requires AgentProvider) | | agentUrl | string | No* | Direct agent URL | | agentName | string | No | Display name for the agent | | proxyUrl | string | No | Server proxy endpoint (default: /api/agent/chat) | | layout | 'fullscreen' \| 'sidebar' \| 'float' | No | Chat layout style | | className | string | No | Additional CSS classes | | showThinking | boolean | No | Show agent thinking indicators | | showChainOfThought | boolean | No | Show reasoning steps | | allowCancel | boolean | No | Allow canceling streaming responses | | onSend | (message: string) => void | No | Callback when message is sent | | onResponse | (response: any) => void | No | Callback when response received | | onError | (error: Error) => void | No | Callback on error | | renderCitations | (citations, text) => ReactNode | No | Custom citation renderer | | renderError | (error) => ReactNode | No | Custom error renderer | | renderForm | (form) => ReactNode | No | Custom form renderer |

*One of agent, agentId, or agentUrl is required.

<AgentProvider>

Context provider for multi-agent applications.

import { AgentProvider } from '@kuntur/a2a-carbon-chat-adapter';

<AgentProvider
  agents={[
    { id: 'research', name: 'Research Agent', url: 'https://...' },
    { id: 'code', name: 'Code Agent', url: 'https://...' },
  ]}
  defaultAgentId="research"
  persistSelection={true}
  storageKey="my-app-agent"
>
  {children}
</AgentProvider>

Props:

| Prop | Type | Required | Description | |------|------|----------|-------------| | agents | AgentConfig[] | Yes | Array of agent configurations | | defaultAgentId | string | No | Initially selected agent ID | | persistSelection | boolean | No | Save selection to localStorage | | storageKey | string | No | localStorage key (default: selected-agent) |

<AgentSwitcher>

UI component for switching between agents.

import { AgentSwitcher } from '@kuntur/a2a-carbon-chat-adapter';

<AgentSwitcher
  agents={agents}
  currentAgentId={currentAgentId}
  onSelect={handleSelect}
  variant="dropdown" // 'dropdown' | 'tabs' | 'cards'
  showDescriptions={true}
  showIcons={true}
  className="my-switcher"
/>

Props:

| Prop | Type | Required | Description | |------|------|----------|-------------| | agents | AgentConfig[] | Yes | Available agents | | currentAgentId | string | No | Currently selected agent ID | | onSelect | (agentId: string) => void | Yes | Selection callback | | variant | 'dropdown' \| 'tabs' \| 'cards' | No | Display style | | showDescriptions | boolean | No | Show agent descriptions | | showIcons | boolean | No | Show agent icons | | className | string | No | Additional CSS classes |

Hooks

useA2AAgent

Programmatic agent interaction without UI components.

import { useA2AAgent } from '@kuntur/a2a-carbon-chat-adapter';

const {
  agent,           // Current agent config
  state,           // Connection state
  sendMessage,     // Send a message
  cancelStream,    // Cancel ongoing stream
  disconnect,      // Disconnect from agent
  isStreaming,     // Boolean: is currently streaming
  isConnected,     // Boolean: is connected
  error,           // Last error (if any)
} = useA2AAgent({
  agent: { id: 'agent', name: 'Agent', url: 'https://...' },
  proxyUrl: '/api/agent/chat',
  onMessage: (message) => console.log('Message:', message),
  onError: (error) => console.error('Error:', error),
  onStreamStart: () => console.log('Stream started'),
  onStreamEnd: () => console.log('Stream ended'),
});

// Send a message
await sendMessage('Hello, agent!');

// Cancel streaming
cancelStream();

// Disconnect
disconnect();

Options:

| Option | Type | Required | Description | |--------|------|----------|-------------| | agent | AgentConfig | Yes | Agent configuration | | proxyUrl | string | No | Server proxy endpoint | | onMessage | (message: any) => void | No | Message received callback | | onError | (error: Error) => void | No | Error callback | | onStreamStart | () => void | No | Stream start callback | | onStreamEnd | () => void | No | Stream end callback |

Returns:

| Property | Type | Description | |----------|------|-------------| | agent | AgentConfig | Current agent configuration | | state | AgentState | Connection state object | | sendMessage | (message: string) => Promise<void> | Send message function | | cancelStream | () => void | Cancel streaming function | | disconnect | () => void | Disconnect function | | isStreaming | boolean | Currently streaming | | isConnected | boolean | Currently connected | | error | Error \| null | Last error |

useMultiAgent

Manage multiple agents without AgentProvider.

import { useMultiAgent } from '@kuntur/a2a-carbon-chat-adapter';

const {
  agents,          // All registered agents
  currentAgent,    // Currently selected agent
  switchAgent,     // Switch to agent by ID
  registerAgent,   // Add new agent
  unregisterAgent, // Remove agent
} = useMultiAgent({
  agents: [
    { id: 'research', name: 'Research', url: 'https://...' },
    { id: 'code', name: 'Code', url: 'https://...' },
  ],
  defaultAgentId: 'research',
});

// Switch agents
switchAgent('code');

// Add new agent
registerAgent({ id: 'new', name: 'New Agent', url: 'https://...' });

// Remove agent
unregisterAgent('old');

useAgentContext

Access AgentProvider context (must be used within AgentProvider).

import { useAgentContext } from '@kuntur/a2a-carbon-chat-adapter';

const {
  currentAgent,  // Currently selected agent
  agents,        // All available agents
  selectAgent,   // Select agent by ID
  getAgent,      // Get agent by ID
  hasAgent,      // Check if agent exists
} = useAgentContext();

Server Utilities

createA2AHandler

Create a server-side API handler for proxying A2A requests.

import { createA2AHandler } from '@kuntur/a2a-carbon-chat-adapter/server';

const handler = createA2AHandler({
  allowedAgentUrls: ['https://trusted-agent.example.com'],
  timeout: 120000,
});

// Returns a function: (request: Request) => Promise<Response>

Options:

| Option | Type | Required | Description | |--------|------|----------|-------------| | allowedAgentUrls | string[] | No | Whitelist of allowed agent URLs (security) | | timeout | number | No | Request timeout in ms (default: 120000) |

Returns: (request: Request) => Promise<Response>

Renderers

Built-in components for rendering A2A UI extensions.

CitationRenderer

Renders text with inline citations.

import { CitationRenderer } from '@kuntur/a2a-carbon-chat-adapter';

<CitationRenderer
  text="This is cited text [1]."
  citations={[
    { id: '1', url: 'https://...', title: 'Source' }
  ]}
/>

ErrorRenderer

Renders formatted error messages.

import { ErrorRenderer } from '@kuntur/a2a-carbon-chat-adapter';

<ErrorRenderer
  error={{
    message: 'Something went wrong',
    code: 'ERROR_CODE',
    stack: '...',
  }}
  showStack={true}
/>

FormRenderer

Renders dynamic forms from agent requests.

import { FormRenderer } from '@kuntur/a2a-carbon-chat-adapter';

<FormRenderer
  form={{
    fields: [
      { name: 'email', type: 'email', label: 'Email', required: true },
      { name: 'message', type: 'textarea', label: 'Message' },
    ],
  }}
  onSubmit={(values) => console.log('Submitted:', values)}
/>

Types

import type {
  AgentConfig,
  AgentState,
  A2AChatProps,
  AgentProviderProps,
  AgentSwitcherProps,
  ExtensionResult,
  Citation,
  FormField,
  // ... more types
} from '@kuntur/a2a-carbon-chat-adapter';

Advanced Usage

Multi-Agent Setup

import {
  AgentProvider,
  A2AChat,
  AgentSwitcher,
  useAgentContext,
} from '@kuntur/a2a-carbon-chat-adapter';

const agents = [
  {
    id: 'research',
    name: 'Research Agent',
    url: 'https://research.example.com',
    description: 'Specialized in research and analysis',
  },
  {
    id: 'code',
    name: 'Code Agent',
    url: 'https://code.example.com',
    description: 'Specialized in code generation',
  },
];

function App() {
  return (
    <AgentProvider
      agents={agents}
      defaultAgentId="research"
      persistSelection={true}
    >
      <ChatWithSwitcher />
    </AgentProvider>
  );
}

function ChatWithSwitcher() {
  const { agents, currentAgent, selectAgent } = useAgentContext();

  return (
    <div className="app-container">
      <AgentSwitcher
        agents={agents}
        currentAgentId={currentAgent?.id}
        onSelect={selectAgent}
        variant="tabs"
        showDescriptions={true}
      />
      <A2AChat proxyUrl="/api/agent/chat" />
    </div>
  );
}

Custom Citation Renderer

import { A2AChat } from '@kuntur/a2a-carbon-chat-adapter';

function CustomCitationRenderer({ citations, text }) {
  return (
    <div className="custom-citations">
      <p>{text}</p>
      <ul>
        {citations.map((citation) => (
          <li key={citation.id}>
            <a href={citation.url} target="_blank" rel="noopener noreferrer">
              [{citation.id}] {citation.title}
            </a>
          </li>
        ))}
      </ul>
    </div>
  );
}

function App() {
  return (
    <A2AChat
      agentUrl="https://agent.example.com"
      renderCitations={(citations, text) => (
        <CustomCitationRenderer citations={citations} text={text} />
      )}
    />
  );
}

Programmatic Message Sending

import { useA2AAgent } from '@kuntur/a2a-carbon-chat-adapter';
import { useState } from 'react';

function CustomChat() {
  const [messages, setMessages] = useState([]);
  
  const { sendMessage, isStreaming, error } = useA2AAgent({
    agent: {
      id: 'agent',
      name: 'My Agent',
      url: 'https://agent.example.com',
    },
    proxyUrl: '/api/agent/chat',
    onMessage: (message) => {
      setMessages((prev) => [...prev, message]);
    },
  });

  const handleSend = async (text: string) => {
    setMessages((prev) => [...prev, { role: 'user', content: text }]);
    await sendMessage(text);
  };

  return (
    <div>
      <div className="messages">
        {messages.map((msg, i) => (
          <div key={i} className={`message ${msg.role}`}>
            {msg.content}
          </div>
        ))}
      </div>
      {error && <div className="error">{error.message}</div>}
      <input
        type="text"
        onKeyPress={(e) => {
          if (e.key === 'Enter' && !isStreaming) {
            handleSend(e.currentTarget.value);
            e.currentTarget.value = '';
          }
        }}
        disabled={isStreaming}
      />
    </div>
  );
}

Extension URI Handling

The library automatically handles A2A extension URIs:

// Agent sends extension URI in response
{
  "content": "agentstack://extension/citation/abc123",
  "metadata": {
    "extensions": {
      "abc123": {
        "type": "citation",
        "data": {
          "url": "https://example.com",
          "title": "Example Source"
        }
      }
    }
  }
}

// Library automatically:
// 1. Detects extension URI
// 2. Looks up extension data in metadata
// 3. Renders using CitationRenderer
// 4. Replaces URI with rendered component

Error Handling

import { A2AChat } from '@kuntur/a2a-carbon-chat-adapter';

function App() {
  const handleError = (error: Error) => {
    // Log to error tracking service
    console.error('Agent error:', error);
    
    // Show user-friendly message
    if (error.message.includes('timeout')) {
      alert('The agent is taking too long to respond. Please try again.');
    } else if (error.message.includes('network')) {
      alert('Network error. Please check your connection.');
    } else {
      alert('An error occurred. Please try again.');
    }
  };

  return (
    <A2AChat
      agentUrl="https://agent.example.com"
      onError={handleError}
    />
  );
}

Examples

For complete working examples, see the examples repository (coming soon).

Example applications include:

  • Basic Chat: Simple single-agent chat interface
  • Multi-Agent: Agent switching with tabs and dropdown
  • Custom Renderers: Custom citation and form renderers
  • Programmatic API: Using hooks without UI components
  • Next.js Integration: Full Next.js app with API routes
  • Express.js Integration: Express server with proxy setup

Architecture

Protocol Flow

┌─────────────────────────────────────────────────────────────────┐
│                         Browser (Frontend)                       │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  ┌──────────────┐         ┌─────────────────────────────────┐  │
│  │   A2AChat    │────────▶│  useA2AAgent Hook               │  │
│  │  Component   │         │  - Manages connection state     │  │
│  └──────────────┘         │  - Handles streaming            │  │
│         │                 │  - Parses extensions            │  │
│         │                 └─────────────────────────────────┘  │
│         │                              │                        │
│         ▼                              ▼                        │
│  ┌──────────────┐         ┌─────────────────────────────────┐  │
│  │  Renderers   │         │  A2AToCarbonTranslator          │  │
│  │  - Citation  │         │  - Converts A2A → Carbon format │  │
│  │  - Error     │         │  - Handles metadata transform   │  │
│  │  - Form      │         └─────────────────────────────────┘  │
│  └──────────────┘                      │                        │
│                                        │                        │
└────────────────────────────────────────┼────────────────────────┘
                                         │
                                         │ POST /api/agent/chat
                                         │
┌────────────────────────────────────────┼────────────────────────┐
│                    Your Server (Proxy)  │                        │
├────────────────────────────────────────┼────────────────────────┤
│                                        │                        │
│                          ┌─────────────▼──────────────┐         │
│                          │  createA2AHandler          │         │
│                          │  - Validates agent URL     │         │
│                          │  - Forwards request        │         │
│                          │  - Streams response        │         │
│                          └─────────────┬──────────────┘         │
│                                        │                        │
└────────────────────────────────────────┼────────────────────────┘
                                         │
                                         │ POST /chat
                                         │
┌────────────────────────────────────────┼────────────────────────┐
│                      A2A Agent          │                        │
├────────────────────────────────────────┼────────────────────────┤
│                                        │                        │
│                          ┌─────────────▼──────────────┐         │
│                          │  A2A Protocol Handler      │         │
│                          │  - Processes message       │         │
│                          │  - Generates response      │         │
│                          │  - Streams via SSE         │         │
│                          └────────────────────────────┘         │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘

Package Structure

@kuntur/a2a-carbon-chat-adapter/
├── dist/                    # Built output
│   ├── index.js            # ESM main entry
│   ├── index.cjs           # CJS main entry
│   ├── index.d.ts          # TypeScript definitions
│   ├── server.js           # ESM server utilities
│   ├── server.cjs          # CJS server utilities
│   ├── server.d.ts         # Server TypeScript definitions
│   └── styles/
│       └── index.css       # Component styles
├── src/
│   ├── components/         # React components
│   │   ├── A2AChat.tsx
│   │   ├── AgentProvider.tsx
│   │   ├── AgentSwitcher.tsx
│   │   └── renderers/      # Extension renderers
│   ├── hooks/              # React hooks
│   │   ├── useA2AAgent.ts
│   │   ├── useMultiAgent.ts
│   │   └── useAgentContext.ts
│   ├── lib/
│   │   ├── a2a/            # A2A protocol client
│   │   └── translator/     # Protocol translation
│   ├── server/             # Server utilities
│   │   └── create-api-handler.ts
│   └── types/              # TypeScript types
└── package.json

Troubleshooting

CORS Errors

Problem: Browser console shows CORS errors when connecting to agent.

Solution: You must use a server proxy. See Server Proxy Setup.

❌ Access to fetch at 'https://agent.example.com' from origin 'http://localhost:3000'
   has been blocked by CORS policy

Streaming Not Working

Problem: Messages don't stream; they appear all at once.

Solution: Ensure your server proxy is configured correctly:

// Next.js: Add these exports
export const runtime = 'nodejs';
export const dynamic = 'force-dynamic';

// Express: Ensure streaming is not buffered
app.use(express.json({ limit: '50mb' }));

Extension URIs Not Rendering

Problem: Extension URIs appear as plain text instead of rendered components.

Solution: Ensure the agent includes extension metadata:

{
  "content": "See citation [1]",
  "metadata": {
    "extensions": {
      "1": {
        "type": "citation",
        "data": {
          "url": "https://example.com",
          "title": "Example"
        }
      }
    }
  }
}

TypeScript Errors

Problem: TypeScript can't find module declarations.

Solution: Ensure @carbon/ai-chat is installed and types are available:

npm install @carbon/ai-chat @types/react @types/react-dom

Agent Not Responding

Problem: No response from agent after sending message.

Checklist:

  1. ✅ Server proxy is running and accessible
  2. ✅ Agent URL is correct and agent is running
  3. ✅ Agent URL is in allowedAgentUrls (if configured)
  4. ✅ Network tab shows request reaching /api/agent/chat
  5. ✅ Check server logs for errors

Performance Issues

Problem: Chat feels slow or laggy.

Solutions:

  • Reduce timeout in createA2AHandler for faster failures
  • Implement message pagination for long conversations
  • Use showThinking={false} to reduce re-renders
  • Memoize custom renderers with React.memo()

Contributing

Contributions are welcome! Please follow these guidelines:

Development Setup

# Clone repository
git clone https://github.com/Xnvargas/a2a-carbon-chat-adapter.git
cd a2a-carbon-chat-adapter

# Install dependencies
npm install

# Run development build (watch mode)
npm run dev

# Run type checking
npm run typecheck

# Run linting
npm run lint

# Build for production
npm run build

Submitting Changes

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes
  4. Add tests if applicable
  5. Run npm run typecheck and npm run lint
  6. Commit your changes (git commit -m 'Add amazing feature')
  7. Push to the branch (git push origin feature/amazing-feature)
  8. Open a Pull Request

Code Style

  • Use TypeScript for all new code
  • Follow existing code formatting (ESLint config)
  • Add JSDoc comments for public APIs
  • Update documentation for user-facing changes

Reporting Issues

When reporting issues, please include:

  • Library version (npm list @kuntur/a2a-carbon-chat-adapter)
  • Framework and version (Next.js, React, etc.)
  • Minimal reproduction code
  • Expected vs actual behavior
  • Browser console errors (if applicable)

License

MIT © Xavier Vargas

See LICENSE file for details.


Links

  • GitHub: https://github.com/Xnvargas/a2a-carbon-chat-adapter
  • npm: https://www.npmjs.com/package/@kuntur/a2a-carbon-chat-adapter
  • Issues: https://github.com/Xnvargas/a2a-carbon-chat-adapter/issues
  • Examples: https://github.com/Xnvargas/a2a-carbon-chat-adapter-examples (coming soon)
  • A2A Protocol: https://github.com/agentstack/a2a
  • Carbon AI Chat: https://github.com/carbon-design-system/carbon-for-ai

Made with ❤️ for the AI agent community