@a2a-js/sdk 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -248,7 +248,7 @@ expressApp.listen(PORT, () => {
248
248
  `[MyAgent] Server using new framework started on http://localhost:${PORT}`
249
249
  );
250
250
  console.log(
251
- `[MyAgent] Agent Card: http://localhost:${PORT}/.well-known/agent.json`
251
+ `[MyAgent] Agent Card: http://localhost:${PORT}/.well-known/agent-card.json`
252
252
  );
253
253
  console.log("[MyAgent] Press Ctrl+C to stop the server");
254
254
  });
@@ -0,0 +1,6 @@
1
+ // src/constants.ts
2
+ var AGENT_CARD_PATH = ".well-known/agent-card.json";
3
+
4
+ export {
5
+ AGENT_CARD_PATH
6
+ };
@@ -19,41 +19,44 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
19
19
  // src/client/index.ts
20
20
  var client_exports = {};
21
21
  __export(client_exports, {
22
- A2AClient: () => A2AClient
22
+ A2AClient: () => A2AClient,
23
+ createAuthenticatingFetchWithRetry: () => createAuthenticatingFetchWithRetry
23
24
  });
24
25
  module.exports = __toCommonJS(client_exports);
25
26
 
27
+ // src/constants.ts
28
+ var AGENT_CARD_PATH = ".well-known/agent-card.json";
29
+
26
30
  // src/client/client.ts
27
- var A2AClient = class _A2AClient {
28
- agentBaseUrl;
29
- agentCardPath;
31
+ var A2AClient = class {
30
32
  agentCardPromise;
31
- static DEFAULT_AGENT_CARD_PATH = ".well-known/agent.json";
32
33
  requestIdCounter = 1;
33
34
  serviceEndpointUrl;
34
35
  // To be populated from AgentCard after fetching
36
+ fetchImpl;
35
37
  /**
36
38
  * Constructs an A2AClient instance.
37
39
  * It initiates fetching the agent card from the provided agent baseUrl.
38
- * The Agent Card is fetched from a path relative to the agentBaseUrl, which defaults to '.well-known/agent.json'.
40
+ * The Agent Card is fetched from a path relative to the agentBaseUrl, which defaults to '.well-known/agent-card.json'.
39
41
  * The `url` field from the Agent Card will be used as the RPC service endpoint.
40
42
  * @param agentBaseUrl The base URL of the A2A agent (e.g., https://agent.example.com)
41
- * @param agentCardPath path to the agent card, defaults to .well-known/agent.json
43
+ * @param options Optional. The options for the A2AClient including the fetch implementation, agent card path, and authentication handler.
42
44
  */
43
- constructor(agentBaseUrl, agentCardPath = _A2AClient.DEFAULT_AGENT_CARD_PATH) {
44
- this.agentBaseUrl = agentBaseUrl.replace(/\/$/, "");
45
- this.agentCardPath = agentCardPath.replace(/^\//, "");
46
- this.agentCardPromise = this._fetchAndCacheAgentCard();
45
+ constructor(agentBaseUrl, options) {
46
+ this.fetchImpl = options?.fetchImpl ?? fetch;
47
+ this.agentCardPromise = this._fetchAndCacheAgentCard(agentBaseUrl, options?.agentCardPath);
47
48
  }
48
49
  /**
49
50
  * Fetches the Agent Card from the agent's well-known URI and caches its service endpoint URL.
50
51
  * This method is called by the constructor.
52
+ * @param agentBaseUrl The base URL of the A2A agent (e.g., https://agent.example.com)
53
+ * @param agentCardPath path to the agent card, defaults to .well-known/agent-card.json
51
54
  * @returns A Promise that resolves to the AgentCard.
52
55
  */
53
- async _fetchAndCacheAgentCard() {
54
- const agentCardUrl = `${this.agentBaseUrl}/${this.agentCardPath}`;
56
+ async _fetchAndCacheAgentCard(agentBaseUrl, agentCardPath) {
55
57
  try {
56
- const response = await fetch(agentCardUrl, {
58
+ const agentCardUrl = this.resolveAgentCardUrl(agentBaseUrl, agentCardPath);
59
+ const response = await this.fetchImpl(agentCardUrl, {
57
60
  headers: { "Accept": "application/json" }
58
61
  });
59
62
  if (!response.ok) {
@@ -66,7 +69,7 @@ var A2AClient = class _A2AClient {
66
69
  this.serviceEndpointUrl = agentCard.url;
67
70
  return agentCard;
68
71
  } catch (error) {
69
- console.error("Error fetching or parsing Agent Card:");
72
+ console.error("Error fetching or parsing Agent Card:", error);
70
73
  throw error;
71
74
  }
72
75
  }
@@ -75,14 +78,14 @@ var A2AClient = class _A2AClient {
75
78
  * If an `agentBaseUrl` is provided, it fetches the card from that specific URL.
76
79
  * Otherwise, it returns the card fetched and cached during client construction.
77
80
  * @param agentBaseUrl Optional. The base URL of the agent to fetch the card from.
78
- * @param agentCardPath path to the agent card, defaults to .well-known/agent.json
81
+ * @param agentCardPath path to the agent card, defaults to .well-known/agent-card.json
79
82
  * If provided, this will fetch a new card, not use the cached one from the constructor's URL.
80
83
  * @returns A Promise that resolves to the AgentCard.
81
84
  */
82
- async getAgentCard(agentBaseUrl, agentCardPath = _A2AClient.DEFAULT_AGENT_CARD_PATH) {
85
+ async getAgentCard(agentBaseUrl, agentCardPath) {
83
86
  if (agentBaseUrl) {
84
- const agentCardUrl = `${agentBaseUrl.replace(/\/$/, "")}/${agentCardPath.replace(/^\//, "")}`;
85
- const response = await fetch(agentCardUrl, {
87
+ const agentCardUrl = this.resolveAgentCardUrl(agentBaseUrl, agentCardPath);
88
+ const response = await this.fetchImpl(agentCardUrl, {
86
89
  headers: { "Accept": "application/json" }
87
90
  });
88
91
  if (!response.ok) {
@@ -92,6 +95,14 @@ var A2AClient = class _A2AClient {
92
95
  }
93
96
  return this.agentCardPromise;
94
97
  }
98
+ /**
99
+ * Determines the agent card URL based on the agent URL.
100
+ * @param agentBaseUrl The agent URL.
101
+ * @param agentCardPath Optional relative path to the agent card, defaults to .well-known/agent-card.json
102
+ */
103
+ resolveAgentCardUrl(agentBaseUrl, agentCardPath = AGENT_CARD_PATH) {
104
+ return `${agentBaseUrl.replace(/\/$/, "")}/${agentCardPath.replace(/^\//, "")}`;
105
+ }
95
106
  /**
96
107
  * Gets the RPC service endpoint URL. Ensures the agent card has been fetched first.
97
108
  * @returns A Promise that resolves to the service endpoint URL string.
@@ -122,21 +133,15 @@ var A2AClient = class _A2AClient {
122
133
  // Cast because TParams structure varies per method
123
134
  id: requestId
124
135
  };
125
- const httpResponse = await fetch(endpoint, {
126
- method: "POST",
127
- headers: {
128
- "Content-Type": "application/json",
129
- "Accept": "application/json"
130
- // Expect JSON response for non-streaming requests
131
- },
132
- body: JSON.stringify(rpcRequest)
133
- });
136
+ const httpResponse = await this._fetchRpc(endpoint, rpcRequest);
134
137
  if (!httpResponse.ok) {
135
138
  let errorBodyText = "(empty or non-JSON response)";
136
139
  try {
137
140
  errorBodyText = await httpResponse.text();
138
141
  const errorJson = JSON.parse(errorBodyText);
139
- if (!errorJson.jsonrpc && errorJson.error) {
142
+ if (errorJson.jsonrpc && errorJson.error) {
143
+ return errorJson;
144
+ } else if (!errorJson.jsonrpc && errorJson.error) {
140
145
  throw new Error(`RPC error for ${method}: ${errorJson.error.message} (Code: ${errorJson.error.code}, HTTP Status: ${httpResponse.status}) Data: ${JSON.stringify(errorJson.error.data || {})}`);
141
146
  } else if (!errorJson.jsonrpc) {
142
147
  throw new Error(`HTTP error for ${method}! Status: ${httpResponse.status} ${httpResponse.statusText}. Response: ${errorBodyText}`);
@@ -152,6 +157,25 @@ var A2AClient = class _A2AClient {
152
157
  }
153
158
  return rpcResponse;
154
159
  }
160
+ /**
161
+ * Internal helper method to fetch the RPC service endpoint.
162
+ * @param url The URL to fetch.
163
+ * @param rpcRequest The JSON-RPC request to send.
164
+ * @param acceptHeader The Accept header to use. Defaults to "application/json".
165
+ * @returns A Promise that resolves to the fetch HTTP response.
166
+ */
167
+ async _fetchRpc(url, rpcRequest, acceptHeader = "application/json") {
168
+ const requestInit = {
169
+ method: "POST",
170
+ headers: {
171
+ "Content-Type": "application/json",
172
+ "Accept": acceptHeader
173
+ // Expect JSON response for non-streaming requests
174
+ },
175
+ body: JSON.stringify(rpcRequest)
176
+ };
177
+ return this.fetchImpl(url, requestInit);
178
+ }
155
179
  /**
156
180
  * Sends a message to the agent.
157
181
  * The behavior (blocking/non-blocking) and push notification configuration
@@ -186,15 +210,7 @@ var A2AClient = class _A2AClient {
186
210
  params,
187
211
  id: clientRequestId
188
212
  };
189
- const response = await fetch(endpoint, {
190
- method: "POST",
191
- headers: {
192
- "Content-Type": "application/json",
193
- "Accept": "text/event-stream"
194
- // Crucial for SSE
195
- },
196
- body: JSON.stringify(rpcRequest)
197
- });
213
+ const response = await this._fetchRpc(endpoint, rpcRequest, "text/event-stream");
198
214
  if (!response.ok) {
199
215
  let errorBody = "";
200
216
  try {
@@ -278,7 +294,7 @@ var A2AClient = class _A2AClient {
278
294
  params,
279
295
  id: clientRequestId
280
296
  };
281
- const response = await fetch(endpoint, {
297
+ const response = await this.fetchImpl(endpoint, {
282
298
  method: "POST",
283
299
  headers: {
284
300
  "Content-Type": "application/json",
@@ -394,7 +410,41 @@ var A2AClient = class _A2AClient {
394
410
  return "error" in response;
395
411
  }
396
412
  };
413
+
414
+ // src/client/auth-handler.ts
415
+ function createAuthenticatingFetchWithRetry(fetchImpl, authHandler) {
416
+ async function authFetch(url, init) {
417
+ const authHeaders = await authHandler.headers() || {};
418
+ const mergedInit = {
419
+ ...init || {},
420
+ headers: {
421
+ ...authHeaders,
422
+ ...init?.headers || {}
423
+ }
424
+ };
425
+ let response = await fetchImpl(url, mergedInit);
426
+ const updatedHeaders = await authHandler.shouldRetryWithHeaders(mergedInit, response);
427
+ if (updatedHeaders) {
428
+ const retryInit = {
429
+ ...init || {},
430
+ headers: {
431
+ ...updatedHeaders,
432
+ ...init?.headers || {}
433
+ }
434
+ };
435
+ response = await fetchImpl(url, retryInit);
436
+ if (response.ok && authHandler.onSuccessfulRetry) {
437
+ await authHandler.onSuccessfulRetry(updatedHeaders);
438
+ }
439
+ }
440
+ return response;
441
+ }
442
+ Object.setPrototypeOf(authFetch, Object.getPrototypeOf(fetchImpl));
443
+ Object.defineProperties(authFetch, Object.getOwnPropertyDescriptors(fetchImpl));
444
+ return authFetch;
445
+ }
397
446
  // Annotate the CommonJS export names for ESM import in node:
398
447
  0 && (module.exports = {
399
- A2AClient
448
+ A2AClient,
449
+ createAuthenticatingFetchWithRetry
400
450
  });
@@ -1,28 +1,32 @@
1
1
  import { ac as AgentCard, w as MessageSendParams, S as SendMessageResponse, B as Message, aw as Task, aO as TaskStatusUpdateEvent, aQ as TaskArtifactUpdateEvent, Z as TaskPushNotificationConfig, b as SetTaskPushNotificationConfigResponse, X as TaskIdParams, c as GetTaskPushNotificationConfigResponse, V as TaskQueryParams, G as GetTaskResponse, C as CancelTaskResponse, i as JSONRPCResponse, J as JSONRPCErrorResponse } from '../types-DNKcmF0f.cjs';
2
2
 
3
3
  type A2AStreamEventData = Message | Task | TaskStatusUpdateEvent | TaskArtifactUpdateEvent;
4
+ interface A2AClientOptions {
5
+ agentCardPath?: string;
6
+ fetchImpl?: typeof fetch;
7
+ }
4
8
  /**
5
9
  * A2AClient is a TypeScript HTTP client for interacting with A2A-compliant agents.
6
10
  */
7
11
  declare class A2AClient {
8
- private agentBaseUrl;
9
- private agentCardPath;
10
12
  private agentCardPromise;
11
- private static readonly DEFAULT_AGENT_CARD_PATH;
12
13
  private requestIdCounter;
13
14
  private serviceEndpointUrl?;
15
+ private fetchImpl;
14
16
  /**
15
17
  * Constructs an A2AClient instance.
16
18
  * It initiates fetching the agent card from the provided agent baseUrl.
17
- * The Agent Card is fetched from a path relative to the agentBaseUrl, which defaults to '.well-known/agent.json'.
19
+ * The Agent Card is fetched from a path relative to the agentBaseUrl, which defaults to '.well-known/agent-card.json'.
18
20
  * The `url` field from the Agent Card will be used as the RPC service endpoint.
19
21
  * @param agentBaseUrl The base URL of the A2A agent (e.g., https://agent.example.com)
20
- * @param agentCardPath path to the agent card, defaults to .well-known/agent.json
22
+ * @param options Optional. The options for the A2AClient including the fetch implementation, agent card path, and authentication handler.
21
23
  */
22
- constructor(agentBaseUrl: string, agentCardPath?: string);
24
+ constructor(agentBaseUrl: string, options?: A2AClientOptions);
23
25
  /**
24
26
  * Fetches the Agent Card from the agent's well-known URI and caches its service endpoint URL.
25
27
  * This method is called by the constructor.
28
+ * @param agentBaseUrl The base URL of the A2A agent (e.g., https://agent.example.com)
29
+ * @param agentCardPath path to the agent card, defaults to .well-known/agent-card.json
26
30
  * @returns A Promise that resolves to the AgentCard.
27
31
  */
28
32
  private _fetchAndCacheAgentCard;
@@ -31,11 +35,17 @@ declare class A2AClient {
31
35
  * If an `agentBaseUrl` is provided, it fetches the card from that specific URL.
32
36
  * Otherwise, it returns the card fetched and cached during client construction.
33
37
  * @param agentBaseUrl Optional. The base URL of the agent to fetch the card from.
34
- * @param agentCardPath path to the agent card, defaults to .well-known/agent.json
38
+ * @param agentCardPath path to the agent card, defaults to .well-known/agent-card.json
35
39
  * If provided, this will fetch a new card, not use the cached one from the constructor's URL.
36
40
  * @returns A Promise that resolves to the AgentCard.
37
41
  */
38
42
  getAgentCard(agentBaseUrl?: string, agentCardPath?: string): Promise<AgentCard>;
43
+ /**
44
+ * Determines the agent card URL based on the agent URL.
45
+ * @param agentBaseUrl The agent URL.
46
+ * @param agentCardPath Optional relative path to the agent card, defaults to .well-known/agent-card.json
47
+ */
48
+ private resolveAgentCardUrl;
39
49
  /**
40
50
  * Gets the RPC service endpoint URL. Ensures the agent card has been fetched first.
41
51
  * @returns A Promise that resolves to the service endpoint URL string.
@@ -48,6 +58,14 @@ declare class A2AClient {
48
58
  * @returns A Promise that resolves to the RPC response.
49
59
  */
50
60
  private _postRpcRequest;
61
+ /**
62
+ * Internal helper method to fetch the RPC service endpoint.
63
+ * @param url The URL to fetch.
64
+ * @param rpcRequest The JSON-RPC request to send.
65
+ * @param acceptHeader The Accept header to use. Defaults to "application/json".
66
+ * @returns A Promise that resolves to the fetch HTTP response.
67
+ */
68
+ private _fetchRpc;
51
69
  /**
52
70
  * Sends a message to the agent.
53
71
  * The behavior (blocking/non-blocking) and push notification configuration
@@ -121,4 +139,66 @@ declare class A2AClient {
121
139
  isErrorResponse(response: JSONRPCResponse): response is JSONRPCErrorResponse;
122
140
  }
123
141
 
124
- export { A2AClient };
142
+ interface HttpHeaders {
143
+ [key: string]: string;
144
+ }
145
+ /**
146
+ * Generic interface for handling authentication for HTTP requests.
147
+ *
148
+ * - For each HTTP request, this handler is called to provide additional headers to the request through
149
+ * the headers() function.
150
+ * - After the server returns a response, the shouldRetryWithHeaders() function is called. Usually this
151
+ * function responds to a 401 or 403 response or JSON-RPC codes, but can respond to any other signal -
152
+ * that is an implementation detail of the AuthenticationHandler.
153
+ * - If the shouldRetryWithHeaders() function returns new headers, then the request should retried with the provided
154
+ * revised headers. These provisional headers may, or may not, be optimistically stored for subsequent requests -
155
+ * that is an implementation detail of the AuthenticationHandler.
156
+ * - If the request is successful and the onSuccessfulRetry() is defined, then the onSuccessfulRetry() function is
157
+ * called with the headers that were used to successfully complete the request. This callback provides an
158
+ * opportunity to save the headers for subsequent requests if they were not already saved.
159
+ *
160
+ */
161
+ interface AuthenticationHandler {
162
+ /**
163
+ * Provides additional HTTP request headers.
164
+ * @returns HTTP headers which may include Authorization if available.
165
+ */
166
+ headers: () => Promise<HttpHeaders>;
167
+ /**
168
+ * For every HTTP response (even 200s) the shouldRetryWithHeaders() method is called.
169
+ * This method is supposed to check if the request needs to be retried and if, yes,
170
+ * return a set of headers. An A2A server might indicate auth failures in its response
171
+ * by JSON-rpc codes, HTTP codes like 401, 403 or headers like WWW-Authenticate.
172
+ *
173
+ * @param req The RequestInit object used to invoke fetch()
174
+ * @param res The fetch Response object
175
+ * @returns If the HTTP request should be retried then returns the HTTP headers to use,
176
+ * or returns undefined if no retry should be made.
177
+ */
178
+ shouldRetryWithHeaders: (req: RequestInit, res: Response) => Promise<HttpHeaders | undefined>;
179
+ /**
180
+ * If the last HTTP request using the headers from shouldRetryWithHeaders() was successful, and
181
+ * this function is implemented, then it will be called with the headers provided from
182
+ * shouldRetryWithHeaders().
183
+ *
184
+ * This callback allows transient headers to be saved for subsequent requests only when they
185
+ * are validated by the server.
186
+ */
187
+ onSuccessfulRetry?: (headers: HttpHeaders) => Promise<void>;
188
+ }
189
+ /**
190
+ * Higher-order function that wraps fetch with authentication handling logic.
191
+ * Returns a new fetch function that automatically handles authentication retries for 401/403 responses.
192
+ *
193
+ * @param fetchImpl The underlying fetch implementation to wrap
194
+ * @param authHandler Authentication handler for managing auth headers and retries
195
+ * @returns A new fetch function with authentication handling capabilities
196
+ *
197
+ * Usage examples:
198
+ * - const authFetch = createAuthHandlingFetch(fetch, authHandler);
199
+ * - const response = await authFetch(url, options);
200
+ * - const response = await authFetch(url); // Direct function call
201
+ */
202
+ declare function createAuthenticatingFetchWithRetry(fetchImpl: typeof fetch, authHandler: AuthenticationHandler): typeof fetch;
203
+
204
+ export { A2AClient, type A2AClientOptions, type AuthenticationHandler, type HttpHeaders, createAuthenticatingFetchWithRetry };
@@ -1,28 +1,32 @@
1
1
  import { ac as AgentCard, w as MessageSendParams, S as SendMessageResponse, B as Message, aw as Task, aO as TaskStatusUpdateEvent, aQ as TaskArtifactUpdateEvent, Z as TaskPushNotificationConfig, b as SetTaskPushNotificationConfigResponse, X as TaskIdParams, c as GetTaskPushNotificationConfigResponse, V as TaskQueryParams, G as GetTaskResponse, C as CancelTaskResponse, i as JSONRPCResponse, J as JSONRPCErrorResponse } from '../types-DNKcmF0f.js';
2
2
 
3
3
  type A2AStreamEventData = Message | Task | TaskStatusUpdateEvent | TaskArtifactUpdateEvent;
4
+ interface A2AClientOptions {
5
+ agentCardPath?: string;
6
+ fetchImpl?: typeof fetch;
7
+ }
4
8
  /**
5
9
  * A2AClient is a TypeScript HTTP client for interacting with A2A-compliant agents.
6
10
  */
7
11
  declare class A2AClient {
8
- private agentBaseUrl;
9
- private agentCardPath;
10
12
  private agentCardPromise;
11
- private static readonly DEFAULT_AGENT_CARD_PATH;
12
13
  private requestIdCounter;
13
14
  private serviceEndpointUrl?;
15
+ private fetchImpl;
14
16
  /**
15
17
  * Constructs an A2AClient instance.
16
18
  * It initiates fetching the agent card from the provided agent baseUrl.
17
- * The Agent Card is fetched from a path relative to the agentBaseUrl, which defaults to '.well-known/agent.json'.
19
+ * The Agent Card is fetched from a path relative to the agentBaseUrl, which defaults to '.well-known/agent-card.json'.
18
20
  * The `url` field from the Agent Card will be used as the RPC service endpoint.
19
21
  * @param agentBaseUrl The base URL of the A2A agent (e.g., https://agent.example.com)
20
- * @param agentCardPath path to the agent card, defaults to .well-known/agent.json
22
+ * @param options Optional. The options for the A2AClient including the fetch implementation, agent card path, and authentication handler.
21
23
  */
22
- constructor(agentBaseUrl: string, agentCardPath?: string);
24
+ constructor(agentBaseUrl: string, options?: A2AClientOptions);
23
25
  /**
24
26
  * Fetches the Agent Card from the agent's well-known URI and caches its service endpoint URL.
25
27
  * This method is called by the constructor.
28
+ * @param agentBaseUrl The base URL of the A2A agent (e.g., https://agent.example.com)
29
+ * @param agentCardPath path to the agent card, defaults to .well-known/agent-card.json
26
30
  * @returns A Promise that resolves to the AgentCard.
27
31
  */
28
32
  private _fetchAndCacheAgentCard;
@@ -31,11 +35,17 @@ declare class A2AClient {
31
35
  * If an `agentBaseUrl` is provided, it fetches the card from that specific URL.
32
36
  * Otherwise, it returns the card fetched and cached during client construction.
33
37
  * @param agentBaseUrl Optional. The base URL of the agent to fetch the card from.
34
- * @param agentCardPath path to the agent card, defaults to .well-known/agent.json
38
+ * @param agentCardPath path to the agent card, defaults to .well-known/agent-card.json
35
39
  * If provided, this will fetch a new card, not use the cached one from the constructor's URL.
36
40
  * @returns A Promise that resolves to the AgentCard.
37
41
  */
38
42
  getAgentCard(agentBaseUrl?: string, agentCardPath?: string): Promise<AgentCard>;
43
+ /**
44
+ * Determines the agent card URL based on the agent URL.
45
+ * @param agentBaseUrl The agent URL.
46
+ * @param agentCardPath Optional relative path to the agent card, defaults to .well-known/agent-card.json
47
+ */
48
+ private resolveAgentCardUrl;
39
49
  /**
40
50
  * Gets the RPC service endpoint URL. Ensures the agent card has been fetched first.
41
51
  * @returns A Promise that resolves to the service endpoint URL string.
@@ -48,6 +58,14 @@ declare class A2AClient {
48
58
  * @returns A Promise that resolves to the RPC response.
49
59
  */
50
60
  private _postRpcRequest;
61
+ /**
62
+ * Internal helper method to fetch the RPC service endpoint.
63
+ * @param url The URL to fetch.
64
+ * @param rpcRequest The JSON-RPC request to send.
65
+ * @param acceptHeader The Accept header to use. Defaults to "application/json".
66
+ * @returns A Promise that resolves to the fetch HTTP response.
67
+ */
68
+ private _fetchRpc;
51
69
  /**
52
70
  * Sends a message to the agent.
53
71
  * The behavior (blocking/non-blocking) and push notification configuration
@@ -121,4 +139,66 @@ declare class A2AClient {
121
139
  isErrorResponse(response: JSONRPCResponse): response is JSONRPCErrorResponse;
122
140
  }
123
141
 
124
- export { A2AClient };
142
+ interface HttpHeaders {
143
+ [key: string]: string;
144
+ }
145
+ /**
146
+ * Generic interface for handling authentication for HTTP requests.
147
+ *
148
+ * - For each HTTP request, this handler is called to provide additional headers to the request through
149
+ * the headers() function.
150
+ * - After the server returns a response, the shouldRetryWithHeaders() function is called. Usually this
151
+ * function responds to a 401 or 403 response or JSON-RPC codes, but can respond to any other signal -
152
+ * that is an implementation detail of the AuthenticationHandler.
153
+ * - If the shouldRetryWithHeaders() function returns new headers, then the request should retried with the provided
154
+ * revised headers. These provisional headers may, or may not, be optimistically stored for subsequent requests -
155
+ * that is an implementation detail of the AuthenticationHandler.
156
+ * - If the request is successful and the onSuccessfulRetry() is defined, then the onSuccessfulRetry() function is
157
+ * called with the headers that were used to successfully complete the request. This callback provides an
158
+ * opportunity to save the headers for subsequent requests if they were not already saved.
159
+ *
160
+ */
161
+ interface AuthenticationHandler {
162
+ /**
163
+ * Provides additional HTTP request headers.
164
+ * @returns HTTP headers which may include Authorization if available.
165
+ */
166
+ headers: () => Promise<HttpHeaders>;
167
+ /**
168
+ * For every HTTP response (even 200s) the shouldRetryWithHeaders() method is called.
169
+ * This method is supposed to check if the request needs to be retried and if, yes,
170
+ * return a set of headers. An A2A server might indicate auth failures in its response
171
+ * by JSON-rpc codes, HTTP codes like 401, 403 or headers like WWW-Authenticate.
172
+ *
173
+ * @param req The RequestInit object used to invoke fetch()
174
+ * @param res The fetch Response object
175
+ * @returns If the HTTP request should be retried then returns the HTTP headers to use,
176
+ * or returns undefined if no retry should be made.
177
+ */
178
+ shouldRetryWithHeaders: (req: RequestInit, res: Response) => Promise<HttpHeaders | undefined>;
179
+ /**
180
+ * If the last HTTP request using the headers from shouldRetryWithHeaders() was successful, and
181
+ * this function is implemented, then it will be called with the headers provided from
182
+ * shouldRetryWithHeaders().
183
+ *
184
+ * This callback allows transient headers to be saved for subsequent requests only when they
185
+ * are validated by the server.
186
+ */
187
+ onSuccessfulRetry?: (headers: HttpHeaders) => Promise<void>;
188
+ }
189
+ /**
190
+ * Higher-order function that wraps fetch with authentication handling logic.
191
+ * Returns a new fetch function that automatically handles authentication retries for 401/403 responses.
192
+ *
193
+ * @param fetchImpl The underlying fetch implementation to wrap
194
+ * @param authHandler Authentication handler for managing auth headers and retries
195
+ * @returns A new fetch function with authentication handling capabilities
196
+ *
197
+ * Usage examples:
198
+ * - const authFetch = createAuthHandlingFetch(fetch, authHandler);
199
+ * - const response = await authFetch(url, options);
200
+ * - const response = await authFetch(url); // Direct function call
201
+ */
202
+ declare function createAuthenticatingFetchWithRetry(fetchImpl: typeof fetch, authHandler: AuthenticationHandler): typeof fetch;
203
+
204
+ export { A2AClient, type A2AClientOptions, type AuthenticationHandler, type HttpHeaders, createAuthenticatingFetchWithRetry };
@@ -1,34 +1,37 @@
1
+ import {
2
+ AGENT_CARD_PATH
3
+ } from "../chunk-67JNQ6TZ.js";
4
+
1
5
  // src/client/client.ts
2
- var A2AClient = class _A2AClient {
3
- agentBaseUrl;
4
- agentCardPath;
6
+ var A2AClient = class {
5
7
  agentCardPromise;
6
- static DEFAULT_AGENT_CARD_PATH = ".well-known/agent.json";
7
8
  requestIdCounter = 1;
8
9
  serviceEndpointUrl;
9
10
  // To be populated from AgentCard after fetching
11
+ fetchImpl;
10
12
  /**
11
13
  * Constructs an A2AClient instance.
12
14
  * It initiates fetching the agent card from the provided agent baseUrl.
13
- * The Agent Card is fetched from a path relative to the agentBaseUrl, which defaults to '.well-known/agent.json'.
15
+ * The Agent Card is fetched from a path relative to the agentBaseUrl, which defaults to '.well-known/agent-card.json'.
14
16
  * The `url` field from the Agent Card will be used as the RPC service endpoint.
15
17
  * @param agentBaseUrl The base URL of the A2A agent (e.g., https://agent.example.com)
16
- * @param agentCardPath path to the agent card, defaults to .well-known/agent.json
18
+ * @param options Optional. The options for the A2AClient including the fetch implementation, agent card path, and authentication handler.
17
19
  */
18
- constructor(agentBaseUrl, agentCardPath = _A2AClient.DEFAULT_AGENT_CARD_PATH) {
19
- this.agentBaseUrl = agentBaseUrl.replace(/\/$/, "");
20
- this.agentCardPath = agentCardPath.replace(/^\//, "");
21
- this.agentCardPromise = this._fetchAndCacheAgentCard();
20
+ constructor(agentBaseUrl, options) {
21
+ this.fetchImpl = options?.fetchImpl ?? fetch;
22
+ this.agentCardPromise = this._fetchAndCacheAgentCard(agentBaseUrl, options?.agentCardPath);
22
23
  }
23
24
  /**
24
25
  * Fetches the Agent Card from the agent's well-known URI and caches its service endpoint URL.
25
26
  * This method is called by the constructor.
27
+ * @param agentBaseUrl The base URL of the A2A agent (e.g., https://agent.example.com)
28
+ * @param agentCardPath path to the agent card, defaults to .well-known/agent-card.json
26
29
  * @returns A Promise that resolves to the AgentCard.
27
30
  */
28
- async _fetchAndCacheAgentCard() {
29
- const agentCardUrl = `${this.agentBaseUrl}/${this.agentCardPath}`;
31
+ async _fetchAndCacheAgentCard(agentBaseUrl, agentCardPath) {
30
32
  try {
31
- const response = await fetch(agentCardUrl, {
33
+ const agentCardUrl = this.resolveAgentCardUrl(agentBaseUrl, agentCardPath);
34
+ const response = await this.fetchImpl(agentCardUrl, {
32
35
  headers: { "Accept": "application/json" }
33
36
  });
34
37
  if (!response.ok) {
@@ -41,7 +44,7 @@ var A2AClient = class _A2AClient {
41
44
  this.serviceEndpointUrl = agentCard.url;
42
45
  return agentCard;
43
46
  } catch (error) {
44
- console.error("Error fetching or parsing Agent Card:");
47
+ console.error("Error fetching or parsing Agent Card:", error);
45
48
  throw error;
46
49
  }
47
50
  }
@@ -50,14 +53,14 @@ var A2AClient = class _A2AClient {
50
53
  * If an `agentBaseUrl` is provided, it fetches the card from that specific URL.
51
54
  * Otherwise, it returns the card fetched and cached during client construction.
52
55
  * @param agentBaseUrl Optional. The base URL of the agent to fetch the card from.
53
- * @param agentCardPath path to the agent card, defaults to .well-known/agent.json
56
+ * @param agentCardPath path to the agent card, defaults to .well-known/agent-card.json
54
57
  * If provided, this will fetch a new card, not use the cached one from the constructor's URL.
55
58
  * @returns A Promise that resolves to the AgentCard.
56
59
  */
57
- async getAgentCard(agentBaseUrl, agentCardPath = _A2AClient.DEFAULT_AGENT_CARD_PATH) {
60
+ async getAgentCard(agentBaseUrl, agentCardPath) {
58
61
  if (agentBaseUrl) {
59
- const agentCardUrl = `${agentBaseUrl.replace(/\/$/, "")}/${agentCardPath.replace(/^\//, "")}`;
60
- const response = await fetch(agentCardUrl, {
62
+ const agentCardUrl = this.resolveAgentCardUrl(agentBaseUrl, agentCardPath);
63
+ const response = await this.fetchImpl(agentCardUrl, {
61
64
  headers: { "Accept": "application/json" }
62
65
  });
63
66
  if (!response.ok) {
@@ -67,6 +70,14 @@ var A2AClient = class _A2AClient {
67
70
  }
68
71
  return this.agentCardPromise;
69
72
  }
73
+ /**
74
+ * Determines the agent card URL based on the agent URL.
75
+ * @param agentBaseUrl The agent URL.
76
+ * @param agentCardPath Optional relative path to the agent card, defaults to .well-known/agent-card.json
77
+ */
78
+ resolveAgentCardUrl(agentBaseUrl, agentCardPath = AGENT_CARD_PATH) {
79
+ return `${agentBaseUrl.replace(/\/$/, "")}/${agentCardPath.replace(/^\//, "")}`;
80
+ }
70
81
  /**
71
82
  * Gets the RPC service endpoint URL. Ensures the agent card has been fetched first.
72
83
  * @returns A Promise that resolves to the service endpoint URL string.
@@ -97,21 +108,15 @@ var A2AClient = class _A2AClient {
97
108
  // Cast because TParams structure varies per method
98
109
  id: requestId
99
110
  };
100
- const httpResponse = await fetch(endpoint, {
101
- method: "POST",
102
- headers: {
103
- "Content-Type": "application/json",
104
- "Accept": "application/json"
105
- // Expect JSON response for non-streaming requests
106
- },
107
- body: JSON.stringify(rpcRequest)
108
- });
111
+ const httpResponse = await this._fetchRpc(endpoint, rpcRequest);
109
112
  if (!httpResponse.ok) {
110
113
  let errorBodyText = "(empty or non-JSON response)";
111
114
  try {
112
115
  errorBodyText = await httpResponse.text();
113
116
  const errorJson = JSON.parse(errorBodyText);
114
- if (!errorJson.jsonrpc && errorJson.error) {
117
+ if (errorJson.jsonrpc && errorJson.error) {
118
+ return errorJson;
119
+ } else if (!errorJson.jsonrpc && errorJson.error) {
115
120
  throw new Error(`RPC error for ${method}: ${errorJson.error.message} (Code: ${errorJson.error.code}, HTTP Status: ${httpResponse.status}) Data: ${JSON.stringify(errorJson.error.data || {})}`);
116
121
  } else if (!errorJson.jsonrpc) {
117
122
  throw new Error(`HTTP error for ${method}! Status: ${httpResponse.status} ${httpResponse.statusText}. Response: ${errorBodyText}`);
@@ -127,6 +132,25 @@ var A2AClient = class _A2AClient {
127
132
  }
128
133
  return rpcResponse;
129
134
  }
135
+ /**
136
+ * Internal helper method to fetch the RPC service endpoint.
137
+ * @param url The URL to fetch.
138
+ * @param rpcRequest The JSON-RPC request to send.
139
+ * @param acceptHeader The Accept header to use. Defaults to "application/json".
140
+ * @returns A Promise that resolves to the fetch HTTP response.
141
+ */
142
+ async _fetchRpc(url, rpcRequest, acceptHeader = "application/json") {
143
+ const requestInit = {
144
+ method: "POST",
145
+ headers: {
146
+ "Content-Type": "application/json",
147
+ "Accept": acceptHeader
148
+ // Expect JSON response for non-streaming requests
149
+ },
150
+ body: JSON.stringify(rpcRequest)
151
+ };
152
+ return this.fetchImpl(url, requestInit);
153
+ }
130
154
  /**
131
155
  * Sends a message to the agent.
132
156
  * The behavior (blocking/non-blocking) and push notification configuration
@@ -161,15 +185,7 @@ var A2AClient = class _A2AClient {
161
185
  params,
162
186
  id: clientRequestId
163
187
  };
164
- const response = await fetch(endpoint, {
165
- method: "POST",
166
- headers: {
167
- "Content-Type": "application/json",
168
- "Accept": "text/event-stream"
169
- // Crucial for SSE
170
- },
171
- body: JSON.stringify(rpcRequest)
172
- });
188
+ const response = await this._fetchRpc(endpoint, rpcRequest, "text/event-stream");
173
189
  if (!response.ok) {
174
190
  let errorBody = "";
175
191
  try {
@@ -253,7 +269,7 @@ var A2AClient = class _A2AClient {
253
269
  params,
254
270
  id: clientRequestId
255
271
  };
256
- const response = await fetch(endpoint, {
272
+ const response = await this.fetchImpl(endpoint, {
257
273
  method: "POST",
258
274
  headers: {
259
275
  "Content-Type": "application/json",
@@ -369,6 +385,40 @@ var A2AClient = class _A2AClient {
369
385
  return "error" in response;
370
386
  }
371
387
  };
388
+
389
+ // src/client/auth-handler.ts
390
+ function createAuthenticatingFetchWithRetry(fetchImpl, authHandler) {
391
+ async function authFetch(url, init) {
392
+ const authHeaders = await authHandler.headers() || {};
393
+ const mergedInit = {
394
+ ...init || {},
395
+ headers: {
396
+ ...authHeaders,
397
+ ...init?.headers || {}
398
+ }
399
+ };
400
+ let response = await fetchImpl(url, mergedInit);
401
+ const updatedHeaders = await authHandler.shouldRetryWithHeaders(mergedInit, response);
402
+ if (updatedHeaders) {
403
+ const retryInit = {
404
+ ...init || {},
405
+ headers: {
406
+ ...updatedHeaders,
407
+ ...init?.headers || {}
408
+ }
409
+ };
410
+ response = await fetchImpl(url, retryInit);
411
+ if (response.ok && authHandler.onSuccessfulRetry) {
412
+ await authHandler.onSuccessfulRetry(updatedHeaders);
413
+ }
414
+ }
415
+ return response;
416
+ }
417
+ Object.setPrototypeOf(authFetch, Object.getPrototypeOf(fetchImpl));
418
+ Object.defineProperties(authFetch, Object.getOwnPropertyDescriptors(fetchImpl));
419
+ return authFetch;
420
+ }
372
421
  export {
373
- A2AClient
422
+ A2AClient,
423
+ createAuthenticatingFetchWithRetry
374
424
  };
package/dist/index.cjs CHANGED
@@ -2,6 +2,10 @@ var __defProp = Object.defineProperty;
2
2
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
4
4
  var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
5
9
  var __copyProps = (to, from, except, desc) => {
6
10
  if (from && typeof from === "object" || typeof from === "function") {
7
11
  for (let key of __getOwnPropNames(from))
@@ -14,4 +18,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
14
18
 
15
19
  // src/index.ts
16
20
  var index_exports = {};
21
+ __export(index_exports, {
22
+ AGENT_CARD_PATH: () => AGENT_CARD_PATH
23
+ });
17
24
  module.exports = __toCommonJS(index_exports);
25
+
26
+ // src/constants.ts
27
+ var AGENT_CARD_PATH = ".well-known/agent-card.json";
28
+ // Annotate the CommonJS export names for ESM import in node:
29
+ 0 && (module.exports = {
30
+ AGENT_CARD_PATH
31
+ });
package/dist/index.d.cts CHANGED
@@ -6,4 +6,12 @@ export { A as A2AError, e as A2ARequest, a9 as APIKeySecurityScheme, aa as Agent
6
6
  */
7
7
  type A2AResponse = SendMessageResponse | SendStreamingMessageResponse | GetTaskResponse | CancelTaskResponse | SetTaskPushNotificationConfigResponse | GetTaskPushNotificationConfigResponse | ListTaskPushNotificationConfigSuccessResponse | DeleteTaskPushNotificationConfigSuccessResponse | GetAuthenticatedExtendedCardSuccessResponse | JSONRPCErrorResponse;
8
8
 
9
- export { type A2AResponse, CancelTaskResponse, DeleteTaskPushNotificationConfigSuccessResponse, GetAuthenticatedExtendedCardSuccessResponse, GetTaskPushNotificationConfigResponse, GetTaskResponse, JSONRPCErrorResponse, ListTaskPushNotificationConfigSuccessResponse, SendMessageResponse, SendStreamingMessageResponse, SetTaskPushNotificationConfigResponse };
9
+ /**
10
+ * Shared constants for the A2A library
11
+ */
12
+ /**
13
+ * The well-known path for the agent card
14
+ */
15
+ declare const AGENT_CARD_PATH = ".well-known/agent-card.json";
16
+
17
+ export { type A2AResponse, AGENT_CARD_PATH, CancelTaskResponse, DeleteTaskPushNotificationConfigSuccessResponse, GetAuthenticatedExtendedCardSuccessResponse, GetTaskPushNotificationConfigResponse, GetTaskResponse, JSONRPCErrorResponse, ListTaskPushNotificationConfigSuccessResponse, SendMessageResponse, SendStreamingMessageResponse, SetTaskPushNotificationConfigResponse };
package/dist/index.d.ts CHANGED
@@ -6,4 +6,12 @@ export { A as A2AError, e as A2ARequest, a9 as APIKeySecurityScheme, aa as Agent
6
6
  */
7
7
  type A2AResponse = SendMessageResponse | SendStreamingMessageResponse | GetTaskResponse | CancelTaskResponse | SetTaskPushNotificationConfigResponse | GetTaskPushNotificationConfigResponse | ListTaskPushNotificationConfigSuccessResponse | DeleteTaskPushNotificationConfigSuccessResponse | GetAuthenticatedExtendedCardSuccessResponse | JSONRPCErrorResponse;
8
8
 
9
- export { type A2AResponse, CancelTaskResponse, DeleteTaskPushNotificationConfigSuccessResponse, GetAuthenticatedExtendedCardSuccessResponse, GetTaskPushNotificationConfigResponse, GetTaskResponse, JSONRPCErrorResponse, ListTaskPushNotificationConfigSuccessResponse, SendMessageResponse, SendStreamingMessageResponse, SetTaskPushNotificationConfigResponse };
9
+ /**
10
+ * Shared constants for the A2A library
11
+ */
12
+ /**
13
+ * The well-known path for the agent card
14
+ */
15
+ declare const AGENT_CARD_PATH = ".well-known/agent-card.json";
16
+
17
+ export { type A2AResponse, AGENT_CARD_PATH, CancelTaskResponse, DeleteTaskPushNotificationConfigSuccessResponse, GetAuthenticatedExtendedCardSuccessResponse, GetTaskPushNotificationConfigResponse, GetTaskResponse, JSONRPCErrorResponse, ListTaskPushNotificationConfigSuccessResponse, SendMessageResponse, SendStreamingMessageResponse, SetTaskPushNotificationConfigResponse };
package/dist/index.js CHANGED
@@ -0,0 +1,6 @@
1
+ import {
2
+ AGENT_CARD_PATH
3
+ } from "./chunk-67JNQ6TZ.js";
4
+ export {
5
+ AGENT_CARD_PATH
6
+ };
@@ -239,6 +239,9 @@ var JsonRpcTransportHandler = class {
239
239
  }
240
240
  };
241
241
 
242
+ // src/constants.ts
243
+ var AGENT_CARD_PATH = ".well-known/agent-card.json";
244
+
242
245
  // src/server/express/a2a_express_app.ts
243
246
  var A2AExpressApp = class {
244
247
  requestHandler;
@@ -253,12 +256,13 @@ var A2AExpressApp = class {
253
256
  * @param app Optional existing Express app.
254
257
  * @param baseUrl The base URL for A2A endpoints (e.g., "/a2a/api").
255
258
  * @param middlewares Optional array of Express middlewares to apply to the A2A routes.
259
+ * @param agentCardPath Optional custom path for the agent card endpoint (defaults to /.well-known/agent-card.json).
256
260
  * @returns The Express app with A2A routes.
257
261
  */
258
- setupRoutes(app, baseUrl = "", middlewares) {
262
+ setupRoutes(app, baseUrl = "", middlewares, agentCardPath = AGENT_CARD_PATH) {
259
263
  const router = import_express.default.Router();
260
264
  router.use(import_express.default.json(), ...middlewares ?? []);
261
- router.get("/.well-known/agent.json", async (req, res) => {
265
+ router.get(`/${agentCardPath}`, async (req, res) => {
262
266
  try {
263
267
  const agentCard = await this.requestHandler.getAgentCard();
264
268
  res.json(agentCard);
@@ -11,9 +11,10 @@ declare class A2AExpressApp {
11
11
  * @param app Optional existing Express app.
12
12
  * @param baseUrl The base URL for A2A endpoints (e.g., "/a2a/api").
13
13
  * @param middlewares Optional array of Express middlewares to apply to the A2A routes.
14
+ * @param agentCardPath Optional custom path for the agent card endpoint (defaults to /.well-known/agent-card.json).
14
15
  * @returns The Express app with A2A routes.
15
16
  */
16
- setupRoutes(app: Express, baseUrl?: string, middlewares?: Array<RequestHandler | ErrorRequestHandler>): Express;
17
+ setupRoutes(app: Express, baseUrl?: string, middlewares?: Array<RequestHandler | ErrorRequestHandler>, agentCardPath?: string): Express;
17
18
  }
18
19
 
19
20
  export { A2AExpressApp };
@@ -11,9 +11,10 @@ declare class A2AExpressApp {
11
11
  * @param app Optional existing Express app.
12
12
  * @param baseUrl The base URL for A2A endpoints (e.g., "/a2a/api").
13
13
  * @param middlewares Optional array of Express middlewares to apply to the A2A routes.
14
+ * @param agentCardPath Optional custom path for the agent card endpoint (defaults to /.well-known/agent-card.json).
14
15
  * @returns The Express app with A2A routes.
15
16
  */
16
- setupRoutes(app: Express, baseUrl?: string, middlewares?: Array<RequestHandler | ErrorRequestHandler>): Express;
17
+ setupRoutes(app: Express, baseUrl?: string, middlewares?: Array<RequestHandler | ErrorRequestHandler>, agentCardPath?: string): Express;
17
18
  }
18
19
 
19
20
  export { A2AExpressApp };
@@ -1,3 +1,6 @@
1
+ import {
2
+ AGENT_CARD_PATH
3
+ } from "../../chunk-67JNQ6TZ.js";
1
4
  import {
2
5
  A2AError,
3
6
  JsonRpcTransportHandler
@@ -18,12 +21,13 @@ var A2AExpressApp = class {
18
21
  * @param app Optional existing Express app.
19
22
  * @param baseUrl The base URL for A2A endpoints (e.g., "/a2a/api").
20
23
  * @param middlewares Optional array of Express middlewares to apply to the A2A routes.
24
+ * @param agentCardPath Optional custom path for the agent card endpoint (defaults to /.well-known/agent-card.json).
21
25
  * @returns The Express app with A2A routes.
22
26
  */
23
- setupRoutes(app, baseUrl = "", middlewares) {
27
+ setupRoutes(app, baseUrl = "", middlewares, agentCardPath = AGENT_CARD_PATH) {
24
28
  const router = express.Router();
25
29
  router.use(express.json(), ...middlewares ?? []);
26
- router.get("/.well-known/agent.json", async (req, res) => {
30
+ router.get(`/${agentCardPath}`, async (req, res) => {
27
31
  try {
28
32
  const agentCard = await this.requestHandler.getAgentCard();
29
33
  res.json(agentCard);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a2a-js/sdk",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Server & Client SDK for Agent2Agent protocol",
5
5
  "repository": {
6
6
  "type": "git",