@mailmodo/a2a 0.3.5 → 0.3.7

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
@@ -1,4 +1,4 @@
1
- # A2A JavaScript SDK
1
+ # A2A JavaScript SDK - Mailmodo Edition
2
2
 
3
3
  [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
4
4
 
@@ -13,26 +13,63 @@
13
13
 
14
14
  <!-- markdownlint-enable no-inline-html -->
15
15
 
16
+ > **Note**: This is Mailmodo's customized fork of the [official A2A JavaScript SDK](https://github.com/a2aproject/a2a-js). Key differences:
17
+ > - **Dual Module Support**: Supports both **CommonJS** and **ESM** (upstream only supports ESM)
18
+ > - **Backwards Compatibility**: Works with older TypeScript configurations using `typesVersions`
19
+
16
20
  ## Installation
17
21
 
18
- You can install the A2A SDK using `npm`.
22
+ You can install the Mailmodo A2A SDK using `npm`.
19
23
 
20
24
  ```bash
21
- npm install @a2a-js/sdk
25
+ npm install @mailmodo/a2a
22
26
  ```
23
27
 
24
28
  ### For Server Usage
25
29
 
26
- If you plan to use the A2A server functionality (`A2AExpressApp`), you'll also need to install Express as it's a peer dependency:
30
+ If you plan to use the Express integration (imports from `@mailmodo/a2a/server/express`) for A2A server, you'll also need to install Express as it's a peer dependency:
27
31
 
28
32
  ```bash
29
33
  npm install express
30
34
  ```
31
35
 
32
- You can also find JavaScript samples [here](https://github.com/google-a2a/a2a-samples/tree/main/samples/js).
36
+ You can also find some samples [here](https://github.com/mailmodo/a2a-js/tree/main/src/samples).
37
+
38
+ ---
39
+
40
+ ## Module System Compatibility
41
+
42
+ This package supports both **CommonJS** and **ESM** module systems:
43
+
44
+ ### CommonJS (Node.js)
45
+ ```javascript
46
+ const { Task, Message } = require('@mailmodo/a2a');
47
+ const { DefaultRequestHandler } = require('@mailmodo/a2a/server');
48
+ const { A2AExpressApp } = require('@mailmodo/a2a/server/express');
49
+ const { ClientFactory } = require('@mailmodo/a2a/client');
50
+ ```
51
+
52
+ ### ESM (ES Modules)
53
+ ```javascript
54
+ import { Task, Message } from '@mailmodo/a2a';
55
+ import { DefaultRequestHandler } from '@mailmodo/a2a/server';
56
+ import { A2AExpressApp } from '@mailmodo/a2a/server/express';
57
+ import { ClientFactory } from '@mailmodo/a2a/client';
58
+ ```
59
+
33
60
 
34
61
  ---
35
62
 
63
+ ## Compatibility
64
+
65
+ This SDK implements the A2A Protocol Specification [`v0.3.0`](https://a2a-protocol.org/v0.3.0/specification).
66
+
67
+ | Transport | Client | Server |
68
+ | :--- | :---: | :---: |
69
+ | **JSON-RPC** | ✅ | ✅ |
70
+ | **HTTP+JSON/REST** | ✅ | ✅ |
71
+ | **gRPC** | ❌ | ❌ |
72
+
36
73
  ## Quickstart
37
74
 
38
75
  This example shows how to create a simple "Hello World" agent server and a client to interact with it.
@@ -45,15 +82,15 @@ The core of an A2A server is the `AgentExecutor`, which contains your agent's lo
45
82
  // server.ts
46
83
  import express from 'express';
47
84
  import { v4 as uuidv4 } from 'uuid';
48
- import type { AgentCard, Message } from '@a2a-js/sdk';
85
+ import { AgentCard, Message, AGENT_CARD_PATH } from '@mailmodo/a2a';
49
86
  import {
50
87
  AgentExecutor,
51
88
  RequestContext,
52
89
  ExecutionEventBus,
53
90
  DefaultRequestHandler,
54
91
  InMemoryTaskStore,
55
- } from '@a2a-js/sdk/server';
56
- import { A2AExpressApp } from '@a2a-js/sdk/server/express';
92
+ } from '@mailmodo/a2a/server';
93
+ import { agentCardHandler, jsonRpcHandler, restHandler, UserBuilder } from '@mailmodo/a2a/server/express';
57
94
 
58
95
  // 1. Define your agent's identity card.
59
96
  const helloAgentCard: AgentCard = {
@@ -61,9 +98,17 @@ const helloAgentCard: AgentCard = {
61
98
  description: 'A simple agent that says hello.',
62
99
  protocolVersion: '0.3.0',
63
100
  version: '0.1.0',
64
- url: 'http://localhost:4000/', // The public URL of your agent server
101
+ url: 'http://localhost:4000/a2a/jsonrpc', // The public URL of your agent server
65
102
  skills: [{ id: 'chat', name: 'Chat', description: 'Say hello', tags: ['chat'] }],
66
- // --- Other AgentCard fields omitted for brevity ---
103
+ capabilities: {
104
+ pushNotifications: false,
105
+ },
106
+ defaultInputModes: ['text'],
107
+ defaultOutputModes: ['text'],
108
+ additionalInterfaces: [
109
+ { url: 'http://localhost:4000/a2a/jsonrpc', transport: 'JSONRPC' }, // Default JSON-RPC transport
110
+ { url: 'http://localhost:4000/a2a/rest', transport: 'HTTP+JSON' }, // HTTP+JSON/REST transport
111
+ ],
67
112
  };
68
113
 
69
114
  // 2. Implement the agent's logic.
@@ -96,27 +141,33 @@ const requestHandler = new DefaultRequestHandler(
96
141
  agentExecutor
97
142
  );
98
143
 
99
- const appBuilder = new A2AExpressApp(requestHandler);
100
- const expressApp = appBuilder.setupRoutes(express());
144
+ const app = express();
145
+
146
+ app.use(`/${AGENT_CARD_PATH}`, agentCardHandler({ agentCardProvider: requestHandler }));
147
+ app.use('/a2a/jsonrpc', jsonRpcHandler({ requestHandler, userBuilder: UserBuilder.noAuthentication }));
148
+ app.use('/a2a/rest', restHandler({ requestHandler, userBuilder: UserBuilder.noAuthentication }));
101
149
 
102
- expressApp.listen(4000, () => {
150
+ app.listen(4000, () => {
103
151
  console.log(`🚀 Server started on http://localhost:4000`);
104
152
  });
105
153
  ```
106
154
 
107
155
  ### Client: Sending a Message
108
156
 
109
- The `A2AClient` makes it easy to communicate with any A2A-compliant agent.
157
+ The [`ClientFactory`](src/client/factory.ts) makes it easy to communicate with any A2A-compliant agent.
110
158
 
111
159
  ```typescript
112
160
  // client.ts
113
- import { A2AClient, SendMessageSuccessResponse } from '@a2a-js/sdk/client';
114
- import { Message, MessageSendParams } from '@a2a-js/sdk';
161
+ import { ClientFactory } from '@mailmodo/a2a/client';
162
+ import { Message, MessageSendParams, SendMessageSuccessResponse } from '@mailmodo/a2a';
115
163
  import { v4 as uuidv4 } from 'uuid';
116
164
 
117
165
  async function run() {
118
- // Create a client pointing to the agent's Agent Card URL.
119
- const client = await A2AClient.fromCardUrl('http://localhost:4000/.well-known/agent-card.json');
166
+ const factory = new ClientFactory();
167
+
168
+ // createFromUrl accepts baseUrl and optional path,
169
+ // (the default path is /.well-known/agent-card.json)
170
+ const client = await factory.createFromUrl('http://localhost:4000');
120
171
 
121
172
  const sendParams: MessageSendParams = {
122
173
  message: {
@@ -127,13 +178,12 @@ async function run() {
127
178
  },
128
179
  };
129
180
 
130
- const response = await client.sendMessage(sendParams);
131
-
132
- if ('error' in response) {
133
- console.error('Error:', response.error.message);
134
- } else {
135
- const result = (response as SendMessageSuccessResponse).result as Message;
181
+ try {
182
+ const response = await client.sendMessage(sendParams);
183
+ const result = response as Message;
136
184
  console.log('Agent response:', result.parts[0].text); // "Hello, world!"
185
+ } catch(e) {
186
+ console.error('Error:', e);
137
187
  }
138
188
  }
139
189
 
@@ -152,7 +202,7 @@ This agent creates a task, attaches a file artifact to it, and marks it as compl
152
202
 
153
203
  ```typescript
154
204
  // server.ts
155
- import { Task, TaskArtifactUpdateEvent, TaskStatusUpdateEvent } from '@a2a-js/sdk';
205
+ import { Task, TaskArtifactUpdateEvent, TaskStatusUpdateEvent } from '@mailmodo/a2a';
156
206
  // ... other imports from the quickstart server ...
157
207
 
158
208
  class TaskExecutor implements AgentExecutor {
@@ -209,25 +259,25 @@ The client sends a message and receives a `Task` object as the result.
209
259
 
210
260
  ```typescript
211
261
  // client.ts
212
- import { A2AClient, SendMessageSuccessResponse } from '@a2a-js/sdk/client';
213
- import { Message, MessageSendParams, Task } from '@a2a-js/sdk';
262
+ import { ClientFactory } from '@mailmodo/a2a/client';
263
+ import { Message, MessageSendParams, SendMessageSuccessResponse, Task } from '@mailmodo/a2a';
214
264
  // ... other imports ...
215
265
 
216
- const client = await A2AClient.fromCardUrl('http://localhost:4000/.well-known/agent-card.json');
266
+ const factory = new ClientFactory();
217
267
 
218
- const response = await client.sendMessage({
219
- message: {
220
- messageId: uuidv4(),
221
- role: 'user',
222
- parts: [{ kind: 'text', text: 'Do something.' }],
223
- kind: 'message',
224
- },
225
- });
268
+ // createFromUrl accepts baseUrl and optional path,
269
+ // (the default path is /.well-known/agent-card.json)
270
+ const client = await factory.createFromUrl('http://localhost:4000');
226
271
 
227
- if ('error' in response) {
228
- console.error('Error:', response.error.message);
229
- } else {
230
- const result = (response as SendMessageSuccessResponse).result;
272
+ try {
273
+ const result = await client.sendMessage({
274
+ message: {
275
+ messageId: uuidv4(),
276
+ role: 'user',
277
+ parts: [{ kind: 'text', text: 'Do something.' }],
278
+ kind: 'message',
279
+ },
280
+ });
231
281
 
232
282
  // Check if the agent's response is a Task or a direct Message.
233
283
  if (result.kind === 'task') {
@@ -242,6 +292,8 @@ if ('error' in response) {
242
292
  const message = result as Message;
243
293
  console.log('Received direct message:', message.parts[0].text);
244
294
  }
295
+ } catch (e) {
296
+ console.error('Error:', e);
245
297
  }
246
298
  ```
247
299
 
@@ -249,38 +301,49 @@ if ('error' in response) {
249
301
 
250
302
  ## Client Customization
251
303
 
252
- You can provide a custom `fetch` implementation to the `A2AClient` to modify its HTTP request behavior. Common use cases include:
304
+ Client can be customized via [`CallInterceptor`'s](src/client/interceptors.ts) which is a recommended way as it's transport-agnostic.
305
+
306
+ Common use cases include:
253
307
 
254
308
  - **Request Interception**: Log outgoing requests or collect metrics.
255
309
  - **Header Injection**: Add custom headers for authentication, tracing, or routing.
256
- - **Retry Mechanisms**: Implement custom logic for retrying failed requests.
310
+ - **A2A Extensions**: Modifying payloads to include protocol extension data.
257
311
 
258
312
  ### Example: Injecting a Custom Header
259
313
 
260
- This example creates a `fetch` wrapper that adds a unique `X-Request-ID` to every outgoing request.
314
+ This example defines a `CallInterceptor` to update `serviceParameters` which are passed as HTTP headers.
261
315
 
262
316
  ```typescript
263
- import { A2AClient } from '@a2a-js/sdk/client';
264
317
  import { v4 as uuidv4 } from 'uuid';
318
+ import { AfterArgs, BeforeArgs, CallInterceptor, ClientFactory, ClientFactoryOptions } from '@mailmodo/a2a/client';
319
+
320
+ // 1. Define an interceptor
321
+ class RequestIdInterceptor implements CallInterceptor {
322
+ before(args: BeforeArgs): Promise<void> {
323
+ args.options = {
324
+ ...args.options,
325
+ serviceParameters: {
326
+ ...args.options.serviceParameters,
327
+ ['X-Request-ID']: uuidv4(),
328
+ },
329
+ };
330
+ return Promise.resolve();
331
+ }
265
332
 
266
- // 1. Create a wrapper around the global fetch function.
267
- const fetchWithCustomHeader: typeof fetch = async (url, init) => {
268
- const headers = new Headers(init?.headers);
269
- headers.set('X-Request-ID', uuidv4());
270
-
271
- const newInit = { ...init, headers };
272
-
273
- console.log(`Sending request to ${url} with X-Request-ID: ${headers.get('X-Request-ID')}`);
274
-
275
- return fetch(url, newInit);
276
- };
333
+ after(): Promise<void> {
334
+ return Promise.resolve();
335
+ }
336
+ }
277
337
 
278
- // 2. Provide the custom fetch implementation to the client.
279
- const client = await A2AClient.fromCardUrl('http://localhost:4000/.well-known/agent-card.json', {
280
- fetchImpl: fetchWithCustomHeader,
281
- });
338
+ // 2. Register the interceptor in the client factory
339
+ const factory = new ClientFactory(ClientFactoryOptions.createFrom(ClientFactoryOptions.default, {
340
+ clientConfig: {
341
+ interceptors: [new RequestIdInterceptor()]
342
+ }
343
+ }))
344
+ const client = await factory.createFromAgentCardUrl('http://localhost:4000');
282
345
 
283
- // Now, all requests made by this client instance will include the X-Request-ID header.
346
+ // Now, all requests made by clients created by this factory will include the X-Request-ID header.
284
347
  await client.sendMessage({
285
348
  message: {
286
349
  messageId: uuidv4(),
@@ -293,33 +356,33 @@ await client.sendMessage({
293
356
 
294
357
  ### Example: Specifying a Timeout
295
358
 
296
- This example creates a `fetch` wrapper that sets a timeout for every outgoing request.
359
+ Each client method can be configured with an optional `signal` field.
297
360
 
298
361
  ```typescript
299
- import { A2AClient } from '@a2a-js/sdk/client';
362
+ import { ClientFactory } from '@mailmodo/a2a/client';
300
363
 
301
- // 1. Create a wrapper around the global fetch function.
302
- const fetchWithTimeout: typeof fetch = async (url, init) => {
303
- return fetch(url, { ...init, signal: AbortSignal.timeout(5000) });
304
- };
364
+ const factory = new ClientFactory();
305
365
 
306
- // 2. Provide the custom fetch implementation to the client.
307
- const client = await A2AClient.fromCardUrl('http://localhost:4000/.well-known/agent-card.json', {
308
- fetchImpl: fetchWithTimeout,
309
- });
366
+ // createFromUrl accepts baseUrl and optional path,
367
+ // (the default path is /.well-known/agent-card.json)
368
+ const client = await factory.createFromUrl('http://localhost:4000');
310
369
 
311
- // Now, all requests made by this client instance will have a configured timeout.
312
- await client.sendMessage({
313
- message: {
314
- messageId: uuidv4(),
315
- role: 'user',
316
- parts: [{ kind: 'text', text: 'A message requiring custom headers.' }],
317
- kind: 'message',
370
+ await client.sendMessage(
371
+ {
372
+ message: {
373
+ messageId: uuidv4(),
374
+ role: 'user',
375
+ parts: [{ kind: 'text', text: 'A long-running message.' }],
376
+ kind: 'message',
377
+ },
318
378
  },
319
- });
379
+ {
380
+ signal: AbortSignal.timeout(5000), // 5 seconds timeout
381
+ }
382
+ );
320
383
  ```
321
384
 
322
- ### Using the Provided `AuthenticationHandler`
385
+ ### Customizing Transports: Using the Provided `AuthenticationHandler`
323
386
 
324
387
  For advanced authentication scenarios, the SDK includes a higher-order function `createAuthenticatingFetchWithRetry` and an `AuthenticationHandler` interface. This utility automatically adds authorization headers and can retry requests that fail with authentication errors (e.g., 401 Unauthorized).
325
388
 
@@ -327,10 +390,12 @@ Here's how to use it to manage a Bearer token:
327
390
 
328
391
  ```typescript
329
392
  import {
330
- A2AClient,
393
+ ClientFactory,
394
+ ClientFactoryOptions,
395
+ JsonRpcTransportFactory,
331
396
  AuthenticationHandler,
332
397
  createAuthenticatingFetchWithRetry,
333
- } from '@a2a-js/sdk/client';
398
+ } from '@mailmodo/a2a/client';
334
399
 
335
400
  // A simple token provider that simulates fetching a new token.
336
401
  const tokenProvider = {
@@ -367,10 +432,15 @@ const handler: AuthenticationHandler = {
367
432
  // 2. Create the authenticated fetch function.
368
433
  const authFetch = createAuthenticatingFetchWithRetry(fetch, handler);
369
434
 
370
- // 3. Initialize the client with the new fetch implementation.
371
- const client = await A2AClient.fromCardUrl('http://localhost:4000/.well-known/agent-card.json', {
372
- fetchImpl: authFetch,
373
- });
435
+ // 3. Inject new fetch implementation into a client factory.
436
+ const factory = new ClientFactory(ClientFactoryOptions.createFrom(ClientFactoryOptions.default, {
437
+ transports: [
438
+ new JsonRpcTransportFactory({ fetchImpl: authFetch })
439
+ ]
440
+ }))
441
+
442
+ // 4. Clients created from the factory are going to have custom fetch attached.
443
+ const client = await factory.createFromUrl('http://localhost:4000');
374
444
  ```
375
445
 
376
446
  ---
@@ -444,12 +514,16 @@ The `sendMessageStream` method returns an `AsyncGenerator` that yields events as
444
514
 
445
515
  ```typescript
446
516
  // client.ts
447
- import { A2AClient } from '@a2a-js/sdk/client';
448
- import { MessageSendParams } from '@a2a-js/sdk';
517
+ import { ClientFactory } from '@mailmodo/a2a/client';
518
+ import { MessageSendParams } from '@mailmodo/a2a';
449
519
  import { v4 as uuidv4 } from 'uuid';
450
520
  // ... other imports ...
451
521
 
452
- const client = await A2AClient.fromCardUrl('http://localhost:4000/.well-known/agent-card.json');
522
+ const factory = new ClientFactory();
523
+
524
+ // createFromUrl accepts baseUrl and optional path,
525
+ // (the default path is /.well-known/agent-card.json)
526
+ const client = await factory.createFromUrl('http://localhost:4000');
453
527
 
454
528
  async function streamTask() {
455
529
  const streamParams: MessageSendParams = {
@@ -499,7 +573,7 @@ import {
499
573
  RequestContext,
500
574
  ExecutionEventBus,
501
575
  TaskStatusUpdateEvent,
502
- } from '@a2a-js/sdk/server';
576
+ } from '@mailmodo/a2a/server';
503
577
  // ... other imports ...
504
578
 
505
579
  class CancellableExecutor implements AgentExecutor {
@@ -598,7 +672,7 @@ import {
598
672
  DefaultRequestHandler,
599
673
  InMemoryPushNotificationStore,
600
674
  DefaultPushNotificationSender,
601
- } from '@a2a-js/sdk/server';
675
+ } from '@mailmodo/a2a/server';
602
676
 
603
677
  // Optional: Custom push notification store and sender
604
678
  const pushNotificationStore = new InMemoryPushNotificationStore();
@@ -671,8 +745,12 @@ app.post('/webhook/task-updates', (req, res) => {
671
745
 
672
746
  ## License
673
747
 
674
- This project is licensed under the terms of the [Apache 2.0 License](https://raw.githubusercontent.com/google-a2a/a2a-python/refs/heads/main/LICENSE).
748
+ This project is licensed under the terms of the [Apache 2.0 License](LICENSE).
749
+
750
+ ## Upstream & Contributing
751
+
752
+ This is a customized fork of the [official A2A JavaScript SDK](https://github.com/a2aproject/a2a-js) maintained by Mailmodo.
675
753
 
676
- ## Contributing
754
+ For contributing to the upstream project, see the [upstream CONTRIBUTING.md](https://github.com/a2aproject/a2a-js/blob/main/CONTRIBUTING.md).
677
755
 
678
- See [CONTRIBUTING.md](https://github.com/google-a2a/a2a-js/blob/main/CONTRIBUTING.md) for contribution guidelines.
756
+ For issues or contributions specific to this Mailmodo edition, please contact the Mailmodo team.
@@ -1,9 +1,21 @@
1
- import { A as AgentCard, M as MessageSendParams, a as Message, T as Task, b as TaskStatusUpdateEvent, c as TaskArtifactUpdateEvent, i as TaskQueryParams, f as TaskIdParams, d as TaskPushNotificationConfig, a8 as GetTaskPushNotificationConfigParams, L as ListTaskPushNotificationConfigParams, D as DeleteTaskPushNotificationConfigParams } from './types-Due_Cv6t.js';
1
+ import { E as Extensions, ae as AgentCard, x as MessageSendParams, F as Message, ay as Task, aQ as TaskStatusUpdateEvent, aS as TaskArtifactUpdateEvent, X as TaskQueryParams, Z as TaskIdParams, $ as TaskPushNotificationConfig, a3 as GetTaskPushNotificationConfigParams, a7 as ListTaskPushNotificationConfigParams, a9 as DeleteTaskPushNotificationConfigParams } from './extensions-DvruCIzw.mjs';
2
2
 
3
+ /**
4
+ * Represents a user accessing A2A server.
5
+ */
3
6
  interface User {
7
+ /**
8
+ * Indicates whether the user is authenticated.
9
+ */
4
10
  get isAuthenticated(): boolean;
11
+ /**
12
+ * A unique name (identifier) for the user.
13
+ */
5
14
  get userName(): string;
6
15
  }
16
+ /**
17
+ * An implementation of {@link User} representing an unauthenticated user.
18
+ */
7
19
  declare class UnauthenticatedUser implements User {
8
20
  get isAuthenticated(): boolean;
9
21
  get userName(): string;
@@ -13,10 +25,10 @@ declare class ServerCallContext {
13
25
  private readonly _requestedExtensions?;
14
26
  private readonly _user?;
15
27
  private _activatedExtensions?;
16
- constructor(requestedExtensions?: Set<string>, user?: User);
28
+ constructor(requestedExtensions?: Extensions, user?: User);
17
29
  get user(): User | undefined;
18
- get activatedExtensions(): ReadonlySet<string> | undefined;
19
- get requestedExtensions(): ReadonlySet<string> | undefined;
30
+ get activatedExtensions(): Extensions | undefined;
31
+ get requestedExtensions(): Extensions | undefined;
20
32
  addActivatedExtension(uri: string): void;
21
33
  }
22
34
 
@@ -1,9 +1,21 @@
1
- import { A as AgentCard, M as MessageSendParams, a as Message, T as Task, b as TaskStatusUpdateEvent, c as TaskArtifactUpdateEvent, i as TaskQueryParams, f as TaskIdParams, d as TaskPushNotificationConfig, a8 as GetTaskPushNotificationConfigParams, L as ListTaskPushNotificationConfigParams, D as DeleteTaskPushNotificationConfigParams } from './types-Due_Cv6t.mjs';
1
+ import { E as Extensions, ae as AgentCard, x as MessageSendParams, F as Message, ay as Task, aQ as TaskStatusUpdateEvent, aS as TaskArtifactUpdateEvent, X as TaskQueryParams, Z as TaskIdParams, $ as TaskPushNotificationConfig, a3 as GetTaskPushNotificationConfigParams, a7 as ListTaskPushNotificationConfigParams, a9 as DeleteTaskPushNotificationConfigParams } from './extensions-DvruCIzw.js';
2
2
 
3
+ /**
4
+ * Represents a user accessing A2A server.
5
+ */
3
6
  interface User {
7
+ /**
8
+ * Indicates whether the user is authenticated.
9
+ */
4
10
  get isAuthenticated(): boolean;
11
+ /**
12
+ * A unique name (identifier) for the user.
13
+ */
5
14
  get userName(): string;
6
15
  }
16
+ /**
17
+ * An implementation of {@link User} representing an unauthenticated user.
18
+ */
7
19
  declare class UnauthenticatedUser implements User {
8
20
  get isAuthenticated(): boolean;
9
21
  get userName(): string;
@@ -13,10 +25,10 @@ declare class ServerCallContext {
13
25
  private readonly _requestedExtensions?;
14
26
  private readonly _user?;
15
27
  private _activatedExtensions?;
16
- constructor(requestedExtensions?: Set<string>, user?: User);
28
+ constructor(requestedExtensions?: Extensions, user?: User);
17
29
  get user(): User | undefined;
18
- get activatedExtensions(): ReadonlySet<string> | undefined;
19
- get requestedExtensions(): ReadonlySet<string> | undefined;
30
+ get activatedExtensions(): Extensions | undefined;
31
+ get requestedExtensions(): Extensions | undefined;
20
32
  addActivatedExtension(uri: string): void;
21
33
  }
22
34
 
@@ -0,0 +1,38 @@
1
+ // src/extensions.ts
2
+ var Extensions = {
3
+ /**
4
+ * Creates new {@link Extensions} from `current` and `additional`.
5
+ * If `current` already contains `additional` it is returned unmodified.
6
+ */
7
+ createFrom: (current, additional) => {
8
+ if (current?.includes(additional)) {
9
+ return current;
10
+ }
11
+ return [...current ?? [], additional];
12
+ },
13
+ /**
14
+ * Creates {@link Extensions} from comma separated extensions identifiers as per
15
+ * https://a2a-protocol.org/latest/specification/#326-service-parameters.
16
+ * Parses the output of `toServiceParameter`.
17
+ */
18
+ parseServiceParameter: (value) => {
19
+ if (!value) {
20
+ return [];
21
+ }
22
+ const unique = new Set(
23
+ value.split(",").map((ext) => ext.trim()).filter((ext) => ext.length > 0)
24
+ );
25
+ return Array.from(unique);
26
+ },
27
+ /**
28
+ * Converts {@link Extensions} to comma separated extensions identifiers as per
29
+ * https://a2a-protocol.org/latest/specification/#326-service-parameters.
30
+ */
31
+ toServiceParameter: (value) => {
32
+ return value.join(",");
33
+ }
34
+ };
35
+
36
+ export {
37
+ Extensions
38
+ };
@@ -1,3 +1,7 @@
1
+ import {
2
+ Extensions
3
+ } from "./chunk-7JFJW6P6.mjs";
4
+
1
5
  // src/server/error.ts
2
6
  var A2AError = class _A2AError extends Error {
3
7
  code;
@@ -76,16 +80,11 @@ var ServerCallContext = class {
76
80
  return this._requestedExtensions;
77
81
  }
78
82
  addActivatedExtension(uri) {
79
- if (this._requestedExtensions?.has(uri)) {
80
- if (!this._activatedExtensions) {
81
- this._activatedExtensions = /* @__PURE__ */ new Set();
82
- }
83
- this._activatedExtensions.add(uri);
84
- }
83
+ this._activatedExtensions = Extensions.createFrom(this._activatedExtensions, uri);
85
84
  }
86
85
  };
87
86
 
88
- // src/server/transports/jsonrpc_transport_handler.ts
87
+ // src/server/transports/jsonrpc/jsonrpc_transport_handler.ts
89
88
  var JsonRpcTransportHandler = class {
90
89
  requestHandler;
91
90
  constructor(requestHandler) {