@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.
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 +189 -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 +147 -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 +182 -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 +136 -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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-server-utils.test.js","sourceRoot":"","sources":["../../src/util/mcp-server-utils.test.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n McpServerInfo,\n NormalizedMcpServerInfo,\n} from \"../model/mcp-server-info\";\nimport { MCPTransport } from \"../model/mcp-server-info\";\nimport {\n deduplicateMcpServers,\n deriveServerKey,\n normalizeServerInfo,\n} from \"./mcp-server-utils\";\n\ndescribe(\"deriveServerKey\", () => {\n it(\"should extract key from simple domain\", () => {\n expect(deriveServerKey(\"https://linear.app/mcp\")).toBe(\"linear\");\n });\n\n it(\"should extract key from subdomain\", () => {\n expect(deriveServerKey(\"https://mcp.linear.app/mcp\")).toBe(\"linear\");\n });\n\n it(\"should extract key from www subdomain\", () => {\n expect(deriveServerKey(\"https://www.example.com/mcp\")).toBe(\"example\");\n });\n\n it(\"should extract key from api subdomain\", () => {\n expect(deriveServerKey(\"https://api.service.com/mcp\")).toBe(\"service\");\n });\n\n it(\"should extract key from app subdomain\", () => {\n expect(deriveServerKey(\"https://app.tool.com/mcp\")).toBe(\"tool\");\n });\n\n it(\"should handle multi-part TLDs like .co.uk\", () => {\n expect(deriveServerKey(\"https://example.co.uk/mcp\")).toBe(\"example\");\n });\n\n it(\"should handle .com.au TLDs\", () => {\n expect(deriveServerKey(\"https://service.com.au/mcp\")).toBe(\"service\");\n });\n\n it(\"should prefer meaningful part over common prefixes\", () => {\n // mcp.staging.app.com -> after removing TLD: [\"mcp\", \"staging\", \"app\"]\n // Working backwards: checks \"app\" (index 2) - common prefix, continue\n // Checks \"staging\" (index 1) - common prefix, continue\n // Checks \"mcp\" (index 0) - common prefix, continue\n // Falls back - actual behavior returns \"staging\"\n expect(deriveServerKey(\"https://mcp.staging.app.com/mcp\")).toBe(\"staging\");\n });\n\n it(\"should fallback to last part if all are common prefixes\", () => {\n // www.api.mcp.com -> after removing TLD: [\"www\", \"api\", \"mcp\"]\n // All are common prefixes, so falls back to last relevant part\n // The actual implementation returns \"api\" (the middle part)\n // This appears to be the behavior, so we test for it\n expect(deriveServerKey(\"https://www.api.mcp.com/mcp\")).toBe(\"api\");\n });\n\n it(\"should handle single-part hostname\", () => {\n expect(deriveServerKey(\"https://localhost:3000/mcp\")).toBe(\"localhost\");\n });\n\n it(\"should handle invalid URL by sanitizing\", () => {\n const result = deriveServerKey(\"not-a-url!!!\");\n expect(result).toBe(\"not_a_url___\");\n expect(result).toMatch(/^[a-z0-9_]+$/);\n });\n\n it(\"should lowercase the result\", () => {\n expect(deriveServerKey(\"https://EXAMPLE.COM/mcp\")).toBe(\"example\");\n });\n\n it(\"should handle staging subdomain\", () => {\n expect(deriveServerKey(\"https://staging.service.com/mcp\")).toBe(\"service\");\n });\n\n it(\"should handle dev subdomain\", () => {\n expect(deriveServerKey(\"https://dev.tool.com/mcp\")).toBe(\"tool\");\n });\n\n it(\"should handle prod subdomain\", () => {\n expect(deriveServerKey(\"https://prod.service.com/mcp\")).toBe(\"service\");\n });\n});\n\ndescribe(\"normalizeServerInfo\", () => {\n it(\"should normalize string URL to server info\", () => {\n const result = normalizeServerInfo(\"https://example.com/mcp\");\n\n expect(result).toEqual({\n url: \"https://example.com/mcp\",\n transport: MCPTransport.HTTP,\n serverKey: \"example\",\n });\n });\n\n it(\"should preserve existing serverKey\", () => {\n const server: McpServerInfo = {\n url: \"https://example.com/mcp\",\n serverKey: \"custom-key\",\n };\n\n const result = normalizeServerInfo(server);\n\n expect(result.serverKey).toBe(\"custom-key\");\n expect(result.transport).toBe(MCPTransport.HTTP);\n });\n\n it(\"should derive serverKey when not provided\", () => {\n const server: McpServerInfo = {\n url: \"https://mcp.linear.app/mcp\",\n };\n\n const result = normalizeServerInfo(server);\n\n expect(result.serverKey).toBe(\"linear\");\n });\n\n it(\"should preserve existing transport\", () => {\n const server: McpServerInfo = {\n url: \"https://example.com/mcp\",\n transport: MCPTransport.SSE,\n };\n\n const result = normalizeServerInfo(server);\n\n expect(result.transport).toBe(MCPTransport.SSE);\n });\n\n it(\"should default transport to HTTP when not provided\", () => {\n const server: McpServerInfo = {\n url: \"https://example.com/mcp\",\n };\n\n const result = normalizeServerInfo(server);\n\n expect(result.transport).toBe(MCPTransport.HTTP);\n });\n\n it(\"should preserve all other properties\", () => {\n const server: McpServerInfo = {\n url: \"https://example.com/mcp\",\n name: \"Test Server\",\n description: \"A test server\",\n customHeaders: { \"X-API-Key\": \"secret\" },\n handlers: {},\n };\n\n const result = normalizeServerInfo(server);\n\n expect(result.name).toBe(\"Test Server\");\n expect(result.description).toBe(\"A test server\");\n expect(result.customHeaders).toEqual({ \"X-API-Key\": \"secret\" });\n expect(result.handlers).toEqual({});\n });\n\n it(\"should handle server with all optional fields\", () => {\n const server: McpServerInfo = {\n url: \"https://example.com/mcp\",\n name: \"My Server\",\n description: \"Description\",\n transport: MCPTransport.SSE,\n serverKey: \"my-server\",\n customHeaders: { Authorization: \"Bearer token\" },\n };\n\n const result = normalizeServerInfo(server);\n\n expect(result).toEqual({\n url: \"https://example.com/mcp\",\n name: \"My Server\",\n description: \"Description\",\n transport: MCPTransport.SSE,\n serverKey: \"my-server\",\n customHeaders: { Authorization: \"Bearer token\" },\n });\n });\n});\n\ndescribe(\"deduplicateMcpServers\", () => {\n it(\"should return empty array for empty input\", () => {\n expect(deduplicateMcpServers([])).toEqual([]);\n });\n\n it(\"should return single server unchanged\", () => {\n const server: NormalizedMcpServerInfo = {\n url: \"https://example.com/mcp\",\n transport: MCPTransport.HTTP,\n serverKey: \"example\",\n };\n\n expect(deduplicateMcpServers([server])).toEqual([server]);\n });\n\n it(\"should deduplicate servers with same URL and transport\", () => {\n const server1: NormalizedMcpServerInfo = {\n url: \"https://example.com/mcp\",\n transport: MCPTransport.HTTP,\n serverKey: \"example\",\n };\n const server2: NormalizedMcpServerInfo = {\n url: \"https://example.com/mcp\",\n transport: MCPTransport.HTTP,\n serverKey: \"example\",\n };\n\n const result = deduplicateMcpServers([server1, server2]);\n\n expect(result).toHaveLength(1);\n expect(result[0].url).toBe(\"https://example.com/mcp\");\n });\n\n it(\"should keep servers with different URLs\", () => {\n const server1: NormalizedMcpServerInfo = {\n url: \"https://example.com/mcp\",\n transport: MCPTransport.HTTP,\n serverKey: \"example\",\n };\n const server2: NormalizedMcpServerInfo = {\n url: \"https://other.com/mcp\",\n transport: MCPTransport.HTTP,\n serverKey: \"other\",\n };\n\n const result = deduplicateMcpServers([server1, server2]);\n\n expect(result).toHaveLength(2);\n });\n\n it(\"should keep servers with same URL but different transport\", () => {\n const server1: NormalizedMcpServerInfo = {\n url: \"https://example.com/mcp\",\n transport: MCPTransport.HTTP,\n serverKey: \"example\",\n };\n const server2: NormalizedMcpServerInfo = {\n url: \"https://example.com/mcp\",\n transport: MCPTransport.SSE,\n serverKey: \"example\",\n };\n\n const result = deduplicateMcpServers([server1, server2]);\n\n expect(result).toHaveLength(2);\n });\n\n it(\"should ensure unique serverKeys by appending suffixes\", () => {\n const server1: NormalizedMcpServerInfo = {\n url: \"https://example1.com/mcp\",\n transport: MCPTransport.HTTP,\n serverKey: \"linear\",\n };\n const server2: NormalizedMcpServerInfo = {\n url: \"https://example2.com/mcp\",\n transport: MCPTransport.HTTP,\n serverKey: \"linear\",\n };\n const server3: NormalizedMcpServerInfo = {\n url: \"https://example3.com/mcp\",\n transport: MCPTransport.HTTP,\n serverKey: \"linear\",\n };\n\n const result = deduplicateMcpServers([server1, server2, server3]);\n\n expect(result).toHaveLength(3);\n expect(result[0].serverKey).toBe(\"linear\");\n expect(result[1].serverKey).toBe(\"linear-2\");\n expect(result[2].serverKey).toBe(\"linear-3\");\n });\n\n it(\"should handle mixed unique and duplicate serverKeys\", () => {\n const server1: NormalizedMcpServerInfo = {\n url: \"https://example1.com/mcp\",\n transport: MCPTransport.HTTP,\n serverKey: \"linear\",\n };\n const server2: NormalizedMcpServerInfo = {\n url: \"https://example2.com/mcp\",\n transport: MCPTransport.HTTP,\n serverKey: \"notion\",\n };\n const server3: NormalizedMcpServerInfo = {\n url: \"https://example3.com/mcp\",\n transport: MCPTransport.HTTP,\n serverKey: \"linear\",\n };\n\n const result = deduplicateMcpServers([server1, server2, server3]);\n\n expect(result).toHaveLength(3);\n expect(result[0].serverKey).toBe(\"linear\");\n expect(result[1].serverKey).toBe(\"notion\");\n expect(result[2].serverKey).toBe(\"linear-2\");\n });\n\n it(\"should deduplicate by connection first, then ensure serverKey uniqueness\", () => {\n // Same connection, different serverKeys (shouldn't happen in practice but test the logic)\n const server1: NormalizedMcpServerInfo = {\n url: \"https://example.com/mcp\",\n transport: MCPTransport.HTTP,\n serverKey: \"linear\",\n };\n const server2: NormalizedMcpServerInfo = {\n url: \"https://example.com/mcp\",\n transport: MCPTransport.HTTP,\n serverKey: \"notion\",\n };\n\n const result = deduplicateMcpServers([server1, server2]);\n\n // Should deduplicate by connection, keeping only one\n expect(result).toHaveLength(1);\n });\n\n it(\"should preserve server properties when deduplicating\", () => {\n const server1: NormalizedMcpServerInfo = {\n url: \"https://example.com/mcp\",\n transport: MCPTransport.HTTP,\n serverKey: \"example\",\n name: \"First Server\",\n };\n const server2: NormalizedMcpServerInfo = {\n url: \"https://example.com/mcp\",\n transport: MCPTransport.HTTP,\n serverKey: \"example\",\n name: \"Second Server\",\n };\n\n const result = deduplicateMcpServers([server1, server2]);\n\n expect(result).toHaveLength(1);\n // Should keep the last one encountered\n expect(result[0].name).toBe(\"Second Server\");\n });\n\n it(\"should handle servers with different customHeaders as different connections\", () => {\n const server1: NormalizedMcpServerInfo = {\n url: \"https://example.com/mcp\",\n transport: MCPTransport.HTTP,\n serverKey: \"example\",\n customHeaders: { Authorization: \"Bearer token1\" },\n };\n const server2: NormalizedMcpServerInfo = {\n url: \"https://example.com/mcp\",\n transport: MCPTransport.HTTP,\n serverKey: \"example\",\n customHeaders: { Authorization: \"Bearer token2\" },\n };\n\n const result = deduplicateMcpServers([server1, server2]);\n\n expect(result).toHaveLength(2);\n });\n});\n"]}
@@ -4,7 +4,8 @@ import { StagedImage } from "../hooks/use-message-images";
4
4
  * Builds message content with text, MCP resource references, and images
5
5
  * @param text - The text content, may include \@serverKey:uri resource references
6
6
  * @param images - Array of staged images
7
+ * @param resourceNames - Map of resource IDs (serverKey:uri) to their display names
7
8
  * @returns Array of message content parts
8
9
  */
9
- export declare function buildMessageContent(text: string, images: StagedImage[]): TamboAI.Beta.Threads.ChatCompletionContentPart[];
10
+ export declare function buildMessageContent(text: string, images: StagedImage[], resourceNames?: Record<string, string>): TamboAI.Beta.Threads.ChatCompletionContentPart[];
10
11
  //# sourceMappingURL=message-builder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"message-builder.d.ts","sourceRoot":"","sources":["../../src/util/message-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAoD1D;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,WAAW,EAAE,GACpB,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,yBAAyB,EAAE,CAwClD"}
1
+ {"version":3,"file":"message-builder.d.ts","sourceRoot":"","sources":["../../src/util/message-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAsF1D;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,WAAW,EAAE,EACrB,aAAa,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACzC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,yBAAyB,EAAE,CA4BlD"}
@@ -13,7 +13,7 @@
13
13
  */
14
14
  const RESOURCE_REFERENCE_PATTERN = /@([a-zA-Z0-9-]+):(\S+)/g;
15
15
  /**
16
- * Parses MCP resource references from text and extracts them into separate content parts.
16
+ * Parses text with resource references and returns interleaved content parts.
17
17
  * Resource references have the format: \@serverKey:uri
18
18
  *
19
19
  * The serverKey prefix is stripped before sending to the backend because:
@@ -21,54 +21,72 @@ const RESOURCE_REFERENCE_PATTERN = /@([a-zA-Z0-9-]+):(\S+)/g;
21
21
  * - The backend only needs the actual resource URI (e.g., "tambo:test://static/resource/1")
22
22
  * - The backend routes resources based on the thread's MCP server configuration, not client-side keys
23
23
  * @param text - Text potentially containing resource references
24
- * @returns Object with parsed resources (URI only, prefix stripped) and remaining text, plus full reference for display
24
+ * @param resourceNames - Map of full resource IDs (serverKey:uri) to their display names
25
+ * @returns Array of content parts in order (text and resource parts interleaved)
25
26
  */
26
- function parseResourceReferences(text) {
27
- const resources = [];
28
- let remainingText = text;
29
- // Find all resource references
30
- const matches = text.matchAll(RESOURCE_REFERENCE_PATTERN);
27
+ function parseResourceReferences(text, resourceNames) {
28
+ const parts = [];
29
+ // Use matchAll to avoid global regex state issues
30
+ const matches = Array.from(text.matchAll(RESOURCE_REFERENCE_PATTERN));
31
+ let lastIndex = 0;
32
+ // Find all resource references and interleave with text
31
33
  for (const match of matches) {
32
34
  const [fullMatch, serverKey, uri] = match;
33
- // Store the actual URI (without client-side prefix) to send to backend
34
- // And the full reference for display purposes
35
- resources.push({
36
- uri, // Just the URI part for the API
37
- fullReference: `${serverKey}:${uri}`, // Full prefixed version for display
38
- });
39
- // Remove the @ symbol and entire reference from text
40
- remainingText = remainingText.replace(fullMatch, "").trim();
35
+ const fullId = `${serverKey}:${uri}`;
36
+ // Add text before this resource reference (preserve whitespace)
37
+ if (match.index !== undefined && match.index > lastIndex) {
38
+ const textBefore = text.slice(lastIndex, match.index);
39
+ if (textBefore.length > 0) {
40
+ parts.push({
41
+ type: "text",
42
+ text: textBefore,
43
+ });
44
+ }
45
+ }
46
+ const resource = { uri };
47
+ const name = resourceNames[fullId];
48
+ if (name) {
49
+ resource.name = name;
50
+ }
51
+ parts.push({ type: "resource", resource });
52
+ if (match.index !== undefined) {
53
+ lastIndex = match.index + fullMatch.length;
54
+ }
55
+ }
56
+ // Add remaining text after the last resource reference (preserve whitespace)
57
+ if (lastIndex < text.length) {
58
+ const textAfter = text.slice(lastIndex);
59
+ if (textAfter.length > 0) {
60
+ parts.push({
61
+ type: "text",
62
+ text: textAfter,
63
+ });
64
+ }
65
+ }
66
+ // If no resource references were found, return the whole text as a single text part
67
+ if (parts.length === 0 && text.trim()) {
68
+ parts.push({ type: "text", text });
41
69
  }
42
- return { resources, remainingText };
70
+ return parts;
43
71
  }
44
72
  /**
45
73
  * Builds message content with text, MCP resource references, and images
46
74
  * @param text - The text content, may include \@serverKey:uri resource references
47
75
  * @param images - Array of staged images
76
+ * @param resourceNames - Map of resource IDs (serverKey:uri) to their display names
48
77
  * @returns Array of message content parts
49
78
  */
50
- export function buildMessageContent(text, images) {
79
+ export function buildMessageContent(text, images, resourceNames = {}) {
51
80
  const content = [];
52
- if (text.trim()) {
53
- // Parse resource references from text
54
- const { resources, remainingText } = parseResourceReferences(text.trim());
55
- // Add remaining text if any
56
- if (remainingText) {
57
- content.push({
58
- type: "text",
59
- text: remainingText,
60
- });
61
- }
62
- // Add resource content parts with just the URI (client-side prefix stripped)
63
- for (const { uri } of resources) {
64
- content.push({
65
- type: "resource",
66
- resource: {
67
- uri,
68
- },
69
- });
70
- }
81
+ const hasNonWhitespaceText = text.trim().length > 0;
82
+ if (hasNonWhitespaceText) {
83
+ // Parse resource references from the original text so that all
84
+ // user-visible whitespace (including leading/trailing spaces and
85
+ // internal spacing) is preserved in the resulting content parts.
86
+ const parts = parseResourceReferences(text, resourceNames);
87
+ content.push(...parts);
71
88
  }
89
+ // Add images at the end
72
90
  for (const image of images) {
73
91
  content.push({
74
92
  type: "image_url",
@@ -1 +1 @@
1
- {"version":3,"file":"message-builder.js","sourceRoot":"","sources":["../../src/util/message-builder.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;GAYG;AACH,MAAM,0BAA0B,GAAG,yBAAyB,CAAC;AAE7D;;;;;;;;;;GAUG;AACH,SAAS,uBAAuB,CAAC,IAAY;IAI3C,MAAM,SAAS,GAA6C,EAAE,CAAC;IAC/D,IAAI,aAAa,GAAG,IAAI,CAAC;IAEzB,+BAA+B;IAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,0BAA0B,CAAC,CAAC;IAC1D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,CAAC,SAAS,EAAE,SAAS,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC;QAC1C,uEAAuE;QACvE,8CAA8C;QAC9C,SAAS,CAAC,IAAI,CAAC;YACb,GAAG,EAAE,gCAAgC;YACrC,aAAa,EAAE,GAAG,SAAS,IAAI,GAAG,EAAE,EAAE,oCAAoC;SAC3E,CAAC,CAAC;QACH,qDAAqD;QACrD,aAAa,GAAG,aAAa,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9D,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC;AACtC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CACjC,IAAY,EACZ,MAAqB;IAErB,MAAM,OAAO,GAAqD,EAAE,CAAC;IAErE,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;QAChB,sCAAsC;QACtC,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,GAAG,uBAAuB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAE1E,4BAA4B;QAC5B,IAAI,aAAa,EAAE,CAAC;YAClB,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,aAAa;aACpB,CAAC,CAAC;QACL,CAAC;QAED,6EAA6E;QAC7E,KAAK,MAAM,EAAE,GAAG,EAAE,IAAI,SAAS,EAAE,CAAC;YAChC,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI,EAAE,UAAU;gBAChB,QAAQ,EAAE;oBACR,GAAG;iBACJ;aACF,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,OAAO,CAAC,IAAI,CAAC;YACX,IAAI,EAAE,WAAW;YACjB,SAAS,EAAE;gBACT,GAAG,EAAE,KAAK,CAAC,OAAO;aACnB;SACF,CAAC,CAAC;IACL,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACzD,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC","sourcesContent":["import type TamboAI from \"@tambo-ai/typescript-sdk\";\nimport { StagedImage } from \"../hooks/use-message-images\";\n\n/**\n * Regular expression to match MCP resource references in the format: \\@serverKey:uri\n *\n * Examples:\n * - \\@tambo-1hfs429:tambo:test://static/resource/1\n * - \\@linear:file://path/to/file\n *\n * Pattern breakdown:\n * - \\@ - Literal \\@ symbol\n * - ([a-zA-Z0-9-]+) - Server key (alphanumeric + hyphens, client-side routing key)\n * - : - Literal colon separator\n * - (\\S+) - URI (non-whitespace characters, actual resource URI)\n */\nconst RESOURCE_REFERENCE_PATTERN = /@([a-zA-Z0-9-]+):(\\S+)/g;\n\n/**\n * Parses MCP resource references from text and extracts them into separate content parts.\n * Resource references have the format: \\@serverKey:uri\n *\n * The serverKey prefix is stripped before sending to the backend because:\n * - It's a client-side routing key (e.g., \"tambo-1hfs429\") used by React SDK to route to the correct MCP connection\n * - The backend only needs the actual resource URI (e.g., \"tambo:test://static/resource/1\")\n * - The backend routes resources based on the thread's MCP server configuration, not client-side keys\n * @param text - Text potentially containing resource references\n * @returns Object with parsed resources (URI only, prefix stripped) and remaining text, plus full reference for display\n */\nfunction parseResourceReferences(text: string): {\n resources: { uri: string; fullReference: string }[];\n remainingText: string;\n} {\n const resources: { uri: string; fullReference: string }[] = [];\n let remainingText = text;\n\n // Find all resource references\n const matches = text.matchAll(RESOURCE_REFERENCE_PATTERN);\n for (const match of matches) {\n const [fullMatch, serverKey, uri] = match;\n // Store the actual URI (without client-side prefix) to send to backend\n // And the full reference for display purposes\n resources.push({\n uri, // Just the URI part for the API\n fullReference: `${serverKey}:${uri}`, // Full prefixed version for display\n });\n // Remove the @ symbol and entire reference from text\n remainingText = remainingText.replace(fullMatch, \"\").trim();\n }\n\n return { resources, remainingText };\n}\n\n/**\n * Builds message content with text, MCP resource references, and images\n * @param text - The text content, may include \\@serverKey:uri resource references\n * @param images - Array of staged images\n * @returns Array of message content parts\n */\nexport function buildMessageContent(\n text: string,\n images: StagedImage[],\n): TamboAI.Beta.Threads.ChatCompletionContentPart[] {\n const content: TamboAI.Beta.Threads.ChatCompletionContentPart[] = [];\n\n if (text.trim()) {\n // Parse resource references from text\n const { resources, remainingText } = parseResourceReferences(text.trim());\n\n // Add remaining text if any\n if (remainingText) {\n content.push({\n type: \"text\",\n text: remainingText,\n });\n }\n\n // Add resource content parts with just the URI (client-side prefix stripped)\n for (const { uri } of resources) {\n content.push({\n type: \"resource\",\n resource: {\n uri,\n },\n });\n }\n }\n\n for (const image of images) {\n content.push({\n type: \"image_url\",\n image_url: {\n url: image.dataUrl,\n },\n });\n }\n\n if (content.length === 0) {\n throw new Error(\"Message must contain text or images\");\n }\n\n return content;\n}\n"]}
1
+ {"version":3,"file":"message-builder.js","sourceRoot":"","sources":["../../src/util/message-builder.ts"],"names":[],"mappings":"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","sourcesContent":["import type TamboAI from \"@tambo-ai/typescript-sdk\";\nimport { StagedImage } from \"../hooks/use-message-images\";\n\n/**\n * Regular expression to match MCP resource references in the format: \\@serverKey:uri\n *\n * Examples:\n * - \\@tambo-1hfs429:tambo:test://static/resource/1\n * - \\@linear:file://path/to/file\n *\n * Pattern breakdown:\n * - \\@ - Literal \\@ symbol\n * - ([a-zA-Z0-9-]+) - Server key (alphanumeric + hyphens, client-side routing key)\n * - : - Literal colon separator\n * - (\\S+) - URI (non-whitespace characters, actual resource URI)\n */\nconst RESOURCE_REFERENCE_PATTERN = /@([a-zA-Z0-9-]+):(\\S+)/g;\n\n/**\n * Parses text with resource references and returns interleaved content parts.\n * Resource references have the format: \\@serverKey:uri\n *\n * The serverKey prefix is stripped before sending to the backend because:\n * - It's a client-side routing key (e.g., \"tambo-1hfs429\") used by React SDK to route to the correct MCP connection\n * - The backend only needs the actual resource URI (e.g., \"tambo:test://static/resource/1\")\n * - The backend routes resources based on the thread's MCP server configuration, not client-side keys\n * @param text - Text potentially containing resource references\n * @param resourceNames - Map of full resource IDs (serverKey:uri) to their display names\n * @returns Array of content parts in order (text and resource parts interleaved)\n */\nfunction parseResourceReferences(\n text: string,\n resourceNames: Record<string, string>,\n): TamboAI.Beta.Threads.ChatCompletionContentPart[] {\n const parts: TamboAI.Beta.Threads.ChatCompletionContentPart[] = [];\n\n // Use matchAll to avoid global regex state issues\n const matches = Array.from(text.matchAll(RESOURCE_REFERENCE_PATTERN));\n let lastIndex = 0;\n\n // Find all resource references and interleave with text\n for (const match of matches) {\n const [fullMatch, serverKey, uri] = match;\n const fullId = `${serverKey}:${uri}`;\n\n // Add text before this resource reference (preserve whitespace)\n if (match.index !== undefined && match.index > lastIndex) {\n const textBefore = text.slice(lastIndex, match.index);\n if (textBefore.length > 0) {\n parts.push({\n type: \"text\",\n text: textBefore,\n });\n }\n }\n\n const resource: TamboAI.Resource = { uri };\n const name = resourceNames[fullId];\n if (name) {\n resource.name = name;\n }\n parts.push({ type: \"resource\", resource });\n\n if (match.index !== undefined) {\n lastIndex = match.index + fullMatch.length;\n }\n }\n\n // Add remaining text after the last resource reference (preserve whitespace)\n if (lastIndex < text.length) {\n const textAfter = text.slice(lastIndex);\n if (textAfter.length > 0) {\n parts.push({\n type: \"text\",\n text: textAfter,\n });\n }\n }\n\n // If no resource references were found, return the whole text as a single text part\n if (parts.length === 0 && text.trim()) {\n parts.push({ type: \"text\", text });\n }\n\n return parts;\n}\n\n/**\n * Builds message content with text, MCP resource references, and images\n * @param text - The text content, may include \\@serverKey:uri resource references\n * @param images - Array of staged images\n * @param resourceNames - Map of resource IDs (serverKey:uri) to their display names\n * @returns Array of message content parts\n */\nexport function buildMessageContent(\n text: string,\n images: StagedImage[],\n resourceNames: Record<string, string> = {},\n): TamboAI.Beta.Threads.ChatCompletionContentPart[] {\n const content: TamboAI.Beta.Threads.ChatCompletionContentPart[] = [];\n\n const hasNonWhitespaceText = text.trim().length > 0;\n\n if (hasNonWhitespaceText) {\n // Parse resource references from the original text so that all\n // user-visible whitespace (including leading/trailing spaces and\n // internal spacing) is preserved in the resulting content parts.\n const parts = parseResourceReferences(text, resourceNames);\n content.push(...parts);\n }\n\n // Add images at the end\n for (const image of images) {\n content.push({\n type: \"image_url\",\n image_url: {\n url: image.dataUrl,\n },\n });\n }\n\n if (content.length === 0) {\n throw new Error(\"Message must contain text or images\");\n }\n\n return content;\n}\n"]}