### Install Project Dependencies Source: https://github.com/a2aproject/a2a-js/blob/main/src/grpc/README.md Run this command to install the necessary project dependencies, including @bufbuild/buf. ```bash npm install ``` -------------------------------- ### Install A2A SDK Source: https://github.com/a2aproject/a2a-js/blob/main/README.md Install the A2A SDK using npm. This is the base installation for all transports. ```bash npm install @a2a-js/sdk ``` -------------------------------- ### Run Sample Extension Agent Source: https://github.com/a2aproject/a2a-js/blob/main/src/samples/extensions/README.md Execute this command in your terminal to start the sample agent that supports extensions. The agent will be accessible at http://localhost:41241. ```bash npm run agents:extension-agent ``` -------------------------------- ### Install gRPC Dependencies Source: https://github.com/a2aproject/a2a-js/blob/main/README.md For gRPC transport, install the required peer dependencies: @grpc/grpc-js and @bufbuild/protobuf. ```bash npm install @grpc/grpc-js @bufbuild/protobuf ``` -------------------------------- ### Install Express for Server Usage Source: https://github.com/a2aproject/a2a-js/blob/main/README.md If using the Express integration for A2A servers, install Express as it is a peer dependency. ```bash npm install express ``` -------------------------------- ### Run Authentication Agent Source: https://github.com/a2aproject/a2a-js/blob/main/src/samples/authentication/README.md Execute this command in your terminal to start the authentication agent. The agent will be accessible at http://localhost:41241. ```bash npm run agents:authentication-agent ``` -------------------------------- ### Run the Movie Info Agent Source: https://github.com/a2aproject/a2a-js/blob/main/src/samples/agents/movie-agent/README.md After setting the environment variables, use this command to start the Movie Info Agent. The agent will be accessible at http://localhost:41241. ```bash npm run agents:movie-agent ``` -------------------------------- ### Install Latest Alpha Version Source: https://github.com/a2aproject/a2a-js/blob/main/README.md Install the latest alpha version of the A2A SDK, which supports v1.0 of the A2A Protocol. This is useful for testing upcoming features. ```bash npm install @a2a-js/sdk@next ``` -------------------------------- ### Set up Hello World Agent Server Source: https://github.com/a2aproject/a2a-js/blob/main/README.md This snippet demonstrates how to define an agent's identity card, implement its execution logic, and set up both HTTP and gRPC servers. ```typescript // server.ts import express from 'express'; import { Server, ServerCredentials } from '@grpc/grpc-js'; import { v4 as uuidv4 } from 'uuid'; import { AgentCard, Message, AGENT_CARD_PATH } from '@a2a-js/sdk'; import { AgentExecutor, RequestContext, ExecutionEventBus, DefaultRequestHandler, InMemoryTaskStore, } from '@a2a-js/sdk/server'; import { agentCardHandler, jsonRpcHandler, restHandler, UserBuilder } from '@a2a-js/sdk/server/express'; import { grpcService, A2AService } from '@a2a-js/sdk/server/grpc'; // 1. Define your agent's identity card. 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/a2a/jsonrpc', // The public URL of your agent server skills: [{ id: 'chat', name: 'Chat', description: 'Say hello', tags: ['chat'] }], capabilities: { pushNotifications: false, }, defaultInputModes: ['text'], defaultOutputModes: ['text'], additionalInterfaces: [ { url: 'http://localhost:4000/a2a/jsonrpc', transport: 'JSONRPC' }, // Default JSON-RPC transport { url: 'http://localhost:4000/a2a/rest', transport: 'HTTP+JSON' }, // HTTP+JSON/REST transport { url: 'localhost:4001', transport: 'GRPC' }, // GRPC transport ], }; // 2. Implement the agent's logic. class HelloExecutor implements AgentExecutor { async execute(requestContext: RequestContext, eventBus: ExecutionEventBus): Promise { // Create a direct message response. const responseMessage: Message = { kind: 'message', messageId: uuidv4(), role: 'agent', parts: [{ kind: 'text', text: 'Hello, world!' }], // Associate the response with the incoming request's context. contextId: requestContext.contextId, }; // Publish the message and signal that the interaction is finished. eventBus.publish(responseMessage); eventBus.finished(); } // cancelTask is not needed for this simple, non-stateful agent. cancelTask = async (): Promise => {}; } // 3. Set up and run the server. const agentExecutor = new HelloExecutor(); const requestHandler = new DefaultRequestHandler( helloAgentCard, new InMemoryTaskStore(), agentExecutor ); const app = express(); app.use(`/${AGENT_CARD_PATH}`, agentCardHandler({ agentCardProvider: requestHandler })); app.use('/a2a/jsonrpc', jsonRpcHandler({ requestHandler, userBuilder: UserBuilder.noAuthentication })); app.use('/a2a/rest', restHandler({ requestHandler, userBuilder: UserBuilder.noAuthentication })); app.listen(4000, () => { console.log(`🚀 Server started on http://localhost:4000`); }); const server = new Server(); server.addService(A2AService, grpcService({ requestHandler, userBuilder: UserBuilder.noAuthentication, })); server.bindAsync(`localhost:4001`, ServerCredentials.createInsecure(), () => { console.log(`🚀 Server started on localhost:4001`); }); ``` -------------------------------- ### Run Sample Agent Source: https://github.com/a2aproject/a2a-js/blob/main/src/samples/agents/sample-agent/README.md Executes the sample agent script. The agent will be accessible at http://localhost:41241. ```bash npm run agents:sample-agent ``` -------------------------------- ### Generate gRPC Service Definitions Source: https://github.com/a2aproject/a2a-js/blob/main/src/grpc/README.md Execute this command from the src/grpc directory to generate TypeScript files for gRPC services. The output is configured in buf.gen.yaml and placed in the ./pb directory. ```bash npx buf generate ``` -------------------------------- ### Run SUT Agent Source: https://github.com/a2aproject/a2a-js/blob/main/tck/agent/README.md Executes the SUT agent using the configured npm script. The agent will be accessible at http://localhost:41241. ```bash npm run tck:sut-agent ``` -------------------------------- ### Test Agent Extension with cURL Source: https://github.com/a2aproject/a2a-js/blob/main/src/samples/extensions/README.md Send a POST request to the running agent to test the extension feature. Ensure the 'X-A2A-Extensions' header points to the correct extension path. This request sends a user message and expects a response that includes metadata with timestamps. ```bash curl -X POST http://localhost:41241/ \ -H "X-A2A-Extensions: https://github.com/a2aproject/a2a-js/src/samples/extensions/v1" \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "message/send", "params": { "message": { "role": "user", "parts": [ { "kind": "text", "text": "Hello how are you?" } ], "messageId": "9229e770-767c-417b-a0b0-f0741243c589" } } }' ``` -------------------------------- ### Set Environment Variables for Movie Agent Source: https://github.com/a2aproject/a2a-js/blob/main/src/samples/agents/movie-agent/README.md Before running the agent, set the TMDB_API_KEY and GEMINI_API_KEY environment variables. Obtain your TMDB API key from the TMDB developer documentation. ```bash export TMDB_API_KEY= # see https://developer.themoviedb.org/docs/getting-started export GEMINI_API_KEY= ``` -------------------------------- ### Send Message to A2A Agent Source: https://github.com/a2aproject/a2a-js/blob/main/README.md This snippet shows how to use the `ClientFactory` to create a client and send a message to an A2A agent, then log the agent's response. ```typescript // client.ts import { ClientFactory } from '@a2a-js/sdk/client'; import { Message, MessageSendParams, SendMessageSuccessResponse } from '@a2a-js/sdk'; import { v4 as uuidv4 } from 'uuid'; async function run() { const factory = new ClientFactory(); // createFromUrl accepts baseUrl and optional path, // (the default path is /.well-known/agent-card.json) const client = await factory.createFromUrl('http://localhost:4000'); const sendParams: MessageSendParams = { message: { messageId: uuidv4(), role: 'user', parts: [{ kind: 'text', text: 'Hi there!' }], kind: 'message', }, }; try { const response = await client.sendMessage(sendParams); const result = response as Message; console.log('Agent response:', result.parts[0].text); // "Hello, world!" } catch(e) { console.error('Error:', e); } } await run(); ``` -------------------------------- ### Expected Simplified Agent Response with Extension Source: https://github.com/a2aproject/a2a-js/blob/main/src/samples/extensions/README.md This is a sample of the expected JSON response from the agent after a successful extension test. Note the 'timestamp' field within the 'metadata' of the 'TaskStatusUpdateEvent' messages, which is added by the extension. ```json { "jsonrpc": "2.0", "id": 1, "result": { "kind": "task", "status": { "state": "completed", "message": { ... "metadata": { "timestamp": "2025-11-14T15:51:36.725Z" // Timestamp added to metadata on the 'completed' TaskStatusUpdateEvent } }, }, "history": [ { ... }, { ... "metadata": { "timestamp": "2025-11-14T15:51:35.722Z" // Timestamp added to metadata on the 'running' TaskStatusUpdateEvent } }, { ... "metadata": { "timestamp": "2025-11-14T15:51:36.725Z" // Timestamp added to metadata on the 'completed' TaskStatusUpdateEvent } } ] } } ``` -------------------------------- ### Client: Receive Task Response Source: https://github.com/a2aproject/a2a-js/blob/main/README.md Send a message and handle the response, checking if it's a direct message or a Task. If it's a Task, log its status and any artifacts. ```typescript // client.ts import { ClientFactory } from '@a2a-js/sdk/client'; import { Message, MessageSendParams, SendMessageSuccessResponse, Task } from '@a2a-js/sdk'; // ... other imports ... const factory = new ClientFactory(); // createFromUrl accepts baseUrl and optional path, // (the default path is /.well-known/agent-card.json) const client = await factory.createFromUrl('http://localhost:4000'); try { const result = await client.sendMessage({ message: { messageId: uuidv4(), role: 'user', parts: [{ kind: 'text', text: 'Do something.' }], kind: 'message', }, }); // Check if the agent's response is a Task or a direct Message. if (result.kind === 'task') { const task = result as Task; console.log(`Task [${task.id}] completed with status: ${task.status.state}`); if (task.artifacts && task.artifacts.length > 0) { console.log(`Artifact found: ${task.artifacts[0].name}`); console.log(`Content: ${task.artifacts[0].parts[0].text}`); } } else { const message = result as Message; console.log('Received direct message:', message.parts[0].text); } } catch (e) { console.error('Error:', e); } ``` -------------------------------- ### Server: Create Task with Artifact Source: https://github.com/a2aproject/a2a-js/blob/main/README.md Implement AgentExecutor to create a task, publish artifact updates, and signal task completion. Ensures task and artifact data are correctly published via the event bus. ```typescript // server.ts import { Task, TaskArtifactUpdateEvent, TaskStatusUpdateEvent } from '@a2a-js/sdk'; // ... other imports from the quickstart server ... class TaskExecutor implements AgentExecutor { async execute(requestContext: RequestContext, eventBus: ExecutionEventBus): Promise { const { taskId, contextId, userMessage, task } = requestContext; // 1. Create and publish the initial task object if it doesn't exist. if (!task) { const initialTask: Task = { kind: 'task', id: taskId, contextId: contextId, status: { state: 'submitted', timestamp: new Date().toISOString(), }, history: [userMessage], }; eventBus.publish(initialTask); } // 2. Create and publish an artifact. const artifactUpdate: TaskArtifactUpdateEvent = { kind: 'artifact-update', taskId: taskId, contextId: contextId, artifact: { artifactId: 'report-1', name: 'analysis_report.txt', parts: [{ kind: 'text', text: `This is the analysis for task ${taskId}.` }], }, }; eventBus.publish(artifactUpdate); // 3. Publish the final status and mark the event as 'final'. const finalUpdate: TaskStatusUpdateEvent = { kind: 'status-update', taskId: taskId, contextId: contextId, status: { state: 'completed', timestamp: new Date().toISOString() }, final: true, }; eventBus.publish(finalUpdate); eventBus.finished(); } cancelTask = async (): Promise => {}; } ``` -------------------------------- ### Configure DefaultRequestHandler with Custom Push Notifications Source: https://github.com/a2aproject/a2a-js/blob/main/README.md Set up the `DefaultRequestHandler` with optional custom push notification components, including a store and sender. This allows for tailored notification handling with custom timeouts and headers. ```typescript import { DefaultRequestHandler, InMemoryPushNotificationStore, DefaultPushNotificationSender, } from '@a2a-js/sdk/server'; // Optional: Custom push notification store and sender const pushNotificationStore = new InMemoryPushNotificationStore(); const pushNotificationSender = new DefaultPushNotificationSender(pushNotificationStore, { timeout: 5000, // 5 second timeout tokenHeaderName: 'X-A2A-Notification-Token', // Custom header name }); const requestHandler = new DefaultRequestHandler( movieAgentCard, taskStore, agentExecutor, undefined, // eventBusManager (optional) pushNotificationStore, // custom store pushNotificationSender, // custom sender undefined // extendedAgentCard (optional) ); ``` -------------------------------- ### Enable Push Notifications in Agent Card Source: https://github.com/a2aproject/a2a-js/blob/main/README.md Configure an agent card to support push notifications by setting the `pushNotifications` capability to true. This is essential for asynchronous updates. ```typescript const movieAgentCard: AgentCard = { // ... other properties capabilities: { streaming: true, pushNotifications: true, // Enable push notifications stateTransitionHistory: true, }, // ... rest of agent card }; ``` -------------------------------- ### gRPC Client: Send Message Source: https://github.com/a2aproject/a2a-js/blob/main/README.md Use ClientFactory with GrpcTransportFactory to create a client and send messages. Handles potential errors during message sending. ```typescript // client.ts import { ClientFactory, ClientFactoryOptions } from '@a2a-js/sdk/client'; import { GrpcTransportFactory } from '@a2a-js/sdk/client/grpc'; import { Message, MessageSendParams, SendMessageSuccessResponse } from '@a2a-js/sdk'; import { v4 as uuidv4 } from 'uuid'; async function run() { const factory = new ClientFactory({ transports: [new GrpcTransportFactory()] }); // createFromUrl accepts baseUrl and optional path, // (the default path is /.well-known/agent-card.json) const client = await factory.createFromUrl('http://localhost:4000'); const sendParams: MessageSendParams = { message: { messageId: uuidv4(), role: 'user', parts: [{ kind: 'text', text: 'Hi there!' }], kind: 'message', }, }; try { const response = await client.sendMessage(sendParams); const result = response as Message; console.log('Agent response:', result.parts[0].text); // "Hello, world!" } catch(e) { console.error('Error:', e); } } await run(); ``` -------------------------------- ### Test Agent with Bearer Token Authentication Source: https://github.com/a2aproject/a2a-js/blob/main/src/samples/authentication/README.md Use this cURL command to send a POST request to the agent. Ensure you replace with a valid JSON Web Token generated using the provided secret key and payload structure. The token must contain 'email', 'userName', and 'role' in its payload for successful authentication. ```bash curl -X POST http://localhost:41241/ -H "Authorization: Bearer " -H "Content-Type: application/json" -d '{ "jsonrpc": "2.0", "id": 1, "method": "message/send", "params": { "message": { "role": "user", "parts": [ { "kind": "text", "text": "Hello, who am i?" } ], "messageId": "9229e770-767c-417b-a0b0-f0741243c589" } } }' ``` -------------------------------- ### Configure Push Notifications for Messages Source: https://github.com/a2aproject/a2a-js/blob/main/README.md Configure push notification settings when sending a message. The `id` is optional and defaults to the task ID. An optional authentication `token` can be provided for the webhook. ```typescript const pushConfig: PushNotificationConfig = { id: 'my-notification-config', // Optional, defaults to task ID url: 'https://my-app.com/webhook/task-updates', token: 'your-auth-token', // Optional authentication token }; const sendParams: MessageSendParams = { message: { messageId: uuidv4(), role: 'user', parts: [{ kind: 'text', text: 'Hello, agent!' }], kind: 'message', }, configuration: { blocking: true, acceptedOutputModes: ['text/plain'], pushNotificationConfig: pushConfig, // Add push notification config }, }; ``` -------------------------------- ### Implement Webhook Endpoint for Task Updates Source: https://github.com/a2aproject/a2a-js/blob/main/README.md Implement an Express.js webhook endpoint to receive POST requests with task data. It includes logic to verify an authentication token sent in the headers and to log task status updates. ```typescript app.post('/webhook/task-updates', (req, res) => { const task = req.body; // The complete task object // Verify the token if provided const token = req.headers['x-a2a-notification-token']; if (token !== 'your-auth-token') { return res.status(401).json({ error: 'Unauthorized' }); } console.log(`Task ${task.id} status: ${task.status.state}`); // Process the task update // ... res.status(200).json({ received: true }); }); ``` -------------------------------- ### Implement Custom Authentication with Bearer Token Source: https://github.com/a2aproject/a2a-js/blob/main/README.md Use `createAuthenticatingFetchWithRetry` and `AuthenticationHandler` to manage Bearer token authentication. This handler automatically adds authorization headers and retries on 401 errors with a refreshed token. ```typescript import { ClientFactory, ClientFactoryOptions, JsonRpcTransportFactory, AuthenticationHandler, createAuthenticatingFetchWithRetry, } from '@a2a-js/sdk/client'; // A simple token provider that simulates fetching a new token. const tokenProvider = { token: 'initial-stale-token', getNewToken: async () => { console.log('Refreshing auth token...'); tokenProvider.token = `new-token-${Date.now()}`; return tokenProvider.token; }, }; // 1. Implement the AuthenticationHandler interface. const handler: AuthenticationHandler = { // headers() is called on every request to get the current auth headers. headers: async () => ({ Authorization: `Bearer ${tokenProvider.token}`, }), // shouldRetryWithHeaders() is called after a request fails. // It decides if a retry is needed and provides new headers. shouldRetryWithHeaders: async (req: RequestInit, res: Response) => { if (res.status === 401) { // Unauthorized const newToken = await tokenProvider.getNewToken(); // Return new headers to trigger a single retry. return { Authorization: `Bearer ${newToken}` }; } // Return undefined to not retry for other errors. return undefined; }, }; // 2. Create the authenticated fetch function. const authFetch = createAuthenticatingFetchWithRetry(fetch, handler); // 3. Inject new fetch implementation into a client factory. const factory = new ClientFactory(ClientFactoryOptions.createFrom(ClientFactoryOptions.default, { transports: [ new JsonRpcTransportFactory({ fetchImpl: authFetch }) ] })) // 4. Clients created from the factory are going to have custom fetch attached. const client = await factory.createFromUrl('http://localhost:4000'); ``` -------------------------------- ### Implement Cancellable Executor in TypeScript Source: https://github.com/a2aproject/a2a-js/blob/main/README.md Demonstrates an agent that checks for cancellation requests during its multi-step process. It updates the task state to 'canceled' if a cancellation is detected. ```typescript // server.ts import { AgentExecutor, RequestContext, ExecutionEventBus, TaskStatusUpdateEvent, } from '@a2a-js/sdk/server'; // ... other imports ... class CancellableExecutor implements AgentExecutor { // Use a Set to track the IDs of tasks that have been requested to be canceled. private cancelledTasks = new Set(); /** * When a cancellation is requested, add the taskId to our tracking set. * The `execute` loop will handle the rest. */ public async cancelTask(taskId: string, eventBus: ExecutionEventBus): Promise { console.log(`[Executor] Received cancellation request for task: ${taskId}`); this.cancelledTasks.add(taskId); } public async execute(requestContext: RequestContext, eventBus: ExecutionEventBus): Promise { const { taskId, contextId } = requestContext; // Start the task eventBus.publish({ kind: 'status-update', taskId, contextId, status: { state: 'working', timestamp: new Date().toISOString() }, final: false, }); // Simulate a multi-step, long-running process for (let i = 0; i < 5; i++) { // **Cancellation Checkpoint** // Before each step, check if the task has been canceled. if (this.cancelledTasks.has(taskId)) { console.log(`[Executor] Aborting task ${taskId} due to cancellation.`); // Publish the final 'canceled' status. const cancelledUpdate: TaskStatusUpdateEvent = { kind: 'status-update', taskId: taskId, contextId: contextId, status: { state: 'canceled', timestamp: new Date().toISOString() }, final: true, }; eventBus.publish(cancelledUpdate); eventBus.finished(); // Clean up and exit. this.cancelledTasks.delete(taskId); return; } // Simulate one step of work. console.log(`[Executor] Working on step ${i + 1} for task ${taskId}...`); await new Promise((resolve) => setTimeout(resolve, 1000)); } console.log(`[Executor] Task ${taskId} finished all steps without cancellation.`); // If not canceled, finish the work and publish the completed state. const finalUpdate: TaskStatusUpdateEvent = { kind: 'status-update', taskId, contextId, status: { state: 'completed', timestamp: new Date().toISOString() }, final: true, }; eventBus.publish(finalUpdate); eventBus.finished(); } } ``` -------------------------------- ### Set Request Timeout with AbortSignal Source: https://github.com/a2aproject/a2a-js/blob/main/README.md Configure a timeout for client requests using the `signal` option with `AbortSignal.timeout()`. This prevents requests from hanging indefinitely. ```typescript import { ClientFactory } from '@a2a-js/sdk/client'; const factory = new ClientFactory(); // createFromUrl accepts baseUrl and optional path, // (the default path is /.well-known/agent-card.json) const client = await factory.createFromUrl('http://localhost:4000'); await client.sendMessage( { message: { messageId: uuidv4(), role: 'user', parts: [{ kind: 'text', text: 'A long-running message.' }], kind: 'message', }, }, { signal: AbortSignal.timeout(5000), // 5 seconds timeout } ); ``` -------------------------------- ### Expected Response with Successful Authentication Source: https://github.com/a2aproject/a2a-js/blob/main/src/samples/authentication/README.md This JSON structure represents the expected successful response from the agent when the provided Bearer token is valid and authenticated. It includes details about the authenticated user. ```json { "jsonrpc":"2.0", "id":1, "result": { "kind":"message", "role":"agent", "parts":[ { "kind":"text", "text":"The request is coming from the authenticated user , with email and role ." } ] } } } ``` -------------------------------- ### Expected Response with Failed Authentication Source: https://github.com/a2aproject/a2a-js/blob/main/src/samples/authentication/README.md This JSON structure represents the expected response from the agent when the authentication fails due to an invalid or missing Bearer token. ```json { "jsonrpc":"2.0", "id":1, "result": { "kind":"message", "role":"agent", "parts":[ { "kind":"text", "text":"The request is not coming from an authenticated user." } ] } } } ``` -------------------------------- ### Server: Stream Task Updates with AgentExecutor Source: https://github.com/a2aproject/a2a-js/blob/main/README.md Implement the AgentExecutor interface to publish task-related events as the agent processes a task. This includes publishing initial task objects, status updates, artifacts, and final completion states. ```typescript // server.ts // ... imports ... class StreamingExecutor implements AgentExecutor { async execute(requestContext: RequestContext, eventBus: ExecutionEventBus): Promise { const { taskId, contextId, userMessage, task } = requestContext; // 1. Create and publish the initial task object if it doesn't exist. if (!task) { const initialTask: Task = { kind: 'task', id: taskId, contextId: contextId, status: { state: 'submitted', timestamp: new Date().toISOString(), }, history: [userMessage], }; eventBus.publish(initialTask); } // 2. Publish 'working' state. eventBus.publish({ kind: 'status-update', taskId, contextId, status: { state: 'working', timestamp: new Date().toISOString() }, final: false, }); // 3. Simulate work and publish an artifact. await new Promise((resolve) => setTimeout(resolve, 1000)); eventBus.publish({ kind: 'artifact-update', taskId, contextId, artifact: { artifactId: 'result.txt', parts: [{ kind: 'text', text: 'First result.' }] }, }); // 4. Publish final 'completed' state. eventBus.publish({ kind: 'status-update', taskId, contextId, status: { state: 'completed', timestamp: new Date().toISOString() }, final: true, }); eventBus.finished(); } cancelTask = async (): Promise => {}; } ``` -------------------------------- ### Client: Consume Task Event Stream with sendMessageStream Source: https://github.com/a2aproject/a2a-js/blob/main/README.md Use the `sendMessageStream` method from the a2a-js client to receive real-time events from the server. This method returns an AsyncGenerator that yields events as they are published, allowing for real-time processing of task updates. ```typescript // client.ts import { ClientFactory } from '@a2a-js/sdk/client'; import { MessageSendParams } from '@a2a-js/sdk'; import { v4 as uuidv4 } from 'uuid'; // ... other imports ... const factory = new ClientFactory(); // createFromUrl accepts baseUrl and optional path, // (the default path is /.well-known/agent-card.json) const client = await factory.createFromUrl('http://localhost:4000'); async function streamTask() { const streamParams: MessageSendParams = { message: { messageId: uuidv4(), role: 'user', parts: [{ kind: 'text', text: 'Stream me some updates!' }], kind: 'message', }, }; try { const stream = client.sendMessageStream(streamParams); for await (const event of stream) { if (event.kind === 'task') { console.log(`[${event.id}] Task created. Status: ${event.status.state}`); } else if (event.kind === 'status-update') { console.log(`[${event.taskId}] Status Updated: ${event.status.state}`); } else if (event.kind === 'artifact-update') { console.log(`[${event.taskId}] Artifact Received: ${event.artifact.artifactId}`); } } console.log('--- Stream finished ---'); } catch (error) { console.error('Error during streaming:', error); } } await streamTask(); ``` -------------------------------- ### Update Generated Types for Consistency Source: https://github.com/a2aproject/a2a-js/blob/main/src/grpc/README.md After generation, update the TypeScript files to import message types from the shared types definition file (src/types/pb/a2a_types.ts) instead of using local definitions. This ensures type consistency across the SDK. ```typescript import * as pb from "../../types/pb/a2a_types.js"; export type Task = pb.Task; // ... maps other types similarly ``` -------------------------------- ### Inject Custom Request ID Header with Interceptor Source: https://github.com/a2aproject/a2a-js/blob/main/README.md Use a CallInterceptor to add a unique 'X-Request-ID' header to all outgoing requests. This is useful for tracing requests across services. ```typescript import { v4 as uuidv4 } from 'uuid'; import { AfterArgs, BeforeArgs, CallInterceptor, ClientFactory, ClientFactoryOptions } from '@a2a-js/sdk/client'; // 1. Define an interceptor class RequestIdInterceptor implements CallInterceptor { before(args: BeforeArgs): Promise { args.options = { ...args.options, serviceParameters: { ...args.options.serviceParameters, ['X-Request-ID']: uuidv4(), }, }; return Promise.resolve(); } after(): Promise { return Promise.resolve(); } } // 2. Register the interceptor in the client factory const factory = new ClientFactory(ClientFactoryOptions.createFrom(ClientFactoryOptions.default, { clientConfig: { interceptors: [new RequestIdInterceptor()] } })) const client = await factory.createFromAgentCardUrl('http://localhost:4000'); // Now, all requests made by clients created by this factory will include the X-Request-ID header. await client.sendMessage({ message: { messageId: uuidv4(), role: 'user', parts: [{ kind: 'text', text: 'A message requiring custom headers.' }], kind: 'message', }, }); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.