A2A JavaScript SDK: Build Agents in Node.js with TypeScript Support
@a2a-js/sdk) lets you build A2A-compliant agent servers and clients in Node.js with full TypeScript support. The SDK handles Agent Card serving, JSON-RPC parsing, task state management, and SSE streaming so you can focus on your agent's actual capabilities. This guide walks through building a complete server and client from scratch, covering project setup, Agent Card definition, task handling, and streaming.
The JavaScript SDK targets Node.js environments and provides TypeScript type definitions out of the box. If you are building agents for browser-based clients, the SDK's client classes work in browser environments as well, though server functionality requires Node.js. The SDK follows the same design patterns as the Python SDK but uses idiomatic JavaScript conventions: Promises for async operations, EventEmitter patterns for streaming, and standard npm package management.
Step 1: Set Up the Project
Start by creating a new Node.js project and installing the SDK. If you are using TypeScript (recommended for the type safety it provides with the SDK's extensive type definitions), install TypeScript and its Node.js type definitions as development dependencies.
Initialize your project with npm init, then install the SDK with npm install @a2a-js/sdk. For TypeScript projects, also install typescript and @types/node as dev dependencies. Configure your tsconfig.json with target set to ES2020 or later (the SDK uses modern JavaScript features), module set to NodeNext for ESM support, and strict enabled to get the full benefit of the SDK's type definitions.
The SDK has minimal external dependencies. It uses the built-in Node.js HTTP module for server functionality and the Fetch API (available natively in Node.js 18+) for client requests. No additional web framework is required for basic A2A server functionality, though the SDK can integrate with Express, Fastify, or other frameworks if your agent needs additional HTTP endpoints beyond A2A.
Step 2: Define the Agent Card
The Agent Card describes your agent to the world. Build it programmatically using the SDK's type-safe builder or define it as a plain object conforming to the AgentCard interface. The card must include your agent's name, description, endpoint URL, and at least one skill.
The name and description fields should be written for both human readers and LLMs that will evaluate your agent's capabilities. Be specific about what your agent does well and what it does not handle. A description like "Analyzes JavaScript and TypeScript code for performance bottlenecks, memory leaks, and common antipatterns" is far more useful to a client agent deciding whether to delegate than "Does code analysis."
Each skill in the skills array needs an id (a unique machine-readable identifier like "code-review" or "perf-analysis"), a name (human-readable), and a description (detailed enough for an LLM to understand when and how to invoke this skill). Optional inputSchema and outputSchema fields accept JSON Schema objects that define the expected structure of skill inputs and outputs. Including schemas significantly reduces integration errors because client agents can validate their requests before sending.
The supportedInputModes and supportedOutputModes fields declare what content types your agent handles. Most agents support at least text input and text output. Agents that process files should declare the relevant MIME types. Agents that return structured data should include "application/json" in their output modes.
Step 3: Implement the Task Handler
The task handler is an async function that receives an incoming task and produces the response. The SDK calls your handler whenever a client submits a task via tasks/send or tasks/sendSubscribe. Your handler receives the task context (including the task ID, the client's message, and any task history for continued conversations) and returns the result.
The handler function receives a context object with the incoming message parts, task metadata, and helper methods for building responses. For a simple synchronous handler, you read the input from the message parts, process it with your agent's logic (call an LLM, run analysis, query a database), and return an object containing the response message and any artifacts.
For tasks that need client input, the handler can yield an input-required state by returning a response with the appropriate status and a message explaining what information is needed. When the client responds, the SDK calls your handler again with the updated task context containing the new message. Your handler checks the conversation history to understand the context and continues processing.
Error handling in the handler follows standard JavaScript conventions. If your handler throws an exception, the SDK catches it and transitions the task to the failed state with the error message. For expected error conditions (invalid input format, unsupported parameters), return a structured error response rather than throwing, so you can provide a clear, actionable error message to the client agent.
Step 4: Start the A2A Server
With the Agent Card and task handler defined, create the A2A server instance and start it. The SDK provides a server class that takes your Agent Card and handler, configures the HTTP routes (the A2A endpoint for JSON-RPC requests and the well-known URL for Agent Card serving), and starts listening on the specified port.
The server automatically handles Agent Card requests at /.well-known/agent.json, returning your card as JSON with the correct content type headers. It handles A2A JSON-RPC requests at the endpoint URL specified in your Agent Card, parsing incoming requests, validating method names, dispatching to your handler, and formatting responses. It manages task state internally, tracking active tasks and their lifecycle states.
For production deployments, you can configure the server with additional options: TLS certificates for HTTPS, CORS headers for browser-based clients, request size limits to prevent abuse, and authentication middleware that validates credentials before your handler sees the request. The SDK's middleware system lets you add custom request processing stages without modifying the core A2A handling.
If you need to integrate A2A into an existing Express or Fastify application rather than running a standalone server, the SDK provides middleware functions that handle A2A routes within your existing HTTP framework. This lets you add A2A capabilities to an existing service without changing its deployment model.
Step 5: Build a Client
The SDK's client class handles the complete client-side workflow: fetching Agent Cards, submitting tasks, polling for results, and processing responses. Create a client instance, point it at a remote agent's URL, and start submitting tasks.
The client begins by fetching the Agent Card from the target agent's well-known URL. The card is parsed and validated automatically, giving you typed access to the agent's skills, supported modes, and authentication requirements. You can inspect the skills programmatically to determine which skill matches your task before submitting.
To submit a task, construct a message with one or more parts (text, data, or file), generate a task ID, and call the client's send method. The method returns a Promise that resolves with the completed task, including its status, messages, and artifacts. For simple request-response interactions, this is all you need.
The client handles authentication automatically if you provide credentials. For OAuth, pass the client ID, client secret, and token endpoint URL when creating the client. The client acquires tokens, includes them in requests, and refreshes them when they expire. For API key authentication, pass the key and header name. The authentication configuration matches the patterns declared in the Agent Card.
Step 6: Add Streaming Support
For real-time task updates, use the client's sendSubscribe method instead of send. This opens an SSE connection to the server and delivers events as the task progresses. The SDK exposes streaming through an async iterator pattern that works naturally with JavaScript's for-await-of syntax.
Each event from the stream is a typed object, either a TaskStatusUpdateEvent (containing the new status and optional message) or a TaskArtifactUpdateEvent (containing new artifact data). Your client code processes each event as it arrives, updating progress displays, accumulating artifact parts, or handling input-required states by sending follow-up messages.
On the server side, streaming support requires your handler to yield events rather than returning a single result. The SDK provides a streaming context with methods for emitting status updates and artifact parts. Your handler calls these methods as it progresses through its work, and the SDK translates them into properly formatted SSE events on the HTTP response. The streaming lifecycle (connection management, event formatting, clean shutdown on task completion) is handled by the SDK.
Error recovery during streaming uses the SDK's built-in reconnection logic. If the SSE connection drops, the client automatically attempts to reconnect and requests missed events using the Last-Event-ID header. If reconnection fails, the client falls back to polling with tasks/get. You can configure retry intervals, maximum retry counts, and fallback behavior through the client's options.
Integration with Existing Node.js Applications
Most real-world A2A agents do not exist in isolation. They integrate with databases, message queues, external APIs, and existing application logic. The JavaScript SDK is designed to fit into existing Node.js architectures rather than replacing them.
For Express applications, the SDK's Express middleware adds A2A routes to your existing router. Your agent's task handler can access the same database connections, service clients, and configuration that the rest of your application uses. The middleware handles A2A protocol details while your handler focuses on business logic using your existing infrastructure.
For serverless deployments on AWS Lambda, Google Cloud Functions, or Vercel, the SDK provides request handler functions that can be exported directly as the Lambda handler or Cloud Function entry point. The Agent Card is served from the same function, and task state can be stored in DynamoDB, Firestore, or another external store since serverless functions do not maintain in-memory state between invocations.
For containerized deployments, the standalone server works out of the box with Docker. The server binds to a configurable port, responds to health checks on a standard path, and logs in JSON format for compatibility with container orchestration platforms. Horizontal scaling works naturally because the HTTP-based protocol is stateless at the transport level, though task state management requires a shared store when running multiple server instances.
TypeScript Patterns and Type Safety
The SDK's TypeScript definitions cover every A2A protocol type: AgentCard, Task, Message, TextPart, DataPart, FilePart, Artifact, and all event types. Using TypeScript catches protocol errors at compile time rather than runtime, which is particularly valuable for the Agent Card and handler definitions where small mistakes (a missing required field, an incorrect type) cause silent failures that are hard to debug at runtime.
Generic type parameters let you type your skill inputs and outputs precisely. If your agent's skill accepts a specific JSON structure as input, you can define a TypeScript interface for that structure and use it as a type parameter on the handler. The SDK then validates that your handler processes the correct input type and produces the expected output type, catching schema mismatches before deployment.
The client SDK uses conditional types to provide different return types based on how you call methods. The send method returns a fully resolved task, while sendSubscribe returns an async iterable of events. Error handling types distinguish between protocol errors (JSON-RPC errors) and task failures (tasks that reached the failed state), letting your error handling code be specific about what went wrong.
Testing A2A Agents
The SDK includes test utilities for validating your agent's behavior without making real HTTP connections. The test client sends tasks to your handler function directly, bypassing the HTTP layer. This lets you write fast, deterministic unit tests for your task handling logic.
For integration testing, the SDK can spin up a local server on an ephemeral port, letting your tests make real HTTP requests without conflicting with other services. The test server starts and stops within the test lifecycle, and the SDK provides utilities for asserting on task states, message content, and artifact structure.
Agent Card validation is available as a standalone function that checks your card against the A2A specification. Running this validation in your CI pipeline catches configuration errors (missing required fields, invalid schema references, malformed URLs) before deployment.
The A2A JavaScript SDK provides everything needed to build agents in Node.js: type-safe Agent Card definitions, async task handlers, automatic Agent Card serving, SSE streaming, and client classes for discovering and communicating with remote agents. Install with npm install @a2a-js/sdk and start building with TypeScript for the best development experience.