Skip to content

drew-foxall/a2a-js-sdk

Β 
Β 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

178 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

A2A JavaScript SDK with Edge Support

npm version License Upstream

A2A Logo

A JavaScript SDK for building Agent2Agent (A2A) Protocol servers with multi-framework and edge runtime support.

Fork of a2aproject/a2a-js β€” Tracks upstream v0.3.6 with added multi-framework and edge runtime support.

✨ Features

  • 🎯 Multi-Framework: Express, Hono, Elysia, itty-router, Fresh, and Web Standard
  • ⚑ Edge Runtime Native: Cloudflare Workers, Deno, Bun β€” no compatibility layers needed
  • 🌐 Universal JavaScript: Built on web-standard APIs (EventTarget, Request/Response)
  • πŸš€ SSE Streaming: Full Server-Sent Events support across all frameworks
  • πŸ”Œ Pluggable Logger: Console, JSON, or custom logging implementations
  • πŸ“¦ Modular Architecture: Import only what you need from server/core
  • πŸ”„ Full A2A Protocol: Complete implementation of the Agent2Agent specification

Installation

npm install @drew-foxall/a2a-js-sdk
# or
pnpm add @drew-foxall/a2a-js-sdk
# or
yarn add @drew-foxall/a2a-js-sdk

Peer Dependencies

Install the framework you want to use:

# For Express (Node.js)
npm install express

# For Hono (Edge/Serverless)
npm install hono

# For Hono on Node.js (development)
npm install hono @hono/node-server

Quick Start

The examples below show the same "Hello Agent" implemented for different environments.

Shared Agent Logic

First, define your agent card and executor (shared across all implementations):

// shared/agent.ts
import { v4 as uuidv4 } from 'uuid';
import type { AgentCard, Message } from '@drew-foxall/a2a-js-sdk';
import {
  AgentExecutor,
  RequestContext,
  ExecutionEventBus,
} from '@drew-foxall/a2a-js-sdk/server';

export const helloAgentCard: AgentCard = {
  name: 'Hello Agent',
  description: 'A simple agent that says hello.',
  protocolVersion: '0.3.0',
  version: '0.1.0',
  url: 'http://localhost:4000/',
  skills: [{ id: 'chat', name: 'Chat', description: 'Say hello', tags: ['chat'] }],
  capabilities: { pushNotifications: false },
  defaultInputModes: ['text'],
  defaultOutputModes: ['text'],
};

export class HelloExecutor implements AgentExecutor {
  async execute(ctx: RequestContext, eventBus: ExecutionEventBus): Promise<void> {
    const response: Message = {
      kind: 'message',
      messageId: uuidv4(),
      role: 'agent',
      parts: [{ kind: 'text', text: 'Hello, world!' }],
      contextId: ctx.contextId,
    };
    eventBus.publish(response);
    eventBus.finished();
  }
  
  cancelTask = async (): Promise<void> => {};
}

Express Server (Node.js - Original)

The original Express implementation from upstream. Best for traditional Node.js servers.

// server-express.ts
import express from 'express';
import {
  DefaultRequestHandler,
  InMemoryTaskStore,
} from '@drew-foxall/a2a-js-sdk/server';
import { A2AExpressApp } from '@drew-foxall/a2a-js-sdk/server/express';
import { helloAgentCard, HelloExecutor } from './shared/agent';

const requestHandler = new DefaultRequestHandler(
  helloAgentCard,
  new InMemoryTaskStore(),
  new HelloExecutor()
);

const app = express();
const a2aApp = new A2AExpressApp(requestHandler);

// Setup routes with optional base path and middleware
a2aApp.setupRoutes(app, '/a2a', [/* middlewares */]);

app.listen(4000, () => {
  console.log('πŸš€ Express A2A server running on http://localhost:4000');
});

Options:

// With custom user extractor for authentication
const a2aApp = new A2AExpressApp(requestHandler, async (req) => {
  // Extract user from request (e.g., from JWT token)
  return req.user ?? new UnauthenticatedUser();
});

// Setup with REST API enabled (in addition to JSON-RPC)
a2aApp.setupRoutes(app, '/a2a', [], '.well-known/agent-card.json');

Hono Server (Edge/Serverless)

Best for Cloudflare Workers, Vercel Edge Functions, Deno Deploy, and other edge environments.

// worker.ts - Cloudflare Workers / Edge Runtime
import { Hono } from 'hono';
import {
  DefaultRequestHandler,
  InMemoryTaskStore,
} from '@drew-foxall/a2a-js-sdk/server';
import { A2AHonoApp } from '@drew-foxall/a2a-js-sdk/server/hono';
import { helloAgentCard, HelloExecutor } from './shared/agent';

const requestHandler = new DefaultRequestHandler(
  helloAgentCard,
  new InMemoryTaskStore(),
  new HelloExecutor()
);

const app = new Hono();
const a2aApp = new A2AHonoApp(requestHandler, {
  enableRest: true,           // Enable REST API endpoints
  logger: ConsoleLogger.create(),
});
a2aApp.setupRoutes(app);

export default app;

With Authentication:

import { A2AHonoApp, UserBuilder } from '@drew-foxall/a2a-js-sdk/server/hono';

const userBuilder: UserBuilder = async (request) => {
  const token = request.headers.get('Authorization')?.replace('Bearer ', '');
  if (token) {
    const user = await validateToken(token);
    return user;
  }
  return new UnauthenticatedUser();
};

const a2aApp = new A2AHonoApp(requestHandler, { userBuilder });

Deploy to Cloudflare Workers:

# wrangler.toml - No nodejs_compat needed!
name = "a2a-hello-agent"
main = "worker.ts"
compatibility_date = "2024-01-01"
wrangler deploy

Elysia Server (Bun)

Best for Bun-native applications with excellent TypeScript support.

// server-elysia.ts
import { Elysia } from 'elysia';
import { DefaultRequestHandler, InMemoryTaskStore } from '@drew-foxall/a2a-js-sdk/server';
import { A2AElysiaApp } from '@drew-foxall/a2a-js-sdk/server/elysia';
import { helloAgentCard, HelloExecutor } from './shared/agent';

const requestHandler = new DefaultRequestHandler(
  helloAgentCard,
  new InMemoryTaskStore(),
  new HelloExecutor()
);

const a2aApp = new A2AElysiaApp(requestHandler, { enableRest: true });
const routes = a2aApp.getRoutes('/a2a');

const app = new Elysia();
routes.forEach(route => {
  app[route.method](route.path, route.handler);
});

app.listen(4000);

itty-router (Cloudflare Workers - Lightweight)

Best for minimal Cloudflare Workers with the smallest bundle size.

// worker.ts
import { Router } from 'itty-router';
import { DefaultRequestHandler, InMemoryTaskStore } from '@drew-foxall/a2a-js-sdk/server';
import { A2AIttyRouterApp } from '@drew-foxall/a2a-js-sdk/server/itty-router';
import { helloAgentCard, HelloExecutor } from './shared/agent';

const requestHandler = new DefaultRequestHandler(
  helloAgentCard,
  new InMemoryTaskStore(),
  new HelloExecutor()
);

const a2aApp = new A2AIttyRouterApp(requestHandler, { enableRest: true });
const routes = a2aApp.getRoutes('/a2a');

const router = Router();
routes.forEach(route => {
  router[route.method.toLowerCase()](route.pattern, route.handler);
});

export default { fetch: router.handle };

Fresh (Deno)

Best for Deno's web framework with file-based routing.

// routes/a2a/[...path].ts
import { DefaultRequestHandler, InMemoryTaskStore } from '@drew-foxall/a2a-js-sdk/server';
import { A2AFreshApp } from '@drew-foxall/a2a-js-sdk/server/fresh';
import { helloAgentCard, HelloExecutor } from '../../shared/agent.ts';

const requestHandler = new DefaultRequestHandler(
  helloAgentCard,
  new InMemoryTaskStore(),
  new HelloExecutor()
);

const a2aApp = new A2AFreshApp(requestHandler, { enableRest: true });

export const handler = a2aApp.createHandlers('/a2a');

Client

The client works with any A2A server implementation:

import { A2AClient } from '@drew-foxall/a2a-js-sdk/client';
import { v4 as uuidv4 } from 'uuid';

const client = await A2AClient.fromCardUrl('http://localhost:4000/.well-known/agent-card.json');

const response = await client.sendMessage({
  message: {
    messageId: uuidv4(),
    role: 'user',
    parts: [{ kind: 'text', text: 'Hi there!' }],
    kind: 'message',
  },
});

console.log('Response:', response);

πŸ—οΈ Architecture

This SDK uses a layered architecture separating framework-agnostic logic from framework-specific implementations:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                       Transport Layer                           β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”‚
β”‚   β”‚ JsonRpcTransportHandlerβ”‚    β”‚ RestTransportHandler β”‚        β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β”‚
β”‚              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                    β”‚
β”‚                              β–Ό                                  β”‚
β”‚             β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  β”‚
β”‚             β”‚      A2ARequestHandler         β”‚                  β”‚
β”‚             β”‚   (Framework-Agnostic Logic)   β”‚                  β”‚
β”‚             β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                               β”‚
            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
            β–Ό                  β–Ό                  β–Ό
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  server/core  β”‚  β”‚ server/hono   β”‚  β”‚server/express β”‚
    β”‚(Web Standard) β”‚  β”‚ server/elysia β”‚  β”‚  (Original)   β”‚
    β”‚               β”‚  β”‚    etc...     β”‚  β”‚               β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Import Paths

// Core utilities (logging, routes, streaming)
import { 
  ConsoleLogger, JsonLogger, NoopLogger,
  HTTP_STATUS, REST_ROUTES, AGENT_CARD_ROUTE,
  processStream, createSSEEvent,
} from '@drew-foxall/a2a-js-sdk/server/core';

// Framework implementations
import { A2AExpressApp } from '@drew-foxall/a2a-js-sdk/server/express';           // Original Express
import { A2AExpressApp } from '@drew-foxall/a2a-js-sdk/server/express-adapter';   // Core-based Express
import { A2AHonoApp } from '@drew-foxall/a2a-js-sdk/server/hono';
import { A2AElysiaApp } from '@drew-foxall/a2a-js-sdk/server/elysia';
import { A2AIttyRouterApp } from '@drew-foxall/a2a-js-sdk/server/itty-router';
import { A2AFreshApp } from '@drew-foxall/a2a-js-sdk/server/fresh';
import { A2AWebStandardApp } from '@drew-foxall/a2a-js-sdk/server/web-standard';

Available Frameworks

Framework Import Path Best For
Express (Original) server/express Node.js servers (reference implementation)
Express (Core-based) server/express-adapter Parity testing with edge implementations
Hono server/hono Cloudflare Workers, Deno, Bun
Elysia server/elysia Bun-native with excellent TypeScript
itty-router server/itty-router Lightweight Cloudflare Workers
Fresh server/fresh Deno's web framework
Web Standard server/web-standard Any runtime with Request/Response

⚑ Edge Runtime Support

This SDK uses web-standard APIs, making it compatible with all modern JavaScript runtimes:

Runtime Status Notes
Cloudflare Workers βœ… Native No nodejs_compat needed
Vercel Edge Functions βœ… Native Full support
Deno Deploy βœ… Native No npm shims required
Bun βœ… Native Full web API support
Node.js 15+ βœ… Native EventTarget built-in
Browsers βœ… Native Universal JavaScript

Why Edge?

Traditional (Express) Edge (Hono)
Runs on dedicated servers Runs at the edge, close to users
Cold starts in seconds Cold starts in milliseconds
Requires nodejs_compat on CF Workers Native edge runtime support
Full Node.js API access Web-standard APIs only
Best for complex backends Best for low-latency agents

πŸ“š Core Features

Streaming (SSE)

// Server: Publish events
eventBus.publish({ kind: 'status-update', taskId, status: { state: 'working' }, final: false });
eventBus.publish({ kind: 'artifact-update', taskId, artifact: { ... } });
eventBus.publish({ kind: 'status-update', taskId, status: { state: 'completed' }, final: true });
eventBus.finished();

// Client: Consume stream
const stream = client.sendMessageStream(params);
for await (const event of stream) {
  console.log(event.kind, event);
}

Middleware Support

All framework implementations support middleware:

// Express
a2aApp.setupRoutes(app, '/a2a', [authMiddleware, loggingMiddleware]);

// Hono
a2aApp.setupRoutes(app, '/a2a', [authMiddleware, loggingMiddleware]);

Push Notifications

For long-running tasks, configure push notifications:

const sendParams = {
  message: { ... },
  configuration: {
    pushNotificationConfig: {
      url: 'https://my-app.com/webhook',
      token: 'auth-token',
    },
  },
};

Custom Logging

import { ConsoleLogger, JsonLogger, NoopLogger } from '@drew-foxall/a2a-js-sdk/server/core';

// Human-readable for development
const devLogger = ConsoleLogger.create('debug');

// Structured JSON for production
const prodLogger = JsonLogger.create();

// Silent for testing
const testLogger = NoopLogger.create();

πŸ”„ Upstream Tracking

This fork tracks the official a2aproject/a2a-js repository:

This Fork Upstream
v0.4.0 v0.3.6

Staying in Sync

git remote add upstream https://github.com/a2aproject/a2a-js.git
git fetch upstream
git merge upstream/main

πŸ”— Related

License

Apache 2.0

Contributing

Contributions are welcome! Please open an issue or pull request.

  • Edge/Framework improvements: Submit PRs to this repository
  • A2A Protocol issues: Report to the official repository

About

Hono Compatible JavaScript SDK for the Agent2Agent (A2A) Protocol

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages

  • TypeScript 98.2%
  • JavaScript 1.8%