@tambo-ai/react 0.67.1 → 0.69.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.
- package/README.md +3 -5
- package/dist/context-helpers/context-helpers.test.js +16 -4
- package/dist/context-helpers/context-helpers.test.js.map +1 -1
- package/dist/context-helpers/current-interactables-context-helper.d.ts +2 -2
- package/dist/context-helpers/current-interactables-context-helper.d.ts.map +1 -1
- package/dist/context-helpers/current-interactables-context-helper.js +31 -12
- package/dist/context-helpers/current-interactables-context-helper.js.map +1 -1
- package/dist/context-helpers/registry.d.ts +2 -2
- package/dist/context-helpers/registry.d.ts.map +1 -1
- package/dist/context-helpers/registry.js.map +1 -1
- package/dist/context-helpers/types.d.ts +2 -2
- package/dist/context-helpers/types.d.ts.map +1 -1
- package/dist/context-helpers/types.js.map +1 -1
- package/dist/hoc/with-tambo-interactable.d.ts +50 -4
- package/dist/hoc/with-tambo-interactable.d.ts.map +1 -1
- package/dist/hoc/with-tambo-interactable.js +20 -5
- package/dist/hoc/with-tambo-interactable.js.map +1 -1
- package/dist/hooks/use-component-state.d.ts +3 -8
- package/dist/hooks/use-component-state.d.ts.map +1 -1
- package/dist/hooks/use-component-state.js +8 -0
- package/dist/hooks/use-component-state.js.map +1 -1
- package/dist/hooks/use-component-state.test.js +37 -0
- package/dist/hooks/use-component-state.test.js.map +1 -1
- package/dist/hooks/use-message-images.test.js +174 -37
- package/dist/hooks/use-message-images.test.js.map +1 -1
- package/dist/hooks/use-tambo-threads.js +1 -1
- package/dist/hooks/use-tambo-threads.js.map +1 -1
- package/dist/hooks/use-tambo-voice.d.ts +1 -1
- package/dist/hooks/use-tambo-voice.js +1 -1
- package/dist/hooks/use-tambo-voice.js.map +1 -1
- package/dist/hooks/use-tambo-voice.test.d.ts +2 -0
- package/dist/hooks/use-tambo-voice.test.d.ts.map +1 -0
- package/dist/hooks/use-tambo-voice.test.js +239 -0
- package/dist/hooks/use-tambo-voice.test.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/elicitation.d.ts.map +1 -1
- package/dist/mcp/elicitation.js +12 -0
- package/dist/mcp/elicitation.js.map +1 -1
- package/dist/mcp/elicitation.test.js +8 -1
- package/dist/mcp/elicitation.test.js.map +1 -1
- package/dist/mcp/mcp-client.d.ts +6 -10
- package/dist/mcp/mcp-client.d.ts.map +1 -1
- package/dist/mcp/mcp-client.js.map +1 -1
- package/dist/mcp/mcp-constants.d.ts +19 -0
- package/dist/mcp/mcp-constants.d.ts.map +1 -0
- package/dist/mcp/mcp-constants.js +21 -0
- package/dist/mcp/mcp-constants.js.map +1 -0
- package/dist/mcp/mcp-hooks.d.ts +21 -40
- package/dist/mcp/mcp-hooks.d.ts.map +1 -1
- package/dist/mcp/mcp-hooks.js +130 -39
- package/dist/mcp/mcp-hooks.js.map +1 -1
- package/dist/mcp/mcp-hooks.test.js +431 -5
- package/dist/mcp/mcp-hooks.test.js.map +1 -1
- package/dist/mcp/tambo-mcp-provider.d.ts +7 -0
- package/dist/mcp/tambo-mcp-provider.d.ts.map +1 -1
- package/dist/mcp/tambo-mcp-provider.js +205 -155
- package/dist/mcp/tambo-mcp-provider.js.map +1 -1
- package/dist/mcp/tambo-mcp-provider.test.js +37 -0
- package/dist/mcp/tambo-mcp-provider.test.js.map +1 -1
- package/dist/model/component-metadata.d.ts +54 -21
- package/dist/model/component-metadata.d.ts.map +1 -1
- package/dist/model/component-metadata.js.map +1 -1
- package/dist/model/tambo-interactable.d.ts +13 -5
- package/dist/model/tambo-interactable.d.ts.map +1 -1
- package/dist/model/tambo-interactable.js.map +1 -1
- package/dist/providers/__tests__/thread-input-resource-resolution.test.d.ts +2 -0
- package/dist/providers/__tests__/thread-input-resource-resolution.test.d.ts.map +1 -0
- package/dist/providers/__tests__/thread-input-resource-resolution.test.js +592 -0
- package/dist/providers/__tests__/thread-input-resource-resolution.test.js.map +1 -0
- package/dist/providers/index.d.ts +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/tambo-client-provider.d.ts +8 -0
- package/dist/providers/tambo-client-provider.d.ts.map +1 -1
- package/dist/providers/tambo-client-provider.js +10 -11
- package/dist/providers/tambo-client-provider.js.map +1 -1
- package/dist/providers/tambo-client-provider.test.d.ts +2 -0
- package/dist/providers/tambo-client-provider.test.d.ts.map +1 -0
- package/dist/providers/tambo-client-provider.test.js +208 -0
- package/dist/providers/tambo-client-provider.test.js.map +1 -0
- package/dist/providers/tambo-context-attachment-provider.d.ts +34 -92
- package/dist/providers/tambo-context-attachment-provider.d.ts.map +1 -1
- package/dist/providers/tambo-context-attachment-provider.js +62 -105
- package/dist/providers/tambo-context-attachment-provider.js.map +1 -1
- package/dist/providers/tambo-context-attachment-provider.test.js +229 -463
- package/dist/providers/tambo-context-attachment-provider.test.js.map +1 -1
- package/dist/providers/tambo-interactable-provider-partial-updates.test.js +22 -21
- package/dist/providers/tambo-interactable-provider-partial-updates.test.js.map +1 -1
- package/dist/providers/tambo-interactable-provider.d.ts +5 -2
- package/dist/providers/tambo-interactable-provider.d.ts.map +1 -1
- package/dist/providers/tambo-interactable-provider.js +126 -17
- package/dist/providers/tambo-interactable-provider.js.map +1 -1
- package/dist/providers/tambo-interactable-provider.test.js +242 -0
- package/dist/providers/tambo-interactable-provider.test.js.map +1 -1
- package/dist/providers/tambo-interactables-additional-context.test.js +2 -5
- package/dist/providers/tambo-interactables-additional-context.test.js.map +1 -1
- package/dist/providers/tambo-provider.d.ts +2 -3
- package/dist/providers/tambo-provider.d.ts.map +1 -1
- package/dist/providers/tambo-provider.js +6 -5
- package/dist/providers/tambo-provider.js.map +1 -1
- package/dist/providers/tambo-registry-provider.test.js +16 -0
- package/dist/providers/tambo-registry-provider.test.js.map +1 -1
- package/dist/providers/tambo-registry-schema-compat.test.js +31 -0
- package/dist/providers/tambo-registry-schema-compat.test.js.map +1 -1
- package/dist/providers/tambo-thread-input-provider.d.ts +1 -1
- package/dist/providers/tambo-thread-input-provider.d.ts.map +1 -1
- package/dist/providers/tambo-thread-input-provider.js +26 -4
- package/dist/providers/tambo-thread-input-provider.js.map +1 -1
- package/dist/providers/tambo-thread-provider-initial-messages.test.js +84 -2
- package/dist/providers/tambo-thread-provider-initial-messages.test.js.map +1 -1
- package/dist/providers/tambo-thread-provider.d.ts.map +1 -1
- package/dist/providers/tambo-thread-provider.js +53 -42
- package/dist/providers/tambo-thread-provider.js.map +1 -1
- package/dist/providers/tambo-thread-provider.test.js +368 -262
- package/dist/providers/tambo-thread-provider.test.js.map +1 -1
- package/dist/schema/index.d.ts +1 -1
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +2 -1
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/json-schema.d.ts +7 -0
- package/dist/schema/json-schema.d.ts.map +1 -1
- package/dist/schema/json-schema.js +40 -29
- package/dist/schema/json-schema.js.map +1 -1
- package/dist/schema/json-schema.test.d.ts +2 -0
- package/dist/schema/json-schema.test.d.ts.map +1 -0
- package/dist/schema/json-schema.test.js +204 -0
- package/dist/schema/json-schema.test.js.map +1 -0
- package/dist/schema/schema.test.js +237 -0
- package/dist/schema/schema.test.js.map +1 -1
- package/dist/schema/standard-schema.d.ts +1 -0
- package/dist/schema/standard-schema.d.ts.map +1 -1
- package/dist/schema/standard-schema.js +18 -13
- package/dist/schema/standard-schema.js.map +1 -1
- package/dist/schema/standard-schema.test.d.ts +2 -0
- package/dist/schema/standard-schema.test.d.ts.map +1 -0
- package/dist/schema/standard-schema.test.js +165 -0
- package/dist/schema/standard-schema.test.js.map +1 -0
- package/dist/schema/validate.test.js +149 -0
- package/dist/schema/validate.test.js.map +1 -1
- package/dist/schema/zod.d.ts +7 -4
- package/dist/schema/zod.d.ts.map +1 -1
- package/dist/schema/zod.js +65 -22
- package/dist/schema/zod.js.map +1 -1
- package/dist/schema/zod.test.js +112 -0
- package/dist/schema/zod.test.js.map +1 -1
- package/dist/setupTests.js +3 -0
- package/dist/setupTests.js.map +1 -1
- package/dist/testing/tools.d.ts +4 -1
- package/dist/testing/tools.d.ts.map +1 -1
- package/dist/testing/tools.js +6 -1
- package/dist/testing/tools.js.map +1 -1
- package/dist/util/generate-component.d.ts.map +1 -1
- package/dist/util/generate-component.js +18 -3
- package/dist/util/generate-component.js.map +1 -1
- package/dist/util/generate-component.test.d.ts +2 -0
- package/dist/util/generate-component.test.d.ts.map +1 -0
- package/dist/util/generate-component.test.js +340 -0
- package/dist/util/generate-component.test.js.map +1 -0
- package/dist/util/is-promise.d.ts +9 -0
- package/dist/util/is-promise.d.ts.map +1 -0
- package/dist/util/is-promise.js +20 -0
- package/dist/util/is-promise.js.map +1 -0
- package/dist/util/is-promise.test.d.ts +2 -0
- package/dist/util/is-promise.test.d.ts.map +1 -0
- package/dist/util/is-promise.test.js +48 -0
- package/dist/util/is-promise.test.js.map +1 -0
- package/dist/util/message-builder.d.ts +3 -1
- package/dist/util/message-builder.d.ts.map +1 -1
- package/dist/util/message-builder.js +20 -3
- package/dist/util/message-builder.js.map +1 -1
- package/dist/util/message-builder.test.js +269 -0
- package/dist/util/message-builder.test.js.map +1 -1
- package/dist/util/query-utils.test.d.ts +2 -0
- package/dist/util/query-utils.test.d.ts.map +1 -0
- package/dist/util/query-utils.test.js +382 -0
- package/dist/util/query-utils.test.js.map +1 -0
- package/dist/util/registry-validators.d.ts.map +1 -1
- package/dist/util/registry-validators.js +7 -0
- package/dist/util/registry-validators.js.map +1 -1
- package/dist/util/registry-validators.test.js +57 -0
- package/dist/util/registry-validators.test.js.map +1 -1
- package/dist/util/registry.d.ts.map +1 -1
- package/dist/util/registry.js +9 -0
- package/dist/util/registry.js.map +1 -1
- package/dist/util/registry.test.js +323 -1
- package/dist/util/registry.test.js.map +1 -1
- package/dist/util/resource-content-resolver.d.ts +20 -0
- package/dist/util/resource-content-resolver.d.ts.map +1 -0
- package/dist/util/resource-content-resolver.js +93 -0
- package/dist/util/resource-content-resolver.js.map +1 -0
- package/dist/util/resource-content-resolver.test.d.ts +2 -0
- package/dist/util/resource-content-resolver.test.d.ts.map +1 -0
- package/dist/util/resource-content-resolver.test.js +254 -0
- package/dist/util/resource-content-resolver.test.js.map +1 -0
- package/dist/util/resource-validators.test.d.ts +2 -0
- package/dist/util/resource-validators.test.d.ts.map +1 -0
- package/dist/util/resource-validators.test.js +90 -0
- package/dist/util/resource-validators.test.js.map +1 -0
- package/dist/util/tool-caller.d.ts +2 -2
- package/dist/util/tool-caller.d.ts.map +1 -1
- package/dist/util/tool-caller.js +8 -8
- package/dist/util/tool-caller.js.map +1 -1
- package/dist/util/validate-component-name.test.d.ts +2 -0
- package/dist/util/validate-component-name.test.d.ts.map +1 -0
- package/dist/util/validate-component-name.test.js +35 -0
- package/dist/util/validate-component-name.test.js.map +1 -0
- package/esm/context-helpers/context-helpers.test.js +16 -4
- package/esm/context-helpers/context-helpers.test.js.map +1 -1
- package/esm/context-helpers/current-interactables-context-helper.d.ts +2 -2
- package/esm/context-helpers/current-interactables-context-helper.d.ts.map +1 -1
- package/esm/context-helpers/current-interactables-context-helper.js +31 -12
- package/esm/context-helpers/current-interactables-context-helper.js.map +1 -1
- package/esm/context-helpers/registry.d.ts +2 -2
- package/esm/context-helpers/registry.d.ts.map +1 -1
- package/esm/context-helpers/registry.js.map +1 -1
- package/esm/context-helpers/types.d.ts +2 -2
- package/esm/context-helpers/types.d.ts.map +1 -1
- package/esm/context-helpers/types.js.map +1 -1
- package/esm/hoc/with-tambo-interactable.d.ts +50 -4
- package/esm/hoc/with-tambo-interactable.d.ts.map +1 -1
- package/esm/hoc/with-tambo-interactable.js +20 -5
- package/esm/hoc/with-tambo-interactable.js.map +1 -1
- package/esm/hooks/use-component-state.d.ts +3 -8
- package/esm/hooks/use-component-state.d.ts.map +1 -1
- package/esm/hooks/use-component-state.js +8 -0
- package/esm/hooks/use-component-state.js.map +1 -1
- package/esm/hooks/use-component-state.test.js +37 -0
- package/esm/hooks/use-component-state.test.js.map +1 -1
- package/esm/hooks/use-message-images.test.js +174 -37
- package/esm/hooks/use-message-images.test.js.map +1 -1
- package/esm/hooks/use-tambo-threads.js +1 -1
- package/esm/hooks/use-tambo-threads.js.map +1 -1
- package/esm/hooks/use-tambo-voice.d.ts +1 -1
- package/esm/hooks/use-tambo-voice.js +1 -1
- package/esm/hooks/use-tambo-voice.js.map +1 -1
- package/esm/hooks/use-tambo-voice.test.d.ts +2 -0
- package/esm/hooks/use-tambo-voice.test.d.ts.map +1 -0
- package/esm/hooks/use-tambo-voice.test.js +234 -0
- package/esm/hooks/use-tambo-voice.test.js.map +1 -0
- package/esm/index.d.ts +2 -2
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js.map +1 -1
- package/esm/mcp/elicitation.d.ts.map +1 -1
- package/esm/mcp/elicitation.js +12 -0
- package/esm/mcp/elicitation.js.map +1 -1
- package/esm/mcp/elicitation.test.js +8 -1
- package/esm/mcp/elicitation.test.js.map +1 -1
- package/esm/mcp/mcp-client.d.ts +6 -10
- package/esm/mcp/mcp-client.d.ts.map +1 -1
- package/esm/mcp/mcp-client.js.map +1 -1
- package/esm/mcp/mcp-constants.d.ts +19 -0
- package/esm/mcp/mcp-constants.d.ts.map +1 -0
- package/esm/mcp/mcp-constants.js +18 -0
- package/esm/mcp/mcp-constants.js.map +1 -0
- package/esm/mcp/mcp-hooks.d.ts +21 -40
- package/esm/mcp/mcp-hooks.d.ts.map +1 -1
- package/esm/mcp/mcp-hooks.js +97 -40
- package/esm/mcp/mcp-hooks.js.map +1 -1
- package/esm/mcp/mcp-hooks.test.js +431 -5
- package/esm/mcp/mcp-hooks.test.js.map +1 -1
- package/esm/mcp/tambo-mcp-provider.d.ts +7 -0
- package/esm/mcp/tambo-mcp-provider.d.ts.map +1 -1
- package/esm/mcp/tambo-mcp-provider.js +204 -154
- package/esm/mcp/tambo-mcp-provider.js.map +1 -1
- package/esm/mcp/tambo-mcp-provider.test.js +37 -0
- package/esm/mcp/tambo-mcp-provider.test.js.map +1 -1
- package/esm/model/component-metadata.d.ts +54 -21
- package/esm/model/component-metadata.d.ts.map +1 -1
- package/esm/model/component-metadata.js.map +1 -1
- package/esm/model/tambo-interactable.d.ts +13 -5
- package/esm/model/tambo-interactable.d.ts.map +1 -1
- package/esm/model/tambo-interactable.js.map +1 -1
- package/esm/providers/__tests__/thread-input-resource-resolution.test.d.ts +2 -0
- package/esm/providers/__tests__/thread-input-resource-resolution.test.d.ts.map +1 -0
- package/esm/providers/__tests__/thread-input-resource-resolution.test.js +587 -0
- package/esm/providers/__tests__/thread-input-resource-resolution.test.js.map +1 -0
- package/esm/providers/index.d.ts +1 -1
- package/esm/providers/index.d.ts.map +1 -1
- package/esm/providers/index.js.map +1 -1
- package/esm/providers/tambo-client-provider.d.ts +8 -0
- package/esm/providers/tambo-client-provider.d.ts.map +1 -1
- package/esm/providers/tambo-client-provider.js +11 -12
- package/esm/providers/tambo-client-provider.js.map +1 -1
- package/esm/providers/tambo-client-provider.test.d.ts +2 -0
- package/esm/providers/tambo-client-provider.test.d.ts.map +1 -0
- package/esm/providers/tambo-client-provider.test.js +203 -0
- package/esm/providers/tambo-client-provider.test.js.map +1 -0
- package/esm/providers/tambo-context-attachment-provider.d.ts +34 -92
- package/esm/providers/tambo-context-attachment-provider.d.ts.map +1 -1
- package/esm/providers/tambo-context-attachment-provider.js +63 -106
- package/esm/providers/tambo-context-attachment-provider.js.map +1 -1
- package/esm/providers/tambo-context-attachment-provider.test.js +230 -464
- package/esm/providers/tambo-context-attachment-provider.test.js.map +1 -1
- package/esm/providers/tambo-interactable-provider-partial-updates.test.js +22 -21
- package/esm/providers/tambo-interactable-provider-partial-updates.test.js.map +1 -1
- package/esm/providers/tambo-interactable-provider.d.ts +5 -2
- package/esm/providers/tambo-interactable-provider.d.ts.map +1 -1
- package/esm/providers/tambo-interactable-provider.js +126 -17
- package/esm/providers/tambo-interactable-provider.js.map +1 -1
- package/esm/providers/tambo-interactable-provider.test.js +242 -0
- package/esm/providers/tambo-interactable-provider.test.js.map +1 -1
- package/esm/providers/tambo-interactables-additional-context.test.js +2 -5
- package/esm/providers/tambo-interactables-additional-context.test.js.map +1 -1
- package/esm/providers/tambo-provider.d.ts +2 -3
- package/esm/providers/tambo-provider.d.ts.map +1 -1
- package/esm/providers/tambo-provider.js +6 -5
- package/esm/providers/tambo-provider.js.map +1 -1
- package/esm/providers/tambo-registry-provider.test.js +16 -0
- package/esm/providers/tambo-registry-provider.test.js.map +1 -1
- package/esm/providers/tambo-registry-schema-compat.test.js +31 -0
- package/esm/providers/tambo-registry-schema-compat.test.js.map +1 -1
- package/esm/providers/tambo-thread-input-provider.d.ts +1 -1
- package/esm/providers/tambo-thread-input-provider.d.ts.map +1 -1
- package/esm/providers/tambo-thread-input-provider.js +26 -4
- package/esm/providers/tambo-thread-input-provider.js.map +1 -1
- package/esm/providers/tambo-thread-provider-initial-messages.test.js +84 -2
- package/esm/providers/tambo-thread-provider-initial-messages.test.js.map +1 -1
- package/esm/providers/tambo-thread-provider.d.ts.map +1 -1
- package/esm/providers/tambo-thread-provider.js +53 -42
- package/esm/providers/tambo-thread-provider.js.map +1 -1
- package/esm/providers/tambo-thread-provider.test.js +368 -262
- package/esm/providers/tambo-thread-provider.test.js.map +1 -1
- package/esm/schema/index.d.ts +1 -1
- package/esm/schema/index.d.ts.map +1 -1
- package/esm/schema/index.js +1 -1
- package/esm/schema/index.js.map +1 -1
- package/esm/schema/json-schema.d.ts +7 -0
- package/esm/schema/json-schema.d.ts.map +1 -1
- package/esm/schema/json-schema.js +11 -1
- package/esm/schema/json-schema.js.map +1 -1
- package/esm/schema/json-schema.test.d.ts +2 -0
- package/esm/schema/json-schema.test.d.ts.map +1 -0
- package/esm/schema/json-schema.test.js +202 -0
- package/esm/schema/json-schema.test.js.map +1 -0
- package/esm/schema/schema.test.js +238 -1
- package/esm/schema/schema.test.js.map +1 -1
- package/esm/schema/standard-schema.d.ts +1 -0
- package/esm/schema/standard-schema.d.ts.map +1 -1
- package/esm/schema/standard-schema.js +18 -13
- package/esm/schema/standard-schema.js.map +1 -1
- package/esm/schema/standard-schema.test.d.ts +2 -0
- package/esm/schema/standard-schema.test.d.ts.map +1 -0
- package/esm/schema/standard-schema.test.js +130 -0
- package/esm/schema/standard-schema.test.js.map +1 -0
- package/esm/schema/validate.test.js +149 -0
- package/esm/schema/validate.test.js.map +1 -1
- package/esm/schema/zod.d.ts +7 -4
- package/esm/schema/zod.d.ts.map +1 -1
- package/esm/schema/zod.js +65 -22
- package/esm/schema/zod.js.map +1 -1
- package/esm/schema/zod.test.js +113 -1
- package/esm/schema/zod.test.js.map +1 -1
- package/esm/setupTests.js +3 -0
- package/esm/setupTests.js.map +1 -1
- package/esm/testing/tools.d.ts +4 -1
- package/esm/testing/tools.d.ts.map +1 -1
- package/esm/testing/tools.js +6 -1
- package/esm/testing/tools.js.map +1 -1
- package/esm/util/generate-component.d.ts.map +1 -1
- package/esm/util/generate-component.js +18 -3
- package/esm/util/generate-component.js.map +1 -1
- package/esm/util/generate-component.test.d.ts +2 -0
- package/esm/util/generate-component.test.d.ts.map +1 -0
- package/esm/util/generate-component.test.js +302 -0
- package/esm/util/generate-component.test.js.map +1 -0
- package/esm/util/is-promise.d.ts +9 -0
- package/esm/util/is-promise.d.ts.map +1 -0
- package/esm/util/is-promise.js +17 -0
- package/esm/util/is-promise.js.map +1 -0
- package/esm/util/is-promise.test.d.ts +2 -0
- package/esm/util/is-promise.test.d.ts.map +1 -0
- package/esm/util/is-promise.test.js +46 -0
- package/esm/util/is-promise.test.js.map +1 -0
- package/esm/util/message-builder.d.ts +3 -1
- package/esm/util/message-builder.d.ts.map +1 -1
- package/esm/util/message-builder.js +20 -3
- package/esm/util/message-builder.js.map +1 -1
- package/esm/util/message-builder.test.js +269 -0
- package/esm/util/message-builder.test.js.map +1 -1
- package/esm/util/query-utils.test.d.ts +2 -0
- package/esm/util/query-utils.test.d.ts.map +1 -0
- package/esm/util/query-utils.test.js +380 -0
- package/esm/util/query-utils.test.js.map +1 -0
- package/esm/util/registry-validators.d.ts.map +1 -1
- package/esm/util/registry-validators.js +7 -0
- package/esm/util/registry-validators.js.map +1 -1
- package/esm/util/registry-validators.test.js +57 -0
- package/esm/util/registry-validators.test.js.map +1 -1
- package/esm/util/registry.d.ts.map +1 -1
- package/esm/util/registry.js +9 -0
- package/esm/util/registry.js.map +1 -1
- package/esm/util/registry.test.js +324 -2
- package/esm/util/registry.test.js.map +1 -1
- package/esm/util/resource-content-resolver.d.ts +20 -0
- package/esm/util/resource-content-resolver.d.ts.map +1 -0
- package/esm/util/resource-content-resolver.js +89 -0
- package/esm/util/resource-content-resolver.js.map +1 -0
- package/esm/util/resource-content-resolver.test.d.ts +2 -0
- package/esm/util/resource-content-resolver.test.d.ts.map +1 -0
- package/esm/util/resource-content-resolver.test.js +252 -0
- package/esm/util/resource-content-resolver.test.js.map +1 -0
- package/esm/util/resource-validators.test.d.ts +2 -0
- package/esm/util/resource-validators.test.d.ts.map +1 -0
- package/esm/util/resource-validators.test.js +88 -0
- package/esm/util/resource-validators.test.js.map +1 -0
- package/esm/util/tool-caller.d.ts +2 -2
- package/esm/util/tool-caller.d.ts.map +1 -1
- package/esm/util/tool-caller.js +8 -8
- package/esm/util/tool-caller.js.map +1 -1
- package/esm/util/validate-component-name.test.d.ts +2 -0
- package/esm/util/validate-component-name.test.d.ts.map +1 -0
- package/esm/util/validate-component-name.test.js +33 -0
- package/esm/util/validate-component-name.test.js.map +1 -0
- package/package.json +15 -23
- package/dist/schema/alias.d.ts +0 -3
- package/dist/schema/alias.d.ts.map +0 -1
- package/dist/schema/alias.js +0 -6
- package/dist/schema/alias.js.map +0 -1
- package/esm/schema/alias.d.ts +0 -3
- package/esm/schema/alias.d.ts.map +0 -1
- package/esm/schema/alias.js +0 -13
- package/esm/schema/alias.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-hooks.js","sourceRoot":"","sources":["../../src/mcp/mcp-hooks.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n GetPromptResult,\n type ListPromptsResult,\n type ListResourcesResult,\n type ReadResourceResult,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { UseQueryResult } from \"@tanstack/react-query\";\nimport { useTamboQueries, useTamboQuery } from \"../hooks\";\nimport { useTamboRegistry } from \"../providers/tambo-registry-provider\";\nimport {\n type ConnectedMcpServer,\n type McpServer,\n useTamboMcpServers,\n} from \"./tambo-mcp-provider\";\n\nexport type ListPromptItem = ListPromptsResult[\"prompts\"][number];\nexport interface ListPromptEntry {\n // Only connected servers produce prompt entries, so expose the connected type\n server: ConnectedMcpServer;\n prompt: ListPromptItem;\n}\n\nexport type ListResourceItem = ListResourcesResult[\"resources\"][number];\n\n/**\n * Registry resource entry - resources from the local registry (not MCP servers).\n *\n * These entries always have `server === null` and represent resources that\n * are registered locally via `TamboRegistryProvider` (static `resources`\n * props or dynamic `ResourceSource` functions).\n */\nexport interface RegistryResourceEntry {\n server: null;\n resource: ListResourceItem;\n}\n\n/**\n * MCP server resource entry - resources from connected MCP servers.\n *\n * These entries always have a non-null `server` with connection metadata and\n * are produced by the active MCP clients managed by `TamboMcpProvider`.\n */\nexport interface McpResourceEntry {\n server: ConnectedMcpServer;\n resource: ListResourceItem;\n}\n\n/**\n * Union type for all resource entries returned by `useTamboMcpResourceList`.\n *\n * - Registry resources have `server === null`.\n * - MCP-backed resources have a non-null `server` with connection metadata.\n */\nexport type ListResourceEntry = RegistryResourceEntry | McpResourceEntry;\n\n/**\n * Type guard for narrowing a `ListResourceEntry` to an MCP-backed resource\n * (entries where `server` is non-null).\n */\nexport function isMcpResourceEntry(\n entry: ListResourceEntry,\n): entry is McpResourceEntry {\n return entry.server !== null && isConnectedMcpServer(entry.server);\n}\n\n/**\n * Hook to get the prompts for all the registered MCP servers.\n * @returns The prompts for the MCP servers, including the server that the prompt was found on.\n */\nexport function useTamboMcpPromptList() {\n const mcpServers = useTamboMcpServers();\n\n const queries = useTamboQueries({\n queries: mcpServers.map((mcpServer) => ({\n queryKey: [\"mcp-prompts\", mcpServer.key],\n // Only run for connected servers that have a client\n enabled: isConnectedMcpServer(mcpServer),\n queryFn: async (): Promise<ListPromptEntry[]> => {\n // Fast path: if this server doesn't have a client, skip work\n if (!isConnectedMcpServer(mcpServer)) return [];\n\n const result = await mcpServer.client.client.listPrompts();\n const prompts: ListPromptItem[] = result?.prompts ?? [];\n // Return prompts without prefixes - we'll apply prefixing in combine\n const promptsEntries = prompts.map((prompt) => ({\n server: mcpServer,\n prompt,\n }));\n return promptsEntries;\n },\n })),\n combine: (results) => {\n const combined = combineArrayResults(results);\n\n // Always apply serverKey prefix to MCP prompts (breaking change for consistency with resources)\n // This ensures clear separation between local and remote prompts\n return {\n ...combined,\n data: combined.data.map((entry) => ({\n ...entry,\n prompt: {\n ...entry.prompt,\n name: `${entry.server.serverKey}:${entry.prompt.name}`,\n },\n })),\n };\n },\n });\n\n return queries;\n}\n// TODO: find a more general place for this\nfunction combineArrayResults<T>(results: UseQueryResult<T[]>[]): {\n data: T[];\n error: Error | null;\n errors: Error[];\n isPending: boolean;\n isSuccess: boolean;\n isError: boolean;\n isPaused: boolean;\n isRefetching: boolean;\n isFetching: boolean;\n isLoading: boolean;\n refetch: () => Promise<void>;\n} {\n const errors = results\n .filter((result) => result.isError)\n .map((result) => result.error);\n\n // Treat queries that are idle (disabled) as non-blocking for aggregate status\n const enabledish = results.filter(\n (r) => r.fetchStatus !== \"idle\" || r.isSuccess || r.isError,\n );\n\n return {\n // Prefer flatMap to avoid extra intermediate arrays\n data: results.flatMap((result) =>\n result.isSuccess && Array.isArray(result.data) ? result.data : [],\n ),\n // Preserve a single error for compatibility and expose the full list for diagnostics\n error: errors[0] ?? null,\n errors,\n isPending: enabledish.some((result) => result.isPending),\n isSuccess:\n enabledish.length > 0 && enabledish.every((result) => result.isSuccess),\n isError: errors.length > 0,\n isPaused: enabledish.some((result) => result.isPaused),\n isRefetching: enabledish.some((result) => result.isRefetching),\n isFetching: enabledish.some((result) => result.isFetching),\n isLoading: enabledish.some((result) => result.isLoading),\n // Aggregate refetch to trigger all underlying queries\n refetch: async () => {\n await Promise.all(\n results.map(async (r) => {\n await r.refetch();\n }),\n );\n },\n };\n}\n\n// Type guard for narrowing to connected servers\nfunction isConnectedMcpServer(server: McpServer): server is ConnectedMcpServer {\n return \"client\" in server && server.client != null;\n}\n\n/**\n * Hook to get the prompt for the specified name.\n * @param promptName - The name of the prompt to get. Can be prefixed with serverKey (e.g., \"linear:issue\") or unprefixed.\n * @param args - The arguments to pass to the prompt.\n * @returns The prompt for the specified name.\n */\nexport function useTamboMcpPrompt(\n promptName: string | undefined,\n args: Record<string, string> = {},\n) {\n // figure out which server has the prompt\n const { data: promptEntries } = useTamboMcpPromptList();\n const promptEntry = promptEntries?.find(\n (prompt) => prompt.prompt.name === promptName,\n );\n // Use the stable server key (and the server instance itself) instead of brittle\n // name/url/transport matching.\n const mcpServer = promptEntry?.server;\n\n // Strip the prefix to get the original prompt name for the MCP server call\n const originalPromptName = promptName?.includes(\":\")\n ? promptName.split(\":\").slice(1).join(\":\")\n : promptName;\n\n // Canonicalize args to avoid unstable cache keys from object identity/order\n const sortedArgsEntries = Object.keys(args)\n .sort()\n .map((k) => [k, args[k]] as const);\n return useTamboQuery({\n // Include server identity and sorted args to prevent stale cache hits\n queryKey: [\"mcp-prompt\", promptName, mcpServer?.key, sortedArgsEntries],\n // Only run when we have a prompt name and a connected server\n enabled: Boolean(\n promptName && mcpServer && isConnectedMcpServer(mcpServer),\n ),\n queryFn: async (): Promise<GetPromptResult | null> => {\n if (\n !originalPromptName ||\n !mcpServer ||\n !isConnectedMcpServer(mcpServer)\n ) {\n return null;\n }\n const result = await mcpServer.client.client.getPrompt({\n name: originalPromptName,\n arguments: args,\n });\n return result ?? null; // return null because react-query doesn't like undefined results\n },\n });\n}\n\n/**\n * Hook to get the resources for all the registered MCP servers and registry.\n * @returns The resources from MCP servers and the local registry, including the server that the resource was found on (null for registry resources).\n */\nexport function useTamboMcpResourceList() {\n const mcpServers = useTamboMcpServers();\n const { resources: staticResources, resourceSource } = useTamboRegistry();\n\n // Build list of queries: MCP servers + optional dynamic resource source\n const queriesToRun = [\n // MCP server queries\n ...mcpServers.map((mcpServer) => ({\n queryKey: [\"mcp-resources\", mcpServer.key],\n // Only run for connected servers that have a client\n enabled: isConnectedMcpServer(mcpServer),\n queryFn: async (): Promise<McpResourceEntry[]> => {\n // Fast path: if this server doesn't have a client, skip work\n if (!isConnectedMcpServer(mcpServer)) return [];\n\n const result = await mcpServer.client.client.listResources();\n const resources: ListResourceItem[] = result?.resources ?? [];\n // Return resources without prefixes - we'll apply prefixing in combine\n const resourceEntries: McpResourceEntry[] = resources.map(\n (resource) => ({\n server: mcpServer,\n resource,\n }),\n );\n return resourceEntries;\n },\n })),\n // Dynamic resource source query (if exists)\n ...(resourceSource\n ? [\n {\n queryKey: [\"registry-resources\", \"dynamic\"],\n enabled: true,\n queryFn: async (): Promise<RegistryResourceEntry[]> => {\n if (!resourceSource) return [];\n const resources = await resourceSource.listResources();\n return resources.map((resource) => ({\n server: null,\n resource,\n }));\n },\n },\n ]\n : []),\n ];\n\n const queries = useTamboQueries({\n queries: queriesToRun,\n combine: (results) => {\n // Type assertion needed because queries can return different entry types\n const combined = combineArrayResults(\n results as UseQueryResult<ListResourceEntry[]>[],\n );\n\n // Add static registry resources (no query needed)\n const staticEntries: RegistryResourceEntry[] = staticResources.map(\n (resource) => ({\n server: null,\n resource,\n }),\n );\n\n // Merge static resources with query results (registry resources first)\n const allData = [...staticEntries, ...combined.data];\n\n // Apply serverKey prefix to ALL MCP resources (breaking change)\n // Registry resources (server: null) are never prefixed\n const prefixedData = allData.map((entry) => {\n if (entry.server === null) {\n // Registry resource - no prefix\n return entry;\n }\n // MCP resource - always prefix with serverKey\n return {\n ...entry,\n resource: {\n ...entry.resource,\n uri: `${entry.server.serverKey}:${entry.resource.uri}`,\n },\n };\n });\n\n return {\n ...combined,\n data: prefixedData,\n };\n },\n });\n\n return queries;\n}\n\n/**\n * Hook to get the resource for the specified URI.\n * @param resourceUri - The URI of the resource to get. Can be prefixed with serverKey (e.g., \"linear:file://foo\") for MCP resources, or unprefixed for registry resources.\n * @returns The resource for the specified URI.\n */\nexport function useTamboMcpResource(resourceUri: string | undefined) {\n const { resourceSource } = useTamboRegistry();\n const { data: resourceEntries } = useTamboMcpResourceList();\n\n // Find which server/source has the resource\n const resourceEntry = resourceEntries?.find(\n (entry) => entry.resource.uri === resourceUri,\n );\n\n // Determine if this is a known registry resource or MCP resource\n const isKnownEntry = resourceEntry != null;\n const isRegistryResource = isKnownEntry && resourceEntry.server === null;\n const mcpServer =\n isKnownEntry && resourceEntry.server != null ? resourceEntry.server : null;\n\n // Strip the prefix to get the original resource URI for the MCP server call\n // Only strip if we found a matching resource entry with an MCP server\n const originalResourceUri =\n isKnownEntry && mcpServer\n ? resourceUri?.replace(`${mcpServer.serverKey}:`, \"\")\n : resourceUri;\n\n // Check if we can fetch this resource. Registry resources can always be\n // fetched when a resourceSource exists, even if they haven't appeared in a\n // previous listResources result.\n const hasRegistrySource = resourceSource != null;\n const hasConnectedMcpServer =\n isKnownEntry && mcpServer != null && isConnectedMcpServer(mcpServer);\n const canFetchResource = Boolean(\n resourceUri && (hasRegistrySource || hasConnectedMcpServer),\n );\n\n let locationKey: string | undefined;\n if (isRegistryResource || (!isKnownEntry && hasRegistrySource)) {\n locationKey = \"registry\";\n } else if (mcpServer) {\n locationKey = mcpServer.key;\n }\n\n return useTamboQuery({\n // Include server identity or \"registry\" to prevent stale cache hits\n queryKey: [\"resource\", resourceUri, locationKey],\n enabled: canFetchResource,\n queryFn: async (): Promise<ReadResourceResult | null> => {\n if (!originalResourceUri) {\n return null;\n }\n\n // Registry resource: use resourceSource.getResource\n // If this is not a known entry but we have a resourceSource, treat it as\n // a registry resource by default.\n if (resourceSource && (!isKnownEntry || isRegistryResource)) {\n const result = await resourceSource.getResource(originalResourceUri);\n return result ?? null;\n }\n\n // MCP resource: use MCP client\n if (mcpServer && isConnectedMcpServer(mcpServer)) {\n const result = await mcpServer.client.client.readResource({\n uri: originalResourceUri,\n });\n return result ?? null;\n }\n\n return null;\n },\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"mcp-hooks.js","sourceRoot":"","sources":["../../src/mcp/mcp-hooks.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n GetPromptResult,\n type ListPromptsResult,\n type ListResourcesResult,\n type ReadResourceResult,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { UseQueryResult } from \"@tanstack/react-query\";\nimport * as React from \"react\";\nimport { useTamboQueries, useTamboQuery } from \"../hooks\";\nimport { useTamboRegistry } from \"../providers/tambo-registry-provider\";\nimport { REGISTRY_SERVER_KEY } from \"./mcp-constants\";\nimport {\n type ConnectedMcpServer,\n type McpServer,\n useTamboMcpServers,\n} from \"./tambo-mcp-provider\";\n\nexport type ListPromptItem = ListPromptsResult[\"prompts\"][number];\nexport interface ListPromptEntry {\n // Only connected servers produce prompt entries, so expose the connected type\n server: ConnectedMcpServer;\n prompt: ListPromptItem;\n}\n\nexport type ListResourceItem = ListResourcesResult[\"resources\"][number];\n\n/**\n * Registry resource entry - resources from the local registry (not MCP servers).\n *\n * These entries always have `server === null`.\n */\nexport interface RegistryResourceEntry {\n server: null;\n resource: ListResourceItem;\n}\n\n/**\n * MCP server resource entry - resources from connected MCP servers.\n */\nexport interface McpResourceEntry {\n server: ConnectedMcpServer;\n resource: ListResourceItem;\n}\n\n/**\n * Union type for all resource entries returned by `useTamboMcpResourceList`.\n */\nexport type ListResourceEntry = RegistryResourceEntry | McpResourceEntry;\n\ntype InternalRegistryResourceEntry = RegistryResourceEntry & {\n isDynamic: boolean;\n};\n\ntype InternalListResourceEntry =\n | McpResourceEntry\n | InternalRegistryResourceEntry;\n\nfunction toPublicResourceEntry(\n entry: InternalListResourceEntry,\n): ListResourceEntry {\n if (entry.server === null) {\n const { isDynamic: _isDynamic, ...publicEntry } = entry;\n return publicEntry;\n }\n return entry;\n}\n\n/**\n * Type guard for narrowing a `ListResourceEntry` to an MCP-backed resource.\n */\nexport function isMcpResourceEntry(\n entry: ListResourceEntry,\n): entry is McpResourceEntry {\n return entry.server !== null && isConnectedMcpServer(entry.server);\n}\n\n/**\n * Hook to get the prompts for all the registered MCP servers.\n * @param search - Optional search string to filter prompts by name (case-insensitive).\n * @returns The prompts for the MCP servers, including the server that the prompt was found on.\n */\nexport function useTamboMcpPromptList(search?: string) {\n const mcpServers = useTamboMcpServers();\n\n const queries = useTamboQueries({\n queries: mcpServers.map((mcpServer) => ({\n // search is NOT in queryKey - we filter locally after fetching\n queryKey: [\"mcp-prompts\", mcpServer.key],\n // Only run for connected servers that have a client\n enabled: isConnectedMcpServer(mcpServer),\n queryFn: async (): Promise<ListPromptEntry[]> => {\n // Fast path: if this server doesn't have a client, skip work\n if (!isConnectedMcpServer(mcpServer)) return [];\n\n const result = await mcpServer.client.client.listPrompts();\n const prompts: ListPromptItem[] = result?.prompts ?? [];\n // Return prompts without prefixes - we'll apply prefixing in combine\n const promptsEntries = prompts.map((prompt) => ({\n server: mcpServer,\n prompt,\n }));\n return promptsEntries;\n },\n })),\n combine: (results) => {\n const combined = combineArrayResults(results);\n\n // Always apply serverKey prefix to MCP prompts (breaking change for consistency with resources)\n // This ensures clear separation between local and remote prompts\n return {\n ...combined,\n data: combined.data.map((entry) => ({\n ...entry,\n prompt: {\n ...entry.prompt,\n name: `${entry.server.serverKey}:${entry.prompt.name}`,\n },\n })),\n };\n },\n });\n\n // Filter results by search string - runs on every search change (not just query completion)\n const filteredData = React.useMemo(() => {\n if (!search) return queries.data;\n\n const normalizedSearch = search.toLowerCase();\n return queries.data.filter((entry) => {\n const name = entry.prompt.name?.toLowerCase() ?? \"\";\n return name.includes(normalizedSearch);\n });\n }, [queries.data, search]);\n\n return {\n ...queries,\n data: filteredData,\n };\n}\n// TODO: find a more general place for this\nfunction combineArrayResults<T>(results: UseQueryResult<T[]>[]): {\n data: T[];\n error: Error | null;\n errors: Error[];\n isPending: boolean;\n isSuccess: boolean;\n isError: boolean;\n isPaused: boolean;\n isRefetching: boolean;\n isFetching: boolean;\n isLoading: boolean;\n refetch: () => Promise<void>;\n} {\n const errors = results\n .filter((result) => result.isError)\n .map((result) => result.error);\n\n // Treat queries that are idle (disabled) as non-blocking for aggregate status\n const enabledish = results.filter(\n (r) => r.fetchStatus !== \"idle\" || r.isSuccess || r.isError,\n );\n\n return {\n // Prefer flatMap to avoid extra intermediate arrays\n data: results.flatMap((result) =>\n result.isSuccess && Array.isArray(result.data) ? result.data : [],\n ),\n // Preserve a single error for compatibility and expose the full list for diagnostics\n error: errors[0] ?? null,\n errors,\n isPending: enabledish.some((result) => result.isPending),\n isSuccess:\n enabledish.length > 0 && enabledish.every((result) => result.isSuccess),\n isError: errors.length > 0,\n isPaused: enabledish.some((result) => result.isPaused),\n isRefetching: enabledish.some((result) => result.isRefetching),\n isFetching: enabledish.some((result) => result.isFetching),\n isLoading: enabledish.some((result) => result.isLoading),\n // Aggregate refetch to trigger all underlying queries\n refetch: async () => {\n await Promise.all(\n results.map(async (r) => {\n await r.refetch();\n }),\n );\n },\n };\n}\n\n/**\n * Type guard for narrowing McpServer to ConnectedMcpServer.\n * A connected server has a non-null client.\n */\nexport function isConnectedMcpServer(\n server: McpServer,\n): server is ConnectedMcpServer {\n return \"client\" in server && server.client != null;\n}\n\n/**\n * Hook to get the prompt for the specified name.\n * @param promptName - The name of the prompt to get. Can be prefixed with serverKey (e.g., \"linear:issue\") or unprefixed.\n * @param args - The arguments to pass to the prompt.\n * @returns The prompt for the specified name.\n */\nexport function useTamboMcpPrompt(\n promptName: string | undefined,\n args: Record<string, string> = {},\n) {\n // figure out which server has the prompt\n const { data: promptEntries } = useTamboMcpPromptList();\n const promptEntry = promptEntries?.find(\n (prompt) => prompt.prompt.name === promptName,\n );\n // Use the stable server key (and the server instance itself) instead of brittle\n // name/url/transport matching.\n const mcpServer = promptEntry?.server;\n\n // Strip the prefix to get the original prompt name for the MCP server call\n const originalPromptName = promptName?.includes(\":\")\n ? promptName.split(\":\").slice(1).join(\":\")\n : promptName;\n\n // Canonicalize args to avoid unstable cache keys from object identity/order\n const sortedArgsEntries = Object.keys(args)\n .sort()\n .map((k) => [k, args[k]] as const);\n return useTamboQuery({\n // Include server identity and sorted args to prevent stale cache hits\n queryKey: [\"mcp-prompt\", promptName, mcpServer?.key, sortedArgsEntries],\n // Only run when we have a prompt name and a connected server\n enabled: Boolean(\n promptName && mcpServer && isConnectedMcpServer(mcpServer),\n ),\n queryFn: async (): Promise<GetPromptResult | null> => {\n if (\n !originalPromptName ||\n !mcpServer ||\n !isConnectedMcpServer(mcpServer)\n ) {\n return null;\n }\n const result = await mcpServer.client.client.getPrompt({\n name: originalPromptName,\n arguments: args,\n });\n return result ?? null; // return null because react-query doesn't like undefined results\n },\n });\n}\n\n/**\n * Hook to get the resources for all the registered MCP servers and registry.\n * @param search - Optional search string. For MCP servers, results are filtered locally after fetching.\n * For registry dynamic sources, the search is passed to listResources(search) for dynamic generation.\n * @returns The resources from MCP servers and the local registry, including the server that the resource was found on (null for registry resources).\n */\nexport function useTamboMcpResourceList(search?: string) {\n const mcpServers = useTamboMcpServers();\n const { resources: staticResources, resourceSource } = useTamboRegistry();\n\n // Build list of queries: MCP servers + optional dynamic resource source\n const queriesToRun = [\n // MCP server queries - search is NOT in queryKey so queries don't re-run on search change\n ...mcpServers.map((mcpServer) => ({\n queryKey: [\"mcp-resources\", mcpServer.key],\n // Only run for connected servers that have a client\n enabled: isConnectedMcpServer(mcpServer),\n queryFn: async (): Promise<McpResourceEntry[]> => {\n // Fast path: if this server doesn't have a client, skip work\n if (!isConnectedMcpServer(mcpServer)) return [];\n\n const result = await mcpServer.client.client.listResources();\n const resources: ListResourceItem[] = result?.resources ?? [];\n // Return resources without prefixes - we'll apply prefixing in combine\n const resourceEntries: McpResourceEntry[] = resources.map(\n (resource) => ({\n server: mcpServer,\n resource,\n }),\n );\n return resourceEntries;\n },\n })),\n // Dynamic resource source query (if exists) - search IS in queryKey to allow dynamic generation\n ...(resourceSource\n ? [\n {\n queryKey: [\"registry-resources\", \"dynamic\", search],\n enabled: true,\n queryFn: async (): Promise<InternalRegistryResourceEntry[]> => {\n if (!resourceSource) return [];\n const resources = await resourceSource.listResources(search);\n return resources.map((resource) => ({\n server: null,\n resource,\n isDynamic: true,\n }));\n },\n },\n ]\n : []),\n ];\n\n const queries = useTamboQueries({\n queries: queriesToRun,\n combine: (results) => {\n // Type assertion needed because queries can return different entry types\n const combined = combineArrayResults(\n results as UseQueryResult<InternalListResourceEntry[]>[],\n );\n\n // Add static registry resources (no query needed)\n const staticEntries: InternalRegistryResourceEntry[] =\n staticResources.map((resource) => ({\n server: null,\n resource,\n isDynamic: false,\n }));\n\n // Merge static resources with query results (registry resources first)\n const allData = [...staticEntries, ...combined.data];\n\n // Apply serverKey prefix to ALL resources for unified @serverKey:uri format\n // Registry resources get REGISTRY_SERVER_KEY prefix, MCP resources get their serverKey\n const prefixedData = allData.map((entry) => {\n if (entry.server === null) {\n // Registry resource - prefix with REGISTRY_SERVER_KEY\n return {\n ...entry,\n resource: {\n ...entry.resource,\n uri: `${REGISTRY_SERVER_KEY}:${entry.resource.uri}`,\n },\n };\n }\n // MCP resource - always prefix with serverKey\n return {\n ...entry,\n resource: {\n ...entry.resource,\n uri: `${entry.server.serverKey}:${entry.resource.uri}`,\n },\n };\n });\n\n return {\n ...combined,\n data: prefixedData,\n };\n },\n });\n\n // Filter results by search string - runs on every search change (not just query completion)\n // - MCP resources are filtered locally\n // - Static registry resources are filtered locally\n // - Dynamic registry resources are already filtered by listResources(search)\n const filteredData = React.useMemo((): InternalListResourceEntry[] => {\n if (!search) return queries.data;\n\n const normalizedSearch = search.toLowerCase();\n return queries.data.filter((entry) => {\n if (entry.server === null && entry.isDynamic) {\n return true;\n }\n\n const name = entry.resource.name?.toLowerCase() ?? \"\";\n const uri = entry.resource.uri.toLowerCase();\n return name.includes(normalizedSearch) || uri.includes(normalizedSearch);\n });\n }, [queries.data, search]);\n\n const publicData = React.useMemo(\n () => filteredData.map(toPublicResourceEntry),\n [filteredData],\n );\n\n return {\n ...queries,\n data: publicData,\n };\n}\n\n/**\n * Hook to get the resource for the specified URI.\n * @param resourceUri - The URI of the resource to get. Must be prefixed:\n * - MCP resources: prefixed with serverKey (e.g., \"linear:file://foo\")\n * - Registry resources: prefixed with \"registry:\" (e.g., \"registry:file://bar\")\n * @returns The resource for the specified URI.\n */\nexport function useTamboMcpResource(resourceUri: string | undefined) {\n const { resourceSource } = useTamboRegistry();\n const { data: resourceEntries } = useTamboMcpResourceList();\n\n // Find which server/source has the resource\n const resourceEntry = resourceEntries?.find(\n (entry) => entry.resource.uri === resourceUri,\n );\n\n // Determine if this is a registry resource or MCP resource\n const isRegistryResource = resourceEntry?.server === null;\n const mcpServer = resourceEntry?.server ?? null;\n\n // Check if the URI has the registry prefix\n const hasRegistryPrefix = Boolean(\n resourceUri?.startsWith(`${REGISTRY_SERVER_KEY}:`),\n );\n\n // Strip the prefix to get the original resource URI for fetching\n let originalResourceUri: string | undefined;\n if (isRegistryResource || hasRegistryPrefix) {\n const prefixLen = REGISTRY_SERVER_KEY.length + 1; // +1 for the colon\n originalResourceUri = resourceUri?.slice(prefixLen);\n } else if (mcpServer) {\n const prefixLen = mcpServer.serverKey.length + 1; // +1 for the colon\n originalResourceUri = resourceUri?.slice(prefixLen);\n }\n\n // Check if we can fetch this resource\n const hasRegistrySource = resourceSource != null;\n const hasConnectedMcpServer =\n mcpServer != null && isConnectedMcpServer(mcpServer);\n const canFetchFromRegistry =\n hasRegistrySource && (isRegistryResource || hasRegistryPrefix);\n const canFetchResource = Boolean(\n resourceUri && (canFetchFromRegistry || hasConnectedMcpServer),\n );\n\n const locationKey =\n isRegistryResource || hasRegistryPrefix\n ? REGISTRY_SERVER_KEY\n : mcpServer?.key;\n\n return useTamboQuery({\n queryKey: [\"resource\", resourceUri, locationKey],\n enabled: canFetchResource,\n queryFn: async (): Promise<ReadResourceResult | null> => {\n if (!originalResourceUri) {\n return null;\n }\n\n // Registry resource: use resourceSource.getResource\n if (resourceSource && (isRegistryResource || hasRegistryPrefix)) {\n const result = await resourceSource.getResource(originalResourceUri);\n return result ?? null;\n }\n\n // MCP resource: use MCP client\n if (mcpServer && isConnectedMcpServer(mcpServer)) {\n const result = await mcpServer.client.client.readResource({\n uri: originalResourceUri,\n });\n return result ?? null;\n }\n\n return null;\n },\n });\n}\n"]}
|
|
@@ -504,6 +504,142 @@ describe("useTamboMcpPromptList - individual server caching", () => {
|
|
|
504
504
|
expect(promptNames).toContain("server-b:prompt-b");
|
|
505
505
|
});
|
|
506
506
|
});
|
|
507
|
+
describe("useTamboMcpPromptList - search filtering", () => {
|
|
508
|
+
let queryClient;
|
|
509
|
+
beforeEach(() => {
|
|
510
|
+
createImpl = jest.fn();
|
|
511
|
+
queryClient = new react_query_1.QueryClient({
|
|
512
|
+
defaultOptions: {
|
|
513
|
+
queries: {
|
|
514
|
+
retry: false,
|
|
515
|
+
},
|
|
516
|
+
},
|
|
517
|
+
});
|
|
518
|
+
});
|
|
519
|
+
afterEach(() => {
|
|
520
|
+
queryClient.clear();
|
|
521
|
+
});
|
|
522
|
+
it("should filter MCP prompts locally by search string", async () => {
|
|
523
|
+
const serverAPrompts = {
|
|
524
|
+
prompts: [
|
|
525
|
+
{ name: "create-issue", description: "Create a new issue" },
|
|
526
|
+
{ name: "list-tasks", description: "List all tasks" },
|
|
527
|
+
{ name: "search-docs", description: "Search documentation" },
|
|
528
|
+
],
|
|
529
|
+
};
|
|
530
|
+
const mockClientA = {
|
|
531
|
+
listTools: jest.fn().mockResolvedValue([]),
|
|
532
|
+
listPrompts: jest.fn().mockResolvedValue(serverAPrompts),
|
|
533
|
+
listResources: jest.fn().mockResolvedValue({ resources: [] }),
|
|
534
|
+
close: jest.fn(),
|
|
535
|
+
};
|
|
536
|
+
const clientA = {
|
|
537
|
+
client: mockClientA,
|
|
538
|
+
listTools: jest.fn().mockResolvedValue([]),
|
|
539
|
+
close: jest.fn(),
|
|
540
|
+
};
|
|
541
|
+
createImpl.mockImplementation(async () => clientA);
|
|
542
|
+
let capturedPrompts = [];
|
|
543
|
+
const Capture = ({ search }) => {
|
|
544
|
+
const { data: prompts } = (0, mcp_hooks_1.useTamboMcpPromptList)(search);
|
|
545
|
+
(0, react_2.useEffect)(() => {
|
|
546
|
+
if (prompts) {
|
|
547
|
+
capturedPrompts = prompts;
|
|
548
|
+
}
|
|
549
|
+
}, [prompts]);
|
|
550
|
+
return null;
|
|
551
|
+
};
|
|
552
|
+
const { rerender } = (0, react_1.render)(react_2.default.createElement(tambo_client_provider_1.TamboClientContext.Provider, { value: {
|
|
553
|
+
client: { baseURL: "https://api.tambo.co" },
|
|
554
|
+
queryClient,
|
|
555
|
+
isUpdatingToken: false,
|
|
556
|
+
} },
|
|
557
|
+
react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, { mcpServers: [
|
|
558
|
+
{
|
|
559
|
+
url: "https://server-a.example",
|
|
560
|
+
transport: mcp_client_1.MCPTransport.SSE,
|
|
561
|
+
},
|
|
562
|
+
] },
|
|
563
|
+
react_2.default.createElement(tambo_mcp_token_provider_1.TamboMcpTokenProvider, null,
|
|
564
|
+
react_2.default.createElement(tambo_mcp_provider_1.TamboMcpProvider, null,
|
|
565
|
+
react_2.default.createElement(Capture, null))))));
|
|
566
|
+
// Wait for all prompts to be loaded (no search)
|
|
567
|
+
await (0, react_1.waitFor)(() => {
|
|
568
|
+
expect(capturedPrompts.length).toBe(3);
|
|
569
|
+
});
|
|
570
|
+
// Now search for "issue"
|
|
571
|
+
rerender(react_2.default.createElement(tambo_client_provider_1.TamboClientContext.Provider, { value: {
|
|
572
|
+
client: { baseURL: "https://api.tambo.co" },
|
|
573
|
+
queryClient,
|
|
574
|
+
isUpdatingToken: false,
|
|
575
|
+
} },
|
|
576
|
+
react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, { mcpServers: [
|
|
577
|
+
{
|
|
578
|
+
url: "https://server-a.example",
|
|
579
|
+
transport: mcp_client_1.MCPTransport.SSE,
|
|
580
|
+
},
|
|
581
|
+
] },
|
|
582
|
+
react_2.default.createElement(tambo_mcp_token_provider_1.TamboMcpTokenProvider, null,
|
|
583
|
+
react_2.default.createElement(tambo_mcp_provider_1.TamboMcpProvider, null,
|
|
584
|
+
react_2.default.createElement(Capture, { search: "issue" }))))));
|
|
585
|
+
// Should only have the create-issue prompt
|
|
586
|
+
await (0, react_1.waitFor)(() => {
|
|
587
|
+
expect(capturedPrompts.length).toBe(1);
|
|
588
|
+
});
|
|
589
|
+
expect(capturedPrompts[0].prompt.name).toBe("server-a:create-issue");
|
|
590
|
+
// MCP listPrompts should only be called once (not re-fetched on search change)
|
|
591
|
+
expect(mockClientA.listPrompts).toHaveBeenCalledTimes(1);
|
|
592
|
+
});
|
|
593
|
+
it("should filter prompts case-insensitively", async () => {
|
|
594
|
+
const serverAPrompts = {
|
|
595
|
+
prompts: [
|
|
596
|
+
{ name: "Create-Issue", description: "Create a new issue" },
|
|
597
|
+
{ name: "list-tasks", description: "List all tasks" },
|
|
598
|
+
],
|
|
599
|
+
};
|
|
600
|
+
const mockClientA = {
|
|
601
|
+
listTools: jest.fn().mockResolvedValue([]),
|
|
602
|
+
listPrompts: jest.fn().mockResolvedValue(serverAPrompts),
|
|
603
|
+
listResources: jest.fn().mockResolvedValue({ resources: [] }),
|
|
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 capturedPrompts = [];
|
|
613
|
+
const Capture = ({ search }) => {
|
|
614
|
+
const { data: prompts } = (0, mcp_hooks_1.useTamboMcpPromptList)(search);
|
|
615
|
+
(0, react_2.useEffect)(() => {
|
|
616
|
+
if (prompts) {
|
|
617
|
+
capturedPrompts = prompts;
|
|
618
|
+
}
|
|
619
|
+
}, [prompts]);
|
|
620
|
+
return null;
|
|
621
|
+
};
|
|
622
|
+
(0, react_1.render)(react_2.default.createElement(tambo_client_provider_1.TamboClientContext.Provider, { value: {
|
|
623
|
+
client: { baseURL: "https://api.tambo.co" },
|
|
624
|
+
queryClient,
|
|
625
|
+
isUpdatingToken: false,
|
|
626
|
+
} },
|
|
627
|
+
react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, { mcpServers: [
|
|
628
|
+
{
|
|
629
|
+
url: "https://server-a.example",
|
|
630
|
+
transport: mcp_client_1.MCPTransport.SSE,
|
|
631
|
+
},
|
|
632
|
+
] },
|
|
633
|
+
react_2.default.createElement(tambo_mcp_token_provider_1.TamboMcpTokenProvider, null,
|
|
634
|
+
react_2.default.createElement(tambo_mcp_provider_1.TamboMcpProvider, null,
|
|
635
|
+
react_2.default.createElement(Capture, { search: "CREATE" }))))));
|
|
636
|
+
// Should find "Create-Issue" even though search is uppercase
|
|
637
|
+
await (0, react_1.waitFor)(() => {
|
|
638
|
+
expect(capturedPrompts.length).toBe(1);
|
|
639
|
+
});
|
|
640
|
+
expect(capturedPrompts[0].prompt.name).toBe("server-a:Create-Issue");
|
|
641
|
+
});
|
|
642
|
+
});
|
|
507
643
|
describe("useTamboMcpResourceList - resource management", () => {
|
|
508
644
|
let queryClient;
|
|
509
645
|
beforeEach(() => {
|
|
@@ -815,6 +951,293 @@ describe("useTamboMcpResourceList - resource management", () => {
|
|
|
815
951
|
expect(updatedUris).not.toContain("server-b:file:///workspace/code.js"); // Server B removed
|
|
816
952
|
});
|
|
817
953
|
});
|
|
954
|
+
describe("useTamboMcpResourceList - search filtering", () => {
|
|
955
|
+
let queryClient;
|
|
956
|
+
beforeEach(() => {
|
|
957
|
+
createImpl = jest.fn();
|
|
958
|
+
queryClient = new react_query_1.QueryClient({
|
|
959
|
+
defaultOptions: {
|
|
960
|
+
queries: {
|
|
961
|
+
retry: false,
|
|
962
|
+
},
|
|
963
|
+
},
|
|
964
|
+
});
|
|
965
|
+
});
|
|
966
|
+
afterEach(() => {
|
|
967
|
+
queryClient.clear();
|
|
968
|
+
});
|
|
969
|
+
it("should filter MCP resources locally by search string", async () => {
|
|
970
|
+
const serverAResources = {
|
|
971
|
+
resources: [
|
|
972
|
+
{
|
|
973
|
+
uri: "file:///home/user/document.txt",
|
|
974
|
+
name: "My Document",
|
|
975
|
+
mimeType: "text/plain",
|
|
976
|
+
},
|
|
977
|
+
{
|
|
978
|
+
uri: "file:///home/user/image.png",
|
|
979
|
+
name: "My Image",
|
|
980
|
+
mimeType: "image/png",
|
|
981
|
+
},
|
|
982
|
+
{
|
|
983
|
+
uri: "file:///home/user/notes.txt",
|
|
984
|
+
name: "Notes",
|
|
985
|
+
mimeType: "text/plain",
|
|
986
|
+
},
|
|
987
|
+
],
|
|
988
|
+
};
|
|
989
|
+
const mockClientA = {
|
|
990
|
+
listTools: jest.fn().mockResolvedValue([]),
|
|
991
|
+
listPrompts: jest.fn().mockResolvedValue({ prompts: [] }),
|
|
992
|
+
listResources: jest.fn().mockResolvedValue(serverAResources),
|
|
993
|
+
close: jest.fn(),
|
|
994
|
+
};
|
|
995
|
+
const clientA = {
|
|
996
|
+
client: mockClientA,
|
|
997
|
+
listTools: jest.fn().mockResolvedValue([]),
|
|
998
|
+
close: jest.fn(),
|
|
999
|
+
};
|
|
1000
|
+
createImpl.mockImplementation(async () => clientA);
|
|
1001
|
+
let capturedResources = [];
|
|
1002
|
+
const Capture = ({ search }) => {
|
|
1003
|
+
const { data: resources } = (0, mcp_hooks_1.useTamboMcpResourceList)(search);
|
|
1004
|
+
(0, react_2.useEffect)(() => {
|
|
1005
|
+
if (resources) {
|
|
1006
|
+
capturedResources = resources;
|
|
1007
|
+
}
|
|
1008
|
+
}, [resources]);
|
|
1009
|
+
return null;
|
|
1010
|
+
};
|
|
1011
|
+
const { rerender } = (0, react_1.render)(react_2.default.createElement(tambo_client_provider_1.TamboClientContext.Provider, { value: {
|
|
1012
|
+
client: { baseURL: "https://api.tambo.co" },
|
|
1013
|
+
queryClient,
|
|
1014
|
+
isUpdatingToken: false,
|
|
1015
|
+
} },
|
|
1016
|
+
react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, { mcpServers: [
|
|
1017
|
+
{
|
|
1018
|
+
url: "https://server-a.example",
|
|
1019
|
+
transport: mcp_client_1.MCPTransport.SSE,
|
|
1020
|
+
},
|
|
1021
|
+
] },
|
|
1022
|
+
react_2.default.createElement(tambo_mcp_token_provider_1.TamboMcpTokenProvider, null,
|
|
1023
|
+
react_2.default.createElement(tambo_mcp_provider_1.TamboMcpProvider, null,
|
|
1024
|
+
react_2.default.createElement(Capture, null))))));
|
|
1025
|
+
// Wait for all resources to be loaded (no search)
|
|
1026
|
+
await (0, react_1.waitFor)(() => {
|
|
1027
|
+
expect(capturedResources.length).toBe(3);
|
|
1028
|
+
});
|
|
1029
|
+
// Now search for "document"
|
|
1030
|
+
rerender(react_2.default.createElement(tambo_client_provider_1.TamboClientContext.Provider, { value: {
|
|
1031
|
+
client: { baseURL: "https://api.tambo.co" },
|
|
1032
|
+
queryClient,
|
|
1033
|
+
isUpdatingToken: false,
|
|
1034
|
+
} },
|
|
1035
|
+
react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, { mcpServers: [
|
|
1036
|
+
{
|
|
1037
|
+
url: "https://server-a.example",
|
|
1038
|
+
transport: mcp_client_1.MCPTransport.SSE,
|
|
1039
|
+
},
|
|
1040
|
+
] },
|
|
1041
|
+
react_2.default.createElement(tambo_mcp_token_provider_1.TamboMcpTokenProvider, null,
|
|
1042
|
+
react_2.default.createElement(tambo_mcp_provider_1.TamboMcpProvider, null,
|
|
1043
|
+
react_2.default.createElement(Capture, { search: "document" }))))));
|
|
1044
|
+
// Should only have the document resource
|
|
1045
|
+
await (0, react_1.waitFor)(() => {
|
|
1046
|
+
expect(capturedResources.length).toBe(1);
|
|
1047
|
+
});
|
|
1048
|
+
expect(capturedResources[0].resource.name).toBe("My Document");
|
|
1049
|
+
// MCP listResources should only be called once (not re-fetched on search change)
|
|
1050
|
+
expect(mockClientA.listResources).toHaveBeenCalledTimes(1);
|
|
1051
|
+
});
|
|
1052
|
+
it("should pass search string to registry listResources for dynamic generation", async () => {
|
|
1053
|
+
const listResources = jest
|
|
1054
|
+
.fn()
|
|
1055
|
+
.mockImplementation(async (search) => {
|
|
1056
|
+
// Dynamically generate resources based on search
|
|
1057
|
+
if (!search)
|
|
1058
|
+
return [];
|
|
1059
|
+
return [
|
|
1060
|
+
{
|
|
1061
|
+
uri: `dynamic://${search}`,
|
|
1062
|
+
name: `Dynamic Resource for ${search}`,
|
|
1063
|
+
mimeType: "text/plain",
|
|
1064
|
+
},
|
|
1065
|
+
];
|
|
1066
|
+
});
|
|
1067
|
+
const getResource = jest.fn().mockResolvedValue({
|
|
1068
|
+
contents: [{ uri: "test", text: "test content", mimeType: "text/plain" }],
|
|
1069
|
+
});
|
|
1070
|
+
let capturedResources = [];
|
|
1071
|
+
const Capture = ({ search }) => {
|
|
1072
|
+
const { data: resources } = (0, mcp_hooks_1.useTamboMcpResourceList)(search);
|
|
1073
|
+
(0, react_2.useEffect)(() => {
|
|
1074
|
+
if (resources) {
|
|
1075
|
+
capturedResources = resources;
|
|
1076
|
+
}
|
|
1077
|
+
}, [resources]);
|
|
1078
|
+
return null;
|
|
1079
|
+
};
|
|
1080
|
+
const { rerender } = (0, react_1.render)(react_2.default.createElement(tambo_client_provider_1.TamboClientContext.Provider, { value: {
|
|
1081
|
+
client: { baseURL: "https://api.tambo.co" },
|
|
1082
|
+
queryClient,
|
|
1083
|
+
isUpdatingToken: false,
|
|
1084
|
+
} },
|
|
1085
|
+
react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, { listResources: listResources, getResource: getResource },
|
|
1086
|
+
react_2.default.createElement(tambo_mcp_token_provider_1.TamboMcpTokenProvider, null,
|
|
1087
|
+
react_2.default.createElement(tambo_mcp_provider_1.TamboMcpProvider, null,
|
|
1088
|
+
react_2.default.createElement(Capture, { search: "foo" }))))));
|
|
1089
|
+
// Wait for dynamic resource to be generated
|
|
1090
|
+
await (0, react_1.waitFor)(() => {
|
|
1091
|
+
expect(capturedResources.length).toBe(1);
|
|
1092
|
+
});
|
|
1093
|
+
// Verify listResources was called with the search string
|
|
1094
|
+
expect(listResources).toHaveBeenCalledWith("foo");
|
|
1095
|
+
expect(capturedResources[0].resource.name).toBe("Dynamic Resource for foo");
|
|
1096
|
+
// Registry resources get the "registry:" prefix
|
|
1097
|
+
expect(capturedResources[0].resource.uri).toBe("registry:dynamic://foo");
|
|
1098
|
+
// Now search for "bar"
|
|
1099
|
+
rerender(react_2.default.createElement(tambo_client_provider_1.TamboClientContext.Provider, { value: {
|
|
1100
|
+
client: { baseURL: "https://api.tambo.co" },
|
|
1101
|
+
queryClient,
|
|
1102
|
+
isUpdatingToken: false,
|
|
1103
|
+
} },
|
|
1104
|
+
react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, { listResources: listResources, getResource: getResource },
|
|
1105
|
+
react_2.default.createElement(tambo_mcp_token_provider_1.TamboMcpTokenProvider, null,
|
|
1106
|
+
react_2.default.createElement(tambo_mcp_provider_1.TamboMcpProvider, null,
|
|
1107
|
+
react_2.default.createElement(Capture, { search: "bar" }))))));
|
|
1108
|
+
// Wait for new dynamic resource
|
|
1109
|
+
await (0, react_1.waitFor)(() => {
|
|
1110
|
+
expect(capturedResources[0].resource.uri).toBe("registry:dynamic://bar");
|
|
1111
|
+
});
|
|
1112
|
+
// listResources should be called again with new search
|
|
1113
|
+
expect(listResources).toHaveBeenCalledWith("bar");
|
|
1114
|
+
expect(capturedResources[0].resource.name).toBe("Dynamic Resource for bar");
|
|
1115
|
+
});
|
|
1116
|
+
it("should not locally substring-filter dynamic registry resources", async () => {
|
|
1117
|
+
const listResources = jest
|
|
1118
|
+
.fn()
|
|
1119
|
+
.mockImplementation(async (search) => {
|
|
1120
|
+
if (!search)
|
|
1121
|
+
return [];
|
|
1122
|
+
// Simulate a provider that matches resources using non-substring logic.
|
|
1123
|
+
return [
|
|
1124
|
+
{
|
|
1125
|
+
uri: "dynamic://matched-by-provider",
|
|
1126
|
+
name: "Smart Match",
|
|
1127
|
+
mimeType: "text/plain",
|
|
1128
|
+
},
|
|
1129
|
+
];
|
|
1130
|
+
});
|
|
1131
|
+
const getResource = jest.fn().mockResolvedValue({
|
|
1132
|
+
contents: [{ uri: "test", text: "test content", mimeType: "text/plain" }],
|
|
1133
|
+
});
|
|
1134
|
+
let capturedResources = [];
|
|
1135
|
+
const Capture = ({ search }) => {
|
|
1136
|
+
const { data: resources } = (0, mcp_hooks_1.useTamboMcpResourceList)(search);
|
|
1137
|
+
(0, react_2.useEffect)(() => {
|
|
1138
|
+
if (resources) {
|
|
1139
|
+
capturedResources = resources;
|
|
1140
|
+
}
|
|
1141
|
+
}, [resources]);
|
|
1142
|
+
return null;
|
|
1143
|
+
};
|
|
1144
|
+
(0, react_1.render)(react_2.default.createElement(tambo_client_provider_1.TamboClientContext.Provider, { value: {
|
|
1145
|
+
client: { baseURL: "https://api.tambo.co" },
|
|
1146
|
+
queryClient,
|
|
1147
|
+
isUpdatingToken: false,
|
|
1148
|
+
} },
|
|
1149
|
+
react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, { listResources: listResources, getResource: getResource },
|
|
1150
|
+
react_2.default.createElement(tambo_mcp_token_provider_1.TamboMcpTokenProvider, null,
|
|
1151
|
+
react_2.default.createElement(tambo_mcp_provider_1.TamboMcpProvider, null,
|
|
1152
|
+
react_2.default.createElement(Capture, { search: "foo" }))))));
|
|
1153
|
+
await (0, react_1.waitFor)(() => {
|
|
1154
|
+
expect(capturedResources.length).toBe(1);
|
|
1155
|
+
});
|
|
1156
|
+
expect(listResources).toHaveBeenCalledWith("foo");
|
|
1157
|
+
expect(capturedResources[0].server).toBeNull();
|
|
1158
|
+
expect(capturedResources[0].resource.name).toBe("Smart Match");
|
|
1159
|
+
expect(capturedResources[0].resource.uri).toBe("registry:dynamic://matched-by-provider");
|
|
1160
|
+
});
|
|
1161
|
+
it("should combine MCP filtered resources with dynamically generated registry resources", async () => {
|
|
1162
|
+
const serverAResources = {
|
|
1163
|
+
resources: [
|
|
1164
|
+
{
|
|
1165
|
+
uri: "file:///apple.txt",
|
|
1166
|
+
name: "Apple",
|
|
1167
|
+
mimeType: "text/plain",
|
|
1168
|
+
},
|
|
1169
|
+
{
|
|
1170
|
+
uri: "file:///banana.txt",
|
|
1171
|
+
name: "Banana",
|
|
1172
|
+
mimeType: "text/plain",
|
|
1173
|
+
},
|
|
1174
|
+
],
|
|
1175
|
+
};
|
|
1176
|
+
const mockClientA = {
|
|
1177
|
+
listTools: jest.fn().mockResolvedValue([]),
|
|
1178
|
+
listPrompts: jest.fn().mockResolvedValue({ prompts: [] }),
|
|
1179
|
+
listResources: jest.fn().mockResolvedValue(serverAResources),
|
|
1180
|
+
close: jest.fn(),
|
|
1181
|
+
};
|
|
1182
|
+
const clientA = {
|
|
1183
|
+
client: mockClientA,
|
|
1184
|
+
listTools: jest.fn().mockResolvedValue([]),
|
|
1185
|
+
close: jest.fn(),
|
|
1186
|
+
};
|
|
1187
|
+
createImpl.mockImplementation(async () => clientA);
|
|
1188
|
+
const listResources = jest
|
|
1189
|
+
.fn()
|
|
1190
|
+
.mockImplementation(async (search) => {
|
|
1191
|
+
// Always return a resource that includes the search term
|
|
1192
|
+
return [
|
|
1193
|
+
{
|
|
1194
|
+
uri: `registry://${search ?? "all"}`,
|
|
1195
|
+
name: `Registry ${search ?? "all"}`,
|
|
1196
|
+
mimeType: "text/plain",
|
|
1197
|
+
},
|
|
1198
|
+
];
|
|
1199
|
+
});
|
|
1200
|
+
const getResource = jest.fn().mockResolvedValue({
|
|
1201
|
+
contents: [{ uri: "test", text: "test", mimeType: "text/plain" }],
|
|
1202
|
+
});
|
|
1203
|
+
let capturedResources = [];
|
|
1204
|
+
const Capture = ({ search }) => {
|
|
1205
|
+
const { data: resources } = (0, mcp_hooks_1.useTamboMcpResourceList)(search);
|
|
1206
|
+
(0, react_2.useEffect)(() => {
|
|
1207
|
+
if (resources) {
|
|
1208
|
+
capturedResources = resources;
|
|
1209
|
+
}
|
|
1210
|
+
}, [resources]);
|
|
1211
|
+
return null;
|
|
1212
|
+
};
|
|
1213
|
+
(0, react_1.render)(react_2.default.createElement(tambo_client_provider_1.TamboClientContext.Provider, { value: {
|
|
1214
|
+
client: { baseURL: "https://api.tambo.co" },
|
|
1215
|
+
queryClient,
|
|
1216
|
+
isUpdatingToken: false,
|
|
1217
|
+
} },
|
|
1218
|
+
react_2.default.createElement(tambo_registry_provider_1.TamboRegistryProvider, { mcpServers: [
|
|
1219
|
+
{
|
|
1220
|
+
url: "https://server-a.example",
|
|
1221
|
+
transport: mcp_client_1.MCPTransport.SSE,
|
|
1222
|
+
},
|
|
1223
|
+
], listResources: listResources, getResource: getResource },
|
|
1224
|
+
react_2.default.createElement(tambo_mcp_token_provider_1.TamboMcpTokenProvider, null,
|
|
1225
|
+
react_2.default.createElement(tambo_mcp_provider_1.TamboMcpProvider, null,
|
|
1226
|
+
react_2.default.createElement(Capture, { search: "apple" }))))));
|
|
1227
|
+
// Wait for resources
|
|
1228
|
+
await (0, react_1.waitFor)(() => {
|
|
1229
|
+
// Should have: 1 MCP resource (Apple filtered) + 1 registry resource (dynamic)
|
|
1230
|
+
expect(capturedResources.length).toBe(2);
|
|
1231
|
+
});
|
|
1232
|
+
const uris = capturedResources.map((r) => r.resource.uri);
|
|
1233
|
+
// MCP "Apple" should match (filtered locally)
|
|
1234
|
+
expect(uris).toContain("server-a:file:///apple.txt");
|
|
1235
|
+
// Registry dynamic resource
|
|
1236
|
+
expect(uris).toContain("registry:registry://apple");
|
|
1237
|
+
// MCP "Banana" should NOT be included (filtered out)
|
|
1238
|
+
expect(uris).not.toContain("server-a:file:///banana.txt");
|
|
1239
|
+
});
|
|
1240
|
+
});
|
|
818
1241
|
describe("useTamboMcpResource - read individual resource", () => {
|
|
819
1242
|
let queryClient;
|
|
820
1243
|
beforeEach(() => {
|
|
@@ -991,13 +1414,14 @@ describe("useTamboMcpResource - read individual resource", () => {
|
|
|
991
1414
|
uri: "file:///home/user/doc.txt",
|
|
992
1415
|
});
|
|
993
1416
|
});
|
|
994
|
-
it("should read registry resources via resourceSource
|
|
995
|
-
const
|
|
1417
|
+
it("should read registry resources via resourceSource with registry prefix", async () => {
|
|
1418
|
+
const originalUri = "file:///local/registry-doc.txt";
|
|
1419
|
+
const prefixedUri = `registry:${originalUri}`;
|
|
996
1420
|
const listResources = jest.fn().mockResolvedValue([]);
|
|
997
1421
|
const getResource = jest.fn().mockResolvedValue({
|
|
998
1422
|
contents: [
|
|
999
1423
|
{
|
|
1000
|
-
uri:
|
|
1424
|
+
uri: originalUri,
|
|
1001
1425
|
mimeType: "text/plain",
|
|
1002
1426
|
text: "Registry content",
|
|
1003
1427
|
},
|
|
@@ -1005,7 +1429,8 @@ describe("useTamboMcpResource - read individual resource", () => {
|
|
|
1005
1429
|
});
|
|
1006
1430
|
let capturedResourceData = null;
|
|
1007
1431
|
const Capture = () => {
|
|
1008
|
-
|
|
1432
|
+
// Request with registry: prefix
|
|
1433
|
+
const { data: resourceData } = (0, mcp_hooks_1.useTamboMcpResource)(prefixedUri);
|
|
1009
1434
|
(0, react_2.useEffect)(() => {
|
|
1010
1435
|
if (resourceData) {
|
|
1011
1436
|
capturedResourceData = resourceData;
|
|
@@ -1025,7 +1450,8 @@ describe("useTamboMcpResource - read individual resource", () => {
|
|
|
1025
1450
|
await (0, react_1.waitFor)(() => {
|
|
1026
1451
|
expect(capturedResourceData).not.toBeNull();
|
|
1027
1452
|
});
|
|
1028
|
-
|
|
1453
|
+
// getResource should be called with the original URI (prefix stripped)
|
|
1454
|
+
expect(getResource).toHaveBeenCalledWith(originalUri);
|
|
1029
1455
|
expect(capturedResourceData.contents[0].text).toBe("Registry content");
|
|
1030
1456
|
});
|
|
1031
1457
|
});
|