@mastra/client-js 0.0.0-storage-20250225005900 → 0.0.0-taofeeqInngest-20250603090617

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/src/example.ts CHANGED
@@ -1,43 +1,64 @@
1
1
  import { MastraClient } from './client';
2
+ // import type { WorkflowRunResult } from './types';
3
+
4
+ // Agent
2
5
 
3
6
  (async () => {
4
7
  const client = new MastraClient({
5
8
  baseUrl: 'http://localhost:4111',
6
9
  });
7
10
 
11
+ console.log('Starting agent...');
12
+
8
13
  try {
9
14
  const agent = client.getAgent('weatherAgent');
10
15
  const response = await agent.stream({
11
- messages: [
12
- {
13
- role: 'user',
14
- content: 'Hello, world!',
15
- },
16
- ],
16
+ messages: 'what is the weather in new york?',
17
+ });
18
+
19
+ response.processDataStream({
20
+ onTextPart: text => {
21
+ process.stdout.write(text);
22
+ },
23
+ onFilePart: file => {
24
+ console.log(file);
25
+ },
26
+ onDataPart: data => {
27
+ console.log(data);
28
+ },
29
+ onErrorPart: error => {
30
+ console.error(error);
31
+ },
17
32
  });
33
+ } catch (error) {
34
+ console.error(error);
35
+ }
36
+ })();
18
37
 
19
- const reader = response?.body?.getReader();
20
- const decoder = new TextDecoder();
21
- let buffer = '';
38
+ // Workflow
39
+ // (async () => {
40
+ // const client = new MastraClient({
41
+ // baseUrl: 'http://localhost:4111',
42
+ // });
22
43
 
23
- while (true) {
24
- if (!reader) break;
25
- const { value, done } = await reader.read();
26
- if (done) break;
44
+ // try {
45
+ // const workflowId = 'myWorkflow';
46
+ // const workflow = client.getWorkflow(workflowId);
27
47
 
28
- const chunk = decoder.decode(value);
29
- buffer += chunk;
48
+ // const { runId } = await workflow.createRun();
30
49
 
31
- console.log(buffer);
50
+ // workflow.watch({ runId }, record => {
51
+ // console.log(new Date().toTimeString(), record);
52
+ // });
32
53
 
33
- const matches = buffer.matchAll(/0:"([^"]*)"/g);
54
+ // await workflow.start({
55
+ // runId,
56
+ // triggerData: {
57
+ // city: 'New York',
58
+ // },
59
+ // });
34
60
 
35
- for (const match of matches) {
36
- const content = match[1];
37
- process.stdout.write(`${content}\n`);
38
- }
39
- }
40
- } catch (error) {
41
- console.error(error);
42
- }
43
- })();
61
+ // } catch (e) {
62
+ // console.error('Workflow error:', e);
63
+ // }
64
+ // })();
package/src/index.test.ts CHANGED
@@ -1,7 +1,6 @@
1
- import type { MessageType } from '@mastra/core';
2
1
  import { describe, expect, beforeEach, it, vi } from 'vitest';
3
-
4
2
  import { MastraClient } from './client';
3
+ import type { McpServerListResponse, ServerDetailInfo } from './types';
5
4
 
6
5
  // Mock fetch globally
7
6
  global.fetch = vi.fn();
@@ -12,33 +11,51 @@ describe('MastraClient Resources', () => {
12
11
  baseUrl: 'http://localhost:4111',
13
12
  headers: {
14
13
  Authorization: 'Bearer test-key',
14
+ 'x-mastra-client-type': 'js',
15
15
  },
16
16
  };
17
17
 
18
18
  // Helper to mock successful API responses
19
19
  const mockFetchResponse = (data: any, options: { isStream?: boolean } = {}) => {
20
20
  if (options.isStream) {
21
- const stream = new ReadableStream({
22
- start(controller) {
23
- controller.enqueue(new TextEncoder().encode(JSON.stringify(data)));
24
- controller.close();
25
- },
26
- });
27
- (global.fetch as any).mockResolvedValueOnce({
28
- ok: true,
29
- headers: {
30
- get: (name: string) => (name === 'Content-Type' ? 'text/event-stream' : null),
31
- },
32
- body: stream,
33
- });
21
+ let contentType = 'text/event-stream';
22
+ let responseBody: ReadableStream;
23
+
24
+ if (data instanceof ReadableStream) {
25
+ responseBody = data;
26
+ contentType = 'audio/mp3';
27
+ } else {
28
+ responseBody = new ReadableStream({
29
+ start(controller) {
30
+ controller.enqueue(new TextEncoder().encode(JSON.stringify(data)));
31
+ controller.close();
32
+ },
33
+ });
34
+ }
35
+
36
+ const headers = new Headers();
37
+ if (contentType === 'audio/mp3') {
38
+ headers.set('Transfer-Encoding', 'chunked');
39
+ }
40
+ headers.set('Content-Type', contentType);
41
+
42
+ (global.fetch as any).mockResolvedValueOnce(
43
+ new Response(responseBody, {
44
+ status: 200,
45
+ statusText: 'OK',
46
+ headers,
47
+ }),
48
+ );
34
49
  } else {
35
- (global.fetch as any).mockResolvedValueOnce({
36
- ok: true,
37
- headers: {
38
- get: (name: string) => (name === 'Content-Type' ? 'application/json' : null),
39
- },
40
- json: async () => data,
50
+ const response = new Response(undefined, {
51
+ status: 200,
52
+ statusText: 'OK',
53
+ headers: new Headers({
54
+ 'Content-Type': 'application/json',
55
+ }),
41
56
  });
57
+ response.json = () => Promise.resolve(data);
58
+ (global.fetch as any).mockResolvedValueOnce(response);
42
59
  }
43
60
  };
44
61
 
@@ -219,6 +236,7 @@ describe('MastraClient Resources', () => {
219
236
  model: 'gpt-4',
220
237
  instructions: 'Test instructions',
221
238
  tools: {},
239
+ workflows: {},
222
240
  };
223
241
  mockFetchResponse(mockResponse);
224
242
 
@@ -241,7 +259,7 @@ describe('MastraClient Resources', () => {
241
259
  const result = await agent.generate({
242
260
  messages: [],
243
261
  threadId: 'test-thread',
244
- resourceid: 'test-resource',
262
+ resourceId: 'test-resource',
245
263
  output: {},
246
264
  });
247
265
  expect(result).toEqual(mockResponse);
@@ -253,7 +271,7 @@ describe('MastraClient Resources', () => {
253
271
  body: JSON.stringify({
254
272
  messages: [],
255
273
  threadId: 'test-thread',
256
- resourceid: 'test-resource',
274
+ resourceId: 'test-resource',
257
275
  output: {},
258
276
  }),
259
277
  }),
@@ -301,19 +319,18 @@ describe('MastraClient Resources', () => {
301
319
  });
302
320
 
303
321
  it('should get agent evals', async () => {
304
- const mockResponse = {
305
- name: 'Test Agent',
306
- evals: [{ id: 'eval1' }],
307
- };
322
+ const mockResponse = { data: 'test' };
308
323
  mockFetchResponse(mockResponse);
309
324
  const result = await agent.evals();
310
325
  expect(result).toEqual(mockResponse);
311
- expect(global.fetch).toHaveBeenCalledWith(`${clientOptions.baseUrl}/api/agents/test-agent/evals/ci`, {
312
- headers: {
313
- Authorization: 'Bearer test-key',
314
- 'Content-Type': 'application/json',
315
- },
316
- });
326
+ expect(global.fetch).toHaveBeenCalledWith(
327
+ `${clientOptions.baseUrl}/api/agents/test-agent/evals/ci`,
328
+ expect.objectContaining({
329
+ headers: expect.objectContaining({
330
+ Authorization: 'Bearer test-key',
331
+ }),
332
+ }),
333
+ );
317
334
  });
318
335
 
319
336
  it('should get live evals', async () => {
@@ -333,6 +350,106 @@ describe('MastraClient Resources', () => {
333
350
  });
334
351
  });
335
352
 
353
+ describe('Agent Voice Resource', () => {
354
+ const agentId = 'test-agent';
355
+ let agent: ReturnType<typeof client.getAgent>;
356
+ beforeEach(() => {
357
+ agent = client.getAgent(agentId);
358
+ });
359
+ it('should get available speakers', async () => {
360
+ const mockResponse = [{ voiceId: 'speaker1' }];
361
+ mockFetchResponse(mockResponse);
362
+
363
+ const result = await agent.voice.getSpeakers();
364
+
365
+ expect(result).toEqual(mockResponse);
366
+ expect(global.fetch).toHaveBeenCalledWith(
367
+ `${clientOptions.baseUrl}/api/agents/test-agent/voice/speakers`,
368
+ expect.objectContaining({
369
+ headers: expect.objectContaining(clientOptions.headers),
370
+ }),
371
+ );
372
+ });
373
+
374
+ it(`should call speak without options`, async () => {
375
+ const mockAudioStream = new ReadableStream();
376
+ mockFetchResponse(mockAudioStream, { isStream: true });
377
+
378
+ const result = await agent.voice.speak('test');
379
+
380
+ expect(result).toBeInstanceOf(Response);
381
+ expect(result.body).toBeInstanceOf(ReadableStream);
382
+ expect(global.fetch).toHaveBeenCalledWith(
383
+ `${clientOptions.baseUrl}/api/agents/test-agent/voice/speak`,
384
+ expect.objectContaining({
385
+ method: 'POST',
386
+ headers: expect.objectContaining(clientOptions.headers),
387
+ }),
388
+ );
389
+ });
390
+
391
+ it(`should call speak with options`, async () => {
392
+ const mockAudioStream = new ReadableStream();
393
+ mockFetchResponse(mockAudioStream, { isStream: true });
394
+
395
+ const result = await agent.voice.speak('test', { speaker: 'speaker1' });
396
+ expect(result).toBeInstanceOf(Response);
397
+ expect(result.body).toBeInstanceOf(ReadableStream);
398
+ expect(global.fetch).toHaveBeenCalledWith(
399
+ `${clientOptions.baseUrl}/api/agents/test-agent/voice/speak`,
400
+ expect.objectContaining({
401
+ method: 'POST',
402
+ headers: expect.objectContaining(clientOptions.headers),
403
+ }),
404
+ );
405
+ });
406
+
407
+ it(`should call listen with audio file`, async () => {
408
+ const transcriptionResponse = { text: 'Hello world' };
409
+ mockFetchResponse(transcriptionResponse);
410
+
411
+ const audioBlob = new Blob(['test audio data'], { type: 'audio/wav' });
412
+
413
+ const result = await agent.voice.listen(audioBlob, { filetype: 'wav' });
414
+ expect(result).toEqual(transcriptionResponse);
415
+
416
+ expect(global.fetch).toHaveBeenCalledTimes(1);
417
+ const [url, config] = (global.fetch as any).mock.calls[0];
418
+ expect(url).toBe(`${clientOptions.baseUrl}/api/agents/test-agent/voice/listen`);
419
+ expect(config.method).toBe('POST');
420
+ expect(config.headers).toMatchObject(clientOptions.headers);
421
+
422
+ const formData = config.body;
423
+ expect(formData).toBeInstanceOf(FormData);
424
+ const audioContent = formData.get('audio');
425
+ expect(audioContent).toBeInstanceOf(Blob);
426
+ expect(audioContent.type).toBe('audio/wav');
427
+ });
428
+
429
+ it(`should call listen with audio blob and options`, async () => {
430
+ const transcriptionResponse = { text: 'Hello world' };
431
+ mockFetchResponse(transcriptionResponse);
432
+
433
+ const audioBlob = new Blob(['test audio data'], { type: 'audio/mp3' });
434
+
435
+ const result = await agent.voice.listen(audioBlob, { filetype: 'mp3' });
436
+
437
+ expect(result).toEqual(transcriptionResponse);
438
+
439
+ expect(global.fetch).toHaveBeenCalledTimes(1);
440
+ const [url, config] = (global.fetch as any).mock.calls[0];
441
+ expect(url).toBe(`${clientOptions.baseUrl}/api/agents/test-agent/voice/listen`);
442
+ expect(config.method).toBe('POST');
443
+ expect(config.headers).toMatchObject(clientOptions.headers);
444
+
445
+ const formData = config.body as FormData;
446
+ expect(formData).toBeInstanceOf(FormData);
447
+ const audioContent = formData.get('audio');
448
+ expect(audioContent).toBeInstanceOf(Blob);
449
+ expect(formData.get('options')).toBe(JSON.stringify({ filetype: 'mp3' }));
450
+ });
451
+ });
452
+
336
453
  const agentId = 'test-agent';
337
454
 
338
455
  describe('Memory Thread Resource', () => {
@@ -372,7 +489,7 @@ describe('MastraClient Resources', () => {
372
489
  const result = await memoryThread.update({
373
490
  title: 'Updated Thread',
374
491
  metadata: { updated: true },
375
- resourceid: 'test-resource',
492
+ resourceId: 'test-resource',
376
493
  });
377
494
  expect(result).toEqual(mockResponse);
378
495
  expect(global.fetch).toHaveBeenCalledWith(
@@ -412,29 +529,57 @@ describe('MastraClient Resources', () => {
412
529
  });
413
530
 
414
531
  it('should save messages to memory', async () => {
415
- const messages: MessageType[] = [
532
+ const messages = [
416
533
  {
417
534
  id: '1',
418
- type: 'text',
535
+ type: 'text' as const,
419
536
  content: 'test',
420
- role: 'user',
537
+ role: 'user' as const,
421
538
  threadId: 'test-thread',
422
- createdAt: new Date(),
539
+ resourceId: 'test-resource',
540
+ createdAt: new Date('2025-03-26T10:40:55.116Z'),
423
541
  },
424
542
  ];
425
543
  mockFetchResponse(messages);
426
- const result = await client.saveMessageToMemory({ messages, agentId });
544
+ const result = await client.saveMessageToMemory({ agentId, messages });
427
545
  expect(result).toEqual(messages);
428
546
  expect(global.fetch).toHaveBeenCalledWith(
429
547
  `${clientOptions.baseUrl}/api/memory/save-messages?agentId=${agentId}`,
430
- {
548
+ expect.objectContaining({
431
549
  method: 'POST',
432
- headers: {
550
+ headers: expect.objectContaining({
433
551
  Authorization: 'Bearer test-key',
434
- 'Content-Type': 'application/json',
552
+ }),
553
+ }),
554
+ );
555
+ });
556
+
557
+ it('should get thread messages with limit', async () => {
558
+ const mockResponse = {
559
+ messages: [
560
+ {
561
+ id: '1',
562
+ content: 'test',
563
+ threadId,
564
+ role: 'user',
565
+ type: 'text',
566
+ resourceId: 'test-resource',
567
+ createdAt: new Date(),
435
568
  },
436
- body: JSON.stringify({ messages, agentId }),
437
- },
569
+ ],
570
+ uiMessages: [],
571
+ };
572
+ mockFetchResponse(mockResponse);
573
+
574
+ const limit = 5;
575
+ const result = await memoryThread.getMessages({ limit });
576
+
577
+ expect(result).toEqual(mockResponse);
578
+ expect(global.fetch).toHaveBeenCalledWith(
579
+ `${clientOptions.baseUrl}/api/memory/threads/${threadId}/messages?agentId=${agentId}&limit=${limit}`,
580
+ expect.objectContaining({
581
+ headers: expect.objectContaining(clientOptions.headers),
582
+ }),
438
583
  );
439
584
  });
440
585
  });
@@ -467,21 +612,19 @@ describe('MastraClient Resources', () => {
467
612
  });
468
613
 
469
614
  it('should execute tool', async () => {
470
- const mockResponse = {
471
- result: 'Tool execution result',
472
- };
615
+ const mockResponse = { data: 'test' };
473
616
  mockFetchResponse(mockResponse);
474
-
475
- const result = await tool.execute({ data: '' });
617
+ const result = await tool.execute({ data: '', runId: 'test-run-id' });
476
618
  expect(result).toEqual(mockResponse);
477
- expect(global.fetch).toHaveBeenCalledWith(`${clientOptions.baseUrl}/api/tools/test-tool/execute`, {
478
- method: 'POST',
479
- headers: {
480
- Authorization: 'Bearer test-key',
481
- 'Content-Type': 'application/json',
482
- },
483
- body: JSON.stringify({ data: '' }),
484
- });
619
+ expect(global.fetch).toHaveBeenCalledWith(
620
+ `${clientOptions.baseUrl}/api/tools/test-tool/execute?runId=test-run-id`,
621
+ expect.objectContaining({
622
+ method: 'POST',
623
+ headers: expect.objectContaining({
624
+ Authorization: 'Bearer test-key',
625
+ }),
626
+ }),
627
+ );
485
628
  });
486
629
  });
487
630
 
@@ -519,14 +662,14 @@ describe('MastraClient Resources', () => {
519
662
  };
520
663
  mockFetchResponse(mockResponse);
521
664
 
522
- const result = await workflow.execute({ trigger: 'test' });
665
+ const result = await workflow.startAsync({ triggerData: { test: 'test' } });
523
666
  expect(result).toEqual(mockResponse);
524
667
  expect(global.fetch).toHaveBeenCalledWith(
525
- `${clientOptions.baseUrl}/api/workflows/test-workflow/execute`,
668
+ `${clientOptions.baseUrl}/api/workflows/test-workflow/start-async?`,
526
669
  expect.objectContaining({
527
670
  method: 'POST',
528
671
  headers: expect.objectContaining(clientOptions.headers),
529
- body: JSON.stringify({ trigger: 'test' }),
672
+ body: JSON.stringify({ test: 'test' }),
530
673
  }),
531
674
  );
532
675
  });
@@ -594,4 +737,94 @@ describe('MastraClient Resources', () => {
594
737
  );
595
738
  });
596
739
  });
740
+
741
+ describe('MCP Server Registry Client Methods', () => {
742
+ const mockServerInfo1 = {
743
+ id: 'mcp-server-1',
744
+ name: 'Test MCP Server 1',
745
+ version_detail: { version: '1.0.0', release_date: '2023-01-01T00:00:00Z', is_latest: true },
746
+ };
747
+ const mockServerInfo2 = {
748
+ id: 'mcp-server-2',
749
+ name: 'Test MCP Server 2',
750
+ version_detail: { version: '1.1.0', release_date: '2023-02-01T00:00:00Z', is_latest: true },
751
+ };
752
+
753
+ const mockServerDetail1: ServerDetailInfo = {
754
+ ...mockServerInfo1,
755
+ description: 'Detailed description for server 1',
756
+ package_canonical: 'npm',
757
+ packages: [{ registry_name: 'npm', name: '@example/server1', version: '1.0.0' }],
758
+ remotes: [{ transport_type: 'sse', url: 'http://localhost/sse1' }],
759
+ };
760
+
761
+ describe('getMcpServers()', () => {
762
+ it('should fetch a list of MCP servers', async () => {
763
+ const mockResponse: McpServerListResponse = {
764
+ servers: [mockServerInfo1, mockServerInfo2],
765
+ total_count: 2,
766
+ next: null,
767
+ };
768
+ mockFetchResponse(mockResponse);
769
+
770
+ const result = await client.getMcpServers();
771
+ expect(result).toEqual(mockResponse);
772
+ expect(global.fetch).toHaveBeenCalledWith(
773
+ `${clientOptions.baseUrl}/api/mcp/v0/servers`,
774
+ expect.objectContaining({
775
+ headers: expect.objectContaining(clientOptions.headers),
776
+ }),
777
+ );
778
+ });
779
+
780
+ it('should fetch MCP servers with limit and offset parameters', async () => {
781
+ const mockResponse: McpServerListResponse = {
782
+ servers: [mockServerInfo1],
783
+ total_count: 2,
784
+ next: '/api/mcp/v0/servers?limit=1&offset=1',
785
+ };
786
+ mockFetchResponse(mockResponse);
787
+
788
+ const result = await client.getMcpServers({ limit: 1, offset: 0 });
789
+ expect(result).toEqual(mockResponse);
790
+ expect(global.fetch).toHaveBeenCalledWith(
791
+ `${clientOptions.baseUrl}/api/mcp/v0/servers?limit=1&offset=0`,
792
+ expect.objectContaining({
793
+ headers: expect.objectContaining(clientOptions.headers),
794
+ }),
795
+ );
796
+ });
797
+ });
798
+
799
+ describe('getMcpServerDetails()', () => {
800
+ const serverId = 'mcp-server-1';
801
+
802
+ it('should fetch details for a specific MCP server', async () => {
803
+ mockFetchResponse(mockServerDetail1);
804
+
805
+ const result = await client.getMcpServerDetails(serverId);
806
+ expect(result).toEqual(mockServerDetail1);
807
+ expect(global.fetch).toHaveBeenCalledWith(
808
+ `${clientOptions.baseUrl}/api/mcp/v0/servers/${serverId}`,
809
+ expect.objectContaining({
810
+ headers: expect.objectContaining(clientOptions.headers),
811
+ }),
812
+ );
813
+ });
814
+
815
+ it('should fetch MCP server details with a version parameter', async () => {
816
+ mockFetchResponse(mockServerDetail1);
817
+ const version = '1.0.0';
818
+
819
+ const result = await client.getMcpServerDetails(serverId, { version });
820
+ expect(result).toEqual(mockServerDetail1);
821
+ expect(global.fetch).toHaveBeenCalledWith(
822
+ `${clientOptions.baseUrl}/api/mcp/v0/servers/${serverId}?version=${version}`,
823
+ expect.objectContaining({
824
+ headers: expect.objectContaining(clientOptions.headers),
825
+ }),
826
+ );
827
+ });
828
+ });
829
+ });
597
830
  });
@@ -0,0 +1,88 @@
1
+ import type { TaskSendParams, TaskQueryParams, TaskIdParams, Task, AgentCard, JSONRPCResponse } from '@mastra/core/a2a';
2
+ import type { ClientOptions } from '../types';
3
+ import { BaseResource } from './base';
4
+
5
+ /**
6
+ * Class for interacting with an agent via the A2A protocol
7
+ */
8
+ export class A2A extends BaseResource {
9
+ constructor(
10
+ options: ClientOptions,
11
+ private agentId: string,
12
+ ) {
13
+ super(options);
14
+ }
15
+
16
+ /**
17
+ * Get the agent card with metadata about the agent
18
+ * @returns Promise containing the agent card information
19
+ */
20
+ async getCard(): Promise<AgentCard> {
21
+ return this.request(`/.well-known/${this.agentId}/agent.json`);
22
+ }
23
+
24
+ /**
25
+ * Send a message to the agent and get a response
26
+ * @param params - Parameters for the task
27
+ * @returns Promise containing the task response
28
+ */
29
+ async sendMessage(params: TaskSendParams): Promise<{ task: Task }> {
30
+ const response = await this.request<JSONRPCResponse<Task>>(`/a2a/${this.agentId}`, {
31
+ method: 'POST',
32
+ body: {
33
+ method: 'tasks/send',
34
+ params,
35
+ },
36
+ });
37
+
38
+ return { task: response.result! };
39
+ }
40
+
41
+ /**
42
+ * Get the status and result of a task
43
+ * @param params - Parameters for querying the task
44
+ * @returns Promise containing the task response
45
+ */
46
+ async getTask(params: TaskQueryParams): Promise<Task> {
47
+ const response = await this.request<JSONRPCResponse<Task>>(`/a2a/${this.agentId}`, {
48
+ method: 'POST',
49
+ body: {
50
+ method: 'tasks/get',
51
+ params,
52
+ },
53
+ });
54
+
55
+ return response.result!;
56
+ }
57
+
58
+ /**
59
+ * Cancel a running task
60
+ * @param params - Parameters identifying the task to cancel
61
+ * @returns Promise containing the task response
62
+ */
63
+ async cancelTask(params: TaskIdParams): Promise<{ task: Task }> {
64
+ return this.request(`/a2a/${this.agentId}`, {
65
+ method: 'POST',
66
+ body: {
67
+ method: 'tasks/cancel',
68
+ params,
69
+ },
70
+ });
71
+ }
72
+
73
+ /**
74
+ * Send a message and subscribe to streaming updates (not fully implemented)
75
+ * @param params - Parameters for the task
76
+ * @returns Promise containing the task response
77
+ */
78
+ async sendAndSubscribe(params: TaskSendParams): Promise<Response> {
79
+ return this.request(`/a2a/${this.agentId}`, {
80
+ method: 'POST',
81
+ body: {
82
+ method: 'tasks/sendSubscribe',
83
+ params,
84
+ },
85
+ stream: true,
86
+ });
87
+ }
88
+ }