@tambo-ai/react 0.63.0 → 0.64.0

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.
Files changed (39) hide show
  1. package/dist/hooks/use-tambo-voice.js.map +1 -1
  2. package/dist/mcp/__tests__/mcp-hooks.test.js +479 -16
  3. package/dist/mcp/__tests__/mcp-hooks.test.js.map +1 -1
  4. package/dist/mcp/__tests__/tambo-mcp-provider.test.js +156 -0
  5. package/dist/mcp/__tests__/tambo-mcp-provider.test.js.map +1 -1
  6. package/dist/mcp/index.d.ts +2 -1
  7. package/dist/mcp/index.d.ts.map +1 -1
  8. package/dist/mcp/index.js +3 -1
  9. package/dist/mcp/index.js.map +1 -1
  10. package/dist/mcp/mcp-hooks.d.ts +93 -3
  11. package/dist/mcp/mcp-hooks.d.ts.map +1 -1
  12. package/dist/mcp/mcp-hooks.js +111 -4
  13. package/dist/mcp/mcp-hooks.js.map +1 -1
  14. package/dist/mcp/tambo-mcp-provider.d.ts +16 -0
  15. package/dist/mcp/tambo-mcp-provider.d.ts.map +1 -1
  16. package/dist/mcp/tambo-mcp-provider.js +71 -7
  17. package/dist/mcp/tambo-mcp-provider.js.map +1 -1
  18. package/dist/providers/tambo-context-attachment-provider.js +2 -2
  19. package/dist/providers/tambo-context-attachment-provider.js.map +1 -1
  20. package/esm/hooks/use-tambo-voice.js.map +1 -1
  21. package/esm/mcp/__tests__/mcp-hooks.test.js +480 -17
  22. package/esm/mcp/__tests__/mcp-hooks.test.js.map +1 -1
  23. package/esm/mcp/__tests__/tambo-mcp-provider.test.js +156 -0
  24. package/esm/mcp/__tests__/tambo-mcp-provider.test.js.map +1 -1
  25. package/esm/mcp/index.d.ts +2 -1
  26. package/esm/mcp/index.d.ts.map +1 -1
  27. package/esm/mcp/index.js +1 -1
  28. package/esm/mcp/index.js.map +1 -1
  29. package/esm/mcp/mcp-hooks.d.ts +93 -3
  30. package/esm/mcp/mcp-hooks.d.ts.map +1 -1
  31. package/esm/mcp/mcp-hooks.js +109 -4
  32. package/esm/mcp/mcp-hooks.js.map +1 -1
  33. package/esm/mcp/tambo-mcp-provider.d.ts +16 -0
  34. package/esm/mcp/tambo-mcp-provider.d.ts.map +1 -1
  35. package/esm/mcp/tambo-mcp-provider.js +71 -7
  36. package/esm/mcp/tambo-mcp-provider.js.map +1 -1
  37. package/esm/providers/tambo-context-attachment-provider.js +2 -2
  38. package/esm/providers/tambo-context-attachment-provider.js.map +1 -1
  39. package/package.json +1 -1
@@ -6,7 +6,7 @@ import { TamboMcpTokenProvider } from "../../providers/tambo-mcp-token-provider"
6
6
  import { TamboRegistryProvider } from "../../providers/tambo-registry-provider";
7
7
  import { MCPTransport } from "../mcp-client";
8
8
  import { TamboMcpProvider, useTamboMcpServers } from "../tambo-mcp-provider";
9
- import { useTamboMcpPromptList } from "../mcp-hooks";
9
+ import { useTamboMcpPromptList, useTamboMcpResourceList, useTamboMcpResource, } from "../mcp-hooks";
10
10
  // Mock the MCP client to avoid ES module issues
11
11
  let createImpl = jest.fn();
12
12
  jest.mock("../mcp-client", () => ({
@@ -102,16 +102,16 @@ describe("useTamboMcpPromptList - individual server caching", () => {
102
102
  await waitFor(() => {
103
103
  expect(capturedPrompts.length).toBe(4);
104
104
  });
105
- // Verify all prompts are present
105
+ // Verify all prompts are present (with prefixes since we have 2 servers)
106
106
  const promptNames = capturedPrompts.map((p) => p.prompt.name);
107
- expect(promptNames).toContain("prompt-a1");
108
- expect(promptNames).toContain("prompt-a2");
109
- expect(promptNames).toContain("prompt-b1");
110
- expect(promptNames).toContain("prompt-b2");
107
+ expect(promptNames).toContain("server-a:prompt-a1");
108
+ expect(promptNames).toContain("server-a:prompt-a2");
109
+ expect(promptNames).toContain("server-b:prompt-b1");
110
+ expect(promptNames).toContain("server-b:prompt-b2");
111
111
  // Verify each prompt has the correct server info
112
- const promptA1 = capturedPrompts.find((p) => p.prompt.name === "prompt-a1");
112
+ const promptA1 = capturedPrompts.find((p) => p.prompt.name === "server-a:prompt-a1");
113
113
  expect(promptA1?.server.url).toBe("https://server-a.example");
114
- const promptB1 = capturedPrompts.find((p) => p.prompt.name === "prompt-b1");
114
+ const promptB1 = capturedPrompts.find((p) => p.prompt.name === "server-b:prompt-b1");
115
115
  expect(promptB1?.server.url).toBe("https://server-b.example");
116
116
  });
117
117
  it("should remove prompts when a server is removed", async () => {
@@ -188,10 +188,10 @@ describe("useTamboMcpPromptList - individual server caching", () => {
188
188
  expect(capturedPrompts.length).toBe(4);
189
189
  });
190
190
  const initialPromptNames = capturedPrompts.map((p) => p.prompt.name);
191
- expect(initialPromptNames).toContain("prompt-a1");
192
- expect(initialPromptNames).toContain("prompt-a2");
193
- expect(initialPromptNames).toContain("prompt-b1");
194
- expect(initialPromptNames).toContain("prompt-b2");
191
+ expect(initialPromptNames).toContain("server-a:prompt-a1");
192
+ expect(initialPromptNames).toContain("server-a:prompt-a2");
193
+ expect(initialPromptNames).toContain("server-b:prompt-b1");
194
+ expect(initialPromptNames).toContain("server-b:prompt-b2");
195
195
  // Remove server B
196
196
  rerender(React.createElement(TamboClientContext.Provider, { value: {
197
197
  client: { baseURL: "https://api.tambo.co" },
@@ -208,6 +208,7 @@ describe("useTamboMcpPromptList - individual server caching", () => {
208
208
  ] },
209
209
  React.createElement(Capture, null))))));
210
210
  // Wait for prompts to be updated (server B prompts should disappear)
211
+ // When only 1 server remains, prompts should NOT be prefixed
211
212
  await waitFor(() => {
212
213
  expect(capturedPrompts.length).toBe(2);
213
214
  });
@@ -216,6 +217,7 @@ describe("useTamboMcpPromptList - individual server caching", () => {
216
217
  expect(updatedPromptNames).toContain("prompt-a2");
217
218
  expect(updatedPromptNames).not.toContain("prompt-b1");
218
219
  expect(updatedPromptNames).not.toContain("prompt-b2");
220
+ expect(updatedPromptNames).not.toContain("server-a:prompt-a1"); // No prefix when only 1 server
219
221
  // Verify server B's client was closed
220
222
  expect(clientB.close).toHaveBeenCalled();
221
223
  });
@@ -370,10 +372,10 @@ describe("useTamboMcpPromptList - individual server caching", () => {
370
372
  expect(capturedPrompts.length).toBe(1);
371
373
  expect(mcpServersCount).toBe(2); // Both servers should be in the list
372
374
  });
373
- // Verify only server A's prompts are present
375
+ // Verify only server A's prompts are present (with prefix since 2 servers configured)
374
376
  const promptNames = capturedPrompts.map((p) => p.prompt.name);
375
- expect(promptNames).toContain("prompt-a");
376
- expect(promptNames).not.toContain("prompt-b");
377
+ expect(promptNames).toContain("server-a:prompt-a");
378
+ expect(promptNames).not.toContain("server-b:prompt-b");
377
379
  });
378
380
  it("should add prompts when a new server is added", async () => {
379
381
  const serverAPrompts = {
@@ -458,12 +460,473 @@ describe("useTamboMcpPromptList - individual server caching", () => {
458
460
  ] },
459
461
  React.createElement(Capture, null))))));
460
462
  // Wait for server B prompts to be added
463
+ // Now with 2 servers, prompts should be prefixed
461
464
  await waitFor(() => {
462
465
  expect(capturedPrompts.length).toBe(2);
463
466
  });
464
467
  const promptNames = capturedPrompts.map((p) => p.prompt.name);
465
- expect(promptNames).toContain("prompt-a");
466
- expect(promptNames).toContain("prompt-b");
468
+ expect(promptNames).toContain("server-a:prompt-a");
469
+ expect(promptNames).toContain("server-b:prompt-b");
470
+ });
471
+ });
472
+ describe("useTamboMcpResourceList - resource management", () => {
473
+ let queryClient;
474
+ beforeEach(() => {
475
+ createImpl = jest.fn();
476
+ queryClient = new QueryClient({
477
+ defaultOptions: {
478
+ queries: {
479
+ retry: false,
480
+ },
481
+ },
482
+ });
483
+ });
484
+ afterEach(() => {
485
+ queryClient.clear();
486
+ });
487
+ it("should fetch and combine resources from multiple servers", async () => {
488
+ // Mock two servers with different resources
489
+ const serverAResources = {
490
+ resources: [
491
+ {
492
+ uri: "file:///home/user/doc1.txt",
493
+ name: "Document 1",
494
+ mimeType: "text/plain",
495
+ },
496
+ {
497
+ uri: "file:///home/user/doc2.txt",
498
+ name: "Document 2",
499
+ mimeType: "text/plain",
500
+ },
501
+ ],
502
+ };
503
+ const serverBResources = {
504
+ resources: [
505
+ {
506
+ uri: "file:///workspace/code.js",
507
+ name: "Code File",
508
+ mimeType: "text/javascript",
509
+ },
510
+ {
511
+ uri: "file:///workspace/README.md",
512
+ name: "Readme",
513
+ mimeType: "text/markdown",
514
+ },
515
+ ],
516
+ };
517
+ const mockClientA = {
518
+ listTools: jest.fn().mockResolvedValue([]),
519
+ listPrompts: jest.fn().mockResolvedValue({ prompts: [] }),
520
+ listResources: jest.fn().mockResolvedValue(serverAResources),
521
+ close: jest.fn(),
522
+ };
523
+ const mockClientB = {
524
+ listTools: jest.fn().mockResolvedValue([]),
525
+ listPrompts: jest.fn().mockResolvedValue({ prompts: [] }),
526
+ listResources: jest.fn().mockResolvedValue(serverBResources),
527
+ close: jest.fn(),
528
+ };
529
+ const clientA = {
530
+ client: mockClientA,
531
+ listTools: jest.fn().mockResolvedValue([]),
532
+ close: jest.fn(),
533
+ };
534
+ const clientB = {
535
+ client: mockClientB,
536
+ listTools: jest.fn().mockResolvedValue([]),
537
+ close: jest.fn(),
538
+ };
539
+ createImpl.mockImplementation(async (url) => {
540
+ if (url === "https://server-a.example")
541
+ return clientA;
542
+ if (url === "https://server-b.example")
543
+ return clientB;
544
+ throw new Error(`Unexpected URL: ${url}`);
545
+ });
546
+ let capturedResources = [];
547
+ const Capture = () => {
548
+ const { data: resources } = useTamboMcpResourceList();
549
+ useEffect(() => {
550
+ if (resources) {
551
+ capturedResources = resources;
552
+ }
553
+ }, [resources]);
554
+ return null;
555
+ };
556
+ render(React.createElement(TamboClientContext.Provider, { value: {
557
+ client: { baseURL: "https://api.tambo.co" },
558
+ queryClient,
559
+ isUpdatingToken: false,
560
+ } },
561
+ React.createElement(TamboRegistryProvider, null,
562
+ React.createElement(TamboMcpTokenProvider, null,
563
+ React.createElement(TamboMcpProvider, { mcpServers: [
564
+ {
565
+ url: "https://server-a.example",
566
+ transport: MCPTransport.SSE,
567
+ },
568
+ {
569
+ url: "https://server-b.example",
570
+ transport: MCPTransport.SSE,
571
+ },
572
+ ] },
573
+ React.createElement(Capture, null))))));
574
+ // Wait for all resources to be loaded
575
+ await waitFor(() => {
576
+ expect(capturedResources.length).toBe(4);
577
+ });
578
+ // Verify all resources are present (with prefixes since we have 2 servers)
579
+ const resourceUris = capturedResources.map((r) => r.resource.uri);
580
+ expect(resourceUris).toContain("server-a:file:///home/user/doc1.txt");
581
+ expect(resourceUris).toContain("server-a:file:///home/user/doc2.txt");
582
+ expect(resourceUris).toContain("server-b:file:///workspace/code.js");
583
+ expect(resourceUris).toContain("server-b:file:///workspace/README.md");
584
+ // Verify each resource has the correct server info
585
+ const resource1 = capturedResources.find((r) => r.resource.uri === "server-a:file:///home/user/doc1.txt");
586
+ expect(resource1?.server.url).toBe("https://server-a.example");
587
+ const resource2 = capturedResources.find((r) => r.resource.uri === "server-b:file:///workspace/code.js");
588
+ expect(resource2?.server.url).toBe("https://server-b.example");
589
+ });
590
+ it("should not prefix resources when only one server exists", async () => {
591
+ const serverAResources = {
592
+ resources: [
593
+ {
594
+ uri: "file:///home/user/doc.txt",
595
+ name: "Document",
596
+ mimeType: "text/plain",
597
+ },
598
+ ],
599
+ };
600
+ const mockClientA = {
601
+ listTools: jest.fn().mockResolvedValue([]),
602
+ listPrompts: jest.fn().mockResolvedValue({ prompts: [] }),
603
+ listResources: jest.fn().mockResolvedValue(serverAResources),
604
+ close: jest.fn(),
605
+ };
606
+ const clientA = {
607
+ client: mockClientA,
608
+ listTools: jest.fn().mockResolvedValue([]),
609
+ close: jest.fn(),
610
+ };
611
+ createImpl.mockImplementation(async () => clientA);
612
+ let capturedResources = [];
613
+ const Capture = () => {
614
+ const { data: resources } = useTamboMcpResourceList();
615
+ useEffect(() => {
616
+ if (resources) {
617
+ capturedResources = resources;
618
+ }
619
+ }, [resources]);
620
+ return null;
621
+ };
622
+ render(React.createElement(TamboClientContext.Provider, { value: {
623
+ client: { baseURL: "https://api.tambo.co" },
624
+ queryClient,
625
+ isUpdatingToken: false,
626
+ } },
627
+ React.createElement(TamboRegistryProvider, null,
628
+ React.createElement(TamboMcpTokenProvider, null,
629
+ React.createElement(TamboMcpProvider, { mcpServers: [
630
+ {
631
+ url: "https://server-a.example",
632
+ transport: MCPTransport.SSE,
633
+ },
634
+ ] },
635
+ React.createElement(Capture, null))))));
636
+ await waitFor(() => {
637
+ expect(capturedResources.length).toBe(1);
638
+ });
639
+ // No prefix when only 1 server
640
+ expect(capturedResources[0].resource.uri).toBe("file:///home/user/doc.txt");
641
+ });
642
+ it("should remove resource prefixes when a server is removed", async () => {
643
+ const serverAResources = {
644
+ resources: [
645
+ {
646
+ uri: "file:///home/user/doc1.txt",
647
+ name: "Document 1",
648
+ mimeType: "text/plain",
649
+ },
650
+ {
651
+ uri: "file:///home/user/doc2.txt",
652
+ name: "Document 2",
653
+ mimeType: "text/plain",
654
+ },
655
+ ],
656
+ };
657
+ const serverBResources = {
658
+ resources: [
659
+ {
660
+ uri: "file:///workspace/code.js",
661
+ name: "Code File",
662
+ mimeType: "text/javascript",
663
+ },
664
+ ],
665
+ };
666
+ const mockClientA = {
667
+ listTools: jest.fn().mockResolvedValue([]),
668
+ listPrompts: jest.fn().mockResolvedValue({ prompts: [] }),
669
+ listResources: jest.fn().mockResolvedValue(serverAResources),
670
+ close: jest.fn(),
671
+ };
672
+ const mockClientB = {
673
+ listTools: jest.fn().mockResolvedValue([]),
674
+ listPrompts: jest.fn().mockResolvedValue({ prompts: [] }),
675
+ listResources: jest.fn().mockResolvedValue(serverBResources),
676
+ close: jest.fn(),
677
+ };
678
+ const clientA = {
679
+ client: mockClientA,
680
+ listTools: jest.fn().mockResolvedValue([]),
681
+ close: jest.fn(),
682
+ };
683
+ const clientB = {
684
+ client: mockClientB,
685
+ listTools: jest.fn().mockResolvedValue([]),
686
+ close: jest.fn(),
687
+ };
688
+ createImpl.mockImplementation(async (url) => {
689
+ if (url === "https://server-a.example")
690
+ return clientA;
691
+ if (url === "https://server-b.example")
692
+ return clientB;
693
+ throw new Error(`Unexpected URL: ${url}`);
694
+ });
695
+ let capturedResources = [];
696
+ const Capture = () => {
697
+ const { data: resources } = useTamboMcpResourceList();
698
+ useEffect(() => {
699
+ if (resources) {
700
+ capturedResources = resources;
701
+ }
702
+ }, [resources]);
703
+ return null;
704
+ };
705
+ const { rerender } = render(React.createElement(TamboClientContext.Provider, { value: {
706
+ client: { baseURL: "https://api.tambo.co" },
707
+ queryClient,
708
+ isUpdatingToken: false,
709
+ } },
710
+ React.createElement(TamboRegistryProvider, null,
711
+ React.createElement(TamboMcpTokenProvider, null,
712
+ React.createElement(TamboMcpProvider, { mcpServers: [
713
+ {
714
+ url: "https://server-a.example",
715
+ transport: MCPTransport.SSE,
716
+ },
717
+ {
718
+ url: "https://server-b.example",
719
+ transport: MCPTransport.SSE,
720
+ },
721
+ ] },
722
+ React.createElement(Capture, null))))));
723
+ // Wait for all resources to be loaded (prefixed)
724
+ await waitFor(() => {
725
+ expect(capturedResources.length).toBe(3);
726
+ });
727
+ const initialUris = capturedResources.map((r) => r.resource.uri);
728
+ expect(initialUris).toContain("server-a:file:///home/user/doc1.txt");
729
+ expect(initialUris).toContain("server-b:file:///workspace/code.js");
730
+ // Now remove server B
731
+ rerender(React.createElement(TamboClientContext.Provider, { value: {
732
+ client: { baseURL: "https://api.tambo.co" },
733
+ queryClient,
734
+ isUpdatingToken: false,
735
+ } },
736
+ React.createElement(TamboRegistryProvider, null,
737
+ React.createElement(TamboMcpTokenProvider, null,
738
+ React.createElement(TamboMcpProvider, { mcpServers: [
739
+ {
740
+ url: "https://server-a.example",
741
+ transport: MCPTransport.SSE,
742
+ },
743
+ ] },
744
+ React.createElement(Capture, null))))));
745
+ // Wait for server B resources to be removed and prefixes stripped
746
+ await waitFor(() => {
747
+ expect(capturedResources.length).toBe(2);
748
+ });
749
+ const updatedUris = capturedResources.map((r) => r.resource.uri);
750
+ expect(updatedUris).toContain("file:///home/user/doc1.txt"); // No prefix
751
+ expect(updatedUris).toContain("file:///home/user/doc2.txt");
752
+ expect(updatedUris).not.toContain("server-a:file:///home/user/doc1.txt"); // No prefix when only 1 server
753
+ expect(updatedUris).not.toContain("server-b:file:///workspace/code.js"); // Server B removed
754
+ });
755
+ });
756
+ describe("useTamboMcpResource - read individual resource", () => {
757
+ let queryClient;
758
+ beforeEach(() => {
759
+ createImpl = jest.fn();
760
+ queryClient = new QueryClient({
761
+ defaultOptions: {
762
+ queries: {
763
+ retry: false,
764
+ },
765
+ },
766
+ });
767
+ });
768
+ afterEach(() => {
769
+ queryClient.clear();
770
+ });
771
+ it("should read a resource from a single server (unprefixed)", async () => {
772
+ const serverAResources = {
773
+ resources: [
774
+ {
775
+ uri: "file:///home/user/doc.txt",
776
+ name: "Document",
777
+ mimeType: "text/plain",
778
+ },
779
+ ],
780
+ };
781
+ const resourceContents = {
782
+ contents: [
783
+ {
784
+ uri: "file:///home/user/doc.txt",
785
+ mimeType: "text/plain",
786
+ text: "Hello, world!",
787
+ },
788
+ ],
789
+ };
790
+ const mockClientA = {
791
+ listTools: jest.fn().mockResolvedValue([]),
792
+ listPrompts: jest.fn().mockResolvedValue({ prompts: [] }),
793
+ listResources: jest.fn().mockResolvedValue(serverAResources),
794
+ readResource: jest.fn().mockResolvedValue(resourceContents),
795
+ close: jest.fn(),
796
+ };
797
+ const clientA = {
798
+ client: mockClientA,
799
+ listTools: jest.fn().mockResolvedValue([]),
800
+ close: jest.fn(),
801
+ };
802
+ createImpl.mockImplementation(async () => clientA);
803
+ let capturedResourceData = null;
804
+ const Capture = () => {
805
+ const { data: resourceData } = useTamboMcpResource("file:///home/user/doc.txt");
806
+ useEffect(() => {
807
+ if (resourceData) {
808
+ capturedResourceData = resourceData;
809
+ }
810
+ }, [resourceData]);
811
+ return null;
812
+ };
813
+ render(React.createElement(TamboClientContext.Provider, { value: {
814
+ client: { baseURL: "https://api.tambo.co" },
815
+ queryClient,
816
+ isUpdatingToken: false,
817
+ } },
818
+ React.createElement(TamboRegistryProvider, null,
819
+ React.createElement(TamboMcpTokenProvider, null,
820
+ React.createElement(TamboMcpProvider, { mcpServers: [
821
+ {
822
+ url: "https://server-a.example",
823
+ transport: MCPTransport.SSE,
824
+ },
825
+ ] },
826
+ React.createElement(Capture, null))))));
827
+ await waitFor(() => {
828
+ expect(capturedResourceData).not.toBeNull();
829
+ });
830
+ expect(capturedResourceData.contents[0].text).toBe("Hello, world!");
831
+ expect(mockClientA.readResource).toHaveBeenCalledWith({
832
+ uri: "file:///home/user/doc.txt",
833
+ });
834
+ });
835
+ it("should read a resource from multiple servers (prefixed)", async () => {
836
+ const serverAResources = {
837
+ resources: [
838
+ {
839
+ uri: "file:///home/user/doc.txt",
840
+ name: "Document",
841
+ mimeType: "text/plain",
842
+ },
843
+ ],
844
+ };
845
+ const serverBResources = {
846
+ resources: [
847
+ {
848
+ uri: "file:///workspace/code.js",
849
+ name: "Code",
850
+ mimeType: "text/javascript",
851
+ },
852
+ ],
853
+ };
854
+ const resourceContentsA = {
855
+ contents: [
856
+ {
857
+ uri: "file:///home/user/doc.txt",
858
+ mimeType: "text/plain",
859
+ text: "From server A",
860
+ },
861
+ ],
862
+ };
863
+ const mockClientA = {
864
+ listTools: jest.fn().mockResolvedValue([]),
865
+ listPrompts: jest.fn().mockResolvedValue({ prompts: [] }),
866
+ listResources: jest.fn().mockResolvedValue(serverAResources),
867
+ readResource: jest.fn().mockResolvedValue(resourceContentsA),
868
+ close: jest.fn(),
869
+ };
870
+ const mockClientB = {
871
+ listTools: jest.fn().mockResolvedValue([]),
872
+ listPrompts: jest.fn().mockResolvedValue({ prompts: [] }),
873
+ listResources: jest.fn().mockResolvedValue(serverBResources),
874
+ close: jest.fn(),
875
+ };
876
+ const clientA = {
877
+ client: mockClientA,
878
+ listTools: jest.fn().mockResolvedValue([]),
879
+ close: jest.fn(),
880
+ };
881
+ const clientB = {
882
+ client: mockClientB,
883
+ listTools: jest.fn().mockResolvedValue([]),
884
+ close: jest.fn(),
885
+ };
886
+ createImpl.mockImplementation(async (url) => {
887
+ if (url === "https://server-a.example")
888
+ return clientA;
889
+ if (url === "https://server-b.example")
890
+ return clientB;
891
+ throw new Error(`Unexpected URL: ${url}`);
892
+ });
893
+ let capturedResourceData = null;
894
+ const Capture = () => {
895
+ // Request with prefix
896
+ const { data: resourceData } = useTamboMcpResource("server-a:file:///home/user/doc.txt");
897
+ useEffect(() => {
898
+ if (resourceData) {
899
+ capturedResourceData = resourceData;
900
+ }
901
+ }, [resourceData]);
902
+ return null;
903
+ };
904
+ render(React.createElement(TamboClientContext.Provider, { value: {
905
+ client: { baseURL: "https://api.tambo.co" },
906
+ queryClient,
907
+ isUpdatingToken: false,
908
+ } },
909
+ React.createElement(TamboRegistryProvider, null,
910
+ React.createElement(TamboMcpTokenProvider, null,
911
+ React.createElement(TamboMcpProvider, { mcpServers: [
912
+ {
913
+ url: "https://server-a.example",
914
+ transport: MCPTransport.SSE,
915
+ },
916
+ {
917
+ url: "https://server-b.example",
918
+ transport: MCPTransport.SSE,
919
+ },
920
+ ] },
921
+ React.createElement(Capture, null))))));
922
+ await waitFor(() => {
923
+ expect(capturedResourceData).not.toBeNull();
924
+ });
925
+ expect(capturedResourceData.contents[0].text).toBe("From server A");
926
+ // Verify the prefix was stripped before calling the server
927
+ expect(mockClientA.readResource).toHaveBeenCalledWith({
928
+ uri: "file:///home/user/doc.txt",
929
+ });
467
930
  });
468
931
  });
469
932
  //# sourceMappingURL=mcp-hooks.test.js.map