@tambo-ai/react 0.65.3 → 0.66.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (346) hide show
  1. package/README.md +120 -20
  2. package/dist/{providers/hoc → hoc}/with-tambo-interactable.d.ts +2 -2
  3. package/dist/hoc/with-tambo-interactable.d.ts.map +1 -0
  4. package/dist/{providers/hoc → hoc}/with-tambo-interactable.js +29 -2
  5. package/dist/hoc/with-tambo-interactable.js.map +1 -0
  6. package/dist/hoc/with-tambo-interactable.test.d.ts +2 -0
  7. package/dist/hoc/with-tambo-interactable.test.d.ts.map +1 -0
  8. package/dist/hoc/with-tambo-interactable.test.js +192 -0
  9. package/dist/hoc/with-tambo-interactable.test.js.map +1 -0
  10. package/dist/hooks/index.d.ts +1 -1
  11. package/dist/hooks/index.d.ts.map +1 -1
  12. package/dist/hooks/index.js +2 -1
  13. package/dist/hooks/index.js.map +1 -1
  14. package/dist/hooks/use-current-message.d.ts +51 -7
  15. package/dist/hooks/use-current-message.d.ts.map +1 -1
  16. package/dist/hooks/use-current-message.js +50 -6
  17. package/dist/hooks/use-current-message.js.map +1 -1
  18. package/dist/hooks/use-current-message.test.d.ts +2 -0
  19. package/dist/hooks/use-current-message.test.d.ts.map +1 -0
  20. package/dist/hooks/use-current-message.test.js +264 -0
  21. package/dist/hooks/use-current-message.test.js.map +1 -0
  22. package/dist/index.d.ts +10 -8
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +7 -3
  25. package/dist/index.js.map +1 -1
  26. package/dist/mcp/index.d.ts +1 -1
  27. package/dist/mcp/index.d.ts.map +1 -1
  28. package/dist/mcp/index.js +2 -1
  29. package/dist/mcp/index.js.map +1 -1
  30. package/dist/mcp/mcp-hooks.d.ts +77 -6
  31. package/dist/mcp/mcp-hooks.d.ts.map +1 -1
  32. package/dist/mcp/mcp-hooks.js +104 -40
  33. package/dist/mcp/mcp-hooks.js.map +1 -1
  34. package/dist/mcp/mcp-hooks.test.js +83 -18
  35. package/dist/mcp/mcp-hooks.test.js.map +1 -1
  36. package/dist/mcp/tambo-mcp-provider.d.ts.map +1 -1
  37. package/dist/mcp/tambo-mcp-provider.js +2 -1
  38. package/dist/mcp/tambo-mcp-provider.js.map +1 -1
  39. package/dist/model/component-metadata.d.ts +444 -14
  40. package/dist/model/component-metadata.d.ts.map +1 -1
  41. package/dist/model/component-metadata.js.map +1 -1
  42. package/dist/model/generate-component-response.d.ts +12 -1
  43. package/dist/model/generate-component-response.d.ts.map +1 -1
  44. package/dist/model/generate-component-response.js.map +1 -1
  45. package/dist/model/resource-info.d.ts +55 -0
  46. package/dist/model/resource-info.d.ts.map +1 -0
  47. package/dist/model/resource-info.js +3 -0
  48. package/dist/model/resource-info.js.map +1 -0
  49. package/dist/providers/index.d.ts +1 -1
  50. package/dist/providers/index.d.ts.map +1 -1
  51. package/dist/providers/index.js.map +1 -1
  52. package/dist/providers/tambo-component-provider.d.ts +4 -4
  53. package/dist/providers/tambo-component-provider.d.ts.map +1 -1
  54. package/dist/providers/tambo-component-provider.js.map +1 -1
  55. package/dist/providers/tambo-interactable-provider-partial-updates.test.js +87 -87
  56. package/dist/providers/tambo-interactable-provider-partial-updates.test.js.map +1 -1
  57. package/dist/providers/tambo-interactable-provider.d.ts +2 -3
  58. package/dist/providers/tambo-interactable-provider.d.ts.map +1 -1
  59. package/dist/providers/tambo-interactable-provider.js +47 -41
  60. package/dist/providers/tambo-interactable-provider.js.map +1 -1
  61. package/dist/providers/tambo-interactables-additional-context-edge-cases.test.js +9 -9
  62. package/dist/providers/tambo-interactables-additional-context-edge-cases.test.js.map +1 -1
  63. package/dist/providers/tambo-interactables-additional-context.test.js +11 -11
  64. package/dist/providers/tambo-interactables-additional-context.test.js.map +1 -1
  65. package/dist/providers/tambo-registry-provider.d.ts +28 -7
  66. package/dist/providers/tambo-registry-provider.d.ts.map +1 -1
  67. package/dist/providers/tambo-registry-provider.js +70 -181
  68. package/dist/providers/tambo-registry-provider.js.map +1 -1
  69. package/dist/providers/tambo-registry-provider.test.js +152 -30
  70. package/dist/providers/tambo-registry-provider.test.js.map +1 -1
  71. package/dist/providers/tambo-registry-schema-compat.test.d.ts +2 -0
  72. package/dist/providers/tambo-registry-schema-compat.test.d.ts.map +1 -0
  73. package/dist/providers/tambo-registry-schema-compat.test.js +616 -0
  74. package/dist/providers/tambo-registry-schema-compat.test.js.map +1 -0
  75. package/dist/providers/tambo-stubs.d.ts +2 -2
  76. package/dist/providers/tambo-stubs.d.ts.map +1 -1
  77. package/dist/providers/tambo-stubs.js +5 -0
  78. package/dist/providers/tambo-stubs.js.map +1 -1
  79. package/dist/providers/tambo-thread-input-provider.d.ts +1 -0
  80. package/dist/providers/tambo-thread-input-provider.d.ts.map +1 -1
  81. package/dist/providers/tambo-thread-input-provider.js +3 -3
  82. package/dist/providers/tambo-thread-input-provider.js.map +1 -1
  83. package/dist/providers/tambo-thread-provider.d.ts.map +1 -1
  84. package/dist/providers/tambo-thread-provider.js.map +1 -1
  85. package/dist/providers/tambo-thread-provider.test.js +32 -36
  86. package/dist/providers/tambo-thread-provider.test.js.map +1 -1
  87. package/dist/schema/index.d.ts +6 -0
  88. package/dist/schema/index.d.ts.map +1 -0
  89. package/dist/schema/index.js +18 -0
  90. package/dist/schema/index.js.map +1 -0
  91. package/dist/schema/json-schema.d.ts +35 -0
  92. package/dist/schema/json-schema.d.ts.map +1 -0
  93. package/dist/schema/json-schema.js +103 -0
  94. package/dist/schema/json-schema.js.map +1 -0
  95. package/dist/schema/schema.d.ts +66 -0
  96. package/dist/schema/schema.d.ts.map +1 -0
  97. package/dist/schema/schema.js +192 -0
  98. package/dist/schema/schema.js.map +1 -0
  99. package/dist/schema/schema.test.d.ts +2 -0
  100. package/dist/schema/schema.test.d.ts.map +1 -0
  101. package/dist/schema/schema.test.js +41 -0
  102. package/dist/schema/schema.test.js.map +1 -0
  103. package/dist/schema/standard-schema.d.ts +21 -0
  104. package/dist/schema/standard-schema.d.ts.map +1 -0
  105. package/dist/schema/standard-schema.js +37 -0
  106. package/dist/schema/standard-schema.js.map +1 -0
  107. package/dist/schema/validate.d.ts +14 -0
  108. package/dist/schema/validate.d.ts.map +1 -0
  109. package/dist/schema/validate.js +148 -0
  110. package/dist/schema/validate.js.map +1 -0
  111. package/dist/schema/validate.test.d.ts +2 -0
  112. package/dist/schema/validate.test.d.ts.map +1 -0
  113. package/dist/schema/validate.test.js +128 -0
  114. package/dist/schema/validate.test.js.map +1 -0
  115. package/dist/schema/zod.d.ts +54 -0
  116. package/dist/schema/zod.d.ts.map +1 -0
  117. package/dist/schema/zod.js +135 -0
  118. package/dist/schema/zod.js.map +1 -0
  119. package/dist/testing/tools.d.ts +29 -15
  120. package/dist/testing/tools.d.ts.map +1 -1
  121. package/dist/testing/tools.js +64 -19
  122. package/dist/testing/tools.js.map +1 -1
  123. package/dist/util/generate-component.d.ts.map +1 -1
  124. package/dist/util/generate-component.js +3 -3
  125. package/dist/util/generate-component.js.map +1 -1
  126. package/dist/util/mcp-server-utils.d.ts +23 -0
  127. package/dist/util/mcp-server-utils.d.ts.map +1 -0
  128. package/dist/util/mcp-server-utils.js +107 -0
  129. package/dist/util/mcp-server-utils.js.map +1 -0
  130. package/dist/util/mcp-server-utils.test.d.ts +2 -0
  131. package/dist/util/mcp-server-utils.test.d.ts.map +1 -0
  132. package/dist/util/mcp-server-utils.test.js +287 -0
  133. package/dist/util/mcp-server-utils.test.js.map +1 -0
  134. package/dist/util/message-builder.d.ts +2 -1
  135. package/dist/util/message-builder.d.ts.map +1 -1
  136. package/dist/util/message-builder.js +54 -36
  137. package/dist/util/message-builder.js.map +1 -1
  138. package/dist/util/message-builder.test.js +500 -13
  139. package/dist/util/message-builder.test.js.map +1 -1
  140. package/dist/util/registry-validators.d.ts +26 -0
  141. package/dist/util/registry-validators.d.ts.map +1 -0
  142. package/dist/util/registry-validators.js +105 -0
  143. package/dist/util/registry-validators.js.map +1 -0
  144. package/dist/util/registry-validators.test.d.ts +2 -0
  145. package/dist/util/registry-validators.test.d.ts.map +1 -0
  146. package/dist/util/registry-validators.test.js +235 -0
  147. package/dist/util/registry-validators.test.js.map +1 -0
  148. package/dist/util/registry.d.ts +35 -7
  149. package/dist/util/registry.d.ts.map +1 -1
  150. package/dist/util/registry.js +60 -77
  151. package/dist/util/registry.js.map +1 -1
  152. package/dist/util/registry.test.d.ts +2 -0
  153. package/dist/util/registry.test.d.ts.map +1 -0
  154. package/dist/util/registry.test.js +204 -0
  155. package/dist/util/registry.test.js.map +1 -0
  156. package/dist/util/resource-validators.d.ts +16 -0
  157. package/dist/util/resource-validators.d.ts.map +1 -0
  158. package/dist/util/resource-validators.js +34 -0
  159. package/dist/util/resource-validators.js.map +1 -0
  160. package/dist/util/tool-caller.d.ts +2 -2
  161. package/dist/util/tool-caller.d.ts.map +1 -1
  162. package/dist/util/tool-caller.js +12 -4
  163. package/dist/util/tool-caller.js.map +1 -1
  164. package/esm/{providers/hoc → hoc}/with-tambo-interactable.d.ts +2 -2
  165. package/esm/hoc/with-tambo-interactable.d.ts.map +1 -0
  166. package/esm/{providers/hoc → hoc}/with-tambo-interactable.js +29 -2
  167. package/esm/hoc/with-tambo-interactable.js.map +1 -0
  168. package/esm/hoc/with-tambo-interactable.test.d.ts +2 -0
  169. package/esm/hoc/with-tambo-interactable.test.d.ts.map +1 -0
  170. package/esm/hoc/with-tambo-interactable.test.js +187 -0
  171. package/esm/hoc/with-tambo-interactable.test.js.map +1 -0
  172. package/esm/hooks/index.d.ts +1 -1
  173. package/esm/hooks/index.d.ts.map +1 -1
  174. package/esm/hooks/index.js +1 -1
  175. package/esm/hooks/index.js.map +1 -1
  176. package/esm/hooks/use-current-message.d.ts +51 -7
  177. package/esm/hooks/use-current-message.d.ts.map +1 -1
  178. package/esm/hooks/use-current-message.js +48 -5
  179. package/esm/hooks/use-current-message.js.map +1 -1
  180. package/esm/hooks/use-current-message.test.d.ts +2 -0
  181. package/esm/hooks/use-current-message.test.d.ts.map +1 -0
  182. package/esm/hooks/use-current-message.test.js +259 -0
  183. package/esm/hooks/use-current-message.test.js.map +1 -0
  184. package/esm/index.d.ts +10 -8
  185. package/esm/index.d.ts.map +1 -1
  186. package/esm/index.js +4 -2
  187. package/esm/index.js.map +1 -1
  188. package/esm/mcp/index.d.ts +1 -1
  189. package/esm/mcp/index.d.ts.map +1 -1
  190. package/esm/mcp/index.js +1 -1
  191. package/esm/mcp/index.js.map +1 -1
  192. package/esm/mcp/mcp-hooks.d.ts +77 -6
  193. package/esm/mcp/mcp-hooks.d.ts.map +1 -1
  194. package/esm/mcp/mcp-hooks.js +103 -40
  195. package/esm/mcp/mcp-hooks.js.map +1 -1
  196. package/esm/mcp/mcp-hooks.test.js +84 -19
  197. package/esm/mcp/mcp-hooks.test.js.map +1 -1
  198. package/esm/mcp/tambo-mcp-provider.d.ts.map +1 -1
  199. package/esm/mcp/tambo-mcp-provider.js +2 -1
  200. package/esm/mcp/tambo-mcp-provider.js.map +1 -1
  201. package/esm/model/component-metadata.d.ts +444 -14
  202. package/esm/model/component-metadata.d.ts.map +1 -1
  203. package/esm/model/component-metadata.js.map +1 -1
  204. package/esm/model/generate-component-response.d.ts +12 -1
  205. package/esm/model/generate-component-response.d.ts.map +1 -1
  206. package/esm/model/generate-component-response.js.map +1 -1
  207. package/esm/model/resource-info.d.ts +55 -0
  208. package/esm/model/resource-info.d.ts.map +1 -0
  209. package/esm/model/resource-info.js +2 -0
  210. package/esm/model/resource-info.js.map +1 -0
  211. package/esm/providers/index.d.ts +1 -1
  212. package/esm/providers/index.d.ts.map +1 -1
  213. package/esm/providers/index.js.map +1 -1
  214. package/esm/providers/tambo-component-provider.d.ts +4 -4
  215. package/esm/providers/tambo-component-provider.d.ts.map +1 -1
  216. package/esm/providers/tambo-component-provider.js.map +1 -1
  217. package/esm/providers/tambo-interactable-provider-partial-updates.test.js +1 -1
  218. package/esm/providers/tambo-interactable-provider-partial-updates.test.js.map +1 -1
  219. package/esm/providers/tambo-interactable-provider.d.ts +2 -3
  220. package/esm/providers/tambo-interactable-provider.d.ts.map +1 -1
  221. package/esm/providers/tambo-interactable-provider.js +44 -38
  222. package/esm/providers/tambo-interactable-provider.js.map +1 -1
  223. package/esm/providers/tambo-interactables-additional-context-edge-cases.test.js +3 -3
  224. package/esm/providers/tambo-interactables-additional-context-edge-cases.test.js.map +1 -1
  225. package/esm/providers/tambo-interactables-additional-context.test.js +3 -3
  226. package/esm/providers/tambo-interactables-additional-context.test.js.map +1 -1
  227. package/esm/providers/tambo-registry-provider.d.ts +28 -7
  228. package/esm/providers/tambo-registry-provider.d.ts.map +1 -1
  229. package/esm/providers/tambo-registry-provider.js +67 -175
  230. package/esm/providers/tambo-registry-provider.js.map +1 -1
  231. package/esm/providers/tambo-registry-provider.test.js +148 -26
  232. package/esm/providers/tambo-registry-provider.test.js.map +1 -1
  233. package/esm/providers/tambo-registry-schema-compat.test.d.ts +2 -0
  234. package/esm/providers/tambo-registry-schema-compat.test.d.ts.map +1 -0
  235. package/esm/providers/tambo-registry-schema-compat.test.js +578 -0
  236. package/esm/providers/tambo-registry-schema-compat.test.js.map +1 -0
  237. package/esm/providers/tambo-stubs.d.ts +2 -2
  238. package/esm/providers/tambo-stubs.d.ts.map +1 -1
  239. package/esm/providers/tambo-stubs.js +5 -0
  240. package/esm/providers/tambo-stubs.js.map +1 -1
  241. package/esm/providers/tambo-thread-input-provider.d.ts +1 -0
  242. package/esm/providers/tambo-thread-input-provider.d.ts.map +1 -1
  243. package/esm/providers/tambo-thread-input-provider.js +3 -3
  244. package/esm/providers/tambo-thread-input-provider.js.map +1 -1
  245. package/esm/providers/tambo-thread-provider.d.ts.map +1 -1
  246. package/esm/providers/tambo-thread-provider.js.map +1 -1
  247. package/esm/providers/tambo-thread-provider.test.js +24 -28
  248. package/esm/providers/tambo-thread-provider.test.js.map +1 -1
  249. package/esm/schema/index.d.ts +6 -0
  250. package/esm/schema/index.d.ts.map +1 -0
  251. package/esm/schema/index.js +6 -0
  252. package/esm/schema/index.js.map +1 -0
  253. package/esm/schema/json-schema.d.ts +35 -0
  254. package/esm/schema/json-schema.d.ts.map +1 -0
  255. package/esm/schema/json-schema.js +98 -0
  256. package/esm/schema/json-schema.js.map +1 -0
  257. package/esm/schema/schema.d.ts +66 -0
  258. package/esm/schema/schema.d.ts.map +1 -0
  259. package/esm/schema/schema.js +185 -0
  260. package/esm/schema/schema.js.map +1 -0
  261. package/esm/schema/schema.test.d.ts +2 -0
  262. package/esm/schema/schema.test.d.ts.map +1 -0
  263. package/esm/schema/schema.test.js +39 -0
  264. package/esm/schema/schema.test.js.map +1 -0
  265. package/esm/schema/standard-schema.d.ts +21 -0
  266. package/esm/schema/standard-schema.d.ts.map +1 -0
  267. package/esm/schema/standard-schema.js +34 -0
  268. package/esm/schema/standard-schema.js.map +1 -0
  269. package/esm/schema/validate.d.ts +14 -0
  270. package/esm/schema/validate.d.ts.map +1 -0
  271. package/esm/schema/validate.js +145 -0
  272. package/esm/schema/validate.js.map +1 -0
  273. package/esm/schema/validate.test.d.ts +2 -0
  274. package/esm/schema/validate.test.d.ts.map +1 -0
  275. package/esm/schema/validate.test.js +126 -0
  276. package/esm/schema/validate.test.js.map +1 -0
  277. package/esm/schema/zod.d.ts +54 -0
  278. package/esm/schema/zod.d.ts.map +1 -0
  279. package/esm/schema/zod.js +124 -0
  280. package/esm/schema/zod.js.map +1 -0
  281. package/esm/testing/tools.d.ts +29 -15
  282. package/esm/testing/tools.d.ts.map +1 -1
  283. package/esm/testing/tools.js +62 -16
  284. package/esm/testing/tools.js.map +1 -1
  285. package/esm/util/generate-component.d.ts.map +1 -1
  286. package/esm/util/generate-component.js +3 -3
  287. package/esm/util/generate-component.js.map +1 -1
  288. package/esm/util/mcp-server-utils.d.ts +23 -0
  289. package/esm/util/mcp-server-utils.d.ts.map +1 -0
  290. package/esm/util/mcp-server-utils.js +102 -0
  291. package/esm/util/mcp-server-utils.js.map +1 -0
  292. package/esm/util/mcp-server-utils.test.d.ts +2 -0
  293. package/esm/util/mcp-server-utils.test.d.ts.map +1 -0
  294. package/esm/util/mcp-server-utils.test.js +285 -0
  295. package/esm/util/mcp-server-utils.test.js.map +1 -0
  296. package/esm/util/message-builder.d.ts +2 -1
  297. package/esm/util/message-builder.d.ts.map +1 -1
  298. package/esm/util/message-builder.js +54 -36
  299. package/esm/util/message-builder.js.map +1 -1
  300. package/esm/util/message-builder.test.js +500 -13
  301. package/esm/util/message-builder.test.js.map +1 -1
  302. package/esm/util/registry-validators.d.ts +26 -0
  303. package/esm/util/registry-validators.d.ts.map +1 -0
  304. package/esm/util/registry-validators.js +100 -0
  305. package/esm/util/registry-validators.js.map +1 -0
  306. package/esm/util/registry-validators.test.d.ts +2 -0
  307. package/esm/util/registry-validators.test.d.ts.map +1 -0
  308. package/esm/util/registry-validators.test.js +233 -0
  309. package/esm/util/registry-validators.test.js.map +1 -0
  310. package/esm/util/registry.d.ts +35 -7
  311. package/esm/util/registry.d.ts.map +1 -1
  312. package/esm/util/registry.js +57 -73
  313. package/esm/util/registry.js.map +1 -1
  314. package/esm/util/registry.test.d.ts +2 -0
  315. package/esm/util/registry.test.d.ts.map +1 -0
  316. package/esm/util/registry.test.js +169 -0
  317. package/esm/util/registry.test.js.map +1 -0
  318. package/esm/util/resource-validators.d.ts +16 -0
  319. package/esm/util/resource-validators.d.ts.map +1 -0
  320. package/esm/util/resource-validators.js +30 -0
  321. package/esm/util/resource-validators.js.map +1 -0
  322. package/esm/util/tool-caller.d.ts +2 -2
  323. package/esm/util/tool-caller.d.ts.map +1 -1
  324. package/esm/util/tool-caller.js +12 -4
  325. package/esm/util/tool-caller.js.map +1 -1
  326. package/package.json +13 -8
  327. package/dist/providers/hoc/with-tambo-interactable.d.ts.map +0 -1
  328. package/dist/providers/hoc/with-tambo-interactable.js.map +0 -1
  329. package/dist/util/validate-zod-schema.d.ts +0 -10
  330. package/dist/util/validate-zod-schema.d.ts.map +0 -1
  331. package/dist/util/validate-zod-schema.js +0 -100
  332. package/dist/util/validate-zod-schema.js.map +0 -1
  333. package/dist/util/validate-zod-schema.test.d.ts +0 -2
  334. package/dist/util/validate-zod-schema.test.d.ts.map +0 -1
  335. package/dist/util/validate-zod-schema.test.js +0 -96
  336. package/dist/util/validate-zod-schema.test.js.map +0 -1
  337. package/esm/providers/hoc/with-tambo-interactable.d.ts.map +0 -1
  338. package/esm/providers/hoc/with-tambo-interactable.js.map +0 -1
  339. package/esm/util/validate-zod-schema.d.ts +0 -10
  340. package/esm/util/validate-zod-schema.d.ts.map +0 -1
  341. package/esm/util/validate-zod-schema.js +0 -97
  342. package/esm/util/validate-zod-schema.js.map +0 -1
  343. package/esm/util/validate-zod-schema.test.d.ts +0 -2
  344. package/esm/util/validate-zod-schema.test.d.ts.map +0 -1
  345. package/esm/util/validate-zod-schema.test.js +0 -94
  346. package/esm/util/validate-zod-schema.test.js.map +0 -1
@@ -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
- // Apply prefixing based on current server count
31
- const shouldPrefix = mcpServers.length > 1;
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 for the MCP servers, including the server that the resource was found on.
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 queries = useTamboQueries({
129
- queries: mcpServers.map((mcpServer) => ({
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
- // Apply prefixing based on current server count
150
- const shouldPrefix = mcpServers.length > 1;
151
- if (!shouldPrefix) {
152
- return combined;
153
- }
154
- // Apply prefixes to all resources
155
- return {
156
- ...combined,
157
- data: combined.data.map((entry) => ({
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
- // figure out which server has the resource
215
+ const { resourceSource } = useTamboRegistry();
176
216
  const { data: resourceEntries } = useTamboMcpResourceList();
177
- const resourceEntry = resourceEntries?.find((resource) => resource.resource.uri === resourceUri);
178
- // Use the stable server key (and the server instance itself) instead of brittle
179
- // name/url/transport matching.
180
- const mcpServer = resourceEntry?.server;
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 a server
183
- const originalResourceUri = resourceEntry
184
- ? resourceUri?.replace(`${resourceEntry.server.serverKey}:`, "")
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: ["mcp-resource", resourceUri, mcpServer?.key],
189
- // Only run when we have a resource URI and a connected server
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
- const result = await mcpServer.client.client.readResource({
198
- uri: originalResourceUri,
199
- });
200
- return result ?? null; // return null because react-query doesn't like undefined results
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
  }
@@ -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
- // When only 1 server remains, prompts should NOT be prefixed
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("server-a:prompt-a1"); // No prefix when only 1 server
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?.server.url).toBe("https://server-a.example");
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?.server.url).toBe("https://server-b.example");
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 not prefix resources when only one server exists", async () => {
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
- // No prefix when only 1 server
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 remove resource prefixes when a server is removed", async () => {
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 and prefixes stripped
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
- expect(updatedUris).toContain("file:///home/user/doc1.txt"); // No prefix
751
- expect(updatedUris).toContain("file:///home/user/doc2.txt");
752
- expect(updatedUris).not.toContain("server-a:file:///home/user/doc1.txt"); // No prefix when only 1 server
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 (unprefixed)", async () => {
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
- const { data: resourceData } = useTamboMcpResource("file:///home/user/doc.txt");
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