@tambo-ai/react 0.65.3 → 0.66.1
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 +120 -20
- package/dist/{providers/hoc → hoc}/with-tambo-interactable.d.ts +2 -2
- package/dist/hoc/with-tambo-interactable.d.ts.map +1 -0
- package/dist/{providers/hoc → hoc}/with-tambo-interactable.js +29 -2
- package/dist/hoc/with-tambo-interactable.js.map +1 -0
- package/dist/hoc/with-tambo-interactable.test.d.ts +2 -0
- package/dist/hoc/with-tambo-interactable.test.d.ts.map +1 -0
- package/dist/hoc/with-tambo-interactable.test.js +192 -0
- package/dist/hoc/with-tambo-interactable.test.js.map +1 -0
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +2 -1
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/use-current-message.d.ts +51 -7
- package/dist/hooks/use-current-message.d.ts.map +1 -1
- package/dist/hooks/use-current-message.js +50 -6
- package/dist/hooks/use-current-message.js.map +1 -1
- package/dist/hooks/use-current-message.test.d.ts +2 -0
- package/dist/hooks/use-current-message.test.d.ts.map +1 -0
- package/dist/hooks/use-current-message.test.js +264 -0
- package/dist/hooks/use-current-message.test.js.map +1 -0
- package/dist/index.d.ts +10 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +2 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/mcp-hooks.d.ts +77 -6
- package/dist/mcp/mcp-hooks.d.ts.map +1 -1
- package/dist/mcp/mcp-hooks.js +104 -40
- package/dist/mcp/mcp-hooks.js.map +1 -1
- package/dist/mcp/mcp-hooks.test.js +83 -18
- package/dist/mcp/mcp-hooks.test.js.map +1 -1
- package/dist/mcp/tambo-mcp-provider.d.ts.map +1 -1
- package/dist/mcp/tambo-mcp-provider.js +2 -1
- package/dist/mcp/tambo-mcp-provider.js.map +1 -1
- package/dist/model/component-metadata.d.ts +444 -14
- package/dist/model/component-metadata.d.ts.map +1 -1
- package/dist/model/component-metadata.js.map +1 -1
- package/dist/model/generate-component-response.d.ts +12 -1
- package/dist/model/generate-component-response.d.ts.map +1 -1
- package/dist/model/generate-component-response.js.map +1 -1
- package/dist/model/resource-info.d.ts +55 -0
- package/dist/model/resource-info.d.ts.map +1 -0
- package/dist/model/resource-info.js +3 -0
- package/dist/model/resource-info.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-component-provider.d.ts +4 -4
- package/dist/providers/tambo-component-provider.d.ts.map +1 -1
- package/dist/providers/tambo-component-provider.js.map +1 -1
- package/dist/providers/tambo-interactable-provider-partial-updates.test.js +87 -87
- package/dist/providers/tambo-interactable-provider-partial-updates.test.js.map +1 -1
- package/dist/providers/tambo-interactable-provider.d.ts +2 -3
- package/dist/providers/tambo-interactable-provider.d.ts.map +1 -1
- package/dist/providers/tambo-interactable-provider.js +47 -41
- package/dist/providers/tambo-interactable-provider.js.map +1 -1
- package/dist/providers/tambo-interactables-additional-context-edge-cases.test.js +9 -9
- package/dist/providers/tambo-interactables-additional-context-edge-cases.test.js.map +1 -1
- package/dist/providers/tambo-interactables-additional-context.test.js +11 -11
- package/dist/providers/tambo-interactables-additional-context.test.js.map +1 -1
- package/dist/providers/tambo-registry-provider.d.ts +28 -7
- package/dist/providers/tambo-registry-provider.d.ts.map +1 -1
- package/dist/providers/tambo-registry-provider.js +70 -181
- package/dist/providers/tambo-registry-provider.js.map +1 -1
- package/dist/providers/tambo-registry-provider.test.js +152 -30
- package/dist/providers/tambo-registry-provider.test.js.map +1 -1
- package/dist/providers/tambo-registry-schema-compat.test.d.ts +2 -0
- package/dist/providers/tambo-registry-schema-compat.test.d.ts.map +1 -0
- package/dist/providers/tambo-registry-schema-compat.test.js +616 -0
- package/dist/providers/tambo-registry-schema-compat.test.js.map +1 -0
- package/dist/providers/tambo-stubs.d.ts +2 -2
- package/dist/providers/tambo-stubs.d.ts.map +1 -1
- package/dist/providers/tambo-stubs.js +5 -0
- package/dist/providers/tambo-stubs.js.map +1 -1
- package/dist/providers/tambo-thread-input-provider.d.ts +1 -0
- package/dist/providers/tambo-thread-input-provider.d.ts.map +1 -1
- package/dist/providers/tambo-thread-input-provider.js +3 -3
- package/dist/providers/tambo-thread-input-provider.js.map +1 -1
- package/dist/providers/tambo-thread-provider.d.ts.map +1 -1
- package/dist/providers/tambo-thread-provider.js.map +1 -1
- package/dist/providers/tambo-thread-provider.test.js +32 -36
- package/dist/providers/tambo-thread-provider.test.js.map +1 -1
- package/dist/schema/index.d.ts +6 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +18 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/json-schema.d.ts +35 -0
- package/dist/schema/json-schema.d.ts.map +1 -0
- package/dist/schema/json-schema.js +103 -0
- package/dist/schema/json-schema.js.map +1 -0
- package/dist/schema/schema.d.ts +66 -0
- package/dist/schema/schema.d.ts.map +1 -0
- package/dist/schema/schema.js +189 -0
- package/dist/schema/schema.js.map +1 -0
- package/dist/schema/schema.test.d.ts +2 -0
- package/dist/schema/schema.test.d.ts.map +1 -0
- package/dist/schema/schema.test.js +41 -0
- package/dist/schema/schema.test.js.map +1 -0
- package/dist/schema/standard-schema.d.ts +21 -0
- package/dist/schema/standard-schema.d.ts.map +1 -0
- package/dist/schema/standard-schema.js +37 -0
- package/dist/schema/standard-schema.js.map +1 -0
- package/dist/schema/validate.d.ts +14 -0
- package/dist/schema/validate.d.ts.map +1 -0
- package/dist/schema/validate.js +148 -0
- package/dist/schema/validate.js.map +1 -0
- package/dist/schema/validate.test.d.ts +2 -0
- package/dist/schema/validate.test.d.ts.map +1 -0
- package/dist/schema/validate.test.js +128 -0
- package/dist/schema/validate.test.js.map +1 -0
- package/dist/schema/zod.d.ts +54 -0
- package/dist/schema/zod.d.ts.map +1 -0
- package/dist/schema/zod.js +147 -0
- package/dist/schema/zod.js.map +1 -0
- package/dist/testing/tools.d.ts +29 -15
- package/dist/testing/tools.d.ts.map +1 -1
- package/dist/testing/tools.js +64 -19
- package/dist/testing/tools.js.map +1 -1
- package/dist/util/generate-component.d.ts.map +1 -1
- package/dist/util/generate-component.js +3 -3
- package/dist/util/generate-component.js.map +1 -1
- package/dist/util/mcp-server-utils.d.ts +23 -0
- package/dist/util/mcp-server-utils.d.ts.map +1 -0
- package/dist/util/mcp-server-utils.js +107 -0
- package/dist/util/mcp-server-utils.js.map +1 -0
- package/dist/util/mcp-server-utils.test.d.ts +2 -0
- package/dist/util/mcp-server-utils.test.d.ts.map +1 -0
- package/dist/util/mcp-server-utils.test.js +287 -0
- package/dist/util/mcp-server-utils.test.js.map +1 -0
- package/dist/util/message-builder.d.ts +2 -1
- package/dist/util/message-builder.d.ts.map +1 -1
- package/dist/util/message-builder.js +54 -36
- package/dist/util/message-builder.js.map +1 -1
- package/dist/util/message-builder.test.js +500 -13
- package/dist/util/message-builder.test.js.map +1 -1
- package/dist/util/registry-validators.d.ts +26 -0
- package/dist/util/registry-validators.d.ts.map +1 -0
- package/dist/util/registry-validators.js +105 -0
- package/dist/util/registry-validators.js.map +1 -0
- package/dist/util/registry-validators.test.d.ts +2 -0
- package/dist/util/registry-validators.test.d.ts.map +1 -0
- package/dist/util/registry-validators.test.js +235 -0
- package/dist/util/registry-validators.test.js.map +1 -0
- package/dist/util/registry.d.ts +35 -7
- package/dist/util/registry.d.ts.map +1 -1
- package/dist/util/registry.js +60 -77
- package/dist/util/registry.js.map +1 -1
- package/dist/util/registry.test.d.ts +2 -0
- package/dist/util/registry.test.d.ts.map +1 -0
- package/dist/util/registry.test.js +204 -0
- package/dist/util/registry.test.js.map +1 -0
- package/dist/util/resource-validators.d.ts +16 -0
- package/dist/util/resource-validators.d.ts.map +1 -0
- package/dist/util/resource-validators.js +34 -0
- package/dist/util/resource-validators.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 +12 -4
- package/dist/util/tool-caller.js.map +1 -1
- package/esm/{providers/hoc → hoc}/with-tambo-interactable.d.ts +2 -2
- package/esm/hoc/with-tambo-interactable.d.ts.map +1 -0
- package/esm/{providers/hoc → hoc}/with-tambo-interactable.js +29 -2
- package/esm/hoc/with-tambo-interactable.js.map +1 -0
- package/esm/hoc/with-tambo-interactable.test.d.ts +2 -0
- package/esm/hoc/with-tambo-interactable.test.d.ts.map +1 -0
- package/esm/hoc/with-tambo-interactable.test.js +187 -0
- package/esm/hoc/with-tambo-interactable.test.js.map +1 -0
- package/esm/hooks/index.d.ts +1 -1
- package/esm/hooks/index.d.ts.map +1 -1
- package/esm/hooks/index.js +1 -1
- package/esm/hooks/index.js.map +1 -1
- package/esm/hooks/use-current-message.d.ts +51 -7
- package/esm/hooks/use-current-message.d.ts.map +1 -1
- package/esm/hooks/use-current-message.js +48 -5
- package/esm/hooks/use-current-message.js.map +1 -1
- package/esm/hooks/use-current-message.test.d.ts +2 -0
- package/esm/hooks/use-current-message.test.d.ts.map +1 -0
- package/esm/hooks/use-current-message.test.js +259 -0
- package/esm/hooks/use-current-message.test.js.map +1 -0
- package/esm/index.d.ts +10 -8
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +4 -2
- package/esm/index.js.map +1 -1
- package/esm/mcp/index.d.ts +1 -1
- package/esm/mcp/index.d.ts.map +1 -1
- package/esm/mcp/index.js +1 -1
- package/esm/mcp/index.js.map +1 -1
- package/esm/mcp/mcp-hooks.d.ts +77 -6
- package/esm/mcp/mcp-hooks.d.ts.map +1 -1
- package/esm/mcp/mcp-hooks.js +103 -40
- package/esm/mcp/mcp-hooks.js.map +1 -1
- package/esm/mcp/mcp-hooks.test.js +84 -19
- package/esm/mcp/mcp-hooks.test.js.map +1 -1
- package/esm/mcp/tambo-mcp-provider.d.ts.map +1 -1
- package/esm/mcp/tambo-mcp-provider.js +2 -1
- package/esm/mcp/tambo-mcp-provider.js.map +1 -1
- package/esm/model/component-metadata.d.ts +444 -14
- package/esm/model/component-metadata.d.ts.map +1 -1
- package/esm/model/component-metadata.js.map +1 -1
- package/esm/model/generate-component-response.d.ts +12 -1
- package/esm/model/generate-component-response.d.ts.map +1 -1
- package/esm/model/generate-component-response.js.map +1 -1
- package/esm/model/resource-info.d.ts +55 -0
- package/esm/model/resource-info.d.ts.map +1 -0
- package/esm/model/resource-info.js +2 -0
- package/esm/model/resource-info.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-component-provider.d.ts +4 -4
- package/esm/providers/tambo-component-provider.d.ts.map +1 -1
- package/esm/providers/tambo-component-provider.js.map +1 -1
- package/esm/providers/tambo-interactable-provider-partial-updates.test.js +1 -1
- package/esm/providers/tambo-interactable-provider-partial-updates.test.js.map +1 -1
- package/esm/providers/tambo-interactable-provider.d.ts +2 -3
- package/esm/providers/tambo-interactable-provider.d.ts.map +1 -1
- package/esm/providers/tambo-interactable-provider.js +44 -38
- package/esm/providers/tambo-interactable-provider.js.map +1 -1
- package/esm/providers/tambo-interactables-additional-context-edge-cases.test.js +3 -3
- package/esm/providers/tambo-interactables-additional-context-edge-cases.test.js.map +1 -1
- package/esm/providers/tambo-interactables-additional-context.test.js +3 -3
- package/esm/providers/tambo-interactables-additional-context.test.js.map +1 -1
- package/esm/providers/tambo-registry-provider.d.ts +28 -7
- package/esm/providers/tambo-registry-provider.d.ts.map +1 -1
- package/esm/providers/tambo-registry-provider.js +67 -175
- package/esm/providers/tambo-registry-provider.js.map +1 -1
- package/esm/providers/tambo-registry-provider.test.js +148 -26
- package/esm/providers/tambo-registry-provider.test.js.map +1 -1
- package/esm/providers/tambo-registry-schema-compat.test.d.ts +2 -0
- package/esm/providers/tambo-registry-schema-compat.test.d.ts.map +1 -0
- package/esm/providers/tambo-registry-schema-compat.test.js +578 -0
- package/esm/providers/tambo-registry-schema-compat.test.js.map +1 -0
- package/esm/providers/tambo-stubs.d.ts +2 -2
- package/esm/providers/tambo-stubs.d.ts.map +1 -1
- package/esm/providers/tambo-stubs.js +5 -0
- package/esm/providers/tambo-stubs.js.map +1 -1
- package/esm/providers/tambo-thread-input-provider.d.ts +1 -0
- package/esm/providers/tambo-thread-input-provider.d.ts.map +1 -1
- package/esm/providers/tambo-thread-input-provider.js +3 -3
- package/esm/providers/tambo-thread-input-provider.js.map +1 -1
- package/esm/providers/tambo-thread-provider.d.ts.map +1 -1
- package/esm/providers/tambo-thread-provider.js.map +1 -1
- package/esm/providers/tambo-thread-provider.test.js +24 -28
- package/esm/providers/tambo-thread-provider.test.js.map +1 -1
- package/esm/schema/index.d.ts +6 -0
- package/esm/schema/index.d.ts.map +1 -0
- package/esm/schema/index.js +6 -0
- package/esm/schema/index.js.map +1 -0
- package/esm/schema/json-schema.d.ts +35 -0
- package/esm/schema/json-schema.d.ts.map +1 -0
- package/esm/schema/json-schema.js +98 -0
- package/esm/schema/json-schema.js.map +1 -0
- package/esm/schema/schema.d.ts +66 -0
- package/esm/schema/schema.d.ts.map +1 -0
- package/esm/schema/schema.js +182 -0
- package/esm/schema/schema.js.map +1 -0
- package/esm/schema/schema.test.d.ts +2 -0
- package/esm/schema/schema.test.d.ts.map +1 -0
- package/esm/schema/schema.test.js +39 -0
- package/esm/schema/schema.test.js.map +1 -0
- package/esm/schema/standard-schema.d.ts +21 -0
- package/esm/schema/standard-schema.d.ts.map +1 -0
- package/esm/schema/standard-schema.js +34 -0
- package/esm/schema/standard-schema.js.map +1 -0
- package/esm/schema/validate.d.ts +14 -0
- package/esm/schema/validate.d.ts.map +1 -0
- package/esm/schema/validate.js +145 -0
- package/esm/schema/validate.js.map +1 -0
- package/esm/schema/validate.test.d.ts +2 -0
- package/esm/schema/validate.test.d.ts.map +1 -0
- package/esm/schema/validate.test.js +126 -0
- package/esm/schema/validate.test.js.map +1 -0
- package/esm/schema/zod.d.ts +54 -0
- package/esm/schema/zod.d.ts.map +1 -0
- package/esm/schema/zod.js +136 -0
- package/esm/schema/zod.js.map +1 -0
- package/esm/testing/tools.d.ts +29 -15
- package/esm/testing/tools.d.ts.map +1 -1
- package/esm/testing/tools.js +62 -16
- package/esm/testing/tools.js.map +1 -1
- package/esm/util/generate-component.d.ts.map +1 -1
- package/esm/util/generate-component.js +3 -3
- package/esm/util/generate-component.js.map +1 -1
- package/esm/util/mcp-server-utils.d.ts +23 -0
- package/esm/util/mcp-server-utils.d.ts.map +1 -0
- package/esm/util/mcp-server-utils.js +102 -0
- package/esm/util/mcp-server-utils.js.map +1 -0
- package/esm/util/mcp-server-utils.test.d.ts +2 -0
- package/esm/util/mcp-server-utils.test.d.ts.map +1 -0
- package/esm/util/mcp-server-utils.test.js +285 -0
- package/esm/util/mcp-server-utils.test.js.map +1 -0
- package/esm/util/message-builder.d.ts +2 -1
- package/esm/util/message-builder.d.ts.map +1 -1
- package/esm/util/message-builder.js +54 -36
- package/esm/util/message-builder.js.map +1 -1
- package/esm/util/message-builder.test.js +500 -13
- package/esm/util/message-builder.test.js.map +1 -1
- package/esm/util/registry-validators.d.ts +26 -0
- package/esm/util/registry-validators.d.ts.map +1 -0
- package/esm/util/registry-validators.js +100 -0
- package/esm/util/registry-validators.js.map +1 -0
- package/esm/util/registry-validators.test.d.ts +2 -0
- package/esm/util/registry-validators.test.d.ts.map +1 -0
- package/esm/util/registry-validators.test.js +233 -0
- package/esm/util/registry-validators.test.js.map +1 -0
- package/esm/util/registry.d.ts +35 -7
- package/esm/util/registry.d.ts.map +1 -1
- package/esm/util/registry.js +57 -73
- package/esm/util/registry.js.map +1 -1
- package/esm/util/registry.test.d.ts +2 -0
- package/esm/util/registry.test.d.ts.map +1 -0
- package/esm/util/registry.test.js +169 -0
- package/esm/util/registry.test.js.map +1 -0
- package/esm/util/resource-validators.d.ts +16 -0
- package/esm/util/resource-validators.d.ts.map +1 -0
- package/esm/util/resource-validators.js +30 -0
- package/esm/util/resource-validators.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 +12 -4
- package/esm/util/tool-caller.js.map +1 -1
- package/package.json +13 -8
- package/dist/providers/hoc/with-tambo-interactable.d.ts.map +0 -1
- package/dist/providers/hoc/with-tambo-interactable.js.map +0 -1
- package/dist/util/validate-zod-schema.d.ts +0 -10
- package/dist/util/validate-zod-schema.d.ts.map +0 -1
- package/dist/util/validate-zod-schema.js +0 -100
- package/dist/util/validate-zod-schema.js.map +0 -1
- package/dist/util/validate-zod-schema.test.d.ts +0 -2
- package/dist/util/validate-zod-schema.test.d.ts.map +0 -1
- package/dist/util/validate-zod-schema.test.js +0 -96
- package/dist/util/validate-zod-schema.test.js.map +0 -1
- package/esm/providers/hoc/with-tambo-interactable.d.ts.map +0 -1
- package/esm/providers/hoc/with-tambo-interactable.js.map +0 -1
- package/esm/util/validate-zod-schema.d.ts +0 -10
- package/esm/util/validate-zod-schema.d.ts.map +0 -1
- package/esm/util/validate-zod-schema.js +0 -97
- package/esm/util/validate-zod-schema.js.map +0 -1
- package/esm/util/validate-zod-schema.test.d.ts +0 -2
- package/esm/util/validate-zod-schema.test.d.ts.map +0 -1
- package/esm/util/validate-zod-schema.test.js +0 -94
- package/esm/util/validate-zod-schema.test.js.map +0 -1
package/esm/mcp/mcp-hooks.js
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { useTamboQueries, useTamboQuery } from "../hooks";
|
|
2
|
+
import { useTamboRegistry } from "../providers/tambo-registry-provider";
|
|
2
3
|
import { useTamboMcpServers, } from "./tambo-mcp-provider";
|
|
4
|
+
/**
|
|
5
|
+
* Type guard for narrowing a `ListResourceEntry` to an MCP-backed resource
|
|
6
|
+
* (entries where `server` is non-null).
|
|
7
|
+
*/
|
|
8
|
+
export function isMcpResourceEntry(entry) {
|
|
9
|
+
return entry.server !== null && isConnectedMcpServer(entry.server);
|
|
10
|
+
}
|
|
3
11
|
/**
|
|
4
12
|
* Hook to get the prompts for all the registered MCP servers.
|
|
5
13
|
* @returns The prompts for the MCP servers, including the server that the prompt was found on.
|
|
@@ -27,12 +35,8 @@ export function useTamboMcpPromptList() {
|
|
|
27
35
|
})),
|
|
28
36
|
combine: (results) => {
|
|
29
37
|
const combined = combineArrayResults(results);
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
if (!shouldPrefix) {
|
|
33
|
-
return combined;
|
|
34
|
-
}
|
|
35
|
-
// Apply prefixes to all prompts
|
|
38
|
+
// Always apply serverKey prefix to MCP prompts (breaking change for consistency with resources)
|
|
39
|
+
// This ensures clear separation between local and remote prompts
|
|
36
40
|
return {
|
|
37
41
|
...combined,
|
|
38
42
|
data: combined.data.map((entry) => ({
|
|
@@ -120,13 +124,16 @@ export function useTamboMcpPrompt(promptName, args = {}) {
|
|
|
120
124
|
});
|
|
121
125
|
}
|
|
122
126
|
/**
|
|
123
|
-
* Hook to get the resources for all the registered MCP servers.
|
|
124
|
-
* @returns The resources
|
|
127
|
+
* Hook to get the resources for all the registered MCP servers and registry.
|
|
128
|
+
* @returns The resources from MCP servers and the local registry, including the server that the resource was found on (null for registry resources).
|
|
125
129
|
*/
|
|
126
130
|
export function useTamboMcpResourceList() {
|
|
127
131
|
const mcpServers = useTamboMcpServers();
|
|
128
|
-
const
|
|
129
|
-
|
|
132
|
+
const { resources: staticResources, resourceSource } = useTamboRegistry();
|
|
133
|
+
// Build list of queries: MCP servers + optional dynamic resource source
|
|
134
|
+
const queriesToRun = [
|
|
135
|
+
// MCP server queries
|
|
136
|
+
...mcpServers.map((mcpServer) => ({
|
|
130
137
|
queryKey: ["mcp-resources", mcpServer.key],
|
|
131
138
|
// Only run for connected servers that have a client
|
|
132
139
|
enabled: isConnectedMcpServer(mcpServer),
|
|
@@ -144,23 +151,56 @@ export function useTamboMcpResourceList() {
|
|
|
144
151
|
return resourceEntries;
|
|
145
152
|
},
|
|
146
153
|
})),
|
|
154
|
+
// Dynamic resource source query (if exists)
|
|
155
|
+
...(resourceSource
|
|
156
|
+
? [
|
|
157
|
+
{
|
|
158
|
+
queryKey: ["registry-resources", "dynamic"],
|
|
159
|
+
enabled: true,
|
|
160
|
+
queryFn: async () => {
|
|
161
|
+
if (!resourceSource)
|
|
162
|
+
return [];
|
|
163
|
+
const resources = await resourceSource.listResources();
|
|
164
|
+
return resources.map((resource) => ({
|
|
165
|
+
server: null,
|
|
166
|
+
resource,
|
|
167
|
+
}));
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
]
|
|
171
|
+
: []),
|
|
172
|
+
];
|
|
173
|
+
const queries = useTamboQueries({
|
|
174
|
+
queries: queriesToRun,
|
|
147
175
|
combine: (results) => {
|
|
176
|
+
// Type assertion needed because queries can return different entry types
|
|
148
177
|
const combined = combineArrayResults(results);
|
|
149
|
-
//
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
178
|
+
// Add static registry resources (no query needed)
|
|
179
|
+
const staticEntries = staticResources.map((resource) => ({
|
|
180
|
+
server: null,
|
|
181
|
+
resource,
|
|
182
|
+
}));
|
|
183
|
+
// Merge static resources with query results (registry resources first)
|
|
184
|
+
const allData = [...staticEntries, ...combined.data];
|
|
185
|
+
// Apply serverKey prefix to ALL MCP resources (breaking change)
|
|
186
|
+
// Registry resources (server: null) are never prefixed
|
|
187
|
+
const prefixedData = allData.map((entry) => {
|
|
188
|
+
if (entry.server === null) {
|
|
189
|
+
// Registry resource - no prefix
|
|
190
|
+
return entry;
|
|
191
|
+
}
|
|
192
|
+
// MCP resource - always prefix with serverKey
|
|
193
|
+
return {
|
|
158
194
|
...entry,
|
|
159
195
|
resource: {
|
|
160
196
|
...entry.resource,
|
|
161
197
|
uri: `${entry.server.serverKey}:${entry.resource.uri}`,
|
|
162
198
|
},
|
|
163
|
-
}
|
|
199
|
+
};
|
|
200
|
+
});
|
|
201
|
+
return {
|
|
202
|
+
...combined,
|
|
203
|
+
data: prefixedData,
|
|
164
204
|
};
|
|
165
205
|
},
|
|
166
206
|
});
|
|
@@ -168,36 +208,59 @@ export function useTamboMcpResourceList() {
|
|
|
168
208
|
}
|
|
169
209
|
/**
|
|
170
210
|
* Hook to get the resource for the specified URI.
|
|
171
|
-
* @param resourceUri - The URI of the resource to get. Can be prefixed with serverKey (e.g., "linear:file://foo") or unprefixed.
|
|
211
|
+
* @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.
|
|
172
212
|
* @returns The resource for the specified URI.
|
|
173
213
|
*/
|
|
174
214
|
export function useTamboMcpResource(resourceUri) {
|
|
175
|
-
|
|
215
|
+
const { resourceSource } = useTamboRegistry();
|
|
176
216
|
const { data: resourceEntries } = useTamboMcpResourceList();
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
//
|
|
180
|
-
const
|
|
217
|
+
// Find which server/source has the resource
|
|
218
|
+
const resourceEntry = resourceEntries?.find((entry) => entry.resource.uri === resourceUri);
|
|
219
|
+
// Determine if this is a known registry resource or MCP resource
|
|
220
|
+
const isKnownEntry = resourceEntry != null;
|
|
221
|
+
const isRegistryResource = isKnownEntry && resourceEntry.server === null;
|
|
222
|
+
const mcpServer = isKnownEntry && resourceEntry.server != null ? resourceEntry.server : null;
|
|
181
223
|
// Strip the prefix to get the original resource URI for the MCP server call
|
|
182
|
-
// Only strip if we found a matching resource entry with
|
|
183
|
-
const originalResourceUri =
|
|
184
|
-
? resourceUri?.replace(`${
|
|
224
|
+
// Only strip if we found a matching resource entry with an MCP server
|
|
225
|
+
const originalResourceUri = isKnownEntry && mcpServer
|
|
226
|
+
? resourceUri?.replace(`${mcpServer.serverKey}:`, "")
|
|
185
227
|
: resourceUri;
|
|
228
|
+
// Check if we can fetch this resource. Registry resources can always be
|
|
229
|
+
// fetched when a resourceSource exists, even if they haven't appeared in a
|
|
230
|
+
// previous listResources result.
|
|
231
|
+
const hasRegistrySource = resourceSource != null;
|
|
232
|
+
const hasConnectedMcpServer = isKnownEntry && mcpServer != null && isConnectedMcpServer(mcpServer);
|
|
233
|
+
const canFetchResource = Boolean(resourceUri && (hasRegistrySource || hasConnectedMcpServer));
|
|
234
|
+
let locationKey;
|
|
235
|
+
if (isRegistryResource || (!isKnownEntry && hasRegistrySource)) {
|
|
236
|
+
locationKey = "registry";
|
|
237
|
+
}
|
|
238
|
+
else if (mcpServer) {
|
|
239
|
+
locationKey = mcpServer.key;
|
|
240
|
+
}
|
|
186
241
|
return useTamboQuery({
|
|
187
|
-
// Include server identity to prevent stale cache hits
|
|
188
|
-
queryKey: ["
|
|
189
|
-
|
|
190
|
-
enabled: Boolean(resourceUri && mcpServer && isConnectedMcpServer(mcpServer)),
|
|
242
|
+
// Include server identity or "registry" to prevent stale cache hits
|
|
243
|
+
queryKey: ["resource", resourceUri, locationKey],
|
|
244
|
+
enabled: canFetchResource,
|
|
191
245
|
queryFn: async () => {
|
|
192
|
-
if (!originalResourceUri
|
|
193
|
-
!mcpServer ||
|
|
194
|
-
!isConnectedMcpServer(mcpServer)) {
|
|
246
|
+
if (!originalResourceUri) {
|
|
195
247
|
return null;
|
|
196
248
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
249
|
+
// Registry resource: use resourceSource.getResource
|
|
250
|
+
// If this is not a known entry but we have a resourceSource, treat it as
|
|
251
|
+
// a registry resource by default.
|
|
252
|
+
if (resourceSource && (!isKnownEntry || isRegistryResource)) {
|
|
253
|
+
const result = await resourceSource.getResource(originalResourceUri);
|
|
254
|
+
return result ?? null;
|
|
255
|
+
}
|
|
256
|
+
// MCP resource: use MCP client
|
|
257
|
+
if (mcpServer && isConnectedMcpServer(mcpServer)) {
|
|
258
|
+
const result = await mcpServer.client.client.readResource({
|
|
259
|
+
uri: originalResourceUri,
|
|
260
|
+
});
|
|
261
|
+
return result ?? null;
|
|
262
|
+
}
|
|
263
|
+
return null;
|
|
201
264
|
},
|
|
202
265
|
});
|
|
203
266
|
}
|
package/esm/mcp/mcp-hooks.js.map
CHANGED
|
@@ -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 {\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];\nexport interface ListResourceEntry {\n // Only connected servers produce resource entries, so expose the connected type\n server: ConnectedMcpServer;\n resource: ListResourceItem;\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 // Apply prefixing based on current server count\n const shouldPrefix = mcpServers.length > 1;\n if (!shouldPrefix) {\n return combined;\n }\n\n // Apply prefixes to all 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.\n * @returns The resources for the MCP servers, including the server that the resource was found on.\n */\nexport function useTamboMcpResourceList() {\n const mcpServers = useTamboMcpServers();\n\n const queries = useTamboQueries({\n queries: 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<ListResourceEntry[]> => {\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 = resources.map((resource) => ({\n server: mcpServer,\n resource,\n }));\n return resourceEntries;\n },\n })),\n combine: (results) => {\n const combined = combineArrayResults(results);\n // Apply prefixing based on current server count\n const shouldPrefix = mcpServers.length > 1;\n if (!shouldPrefix) {\n return combined;\n }\n\n // Apply prefixes to all resources\n return {\n ...combined,\n data: combined.data.map((entry) => ({\n ...entry,\n resource: {\n ...entry.resource,\n uri: `${entry.server.serverKey}:${entry.resource.uri}`,\n },\n })),\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\") or unprefixed.\n * @returns The resource for the specified URI.\n */\nexport function useTamboMcpResource(resourceUri: string | undefined) {\n // figure out which server has the resource\n const { data: resourceEntries } = useTamboMcpResourceList();\n const resourceEntry = resourceEntries?.find(\n (resource) => resource.resource.uri === resourceUri,\n );\n // Use the stable server key (and the server instance itself) instead of brittle\n // name/url/transport matching.\n const mcpServer = resourceEntry?.server;\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 a server\n const originalResourceUri = resourceEntry\n ? resourceUri?.replace(`${resourceEntry.server.serverKey}:`, \"\")\n : resourceUri;\n\n return useTamboQuery({\n // Include server identity to prevent stale cache hits\n queryKey: [\"mcp-resource\", resourceUri, mcpServer?.key],\n // Only run when we have a resource URI and a connected server\n enabled: Boolean(\n resourceUri && mcpServer && isConnectedMcpServer(mcpServer),\n ),\n queryFn: async (): Promise<ReadResourceResult | null> => {\n if (\n !originalResourceUri ||\n !mcpServer ||\n !isConnectedMcpServer(mcpServer)\n ) {\n return null;\n }\n const result = await mcpServer.client.client.readResource({\n uri: originalResourceUri,\n });\n return result ?? null; // return null because react-query doesn't like undefined results\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 { 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"]}
|
|
@@ -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, useTamboMcpResourceList, useTamboMcpResource, } from "./mcp-hooks";
|
|
9
|
+
import { useTamboMcpPromptList, useTamboMcpResourceList, useTamboMcpResource, isMcpResourceEntry, } 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", () => ({
|
|
@@ -208,16 +208,16 @@ describe("useTamboMcpPromptList - individual server caching", () => {
|
|
|
208
208
|
React.createElement(TamboMcpProvider, null,
|
|
209
209
|
React.createElement(Capture, null))))));
|
|
210
210
|
// Wait for prompts to be updated (server B prompts should disappear)
|
|
211
|
-
//
|
|
211
|
+
// Prompts are now always prefixed (breaking change)
|
|
212
212
|
await waitFor(() => {
|
|
213
213
|
expect(capturedPrompts.length).toBe(2);
|
|
214
214
|
});
|
|
215
215
|
const updatedPromptNames = capturedPrompts.map((p) => p.prompt.name);
|
|
216
|
-
expect(updatedPromptNames).toContain("prompt-a1");
|
|
217
|
-
expect(updatedPromptNames).toContain("prompt-a2");
|
|
216
|
+
expect(updatedPromptNames).toContain("server-a:prompt-a1");
|
|
217
|
+
expect(updatedPromptNames).toContain("server-a:prompt-a2");
|
|
218
218
|
expect(updatedPromptNames).not.toContain("prompt-b1");
|
|
219
219
|
expect(updatedPromptNames).not.toContain("prompt-b2");
|
|
220
|
-
expect(updatedPromptNames).not.toContain("
|
|
220
|
+
expect(updatedPromptNames).not.toContain("prompt-a1"); // Always prefixed now
|
|
221
221
|
// Verify server B's client was closed
|
|
222
222
|
expect(clientB.close).toHaveBeenCalled();
|
|
223
223
|
});
|
|
@@ -435,11 +435,11 @@ describe("useTamboMcpPromptList - individual server caching", () => {
|
|
|
435
435
|
React.createElement(TamboMcpTokenProvider, null,
|
|
436
436
|
React.createElement(TamboMcpProvider, null,
|
|
437
437
|
React.createElement(Capture, null))))));
|
|
438
|
-
// Wait for initial prompts from server A
|
|
438
|
+
// Wait for initial prompts from server A (always prefixed)
|
|
439
439
|
await waitFor(() => {
|
|
440
440
|
expect(capturedPrompts.length).toBe(1);
|
|
441
441
|
});
|
|
442
|
-
expect(capturedPrompts.map((p) => p.prompt.name)).toContain("prompt-a");
|
|
442
|
+
expect(capturedPrompts.map((p) => p.prompt.name)).toContain("server-a:prompt-a");
|
|
443
443
|
// Add server B
|
|
444
444
|
rerender(React.createElement(TamboClientContext.Provider, { value: {
|
|
445
445
|
client: { baseURL: "https://api.tambo.co" },
|
|
@@ -484,6 +484,28 @@ describe("useTamboMcpResourceList - resource management", () => {
|
|
|
484
484
|
afterEach(() => {
|
|
485
485
|
queryClient.clear();
|
|
486
486
|
});
|
|
487
|
+
it("should identify MCP-backed entries with isMcpResourceEntry", () => {
|
|
488
|
+
const registryEntry = {
|
|
489
|
+
server: null,
|
|
490
|
+
// Resource shape is not important for this helper, so cast to keep the
|
|
491
|
+
// test focused on the discriminant field.
|
|
492
|
+
resource: {
|
|
493
|
+
uri: "file:///registry/doc.txt",
|
|
494
|
+
name: "Registry Doc",
|
|
495
|
+
mimeType: "text/plain",
|
|
496
|
+
},
|
|
497
|
+
};
|
|
498
|
+
const mcpEntry = {
|
|
499
|
+
server: { key: "server-a", client: {} },
|
|
500
|
+
resource: {
|
|
501
|
+
uri: "server-a:file:///home/user/doc.txt",
|
|
502
|
+
name: "Document",
|
|
503
|
+
mimeType: "text/plain",
|
|
504
|
+
},
|
|
505
|
+
};
|
|
506
|
+
expect(isMcpResourceEntry(mcpEntry)).toBe(true);
|
|
507
|
+
expect(isMcpResourceEntry(registryEntry)).toBe(false);
|
|
508
|
+
});
|
|
487
509
|
it("should fetch and combine resources from multiple servers", async () => {
|
|
488
510
|
// Mock two servers with different resources
|
|
489
511
|
const serverAResources = {
|
|
@@ -583,11 +605,15 @@ describe("useTamboMcpResourceList - resource management", () => {
|
|
|
583
605
|
expect(resourceUris).toContain("server-b:file:///workspace/README.md");
|
|
584
606
|
// Verify each resource has the correct server info
|
|
585
607
|
const resource1 = capturedResources.find((r) => r.resource.uri === "server-a:file:///home/user/doc1.txt");
|
|
586
|
-
expect(resource1
|
|
608
|
+
expect(resource1).toBeDefined();
|
|
609
|
+
expect(resource1.server).not.toBeNull();
|
|
610
|
+
expect(resource1.server.url).toBe("https://server-a.example");
|
|
587
611
|
const resource2 = capturedResources.find((r) => r.resource.uri === "server-b:file:///workspace/code.js");
|
|
588
|
-
expect(resource2
|
|
612
|
+
expect(resource2).toBeDefined();
|
|
613
|
+
expect(resource2.server).not.toBeNull();
|
|
614
|
+
expect(resource2.server.url).toBe("https://server-b.example");
|
|
589
615
|
});
|
|
590
|
-
it("should
|
|
616
|
+
it("should always prefix MCP resources even with single server", async () => {
|
|
591
617
|
const serverAResources = {
|
|
592
618
|
resources: [
|
|
593
619
|
{
|
|
@@ -636,10 +662,10 @@ describe("useTamboMcpResourceList - resource management", () => {
|
|
|
636
662
|
await waitFor(() => {
|
|
637
663
|
expect(capturedResources.length).toBe(1);
|
|
638
664
|
});
|
|
639
|
-
//
|
|
640
|
-
expect(capturedResources[0].resource.uri).toBe("file:///home/user/doc.txt");
|
|
665
|
+
// Always prefix MCP resources, even with 1 server (breaking change)
|
|
666
|
+
expect(capturedResources[0].resource.uri).toBe("server-a:file:///home/user/doc.txt");
|
|
641
667
|
});
|
|
642
|
-
it("should
|
|
668
|
+
it("should maintain prefixes even when servers are removed", async () => {
|
|
643
669
|
const serverAResources = {
|
|
644
670
|
resources: [
|
|
645
671
|
{
|
|
@@ -742,14 +768,15 @@ describe("useTamboMcpResourceList - resource management", () => {
|
|
|
742
768
|
React.createElement(TamboMcpTokenProvider, null,
|
|
743
769
|
React.createElement(TamboMcpProvider, null,
|
|
744
770
|
React.createElement(Capture, null))))));
|
|
745
|
-
// Wait for server B resources to be removed
|
|
771
|
+
// Wait for server B resources to be removed (prefixes maintained)
|
|
746
772
|
await waitFor(() => {
|
|
747
773
|
expect(capturedResources.length).toBe(2);
|
|
748
774
|
});
|
|
749
775
|
const updatedUris = capturedResources.map((r) => r.resource.uri);
|
|
750
|
-
|
|
751
|
-
expect(updatedUris).toContain("file:///home/user/
|
|
752
|
-
expect(updatedUris).
|
|
776
|
+
// Prefixes are maintained even with only 1 server (breaking change)
|
|
777
|
+
expect(updatedUris).toContain("server-a:file:///home/user/doc1.txt");
|
|
778
|
+
expect(updatedUris).toContain("server-a:file:///home/user/doc2.txt");
|
|
779
|
+
expect(updatedUris).not.toContain("file:///home/user/doc1.txt"); // Always prefixed now
|
|
753
780
|
expect(updatedUris).not.toContain("server-b:file:///workspace/code.js"); // Server B removed
|
|
754
781
|
});
|
|
755
782
|
});
|
|
@@ -768,7 +795,7 @@ describe("useTamboMcpResource - read individual resource", () => {
|
|
|
768
795
|
afterEach(() => {
|
|
769
796
|
queryClient.clear();
|
|
770
797
|
});
|
|
771
|
-
it("should read a resource from a single server (
|
|
798
|
+
it("should read a resource from a single server (prefixed)", async () => {
|
|
772
799
|
const serverAResources = {
|
|
773
800
|
resources: [
|
|
774
801
|
{
|
|
@@ -802,7 +829,8 @@ describe("useTamboMcpResource - read individual resource", () => {
|
|
|
802
829
|
createImpl.mockImplementation(async () => clientA);
|
|
803
830
|
let capturedResourceData = null;
|
|
804
831
|
const Capture = () => {
|
|
805
|
-
|
|
832
|
+
// MCP resources are always prefixed, even with single server
|
|
833
|
+
const { data: resourceData } = useTamboMcpResource("server-a:file:///home/user/doc.txt");
|
|
806
834
|
useEffect(() => {
|
|
807
835
|
if (resourceData) {
|
|
808
836
|
capturedResourceData = resourceData;
|
|
@@ -928,5 +956,42 @@ describe("useTamboMcpResource - read individual resource", () => {
|
|
|
928
956
|
uri: "file:///home/user/doc.txt",
|
|
929
957
|
});
|
|
930
958
|
});
|
|
959
|
+
it("should read registry resources via resourceSource even when not listed", async () => {
|
|
960
|
+
const registryUri = "file:///local/registry-doc.txt";
|
|
961
|
+
const listResources = jest.fn().mockResolvedValue([]);
|
|
962
|
+
const getResource = jest.fn().mockResolvedValue({
|
|
963
|
+
contents: [
|
|
964
|
+
{
|
|
965
|
+
uri: registryUri,
|
|
966
|
+
mimeType: "text/plain",
|
|
967
|
+
text: "Registry content",
|
|
968
|
+
},
|
|
969
|
+
],
|
|
970
|
+
});
|
|
971
|
+
let capturedResourceData = null;
|
|
972
|
+
const Capture = () => {
|
|
973
|
+
const { data: resourceData } = useTamboMcpResource(registryUri);
|
|
974
|
+
useEffect(() => {
|
|
975
|
+
if (resourceData) {
|
|
976
|
+
capturedResourceData = resourceData;
|
|
977
|
+
}
|
|
978
|
+
}, [resourceData]);
|
|
979
|
+
return null;
|
|
980
|
+
};
|
|
981
|
+
render(React.createElement(TamboClientContext.Provider, { value: {
|
|
982
|
+
client: { baseURL: "https://api.tambo.co" },
|
|
983
|
+
queryClient,
|
|
984
|
+
isUpdatingToken: false,
|
|
985
|
+
} },
|
|
986
|
+
React.createElement(TamboRegistryProvider, { listResources: listResources, getResource: getResource },
|
|
987
|
+
React.createElement(TamboMcpTokenProvider, null,
|
|
988
|
+
React.createElement(TamboMcpProvider, null,
|
|
989
|
+
React.createElement(Capture, null))))));
|
|
990
|
+
await waitFor(() => {
|
|
991
|
+
expect(capturedResourceData).not.toBeNull();
|
|
992
|
+
});
|
|
993
|
+
expect(getResource).toHaveBeenCalledWith(registryUri);
|
|
994
|
+
expect(capturedResourceData.contents[0].text).toBe("Registry content");
|
|
995
|
+
});
|
|
931
996
|
});
|
|
932
997
|
//# sourceMappingURL=mcp-hooks.test.js.map
|