@langwatch/mcp-server 2.1.0 → 3.0.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 (428) hide show
  1. package/.gitignore +7 -0
  2. package/.npmignore +41 -0
  3. package/CHANGELOG.md +218 -201
  4. package/CONTRIBUTING.md +10 -0
  5. package/README.md +70 -68
  6. package/dist/add-dashboard-widget-55UI6H22.js +44 -0
  7. package/dist/add-dashboard-widget-MJHIC3CC.js +45 -0
  8. package/dist/{archive-project-W3IKI7UL.js → archive-project-K5UD7A6X.js} +4 -4
  9. package/dist/archive-project-L4PAKT34.js +19 -0
  10. package/dist/{archive-run-plan-474OKHHU.js → archive-run-plan-5HSQG2HX.js} +4 -4
  11. package/dist/archive-run-plan-AOKNYQIO.js +15 -0
  12. package/dist/archive-scenario-2CWJW2VD.js +19 -0
  13. package/dist/{archive-scenario-V5UNYK26.js → archive-scenario-5IJQESHH.js} +4 -4
  14. package/dist/{archive-test-suite-O7FLDMXK.js → archive-test-suite-OPB7NFI7.js} +4 -4
  15. package/dist/archive-test-suite-ZC3TN6VH.js +15 -0
  16. package/dist/assign-prompt-tag-6DDE6ZZ5.js +23 -0
  17. package/dist/{assign-prompt-tag-Q46WRB6P.js → assign-prompt-tag-D7O23MJM.js} +8 -4
  18. package/dist/chunk-3YLN3ATJ.js +241 -0
  19. package/dist/{chunk-EYXMLE5X.js → chunk-4RX3G62A.js} +56 -47
  20. package/dist/{chunk-UF6XIGYR.js → chunk-6YZUOD34.js} +2 -5
  21. package/dist/{chunk-WVI5CJ6S.js → chunk-7AQLPVFL.js} +2 -2
  22. package/dist/chunk-7CZQDZ2K.js +319 -0
  23. package/dist/{chunk-RVAE2MEA.js → chunk-7GETGE6B.js} +7 -7
  24. package/dist/{chunk-OJLYYJWI.js → chunk-AASW2TO2.js} +7 -10
  25. package/dist/{chunk-QAMP3CGR.js → chunk-B4RFWIUP.js} +4 -4
  26. package/dist/{chunk-ZXKLPC2E.js → chunk-BIIKE3EI.js} +1 -1
  27. package/dist/{chunk-G4ZAZTZW.js → chunk-D2HS73VI.js} +7 -10
  28. package/dist/{chunk-37SMPMQS.js → chunk-DMM3JQ42.js} +7 -2
  29. package/dist/{chunk-WT4PO4A3.js → chunk-DULFP3MK.js} +10 -7
  30. package/dist/chunk-EYPJBL7P.js +516 -0
  31. package/dist/{chunk-NUFPTHKU.js → chunk-FOKVY3BK.js} +1 -1
  32. package/dist/chunk-GA5JUSPT.js +64 -0
  33. package/dist/{chunk-UJPXFA26.js → chunk-HHMNZAPD.js} +101 -74
  34. package/dist/chunk-HLQJXUR6.js +67 -0
  35. package/dist/{chunk-JTQJRNFJ.js → chunk-KILVK3FS.js} +4 -4
  36. package/dist/chunk-MOCU5XWF.js +79 -0
  37. package/dist/chunk-O2WGVCED.js +41 -0
  38. package/dist/{chunk-HV4DWKBI.js → chunk-O7EMWEVJ.js} +2 -4
  39. package/dist/{chunk-UFOYFYI7.js → chunk-QXLMHLRC.js} +6 -9
  40. package/dist/{chunk-QRO35SOM.js → chunk-R5YJIYPF.js} +981 -747
  41. package/dist/{chunk-JR6DJZOU.js → chunk-RABYBFX5.js} +4 -10
  42. package/dist/{chunk-SR6QPRIE.js → chunk-RFC2SBEX.js} +7 -13
  43. package/dist/chunk-SHSVN3SE.js +21 -0
  44. package/dist/{chunk-CKNAH5TG.js → chunk-U24EACC4.js} +3 -10
  45. package/dist/{chunk-56OGTS24.js → chunk-VOUGDY2O.js} +4 -11
  46. package/dist/chunk-WCKZDOGF.js +31 -0
  47. package/dist/chunk-XQYH52SI.js +129 -0
  48. package/dist/{chunk-7E4FZIYJ.js → chunk-XUIJYNRJ.js} +1 -1
  49. package/dist/{chunk-6DJDEXET.js → chunk-YCIUEZVK.js} +6 -12
  50. package/dist/config-2WQGMAEN.js +16 -0
  51. package/dist/config.cjs +109 -0
  52. package/dist/config.js +5 -4
  53. package/dist/{create-agent-JFJ2K6X3.js → create-agent-6JNLMYQD.js} +4 -4
  54. package/dist/create-agent-IRZOU4R5.js +23 -0
  55. package/dist/create-analytics-report-N4L4V5QL.js +19 -0
  56. package/dist/create-analytics-report-QEHXG7FU.js +18 -0
  57. package/dist/{create-annotation-EUXXQ6NS.js → create-annotation-CCEGIZ67.js} +10 -5
  58. package/dist/create-annotation-DTHCMWHB.js +29 -0
  59. package/dist/{create-api-key-BGBB76JX.js → create-api-key-JKM2DSZO.js} +5 -7
  60. package/dist/create-api-key-JPEUIXGM.js +24 -0
  61. package/dist/create-dataset-DJNXYJ5B.js +22 -0
  62. package/dist/{create-dataset-FD5VAXU4.js → create-dataset-WON4SOTC.js} +6 -6
  63. package/dist/create-dataset-records-TMEOTLTO.js +20 -0
  64. package/dist/{create-dataset-records-WQUXXOHI.js → create-dataset-records-UJBW3C7C.js} +5 -7
  65. package/dist/create-evaluator-N5AEHI4K.js +27 -0
  66. package/dist/{create-evaluator-6NV33Z52.js → create-evaluator-XYNY2YFY.js} +4 -4
  67. package/dist/create-mcp-server.cjs +9712 -0
  68. package/dist/create-mcp-server.js +2757 -10
  69. package/dist/{create-project-ZQ7TFIO2.js → create-project-EY4XZ7TH.js} +4 -4
  70. package/dist/create-project-YL345T5L.js +44 -0
  71. package/dist/{create-prompt-BS6QRCUU.js → create-prompt-JZQNQ7I3.js} +5 -7
  72. package/dist/create-prompt-ZUMQHHX7.js +37 -0
  73. package/dist/create-prompt-tag-CJ5FZ436.js +17 -0
  74. package/dist/{create-prompt-tag-FCQ5ELSQ.js → create-prompt-tag-JFP2Q3PO.js} +3 -3
  75. package/dist/create-scenario-CQU2SSFB.js +33 -0
  76. package/dist/{create-scenario-DRPTPXU3.js → create-scenario-S6OWWKJO.js} +5 -5
  77. package/dist/create-test-suite-OLGCAGJA.js +36 -0
  78. package/dist/{create-test-suite-ZIZCCFJ4.js → create-test-suite-Y7P2RI4L.js} +6 -6
  79. package/dist/create-trigger-4CNBTISJ.js +48 -0
  80. package/dist/create-trigger-RQ653WYF.js +49 -0
  81. package/dist/dashboards-BA7M6CMO.js +90 -0
  82. package/dist/dashboards-U663GUIB.js +91 -0
  83. package/dist/{delete-agent-SA4JMUZF.js → delete-agent-J2Q4DRZ2.js} +4 -4
  84. package/dist/delete-agent-ZAU4LTXB.js +18 -0
  85. package/dist/delete-analytics-report-QK6X7P4S.js +19 -0
  86. package/dist/delete-analytics-report-YUYLDHLL.js +18 -0
  87. package/dist/delete-annotation-7X4NYXXM.js +17 -0
  88. package/dist/{delete-annotation-X3KKK7QY.js → delete-annotation-KJOCDLV3.js} +4 -4
  89. package/dist/{delete-dataset-W6EZ44HY.js → delete-dataset-NXP7ZPPV.js} +5 -7
  90. package/dist/delete-dataset-RUTZBBVD.js +18 -0
  91. package/dist/{delete-dataset-records-7CNOTALB.js → delete-dataset-records-4Z7DZCDW.js} +5 -7
  92. package/dist/delete-dataset-records-75UAR5IS.js +19 -0
  93. package/dist/delete-evaluator-E3CUNGTQ.js +16 -0
  94. package/dist/delete-evaluator-RI4GALGX.js +15 -0
  95. package/dist/delete-prompt-tag-LE3WJPKS.js +17 -0
  96. package/dist/{delete-prompt-tag-QB3OCZUV.js → delete-prompt-tag-S4KBZTNG.js} +3 -3
  97. package/dist/delete-workflow-4CZFIQCQ.js +17 -0
  98. package/dist/{delete-workflow-NG2VXYYM.js → delete-workflow-PWQPDW6H.js} +4 -4
  99. package/dist/discover-evaluator-schema-UHWA5HVL.js +120 -0
  100. package/dist/discover-evaluator-schema-XKCLLCB3.js +118 -0
  101. package/dist/discover-scenario-schema-NOJY6S7E.js +137 -0
  102. package/dist/{discover-scenario-schema-W7KBXA4J.js → discover-scenario-schema-OOFIQKOG.js} +37 -17
  103. package/dist/discover-schema-7WHIIATI.js +442 -0
  104. package/dist/{discover-schema-L6QDPON3.js → discover-schema-J2ANOBKO.js} +181 -186
  105. package/dist/get-agent-5T3CXQOR.js +69 -0
  106. package/dist/{get-agent-7THF7HCU.js → get-agent-ZKYQJUQ7.js} +15 -11
  107. package/dist/get-analytics-4MPEK4KV.js +79 -0
  108. package/dist/get-analytics-ZLI3A4F5.js +80 -0
  109. package/dist/get-analytics-report-JCKYMA2S.js +29 -0
  110. package/dist/get-analytics-report-SKIP2JWH.js +30 -0
  111. package/dist/get-dataset-APGZBYMH.js +64 -0
  112. package/dist/get-dataset-LWXDVMVE.js +63 -0
  113. package/dist/get-evaluator-3A7BJBAB.js +47 -0
  114. package/dist/{get-evaluator-RZ44UIN6.js → get-evaluator-IWNJINRY.js} +4 -4
  115. package/dist/get-project-EQVB4LEL.js +26 -0
  116. package/dist/{get-project-QSQEAB33.js → get-project-RK6AJTEP.js} +4 -4
  117. package/dist/{get-prompt-GVKXF5NL.js → get-prompt-TLG2T2DS.js} +53 -58
  118. package/dist/get-prompt-ZER557X3.js +94 -0
  119. package/dist/get-run-plan-5X7DDJRN.js +58 -0
  120. package/dist/{get-run-plan-NY7J5GRE.js → get-run-plan-PEHRO5M3.js} +7 -9
  121. package/dist/{get-scenario-Q3LLQE4T.js → get-scenario-375W5D6I.js} +5 -5
  122. package/dist/get-scenario-UQX45ZZO.js +37 -0
  123. package/dist/{get-simulation-run-WINEYDMK.js → get-simulation-run-2NBXJADG.js} +26 -22
  124. package/dist/get-simulation-run-QZTLGL7W.js +69 -0
  125. package/dist/{get-test-suite-4HKA7OKS.js → get-test-suite-GVIBDG7K.js} +5 -5
  126. package/dist/get-test-suite-OODIR3RI.js +48 -0
  127. package/dist/{get-trace-RCUV7N4O.js → get-trace-AGCHVPJK.js} +11 -13
  128. package/dist/get-trace-KJLYAZRC.js +45 -0
  129. package/dist/get-workflow-3TV2YYC5.js +25 -0
  130. package/dist/{get-workflow-EEOC7Y45.js → get-workflow-7LZSVPP2.js} +4 -4
  131. package/dist/{http-security-EU3CHTY7.js → http-security-DNLBNQZL.js} +2 -2
  132. package/dist/{http-server-WCDUARTH.js → http-server-7CCN3EHJ.js} +4249 -325
  133. package/dist/index.js +59595 -38027
  134. package/dist/langwatch-api-analytics-reports-6CF6KUET.js +17 -0
  135. package/dist/langwatch-api-analytics-reports-RPFB5AO5.js +18 -0
  136. package/dist/{langwatch-api-annotations-6DHVZWUK.js → langwatch-api-annotations-H4AI4TKG.js} +4 -4
  137. package/dist/langwatch-api-annotations-QSJNUQQS.js +17 -0
  138. package/dist/langwatch-api-monitors-ENHLOL3S.js +47 -0
  139. package/dist/{langwatch-api-monitors-6ISFJ7WX.js → langwatch-api-monitors-OHSGADTH.js} +11 -16
  140. package/dist/langwatch-api-secrets-AGW6UUMD.js +55 -0
  141. package/dist/langwatch-api-secrets-CLTS766Q.js +56 -0
  142. package/dist/langwatch-api-triggers-G5UWFNN2.js +22 -0
  143. package/dist/langwatch-api-triggers-QMR7KEXW.js +23 -0
  144. package/dist/langwatch-api-workflows-HKTBIKKY.js +15 -0
  145. package/dist/{langwatch-api-workflows-6UJL5U4Z.js → langwatch-api-workflows-OMROZH2O.js} +4 -4
  146. package/dist/{list-agents-AMVDQPQL.js → list-agents-BYWDNAHT.js} +4 -4
  147. package/dist/list-agents-MG24MHZE.js +42 -0
  148. package/dist/list-analytics-reports-G3EPWGSQ.js +32 -0
  149. package/dist/list-analytics-reports-PPWIFRES.js +33 -0
  150. package/dist/list-annotations-QCLGEK5A.js +38 -0
  151. package/dist/{list-annotations-6PR5NLIC.js → list-annotations-Y4EUBJHG.js} +10 -5
  152. package/dist/list-api-keys-DAVOBPFK.js +42 -0
  153. package/dist/{list-api-keys-GVPNBIHQ.js → list-api-keys-JMW5WRXX.js} +10 -5
  154. package/dist/list-dashboards-2P3VJFRS.js +29 -0
  155. package/dist/list-dashboards-MW5O3MKH.js +30 -0
  156. package/dist/list-dataset-records-KEHT7G6Q.js +43 -0
  157. package/dist/{list-dataset-records-O67HZ35Y.js → list-dataset-records-KMLLMZAZ.js} +14 -10
  158. package/dist/{list-datasets-WXZL6UMP.js → list-datasets-P3JK2ZHS.js} +9 -13
  159. package/dist/list-datasets-Z7OOE4KI.js +56 -0
  160. package/dist/{list-evaluators-PAX72WCN.js → list-evaluators-7RRESBOU.js} +5 -7
  161. package/dist/list-evaluators-VATJVOHR.js +32 -0
  162. package/dist/{list-model-providers-VVLZMLCX.js → list-model-providers-OXDRPUCJ.js} +4 -4
  163. package/dist/list-model-providers-VXQJWDXU.js +35 -0
  164. package/dist/{list-projects-AQVI6SUK.js → list-projects-HUVK7L3W.js} +5 -7
  165. package/dist/list-projects-X72ZXVY5.js +40 -0
  166. package/dist/{list-prompt-tags-WZFBJOHG.js → list-prompt-tags-3I3LWFIR.js} +3 -3
  167. package/dist/list-prompt-tags-DRHUH2ND.js +23 -0
  168. package/dist/list-prompts-6DX3QECJ.js +30 -0
  169. package/dist/{list-prompts-BQOHRHYT.js → list-prompts-PS423RU3.js} +4 -6
  170. package/dist/list-run-plans-5SXPOV3X.js +45 -0
  171. package/dist/{list-run-plans-UBAU2DGH.js → list-run-plans-6VNHINPY.js} +5 -5
  172. package/dist/list-scenarios-EQWCW7Q4.js +45 -0
  173. package/dist/{list-scenarios-MS4IUIVM.js → list-scenarios-GKNH3YND.js} +13 -11
  174. package/dist/{list-simulation-runs-D6YF4ZOU.js → list-simulation-runs-KHY7KALI.js} +12 -8
  175. package/dist/list-simulation-runs-PUDK32C2.js +53 -0
  176. package/dist/list-test-suites-7H2DRILK.js +34 -0
  177. package/dist/{list-test-suites-L7R2O6DR.js → list-test-suites-GY56LGLY.js} +4 -4
  178. package/dist/list-trigger-fires-7RZUP54Y.js +19 -0
  179. package/dist/list-trigger-fires-OZUF65LI.js +20 -0
  180. package/dist/list-triggers-BGUCUXTJ.js +45 -0
  181. package/dist/list-triggers-XC5KMX4C.js +46 -0
  182. package/dist/list-workflows-55NYC3PC.js +33 -0
  183. package/dist/{list-workflows-6MV4XJHF.js → list-workflows-HV7FUWZA.js} +5 -7
  184. package/dist/{rename-prompt-tag-K5LQ4CY6.js → rename-prompt-tag-2I7Z5FCY.js} +3 -3
  185. package/dist/rename-prompt-tag-SZYTOWDX.js +18 -0
  186. package/dist/{rename-test-suite-NTYHAO5X.js → rename-test-suite-27AYY33K.js} +4 -4
  187. package/dist/rename-test-suite-CLIEPZDO.js +20 -0
  188. package/dist/report-issue-5HSL7CNB.js +924 -0
  189. package/dist/{report-issue-RHZZ33IS.js → report-issue-N7ND5TM6.js} +107 -67
  190. package/dist/{rerun-run-plan-MUDWJBE4.js → rerun-run-plan-O3VUZ6CG.js} +5 -5
  191. package/dist/rerun-run-plan-OJTVVGJI.js +21 -0
  192. package/dist/{revoke-api-key-6LAZ3QZZ.js → revoke-api-key-BU6BKGCG.js} +4 -4
  193. package/dist/revoke-api-key-NZNCFOIR.js +15 -0
  194. package/dist/run-agent-244O4Y5M.js +75 -0
  195. package/dist/run-agent-HPCGZGYW.js +74 -0
  196. package/dist/run-plan-2ZPGGZ4H.js +38 -0
  197. package/dist/{run-plan-T4ZYOZGA.js → run-plan-KSXUYQT3.js} +7 -7
  198. package/dist/run-query-4LMOQ5UW.js +47 -0
  199. package/dist/run-query-BQUHIUC2.js +46 -0
  200. package/dist/{run-test-suite-L4MZ7G4D.js → run-test-suite-P3WLVPXC.js} +6 -6
  201. package/dist/run-test-suite-UEOMCON6.js +25 -0
  202. package/dist/run-workflow-EFZ6HG5B.js +36 -0
  203. package/dist/{run-workflow-DFIS23AP.js → run-workflow-I4IHEN54.js} +4 -4
  204. package/dist/search-traces-6IR7PR4L.js +170 -0
  205. package/dist/search-traces-BYML2NQ4.js +171 -0
  206. package/dist/set-model-provider-RBNGWFVI.js +33 -0
  207. package/dist/{set-model-provider-XRNMFOVR.js → set-model-provider-SHEVKWRC.js} +4 -4
  208. package/dist/{update-agent-DUWNYBNA.js → update-agent-PTKTM2B5.js} +4 -4
  209. package/dist/update-agent-ZPXWRMS2.js +20 -0
  210. package/dist/update-dashboard-widget-HKCBXH7X.js +62 -0
  211. package/dist/update-dashboard-widget-IP2TTQ5G.js +63 -0
  212. package/dist/update-dataset-GHIN6JR3.js +22 -0
  213. package/dist/{update-dataset-YSOMDCOB.js → update-dataset-IRVMY5PF.js} +6 -6
  214. package/dist/update-dataset-record-6VI7K2CL.js +19 -0
  215. package/dist/{update-dataset-record-DEA2ITYR.js → update-dataset-record-Z3L7ACNF.js} +4 -4
  216. package/dist/update-evaluator-5NLNE6VY.js +24 -0
  217. package/dist/{update-evaluator-NH7Q5GWV.js → update-evaluator-FEH7H6NS.js} +4 -4
  218. package/dist/update-project-75H3LDQT.js +24 -0
  219. package/dist/{update-project-Q26FBSYH.js → update-project-QPR3QX55.js} +4 -4
  220. package/dist/update-prompt-BCDFTNLK.js +140 -0
  221. package/dist/{update-prompt-ZQQ2C3KU.js → update-prompt-NPRXYSWA.js} +27 -29
  222. package/dist/update-scenario-BNOMDCMV.js +34 -0
  223. package/dist/{update-scenario-WUX34JAR.js → update-scenario-EM5XGMB7.js} +5 -5
  224. package/package.json +35 -39
  225. package/pyproject.toml +23 -8
  226. package/src/config.d.ts +6 -0
  227. package/src/config.ts +22 -29
  228. package/src/create-mcp-server.ts +887 -634
  229. package/src/documentation-fetch.ts +21 -20
  230. package/src/http-security.ts +166 -135
  231. package/src/http-server.ts +189 -224
  232. package/src/index.ts +14 -16
  233. package/src/langwatch-api-agents.ts +18 -21
  234. package/src/langwatch-api-analytics-reports.ts +62 -0
  235. package/src/langwatch-api-annotations.ts +11 -12
  236. package/src/langwatch-api-api-keys.ts +8 -9
  237. package/src/langwatch-api-dashboard-widgets.ts +47 -0
  238. package/src/langwatch-api-dashboards.ts +109 -24
  239. package/src/langwatch-api-datasets.ts +98 -32
  240. package/src/langwatch-api-evaluators.ts +13 -19
  241. package/src/langwatch-api-model-providers.ts +4 -4
  242. package/src/langwatch-api-monitors.ts +14 -18
  243. package/src/langwatch-api-projects.ts +10 -9
  244. package/src/langwatch-api-query.ts +114 -0
  245. package/src/langwatch-api-run-plans.ts +12 -29
  246. package/src/langwatch-api-scenarios.ts +9 -11
  247. package/src/langwatch-api-secrets.ts +31 -24
  248. package/src/langwatch-api-simulation-runs.ts +6 -8
  249. package/src/langwatch-api-test-suites.ts +14 -33
  250. package/src/langwatch-api-triggers.ts +96 -34
  251. package/src/langwatch-api-workflows.ts +9 -9
  252. package/src/langwatch-api.ts +118 -124
  253. package/src/public-http-request.ts +21 -12
  254. package/src/schemas/analytics-metrics.ts +3 -15
  255. package/src/schemas/create-dataset.ts +3 -4
  256. package/src/schemas/run-plan.ts +13 -29
  257. package/src/schemas/suite-fields.ts +6 -23
  258. package/src/schemas/triggers.ts +365 -0
  259. package/src/schemas/widget-sources.ts +33 -0
  260. package/src/tools/add-dashboard-widget.ts +51 -0
  261. package/src/tools/archive-project.ts +2 -4
  262. package/src/tools/archive-run-plan.ts +2 -4
  263. package/src/tools/archive-scenario.ts +2 -4
  264. package/src/tools/archive-test-suite.ts +2 -4
  265. package/src/tools/assign-prompt-tag.ts +6 -2
  266. package/src/tools/create-agent.ts +1 -1
  267. package/src/tools/create-analytics-report.ts +7 -0
  268. package/src/tools/create-annotation.ts +7 -2
  269. package/src/tools/create-api-key.ts +4 -6
  270. package/src/tools/create-dataset-records.ts +4 -8
  271. package/src/tools/create-dataset.ts +5 -7
  272. package/src/tools/create-evaluator.ts +3 -5
  273. package/src/tools/create-project.ts +2 -2
  274. package/src/tools/create-prompt-tag.ts +2 -4
  275. package/src/tools/create-prompt.ts +9 -18
  276. package/src/tools/create-scenario.ts +5 -7
  277. package/src/tools/create-test-suite.ts +4 -9
  278. package/src/tools/create-trigger.ts +55 -0
  279. package/src/tools/dashboard-widget-queries.ts +45 -0
  280. package/src/tools/dashboards.ts +104 -0
  281. package/src/tools/delete-agent.ts +1 -1
  282. package/src/tools/delete-analytics-report.ts +7 -0
  283. package/src/tools/delete-annotation.ts +1 -1
  284. package/src/tools/delete-dataset-records.ts +4 -8
  285. package/src/tools/delete-dataset.ts +3 -7
  286. package/src/tools/delete-evaluator.ts +4 -6
  287. package/src/tools/delete-prompt-tag.ts +2 -4
  288. package/src/tools/delete-workflow.ts +1 -1
  289. package/src/tools/discover-evaluator-schema.ts +34 -25
  290. package/src/tools/discover-scenario-schema.ts +41 -21
  291. package/src/tools/discover-schema.ts +149 -27
  292. package/src/tools/experiment-run-status.ts +2 -8
  293. package/src/tools/format-dataset-mutation.ts +5 -10
  294. package/src/tools/format-run-plan.ts +7 -21
  295. package/src/tools/format-scenario.ts +2 -4
  296. package/src/tools/format-suite-details.ts +6 -12
  297. package/src/tools/get-agent.ts +15 -6
  298. package/src/tools/get-analytics-report.ts +23 -0
  299. package/src/tools/get-analytics.ts +52 -46
  300. package/src/tools/get-dataset.ts +34 -16
  301. package/src/tools/get-evaluator.ts +4 -11
  302. package/src/tools/get-experiment-results.ts +219 -186
  303. package/src/tools/get-project.ts +2 -4
  304. package/src/tools/get-prompt.ts +63 -84
  305. package/src/tools/get-run-plan.ts +4 -6
  306. package/src/tools/get-scenario.ts +4 -6
  307. package/src/tools/get-simulation-run.ts +27 -24
  308. package/src/tools/get-test-suite.ts +2 -5
  309. package/src/tools/get-trace.ts +16 -17
  310. package/src/tools/get-workflow.ts +1 -1
  311. package/src/tools/list-agents.ts +1 -1
  312. package/src/tools/list-analytics-reports.ts +27 -0
  313. package/src/tools/list-annotations.ts +8 -5
  314. package/src/tools/list-api-keys.ts +7 -2
  315. package/src/tools/list-dashboards.ts +20 -21
  316. package/src/tools/list-dataset-records.ts +8 -6
  317. package/src/tools/list-datasets.ts +12 -23
  318. package/src/tools/list-evaluators.ts +4 -8
  319. package/src/tools/list-experiment-runs.ts +5 -9
  320. package/src/tools/list-experiments.ts +5 -11
  321. package/src/tools/list-model-providers.ts +3 -5
  322. package/src/tools/list-projects.ts +2 -4
  323. package/src/tools/list-prompt-tags.ts +3 -3
  324. package/src/tools/list-prompts.ts +4 -8
  325. package/src/tools/list-run-plans.ts +2 -2
  326. package/src/tools/list-scenarios.ts +14 -15
  327. package/src/tools/list-simulation-runs.ts +8 -4
  328. package/src/tools/list-test-suites.ts +1 -1
  329. package/src/tools/list-trigger-fires.ts +17 -0
  330. package/src/tools/list-triggers.ts +39 -0
  331. package/src/tools/list-workflows.ts +2 -4
  332. package/src/tools/rename-prompt-tag.ts +1 -1
  333. package/src/tools/rename-test-suite.ts +2 -5
  334. package/src/tools/report-issue.ts +7 -9
  335. package/src/tools/rerun-run-plan.ts +2 -5
  336. package/src/tools/revoke-api-key.ts +2 -4
  337. package/src/tools/run-agent.ts +29 -26
  338. package/src/tools/run-experiment.ts +37 -34
  339. package/src/tools/run-plan.ts +4 -7
  340. package/src/tools/run-query.ts +47 -0
  341. package/src/tools/run-test-suite.ts +4 -7
  342. package/src/tools/run-workflow.ts +2 -5
  343. package/src/tools/search-traces.ts +161 -47
  344. package/src/tools/set-model-provider.ts +4 -6
  345. package/src/tools/test-agent.ts +2 -2
  346. package/src/tools/update-agent.ts +1 -1
  347. package/src/tools/update-dashboard-widget.ts +86 -0
  348. package/src/tools/update-dataset-record.ts +1 -1
  349. package/src/tools/update-dataset.ts +5 -7
  350. package/src/tools/update-evaluator.ts +3 -5
  351. package/src/tools/update-project.ts +1 -1
  352. package/src/tools/update-prompt.ts +44 -59
  353. package/src/tools/update-scenario.ts +5 -7
  354. package/src/tools/update-test-suite.ts +6 -11
  355. package/src/utils/date-parsing.ts +1 -1
  356. package/src/utils/escape-markdown.ts +3 -8
  357. package/src/utils/format-evaluations.ts +6 -6
  358. package/src/utils/markdown-table.ts +58 -0
  359. package/src/utils/trace-id-shape.ts +20 -0
  360. package/tsconfig.app.json +24 -0
  361. package/tsconfig.json +14 -12
  362. package/uv.lock +667 -341
  363. package/vitest.integration.config.js +10 -0
  364. package/vitest.scenario.config.js +9 -0
  365. package/.eslintrc.cjs +0 -36
  366. package/dist/chunk-AN4OUA4D.js +0 -86
  367. package/dist/chunk-GELJ2MQR.js +0 -226
  368. package/dist/chunk-K37ZNPXK.js +0 -24
  369. package/dist/chunk-S4GVX36W.js +0 -39
  370. package/dist/config.d.ts +0 -17
  371. package/dist/create-dashboard-Z76AIPLS.js +0 -19
  372. package/dist/create-mcp-server.d.ts +0 -10
  373. package/dist/delete-dashboard-LYMRPF27.js +0 -19
  374. package/dist/delete-evaluator-RKYC4TNB.js +0 -16
  375. package/dist/discover-evaluator-schema-ESSHQGPY.js +0 -1912
  376. package/dist/get-analytics-R6AZD3AW.js +0 -73
  377. package/dist/get-dashboard-P4WWVHFU.js +0 -30
  378. package/dist/get-dataset-AKTYSQDI.js +0 -49
  379. package/dist/index.d.ts +0 -2
  380. package/dist/langwatch-api-dashboards-2KJL2M42.js +0 -18
  381. package/dist/langwatch-api-secrets-OEBD6GBU.js +0 -41
  382. package/dist/langwatch-api-triggers-5E6DDGX6.js +0 -31
  383. package/dist/list-dashboards-Z3BZKDGF.js +0 -33
  384. package/dist/run-agent-CAGWGTYT.js +0 -72
  385. package/dist/search-traces-4XTJYXIK.js +0 -79
  386. package/src/__tests__/agent-tools.unit.test.ts +0 -257
  387. package/src/__tests__/all-tools.integration.test.ts +0 -1872
  388. package/src/__tests__/config.unit.test.ts +0 -202
  389. package/src/__tests__/datasets.integration.test.ts +0 -620
  390. package/src/__tests__/datasets.unit.test.ts +0 -244
  391. package/src/__tests__/date-parsing.unit.test.ts +0 -78
  392. package/src/__tests__/discover-evaluator-schema.unit.test.ts +0 -89
  393. package/src/__tests__/discover-schema.unit.test.ts +0 -118
  394. package/src/__tests__/docker.integration.test.ts +0 -318
  395. package/src/__tests__/documentation-fetch-security.integration.test.ts +0 -182
  396. package/src/__tests__/evaluator-tools.unit.test.ts +0 -262
  397. package/src/__tests__/experiment-list-tools.unit.test.ts +0 -157
  398. package/src/__tests__/experiment-status.unit.test.ts +0 -194
  399. package/src/__tests__/feature-map-tool-drift.unit.test.ts +0 -93
  400. package/src/__tests__/get-experiment-results.unit.test.ts +0 -313
  401. package/src/__tests__/http-security-origin.unit.test.ts +0 -97
  402. package/src/__tests__/http-security-sessions.unit.test.ts +0 -209
  403. package/src/__tests__/http-security-verifier.unit.test.ts +0 -248
  404. package/src/__tests__/http-server-allocation.integration.test.ts +0 -279
  405. package/src/__tests__/http-server-origin.integration.test.ts +0 -169
  406. package/src/__tests__/http-server-session-auth.integration.test.ts +0 -341
  407. package/src/__tests__/http-transport.integration.test.ts +0 -395
  408. package/src/__tests__/integration.integration.test.ts +0 -284
  409. package/src/__tests__/langwatch-api.unit.test.ts +0 -661
  410. package/src/__tests__/model-provider-tools.unit.test.ts +0 -190
  411. package/src/__tests__/project-api-key-tools.unit.test.ts +0 -451
  412. package/src/__tests__/prompt-response-types.unit.test.ts +0 -51
  413. package/src/__tests__/prompts.integration.test.ts +0 -736
  414. package/src/__tests__/public-http-request-security.integration.test.ts +0 -118
  415. package/src/__tests__/report-issue.integration.test.ts +0 -147
  416. package/src/__tests__/run-plan-tools.unit.test.ts +0 -565
  417. package/src/__tests__/scenario-tools.integration.test.ts +0 -286
  418. package/src/__tests__/scenario-tools.unit.test.ts +0 -207
  419. package/src/__tests__/schemas.unit.test.ts +0 -85
  420. package/src/__tests__/simulation-run-tools.unit.test.ts +0 -116
  421. package/src/__tests__/support/http-server-harness.ts +0 -138
  422. package/src/__tests__/test-suite-tools.unit.test.ts +0 -615
  423. package/src/__tests__/tools.integration.test.ts +0 -896
  424. package/src/schemas/filter-fields.ts +0 -119
  425. package/src/schemas/index.ts +0 -9
  426. package/src/tools/create-dashboard.ts +0 -7
  427. package/src/tools/delete-dashboard.ts +0 -7
  428. package/src/tools/get-dashboard.ts +0 -22
@@ -1,1872 +0,0 @@
1
- import { createServer, type Server } from "http";
2
- import { afterAll, beforeAll, describe, expect, it, vi, beforeEach } from "vitest";
3
- import { initConfig } from "../config.js";
4
- import {
5
- fetchDocumentation,
6
- resolveDocumentationUrl,
7
- } from "../documentation-fetch.js";
8
- import {
9
- deleteAgent,
10
- getAgent,
11
- updateAgent,
12
- } from "../langwatch-api-agents.js";
13
-
14
- // --- Canned responses for every API endpoint ---
15
-
16
- const CANNED_TRACES_SEARCH = {
17
- traces: [
18
- {
19
- trace_id: "trace-001",
20
- formatted_trace:
21
- "Root [server] 1200ms\n LLM Call [llm] 500ms\n Input: Hello, how are you?\n Output: I am fine, thank you!",
22
- input: { value: "Hello, how are you?" },
23
- output: { value: "I am fine, thank you!" },
24
- timestamps: { started_at: 1700000000000 },
25
- metadata: { user_id: "user-42", thread_id: "thread-1" },
26
- evaluations: [
27
- {
28
- evaluator_id: "eval-1",
29
- name: "Faithfulness",
30
- score: 0.95,
31
- passed: true,
32
- },
33
- ],
34
- },
35
- ],
36
- pagination: { totalHits: 1 },
37
- };
38
-
39
- const CANNED_TRACES_SEARCH_WITH_SCROLL = {
40
- traces: [
41
- {
42
- trace_id: "trace-page-1",
43
- input: { value: "Page 1 input" },
44
- output: { value: "Page 1 output" },
45
- },
46
- ],
47
- pagination: { totalHits: 50, scrollId: "scroll-token-abc" },
48
- };
49
-
50
- const CANNED_TRACES_EMPTY = {
51
- traces: [],
52
- pagination: { totalHits: 0 },
53
- };
54
-
55
- const CANNED_TRACE_DETAIL = {
56
- trace_id: "trace-001",
57
- formatted_trace:
58
- "Root [server] 1200ms\n LLM Call [llm] 500ms\n Input: Hello\n Output: Hi there",
59
- timestamps: {
60
- started_at: 1700000000000,
61
- updated_at: 1700000001000,
62
- inserted_at: 1700000001000,
63
- },
64
- metadata: {
65
- user_id: "user-42",
66
- thread_id: "thread-1",
67
- customer_id: "cust-100",
68
- labels: ["production"],
69
- },
70
- evaluations: [
71
- {
72
- evaluator_id: "eval-1",
73
- name: "Faithfulness",
74
- score: 0.95,
75
- passed: true,
76
- },
77
- ],
78
- };
79
-
80
- const CANNED_ANALYTICS = {
81
- currentPeriod: [
82
- { date: "2024-01-01", "0__trace_id_cardinality": 42 },
83
- { date: "2024-01-02", "0__trace_id_cardinality": 58 },
84
- ],
85
- previousPeriod: [],
86
- };
87
-
88
- const CANNED_PROMPTS_LIST = [
89
- {
90
- id: "p1",
91
- handle: "greeting-bot",
92
- name: "Greeting Bot",
93
- latestVersionNumber: 3,
94
- },
95
- {
96
- id: "p2",
97
- handle: "qa-assistant",
98
- name: "QA Assistant",
99
- latestVersionNumber: 1,
100
- },
101
- ];
102
-
103
- const CANNED_PROMPT_DETAIL = {
104
- id: "p1",
105
- handle: "greeting-bot",
106
- name: "Greeting Bot",
107
- version: 3,
108
- versionId: "ver_p1v3",
109
- commitMessage: "Updated tone",
110
- model: "openai/gpt-4o",
111
- messages: [{ role: "system", content: "You are a friendly bot." }],
112
- parameters: {},
113
- tags: [{ name: "latest", versionId: "ver_p1v3" }],
114
- };
115
-
116
- const CANNED_PROMPT_CREATED = {
117
- id: "p-new",
118
- handle: "new-prompt",
119
- name: "New Prompt",
120
- latestVersionNumber: 1,
121
- };
122
-
123
- const CANNED_PROMPT_UPDATED = {
124
- id: "p1",
125
- handle: "greeting-bot",
126
- latestVersionNumber: 4,
127
- };
128
-
129
- const CANNED_SCENARIOS_LIST = [
130
- {
131
- id: "scen_abc123",
132
- name: "Login Flow Happy Path",
133
- situation: "User attempts to log in with valid credentials",
134
- criteria: [
135
- "Responds with a welcome message",
136
- "Includes user name in greeting",
137
- ],
138
- labels: ["auth", "happy-path"],
139
- },
140
- {
141
- id: "scen_def456",
142
- name: "Password Reset",
143
- situation: "User requests a password reset link",
144
- criteria: ["Sends reset email"],
145
- labels: ["auth"],
146
- },
147
- ];
148
-
149
- const CANNED_SCENARIO_DETAIL = {
150
- id: "scen_abc123",
151
- name: "Login Flow Happy Path",
152
- situation: "User attempts to log in with valid credentials",
153
- criteria: [
154
- "Responds with a welcome message",
155
- "Includes user name in greeting",
156
- ],
157
- labels: ["auth", "happy-path"],
158
- };
159
-
160
- const CANNED_SCENARIO_CREATED = {
161
- id: "scen_new789",
162
- name: "New Scenario",
163
- situation: "User does something",
164
- criteria: ["Agent responds correctly"],
165
- labels: ["test"],
166
- };
167
-
168
- const CANNED_SCENARIO_UPDATED = {
169
- id: "scen_abc123",
170
- name: "Login Flow - Updated",
171
- situation: "User logs in with correct email and pass",
172
- criteria: [
173
- "Responds with welcome message",
174
- "Sets session cookie",
175
- "Redirects to dashboard",
176
- ],
177
- labels: ["auth", "happy-path"],
178
- };
179
-
180
- const CANNED_SCENARIO_ARCHIVED = {
181
- id: "scen_abc123",
182
- archived: true,
183
- };
184
-
185
- const CANNED_EVALUATORS_LIST = [
186
- {
187
- id: "evaluator_abc123",
188
- projectId: "proj_1",
189
- name: "Toxicity Check",
190
- slug: "toxicity-check",
191
- type: "evaluator",
192
- config: { evaluatorType: "openai/moderation" },
193
- workflowId: null,
194
- copiedFromEvaluatorId: null,
195
- createdAt: "2024-01-01T00:00:00.000Z",
196
- updatedAt: "2024-01-01T00:00:00.000Z",
197
- fields: [{ identifier: "input", type: "str" }],
198
- outputFields: [{ identifier: "passed", type: "bool" }],
199
- },
200
- {
201
- id: "evaluator_def456",
202
- projectId: "proj_1",
203
- name: "Exact Match",
204
- slug: "exact-match",
205
- type: "evaluator",
206
- config: { evaluatorType: "langevals/exact_match" },
207
- workflowId: null,
208
- copiedFromEvaluatorId: null,
209
- createdAt: "2024-01-02T00:00:00.000Z",
210
- updatedAt: "2024-01-02T00:00:00.000Z",
211
- fields: [
212
- { identifier: "output", type: "str" },
213
- { identifier: "expected_output", type: "str" },
214
- ],
215
- outputFields: [{ identifier: "passed", type: "bool" }],
216
- },
217
- ];
218
-
219
- const CANNED_EVALUATOR_DETAIL = {
220
- id: "evaluator_abc123",
221
- projectId: "proj_1",
222
- name: "Toxicity Check",
223
- slug: "toxicity-check",
224
- type: "evaluator",
225
- config: {
226
- evaluatorType: "openai/moderation",
227
- settings: { model: "text-moderation-stable" },
228
- },
229
- workflowId: null,
230
- copiedFromEvaluatorId: null,
231
- createdAt: "2024-01-01T00:00:00.000Z",
232
- updatedAt: "2024-01-01T00:00:00.000Z",
233
- fields: [
234
- { identifier: "input", type: "str" },
235
- { identifier: "output", type: "str", optional: true },
236
- ],
237
- outputFields: [
238
- { identifier: "passed", type: "bool" },
239
- { identifier: "score", type: "float" },
240
- ],
241
- };
242
-
243
- const CANNED_EVALUATOR_CREATED = {
244
- id: "evaluator_new123",
245
- projectId: "proj_1",
246
- name: "My LLM Judge",
247
- slug: "my-llm-judge",
248
- type: "evaluator",
249
- config: { evaluatorType: "langevals/llm_boolean" },
250
- workflowId: null,
251
- copiedFromEvaluatorId: null,
252
- createdAt: "2024-01-01T00:00:00.000Z",
253
- updatedAt: "2024-01-01T00:00:00.000Z",
254
- fields: [{ identifier: "input", type: "str" }],
255
- outputFields: [{ identifier: "passed", type: "bool" }],
256
- };
257
-
258
- const CANNED_EVALUATOR_UPDATED = {
259
- id: "evaluator_abc123",
260
- projectId: "proj_1",
261
- name: "Updated Toxicity",
262
- slug: "toxicity-check",
263
- type: "evaluator",
264
- config: { evaluatorType: "openai/moderation" },
265
- workflowId: null,
266
- copiedFromEvaluatorId: null,
267
- createdAt: "2024-01-01T00:00:00.000Z",
268
- updatedAt: "2024-01-02T00:00:00.000Z",
269
- fields: [],
270
- outputFields: [],
271
- };
272
-
273
- const CANNED_MODEL_PROVIDERS_LIST = {
274
- openai: {
275
- provider: "openai",
276
- enabled: true,
277
- customKeys: { OPENAI_API_KEY: "HAS_KEY" },
278
- models: ["gpt-4o", "gpt-4o-mini"],
279
- embeddingsModels: ["text-embedding-3-small"],
280
- deploymentMapping: null,
281
- extraHeaders: [],
282
- },
283
- anthropic: {
284
- provider: "anthropic",
285
- enabled: false,
286
- customKeys: null,
287
- models: ["claude-sonnet-4-5-20250929"],
288
- embeddingsModels: null,
289
- deploymentMapping: null,
290
- extraHeaders: [],
291
- },
292
- };
293
-
294
- const CANNED_AGENTS_LIST = {
295
- data: [
296
- { id: "agent_abc", name: "Test Agent", type: "http", config: { url: "http://example.com" }, createdAt: "2024-01-01T00:00:00Z", updatedAt: "2024-01-01T00:00:00Z" },
297
- ],
298
- pagination: { page: 1, limit: 50, total: 1, totalPages: 1 },
299
- };
300
-
301
- const CANNED_AGENT_DETAIL = {
302
- id: "agent_abc", name: "Test Agent", type: "http", config: { url: "http://example.com" }, createdAt: "2024-01-01T00:00:00Z", updatedAt: "2024-01-01T00:00:00Z",
303
- };
304
-
305
- const CANNED_RUN_PLAN = {
306
- id: "plan_abc", name: "Regression Plan", slug: "regression-plan", scope: { mode: "labels", labels: ["auth"] }, scenarioIds: ["scen_abc123"], targets: [{ type: "http", referenceId: "agent_abc" }], repeatCount: 2, simulatorModel: "openai/gpt-5-mini", judgeModel: null, labels: [], archivedAt: null, createdAt: "2024-01-01T00:00:00Z", updatedAt: "2024-01-01T00:00:00Z", platformUrl: "https://app.langwatch.ai/proj/agent-testing/results/regression-plan",
307
- };
308
-
309
- const CANNED_RUN_PLANS_LIST = [CANNED_RUN_PLAN];
310
-
311
- const CANNED_RUN_PLAN_RUN = {
312
- scheduled: true, batchRunId: "batch_123", setId: "set_456", jobCount: 2, skippedArchived: { scenarios: [], targets: [] }, items: [{ scenarioRunId: "run_1", scenarioId: "scen_abc123", target: { type: "http", referenceId: "agent_abc" }, name: "Test" }], runPlanId: "plan_abc", planName: "Regression Plan", created: true, platformUrl: "https://app.langwatch.ai/proj/agent-testing/results/regression-plan",
313
- };
314
-
315
- const CANNED_RUN_PLAN_RERUN = { ...CANNED_RUN_PLAN_RUN, created: false };
316
-
317
- const CANNED_TEST_SUITE = {
318
- id: "suite_abc", name: "Checkout", slug: "checkout", scenarioIds: ["scen_abc123"], scenarioCount: 1, archivedAt: null, createdAt: "2024-01-01T00:00:00Z", updatedAt: "2024-01-01T00:00:00Z", platformUrl: "https://app.langwatch.ai/proj/agent-testing/suites/checkout",
319
- };
320
-
321
- const CANNED_TEST_SUITES_LIST = [CANNED_TEST_SUITE];
322
-
323
- const CANNED_TEST_SUITE_DETAIL = {
324
- ...CANNED_TEST_SUITE,
325
- scenarios: [{ id: "scen_abc123", name: "Login Flow Happy Path" }],
326
- };
327
-
328
- const CANNED_TEST_SUITE_CREATED = {
329
- ...CANNED_TEST_SUITE, id: "suite_new", name: "New Suite", slug: "new-suite", scenarioIds: [], scenarioCount: 0,
330
- };
331
-
332
- const CANNED_TEST_SUITE_RENAMED = { ...CANNED_TEST_SUITE, name: "Checkout v2", slug: "checkout-v2" };
333
-
334
- const CANNED_SIMULATION_RUNS = {
335
- runs: [{ scenarioRunId: "run_abc", scenarioId: "scen_abc123", batchRunId: "batch_xyz", name: "Login Flow", status: "SUCCESS", durationInMs: 5200, totalCost: 0.0042, timestamp: 1700000000000, updatedAt: 1700000001000 }],
336
- hasMore: false,
337
- };
338
-
339
- const CANNED_SIMULATION_RUN_DETAIL = {
340
- scenarioRunId: "run_abc", scenarioId: "scen_abc123", batchRunId: "batch_xyz", name: "Login Flow", status: "SUCCESS", durationInMs: 5200, totalCost: 0.0042, results: { verdict: "passed", reasoning: "All criteria met", metCriteria: ["Greets user"], unmetCriteria: [], error: null }, messages: [{ role: "user", content: "Hello" }, { role: "assistant", content: "Hi there!" }], timestamp: 1700000000000, updatedAt: 1700000001000,
341
- };
342
-
343
- const CANNED_DASHBOARDS_LIST = {
344
- data: [{ id: "dash_abc", name: "Main Dashboard", order: 0, graphCount: 3, createdAt: "2024-01-01T00:00:00Z", updatedAt: "2024-01-01T00:00:00Z" }],
345
- };
346
-
347
- const CANNED_WORKFLOWS_LIST = [
348
- { id: "wf_abc", name: "Test Workflow", icon: null, description: "A workflow", isEvaluator: false, isComponent: false, createdAt: "2024-01-01T00:00:00Z", updatedAt: "2024-01-01T00:00:00Z" },
349
- ];
350
-
351
- const CANNED_MODEL_PROVIDER_SET = {
352
- openai: {
353
- provider: "openai",
354
- enabled: true,
355
- customKeys: { OPENAI_API_KEY: "HAS_KEY" },
356
- models: ["gpt-4o"],
357
- embeddingsModels: null,
358
- deploymentMapping: null,
359
- extraHeaders: [],
360
- },
361
- };
362
-
363
- // --- Mock HTTP Server that handles ALL MCP API endpoints ---
364
-
365
- /** Track last request for each route so tests can assert on request body/params. */
366
- const lastRequests: Record<string, { method: string; url: string; body: string }> = {};
367
-
368
- /**
369
- * Mutable prompt detail so the mock is stateful: a PUT updates it and the
370
- * subsequent confirmation GET reflects the new version. Reset per test.
371
- */
372
- let promptDetailState: typeof CANNED_PROMPT_DETAIL = { ...CANNED_PROMPT_DETAIL };
373
- beforeEach(() => {
374
- promptDetailState = { ...CANNED_PROMPT_DETAIL };
375
- });
376
-
377
- function createMockServer(): Server {
378
- return createServer((req, res) => {
379
- const authToken = req.headers["x-auth-token"];
380
- if (authToken !== "test-integration-key") {
381
- res.writeHead(401, { "Content-Type": "application/json" });
382
- res.end(JSON.stringify({ message: "Invalid auth token." }));
383
- return;
384
- }
385
-
386
- let body = "";
387
- req.on("data", (chunk: string) => (body += chunk));
388
- req.on("end", () => {
389
- const url = req.url ?? "";
390
- const method = req.method ?? "GET";
391
- res.setHeader("Content-Type", "application/json");
392
-
393
- // Store last request for assertions
394
- const routeKey = `${method} ${url.split("?")[0]}`;
395
- lastRequests[routeKey] = { method, url, body };
396
-
397
- // --- Trace endpoints ---
398
- if (url === "/api/traces/search" && method === "POST") {
399
- const parsed = JSON.parse(body);
400
- // Return empty results when a special query is used
401
- if (parsed.query === "__empty__") {
402
- res.writeHead(200);
403
- res.end(JSON.stringify(CANNED_TRACES_EMPTY));
404
- } else if (parsed.pageSize === 5) {
405
- res.writeHead(200);
406
- res.end(JSON.stringify(CANNED_TRACES_SEARCH_WITH_SCROLL));
407
- } else {
408
- res.writeHead(200);
409
- res.end(JSON.stringify(CANNED_TRACES_SEARCH));
410
- }
411
- } else if (
412
- url.match(/^\/api\/traces\/trace-nonexistent(\?|$)/) &&
413
- method === "GET"
414
- ) {
415
- res.writeHead(404);
416
- res.end(JSON.stringify({ message: "Trace not found" }));
417
- } else if (
418
- url.match(/^\/api\/traces\/[^/]+(\?|$)/) &&
419
- method === "GET"
420
- ) {
421
- res.writeHead(200);
422
- res.end(JSON.stringify(CANNED_TRACE_DETAIL));
423
- }
424
- // --- Analytics endpoint ---
425
- else if (url === "/api/analytics/timeseries" && method === "POST") {
426
- res.writeHead(200);
427
- res.end(JSON.stringify(CANNED_ANALYTICS));
428
- }
429
- // --- Prompt endpoints ---
430
- else if (url === "/api/prompts" && method === "GET") {
431
- res.writeHead(200);
432
- res.end(JSON.stringify(CANNED_PROMPTS_LIST));
433
- } else if (url === "/api/prompts" && method === "POST") {
434
- res.writeHead(200);
435
- res.end(JSON.stringify(CANNED_PROMPT_CREATED));
436
- } else if (
437
- url.match(/^\/api\/prompts\/[^/]+$/) &&
438
- method === "GET"
439
- ) {
440
- res.writeHead(200);
441
- res.end(JSON.stringify(promptDetailState));
442
- } else if (
443
- url.match(/^\/api\/prompts\/[^/]+$/) &&
444
- method === "PUT"
445
- ) {
446
- const parsed = JSON.parse(body) as {
447
- commitMessage?: string;
448
- model?: string;
449
- messages?: Array<{ role: string; content: string }>;
450
- tags?: string[];
451
- };
452
- const newVersionId = "ver_p1v4";
453
- promptDetailState = {
454
- ...promptDetailState,
455
- version: 4,
456
- versionId: newVersionId,
457
- commitMessage: parsed.commitMessage ?? promptDetailState.commitMessage,
458
- model: parsed.model ?? promptDetailState.model,
459
- messages: parsed.messages ?? promptDetailState.messages,
460
- tags:
461
- parsed.tags === undefined
462
- ? promptDetailState.tags.map((tag) =>
463
- tag.name === "latest"
464
- ? { name: "latest", versionId: newVersionId }
465
- : tag,
466
- )
467
- : [
468
- { name: "latest", versionId: newVersionId },
469
- ...parsed.tags
470
- .filter((name) => name !== "latest")
471
- .map((name) => ({ name, versionId: newVersionId })),
472
- ],
473
- };
474
- res.writeHead(200);
475
- res.end(JSON.stringify(CANNED_PROMPT_UPDATED));
476
- }
477
- // --- Scenario endpoints ---
478
- else if (url === "/api/scenarios" && method === "GET") {
479
- res.writeHead(200);
480
- res.end(JSON.stringify(CANNED_SCENARIOS_LIST));
481
- } else if (url === "/api/scenarios" && method === "POST") {
482
- res.writeHead(200);
483
- res.end(JSON.stringify(CANNED_SCENARIO_CREATED));
484
- } else if (
485
- url.match(/^\/api\/scenarios\/scen_nonexistent(\?|$)/) &&
486
- method === "GET"
487
- ) {
488
- res.writeHead(404);
489
- res.end(JSON.stringify({ message: "Scenario not found" }));
490
- } else if (
491
- url.match(/^\/api\/scenarios\/[^/]+$/) &&
492
- method === "GET"
493
- ) {
494
- res.writeHead(200);
495
- res.end(JSON.stringify(CANNED_SCENARIO_DETAIL));
496
- } else if (
497
- url.match(/^\/api\/scenarios\/[^/]+$/) &&
498
- method === "PUT"
499
- ) {
500
- res.writeHead(200);
501
- res.end(JSON.stringify(CANNED_SCENARIO_UPDATED));
502
- } else if (
503
- url.match(/^\/api\/scenarios\/[^/]+$/) &&
504
- method === "DELETE"
505
- ) {
506
- res.writeHead(200);
507
- res.end(JSON.stringify(CANNED_SCENARIO_ARCHIVED));
508
- }
509
- // --- Evaluator endpoints ---
510
- else if (url === "/api/evaluators" && method === "GET") {
511
- res.writeHead(200);
512
- res.end(JSON.stringify(CANNED_EVALUATORS_LIST));
513
- } else if (url === "/api/evaluators" && method === "POST") {
514
- res.writeHead(200);
515
- res.end(JSON.stringify(CANNED_EVALUATOR_CREATED));
516
- } else if (
517
- url.match(/^\/api\/evaluators\/evaluator_nonexistent(\?|$)/) &&
518
- method === "GET"
519
- ) {
520
- res.writeHead(404);
521
- res.end(JSON.stringify({ message: "Evaluator not found" }));
522
- } else if (
523
- url.match(/^\/api\/evaluators\/[^/]+$/) &&
524
- method === "GET"
525
- ) {
526
- res.writeHead(200);
527
- res.end(JSON.stringify(CANNED_EVALUATOR_DETAIL));
528
- } else if (
529
- url.match(/^\/api\/evaluators\/[^/]+$/) &&
530
- method === "PUT"
531
- ) {
532
- res.writeHead(200);
533
- res.end(JSON.stringify(CANNED_EVALUATOR_UPDATED));
534
- }
535
- // --- Model Provider endpoints ---
536
- else if (url === "/api/model-providers" && method === "GET") {
537
- res.writeHead(200);
538
- res.end(JSON.stringify(CANNED_MODEL_PROVIDERS_LIST));
539
- } else if (
540
- url.match(/^\/api\/model-providers\/[^/]+$/) &&
541
- method === "PUT"
542
- ) {
543
- res.writeHead(200);
544
- res.end(JSON.stringify(CANNED_MODEL_PROVIDER_SET));
545
- }
546
- // --- Agent endpoints ---
547
- else if (url === "/api/v1/agents" && method === "GET") {
548
- res.writeHead(200);
549
- res.end(JSON.stringify(CANNED_AGENTS_LIST));
550
- } else if (url === "/api/v1/agents" && method === "POST") {
551
- res.writeHead(201);
552
- res.end(JSON.stringify(CANNED_AGENT_DETAIL));
553
- } else if (url?.match(/^\/api\/v1\/agents\/[^/]+$/) && method === "GET") {
554
- res.writeHead(200);
555
- res.end(JSON.stringify(CANNED_AGENT_DETAIL));
556
- } else if (url?.match(/^\/api\/v1\/agents\/[^/]+$/) && method === "PATCH") {
557
- res.writeHead(200);
558
- res.end(JSON.stringify(CANNED_AGENT_DETAIL));
559
- } else if (url?.match(/^\/api\/v1\/agents\/[^/]+$/) && method === "DELETE") {
560
- res.writeHead(200);
561
- res.end(JSON.stringify({ id: "agent_abc", name: "Test Agent" }));
562
- }
563
- // --- Run plan endpoints ---
564
- else if (url === "/api/v1/run-plans/run" && method === "POST") {
565
- res.writeHead(200);
566
- res.end(JSON.stringify(CANNED_RUN_PLAN_RUN));
567
- } else if (url?.match(/^\/api\/v1\/run-plans(\?.*)?$/) && method === "GET") {
568
- res.writeHead(200);
569
- res.end(JSON.stringify(CANNED_RUN_PLANS_LIST));
570
- } else if (url?.match(/^\/api\/v1\/run-plans\/[^/]+\/run$/) && method === "POST") {
571
- res.writeHead(200);
572
- res.end(JSON.stringify(CANNED_RUN_PLAN_RERUN));
573
- } else if (url?.match(/^\/api\/v1\/run-plans\/[^/]+$/) && method === "GET") {
574
- res.writeHead(200);
575
- res.end(JSON.stringify(CANNED_RUN_PLAN));
576
- } else if (url?.match(/^\/api\/v1\/run-plans\/[^/]+$/) && method === "DELETE") {
577
- res.writeHead(200);
578
- res.end(JSON.stringify({ id: "plan_abc", archived: true }));
579
- }
580
- // --- Test suite endpoints ---
581
- else if (url === "/api/v1/test-suites" && method === "GET") {
582
- res.writeHead(200);
583
- res.end(JSON.stringify(CANNED_TEST_SUITES_LIST));
584
- } else if (url === "/api/v1/test-suites" && method === "POST") {
585
- res.writeHead(201);
586
- res.end(JSON.stringify(CANNED_TEST_SUITE_CREATED));
587
- } else if (url?.match(/^\/api\/v1\/test-suites\/[^/]+\/run$/) && method === "POST") {
588
- res.writeHead(200);
589
- res.end(JSON.stringify(CANNED_RUN_PLAN_RUN));
590
- } else if (url?.match(/^\/api\/v1\/test-suites\/[^/]+$/) && method === "GET") {
591
- res.writeHead(200);
592
- res.end(JSON.stringify(CANNED_TEST_SUITE_DETAIL));
593
- } else if (url?.match(/^\/api\/v1\/test-suites\/[^/]+$/) && method === "PATCH") {
594
- res.writeHead(200);
595
- res.end(JSON.stringify(CANNED_TEST_SUITE_RENAMED));
596
- } else if (url?.match(/^\/api\/v1\/test-suites\/[^/]+$/) && method === "DELETE") {
597
- res.writeHead(200);
598
- res.end(JSON.stringify({ id: "suite_abc", archived: true }));
599
- }
600
- // --- Simulation Run endpoints ---
601
- else if (url?.match(/^\/api\/simulation-runs\/[^/]+$/) && method === "GET") {
602
- res.writeHead(200);
603
- res.end(JSON.stringify(CANNED_SIMULATION_RUN_DETAIL));
604
- } else if (url?.match(/^\/api\/simulation-runs/) && method === "GET") {
605
- res.writeHead(200);
606
- res.end(JSON.stringify(CANNED_SIMULATION_RUNS));
607
- }
608
- // --- Dashboard endpoints ---
609
- else if (url === "/api/dashboards" && method === "GET") {
610
- res.writeHead(200);
611
- res.end(JSON.stringify(CANNED_DASHBOARDS_LIST));
612
- } else if (url === "/api/dashboards" && method === "POST") {
613
- res.writeHead(201);
614
- res.end(JSON.stringify({ id: "dash_new", name: "New Dashboard" }));
615
- } else if (url?.match(/^\/api\/dashboards\/[^/]+$/) && method === "GET") {
616
- res.writeHead(200);
617
- res.end(JSON.stringify(CANNED_DASHBOARDS_LIST.data[0]));
618
- } else if (url?.match(/^\/api\/dashboards\/[^/]+$/) && method === "DELETE") {
619
- res.writeHead(200);
620
- res.end(JSON.stringify({ id: "dash_abc", name: "Main Dashboard" }));
621
- }
622
- // --- Workflow endpoints ---
623
- else if (url === "/api/workflows" && method === "GET") {
624
- res.writeHead(200);
625
- res.end(JSON.stringify(CANNED_WORKFLOWS_LIST));
626
- } else if (url?.match(/^\/api\/workflows\/[^/]+\/run$/) && method === "POST") {
627
- res.writeHead(200);
628
- res.end(JSON.stringify({ output: "workflow result" }));
629
- } else if (url?.match(/^\/api\/workflows\/[^/]+$/) && method === "GET") {
630
- res.writeHead(200);
631
- res.end(JSON.stringify(CANNED_WORKFLOWS_LIST[0]));
632
- } else if (url?.match(/^\/api\/workflows\/[^/]+$/) && method === "DELETE") {
633
- res.writeHead(200);
634
- res.end(JSON.stringify({ id: "wf_abc", archived: true }));
635
- }
636
- // --- Monitor endpoints ---
637
- else if (url === "/api/monitors" && method === "GET") {
638
- res.writeHead(200);
639
- res.end(JSON.stringify([
640
- { id: "mon_abc", name: "Toxicity Check", slug: "toxicity-check-x1y2z", checkType: "ragas/toxicity", enabled: true, executionMode: "ON_MESSAGE", sample: 1.0, level: "trace", evaluatorId: null, preconditions: [], parameters: {}, mappings: {}, threadIdleTimeout: null, createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-01T00:00:00Z" },
641
- ]));
642
- } else if (url === "/api/monitors" && method === "POST") {
643
- res.writeHead(201);
644
- res.end(JSON.stringify({ id: "mon_new", name: "New Monitor", slug: "new-monitor-abc12", checkType: "ragas/toxicity", enabled: true, executionMode: "ON_MESSAGE", sample: 1.0, level: "trace", evaluatorId: null, preconditions: [], parameters: {}, mappings: {}, threadIdleTimeout: null, createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-01T00:00:00Z" }));
645
- } else if (url?.match(/^\/api\/monitors\/[^/]+\/toggle$/) && method === "POST") {
646
- const monitorId = url?.split("/")[3];
647
- res.writeHead(200);
648
- res.end(JSON.stringify({ id: monitorId, enabled: true }));
649
- } else if (url?.match(/^\/api\/monitors\/[^/]+$/) && method === "GET") {
650
- res.writeHead(200);
651
- res.end(JSON.stringify({ id: "mon_abc", name: "Toxicity Check", slug: "toxicity-check-x1y2z", checkType: "ragas/toxicity", enabled: true, executionMode: "ON_MESSAGE", sample: 1.0, level: "trace", evaluatorId: null, preconditions: [], parameters: {}, mappings: {}, threadIdleTimeout: null, createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-01T00:00:00Z" }));
652
- } else if (url?.match(/^\/api\/monitors\/[^/]+$/) && method === "PATCH") {
653
- res.writeHead(200);
654
- res.end(JSON.stringify({ id: "mon_abc", name: "Updated Monitor", slug: "toxicity-check-x1y2z", checkType: "ragas/toxicity", enabled: false, executionMode: "ON_MESSAGE", sample: 0.5, level: "trace", evaluatorId: null, preconditions: [], parameters: {}, mappings: {}, threadIdleTimeout: null, createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-02T00:00:00Z" }));
655
- } else if (url?.match(/^\/api\/monitors\/[^/]+$/) && method === "DELETE") {
656
- const monitorId = url?.split("/").pop();
657
- res.writeHead(200);
658
- res.end(JSON.stringify({ id: monitorId, deleted: true }));
659
- }
660
- // --- Secret endpoints ---
661
- else if (url === "/api/secrets" && method === "GET") {
662
- res.writeHead(200);
663
- res.end(JSON.stringify([
664
- { id: "secret_abc", projectId: "proj_123", name: "MY_API_KEY", createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-01T00:00:00Z" },
665
- { id: "secret_def", projectId: "proj_123", name: "DB_PASSWORD", createdAt: "2026-01-02T00:00:00Z", updatedAt: "2026-01-02T00:00:00Z" },
666
- ]));
667
- } else if (url === "/api/secrets" && method === "POST") {
668
- res.writeHead(201);
669
- res.end(JSON.stringify({ id: "secret_new", projectId: "proj_123", name: "NEW_SECRET", createdAt: "2026-01-03T00:00:00Z", updatedAt: "2026-01-03T00:00:00Z" }));
670
- } else if (url?.match(/^\/api\/secrets\/[^/]+$/) && method === "PUT") {
671
- res.writeHead(200);
672
- res.end(JSON.stringify({ id: "secret_abc", projectId: "proj_123", name: "MY_API_KEY", createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-03T00:00:00Z" }));
673
- } else if (url?.match(/^\/api\/secrets\/[^/]+$/) && method === "DELETE") {
674
- const secretId = url?.split("/").pop();
675
- res.writeHead(200);
676
- res.end(JSON.stringify({ id: secretId, deleted: true }));
677
- }
678
- // --- Fallback ---
679
- else {
680
- res.writeHead(404);
681
- res.end(
682
- JSON.stringify({ message: `Not found: ${method} ${url}` }),
683
- );
684
- }
685
- });
686
- });
687
- }
688
-
689
- // --- Integration Tests ---
690
- // These verify that every MCP tool handler correctly communicates with the REST API
691
- // through a real HTTP server. Formatting/digest assertions ensure the full chain works.
692
-
693
- describe("All MCP tools integration", () => {
694
- let server: Server;
695
- let port: number;
696
-
697
- beforeAll(async () => {
698
- server = createMockServer();
699
- await new Promise<void>((resolve) => {
700
- server.listen(0, () => {
701
- const addr = server.address();
702
- port = typeof addr === "object" && addr ? addr.port : 0;
703
- initConfig({
704
- apiKey: "test-integration-key",
705
- endpoint: `http://localhost:${port}`,
706
- });
707
- resolve();
708
- });
709
- });
710
- });
711
-
712
- afterAll(async () => {
713
- await new Promise<void>((resolve) => server.close(() => resolve()));
714
- });
715
-
716
- // =====================
717
- // 1. fetch_langwatch_docs
718
- // =====================
719
- describe("fetch_langwatch_docs", () => {
720
- it("resolves the trusted docs index", () => {
721
- expect(resolveDocumentationUrl("langwatch").toString()).toBe(
722
- "https://langwatch.ai/docs/llms.txt",
723
- );
724
- });
725
-
726
- it("fetches trusted docs without following redirects", async () => {
727
- const mockFetch = vi.fn().mockResolvedValue(
728
- new Response("# Integration Guide"),
729
- );
730
-
731
- const text = await fetchDocumentation(
732
- "langwatch",
733
- "integration",
734
- mockFetch,
735
- );
736
-
737
- expect(text).toContain("Integration Guide");
738
- expect(mockFetch).toHaveBeenCalledWith(
739
- new URL("https://langwatch.ai/docs/integration.md"),
740
- {
741
- redirect: "error",
742
- signal: expect.any(AbortSignal),
743
- },
744
- );
745
- });
746
- });
747
-
748
- // =====================
749
- // 2. fetch_scenario_docs
750
- // =====================
751
- describe("fetch_scenario_docs", () => {
752
- it("resolves the trusted Scenario docs index", () => {
753
- expect(resolveDocumentationUrl("scenario").toString()).toBe(
754
- "https://langwatch.ai/scenario/llms.txt",
755
- );
756
- });
757
-
758
- it("normalizes a relative Scenario docs path", () => {
759
- expect(resolveDocumentationUrl("scenario", "setup").toString()).toBe(
760
- "https://langwatch.ai/scenario/setup.md",
761
- );
762
- });
763
- });
764
-
765
- // =====================
766
- // 3. discover_schema
767
- // =====================
768
- describe("discover_schema", () => {
769
- describe("when category is filters", () => {
770
- /** @scenario Agent discovers available filter fields */
771
- it("returns filter field documentation", async () => {
772
- const { formatSchema } = await import(
773
- "../tools/discover-schema.js"
774
- );
775
- const result = formatSchema("filters");
776
-
777
- expect(result).toContain("## Available Filter Fields");
778
- expect(result).toContain("filters");
779
- });
780
- });
781
-
782
- describe("when category is metrics", () => {
783
- /** @scenario Agent discovers available metrics with allowed aggregations */
784
- it("returns metric documentation", async () => {
785
- const { formatSchema } = await import(
786
- "../tools/discover-schema.js"
787
- );
788
- const result = formatSchema("metrics");
789
-
790
- expect(result).toContain("## Available Metrics");
791
- });
792
- });
793
-
794
- describe("when category is aggregations", () => {
795
- it("returns aggregation types", async () => {
796
- const { formatSchema } = await import(
797
- "../tools/discover-schema.js"
798
- );
799
- const result = formatSchema("aggregations");
800
-
801
- expect(result).toContain("## Available Aggregation Types");
802
- expect(result).toContain("cardinality");
803
- expect(result).toContain("avg");
804
- expect(result).toContain("sum");
805
- });
806
- });
807
-
808
- describe("when category is groups", () => {
809
- /** @scenario Agent discovers available group-by options */
810
- it("returns group-by options", async () => {
811
- const { formatSchema } = await import(
812
- "../tools/discover-schema.js"
813
- );
814
- const result = formatSchema("groups");
815
-
816
- expect(result).toContain("## Available Group-By Options");
817
- });
818
- });
819
-
820
- describe("when category is scenarios", () => {
821
- it("returns scenario schema documentation", async () => {
822
- const { formatScenarioSchema } = await import(
823
- "../tools/discover-scenario-schema.js"
824
- );
825
- const result = formatScenarioSchema();
826
-
827
- expect(result).toContain("# Scenario Schema");
828
- expect(result).toContain("**name** (required)");
829
- expect(result).toContain("**situation** (required)");
830
- });
831
- });
832
-
833
- describe("when category is evaluators", () => {
834
- it("returns evaluator type overview", async () => {
835
- const { formatEvaluatorSchema } = await import(
836
- "../tools/discover-evaluator-schema.js"
837
- );
838
- const result = formatEvaluatorSchema();
839
-
840
- expect(result).toContain("# Available Evaluator Types");
841
- });
842
- });
843
-
844
- describe("when category is evaluators with specific type", () => {
845
- it("returns detailed evaluator schema", async () => {
846
- const { formatEvaluatorSchema } = await import(
847
- "../tools/discover-evaluator-schema.js"
848
- );
849
- const result = formatEvaluatorSchema("langevals/llm_boolean");
850
-
851
- expect(result).toContain("langevals/llm_boolean");
852
- expect(result).toContain("## Settings");
853
- });
854
- });
855
-
856
- describe("when evaluator type is unknown", () => {
857
- it("returns an error message", async () => {
858
- const { formatEvaluatorSchema } = await import(
859
- "../tools/discover-evaluator-schema.js"
860
- );
861
- const result = formatEvaluatorSchema("nonexistent/type");
862
-
863
- expect(result).toContain('Unknown evaluator type');
864
- });
865
- });
866
-
867
- describe("when category is all", () => {
868
- /** @scenario Agent discovers all schema information at once */
869
- it("returns all schema categories", async () => {
870
- const { formatSchema } = await import(
871
- "../tools/discover-schema.js"
872
- );
873
- const result = formatSchema("all");
874
-
875
- expect(result).toContain("## Available Filter Fields");
876
- expect(result).toContain("## Available Metrics");
877
- expect(result).toContain("## Available Aggregation Types");
878
- expect(result).toContain("## Available Group-By Options");
879
- });
880
- });
881
- });
882
-
883
- // =====================
884
- // 4. search_traces
885
- // =====================
886
- describe("search_traces", () => {
887
- describe("when traces are found", () => {
888
- /** @scenario Agent searches traces with a text query */
889
- it("returns formatted trace digests", async () => {
890
- const { handleSearchTraces } = await import(
891
- "../tools/search-traces.js"
892
- );
893
- const result = await handleSearchTraces({
894
- startDate: "24h",
895
- endDate: "now",
896
- });
897
-
898
- expect(result).toContain("trace-001");
899
- expect(result).toContain("LLM Call [llm] 500ms");
900
- expect(result).toContain("1 trace");
901
- });
902
- });
903
-
904
- describe("when no traces match", () => {
905
- it("returns a no-results message", async () => {
906
- const { handleSearchTraces } = await import(
907
- "../tools/search-traces.js"
908
- );
909
- const result = await handleSearchTraces({
910
- query: "__empty__",
911
- });
912
-
913
- expect(result).toBe("No traces found matching your query.");
914
- });
915
- });
916
-
917
- describe("when pagination token is present", () => {
918
- /** @scenario Agent paginates through trace results */
919
- it("includes scroll ID for next page", async () => {
920
- const { handleSearchTraces } = await import(
921
- "../tools/search-traces.js"
922
- );
923
- const result = await handleSearchTraces({
924
- pageSize: 5,
925
- });
926
-
927
- expect(result).toContain("scroll-token-abc");
928
- });
929
- });
930
-
931
- describe("when format is json", () => {
932
- it("returns parseable JSON", async () => {
933
- const { handleSearchTraces } = await import(
934
- "../tools/search-traces.js"
935
- );
936
- const result = await handleSearchTraces({
937
- format: "json",
938
- });
939
-
940
- const parsed = JSON.parse(result);
941
- expect(parsed.traces).toBeDefined();
942
- expect(parsed.traces.length).toBeGreaterThan(0);
943
- expect(parsed.traces[0].trace_id).toBe("trace-001");
944
- });
945
- });
946
-
947
- describe("when filters are applied", () => {
948
- /** @scenario Agent searches traces filtered by user_id */
949
- it("passes filters to the API", async () => {
950
- const { handleSearchTraces } = await import(
951
- "../tools/search-traces.js"
952
- );
953
- await handleSearchTraces({
954
- filters: { "metadata.user_id": ["user-42"] },
955
- });
956
-
957
- const req = lastRequests["POST /api/traces/search"];
958
- expect(req).toBeDefined();
959
- const parsed = JSON.parse(req!.body);
960
- expect(parsed.filters).toEqual({
961
- "metadata.user_id": ["user-42"],
962
- });
963
- });
964
- });
965
-
966
- describe("given a trace has evaluation results", () => {
967
- /** @scenario Agent searches traces and sees evaluation results without a follow-up call */
968
- it("includes evaluation status in the digest without a follow-up call", async () => {
969
- const { handleSearchTraces } = await import(
970
- "../tools/search-traces.js"
971
- );
972
- const result = await handleSearchTraces({
973
- startDate: "24h",
974
- endDate: "now",
975
- });
976
-
977
- expect(result).toContain("- **Faithfulness**: PASSED (score: 0.95)");
978
- });
979
- });
980
- });
981
-
982
- // =====================
983
- // 5. get_trace
984
- // =====================
985
- describe("get_trace", () => {
986
- describe("when trace exists", () => {
987
- /** @scenario Agent gets a single trace by ID in AI-readable format */
988
- it("returns formatted trace with metadata and evaluations", async () => {
989
- const { handleGetTrace } = await import(
990
- "../tools/get-trace.js"
991
- );
992
- const result = await handleGetTrace({ traceId: "trace-001" });
993
-
994
- expect(result).toContain("# Trace: trace-001");
995
- expect(result).toContain("LLM Call [llm] 500ms");
996
- expect(result).toContain("Faithfulness");
997
- expect(result).toContain("PASSED");
998
- expect(result).toContain("**User**: user-42");
999
- expect(result).toContain("**Thread**: thread-1");
1000
- expect(result).toContain("**Customer**: cust-100");
1001
- });
1002
- });
1003
-
1004
- describe("when trace does not exist", () => {
1005
- /** @scenario Agent gets a trace that does not exist */
1006
- it("propagates the 404 error", async () => {
1007
- const { handleGetTrace } = await import(
1008
- "../tools/get-trace.js"
1009
- );
1010
-
1011
- await expect(
1012
- handleGetTrace({ traceId: "trace-nonexistent" }),
1013
- ).rejects.toThrow("404");
1014
- });
1015
- });
1016
-
1017
- describe("when format is json", () => {
1018
- it("returns parseable JSON with full trace data", async () => {
1019
- const { handleGetTrace } = await import(
1020
- "../tools/get-trace.js"
1021
- );
1022
- const result = await handleGetTrace({
1023
- traceId: "trace-001",
1024
- format: "json",
1025
- });
1026
-
1027
- const parsed = JSON.parse(result);
1028
- expect(parsed.trace_id).toBe("trace-001");
1029
- expect(parsed.evaluations).toBeDefined();
1030
- expect(parsed.metadata).toBeDefined();
1031
- });
1032
- });
1033
- });
1034
-
1035
- // =====================
1036
- // 6. get_analytics
1037
- // =====================
1038
- describe("get_analytics", () => {
1039
- describe("when data is available", () => {
1040
- it("returns formatted analytics with markdown table", async () => {
1041
- const { handleGetAnalytics } = await import(
1042
- "../tools/get-analytics.js"
1043
- );
1044
- const result = await handleGetAnalytics({
1045
- metric: "metadata.trace_id",
1046
- aggregation: "cardinality",
1047
- startDate: "7d",
1048
- });
1049
-
1050
- expect(result).toContain("42");
1051
- expect(result).toContain("58");
1052
- expect(result).toContain("| Date | Value |");
1053
- });
1054
- });
1055
-
1056
- describe("when metric and aggregation are specified", () => {
1057
- it("passes them through to the API", async () => {
1058
- const { handleGetAnalytics } = await import(
1059
- "../tools/get-analytics.js"
1060
- );
1061
- await handleGetAnalytics({
1062
- metric: "performance.total_cost",
1063
- aggregation: "sum",
1064
- });
1065
-
1066
- const req = lastRequests["POST /api/analytics/timeseries"];
1067
- expect(req).toBeDefined();
1068
- const parsed = JSON.parse(req!.body);
1069
- expect(parsed.series[0].metric).toBe("performance.total_cost");
1070
- expect(parsed.series[0].aggregation).toBe("sum");
1071
- });
1072
- });
1073
- });
1074
-
1075
- // =====================
1076
- // 7. platform_create_prompt
1077
- // =====================
1078
- describe("platform_create_prompt", () => {
1079
- describe("when valid data is provided", () => {
1080
- it("returns success confirmation with prompt details", async () => {
1081
- const { handleCreatePrompt } = await import(
1082
- "../tools/create-prompt.js"
1083
- );
1084
- const result = await handleCreatePrompt({
1085
- name: "New Prompt",
1086
- messages: [{ role: "system", content: "You are helpful." }],
1087
- model: "openai/gpt-4o",
1088
- });
1089
-
1090
- expect(result).toContain("created successfully");
1091
- expect(result).toContain("p-new");
1092
- expect(result).toContain("**Name**: New Prompt");
1093
- expect(result).toContain("**Model**: openai/gpt-4o");
1094
- });
1095
- });
1096
- });
1097
-
1098
- // =====================
1099
- // 8. platform_list_prompts
1100
- // =====================
1101
- describe("platform_list_prompts", () => {
1102
- describe("when prompts exist", () => {
1103
- it("returns formatted prompt list", async () => {
1104
- const { handleListPrompts } = await import(
1105
- "../tools/list-prompts.js"
1106
- );
1107
- const result = await handleListPrompts();
1108
-
1109
- expect(result).toContain("greeting-bot");
1110
- expect(result).toContain("Greeting Bot");
1111
- expect(result).toContain("qa-assistant");
1112
- expect(result).toContain("QA Assistant");
1113
- expect(result).toContain("# Prompts (2 total)");
1114
- });
1115
- });
1116
- });
1117
-
1118
- // =====================
1119
- // 9. platform_get_prompt
1120
- // =====================
1121
- describe("platform_get_prompt", () => {
1122
- describe("when prompt exists", () => {
1123
- it("returns formatted prompt details with messages and deployments", async () => {
1124
- const { handleGetPrompt } = await import(
1125
- "../tools/get-prompt.js"
1126
- );
1127
- const result = await handleGetPrompt({
1128
- idOrHandle: "greeting-bot",
1129
- });
1130
-
1131
- expect(result).toContain("# Prompt: Greeting Bot");
1132
- expect(result).toContain("gpt-4o");
1133
- expect(result).toContain("You are a friendly bot.");
1134
- expect(result).toContain("v3");
1135
- expect(result).toContain("## Deployments");
1136
- expect(result).toContain("Updated tone");
1137
- });
1138
- });
1139
- });
1140
-
1141
- // =====================
1142
- // 10. platform_update_prompt
1143
- // =====================
1144
- describe("platform_update_prompt", () => {
1145
- describe("when updating a prompt", () => {
1146
- it("returns success message", async () => {
1147
- const { handleUpdatePrompt } = await import(
1148
- "../tools/update-prompt.js"
1149
- );
1150
- const result = await handleUpdatePrompt({
1151
- idOrHandle: "greeting-bot",
1152
- model: "openai/gpt-4o-mini",
1153
- commitMessage: "Switch to mini",
1154
- });
1155
-
1156
- expect(result).toContain("updated successfully");
1157
- expect(result).toContain("Switch to mini");
1158
- expect(result).toContain("**Version**: v4");
1159
- expect(result).toContain("**Version ID**: ver_p1v4");
1160
- });
1161
- });
1162
-
1163
- describe("when an update supplies only messages and a commit message", () => {
1164
- /** @scenario Carrying forward prior fields when an update supplies only messages and a commit message */
1165
- it("does not send fields that would wipe prior prompt configuration", async () => {
1166
- const { handleUpdatePrompt } = await import(
1167
- "../tools/update-prompt.js"
1168
- );
1169
- await handleUpdatePrompt({
1170
- idOrHandle: "greeting-bot",
1171
- messages: [{ role: "system", content: "You are a helpful bot." }],
1172
- commitMessage: "Only messages changed",
1173
- });
1174
-
1175
- const req = lastRequests["PUT /api/prompts/greeting-bot"];
1176
- expect(req).toBeDefined();
1177
- const parsed = JSON.parse(req!.body);
1178
- expect(parsed).not.toHaveProperty("parameters");
1179
- expect(parsed).not.toHaveProperty("inputs");
1180
- expect(parsed).not.toHaveProperty("outputs");
1181
- expect(parsed).not.toHaveProperty("temperature");
1182
- expect(parsed).not.toHaveProperty("responseFormat");
1183
- });
1184
- });
1185
-
1186
- describe("when an update omits tags", () => {
1187
- /** @scenario Tag-to-version mapping stays unchanged when an update omits tags */
1188
- it("does not send a tags field in the update request", async () => {
1189
- const { handleUpdatePrompt } = await import(
1190
- "../tools/update-prompt.js"
1191
- );
1192
- await handleUpdatePrompt({
1193
- idOrHandle: "greeting-bot",
1194
- model: "openai/gpt-4o",
1195
- commitMessage: "No tag change requested",
1196
- });
1197
-
1198
- const req = lastRequests["PUT /api/prompts/greeting-bot"];
1199
- expect(req).toBeDefined();
1200
- const parsed = JSON.parse(req!.body);
1201
- expect(parsed).not.toHaveProperty("tags");
1202
- });
1203
- });
1204
-
1205
- describe("when an update passes tags explicitly", () => {
1206
- /** @scenario Passing tags explicitly moves the tag to the new version */
1207
- it("sends the requested tags in the update request", async () => {
1208
- const { handleUpdatePrompt } = await import(
1209
- "../tools/update-prompt.js"
1210
- );
1211
- await handleUpdatePrompt({
1212
- idOrHandle: "greeting-bot",
1213
- model: "openai/gpt-4o",
1214
- commitMessage: "Deploy new version to production",
1215
- tags: ["production"],
1216
- });
1217
-
1218
- const req = lastRequests["PUT /api/prompts/greeting-bot"];
1219
- expect(req).toBeDefined();
1220
- const parsed = JSON.parse(req!.body);
1221
- expect(parsed.tags).toEqual(["production"]);
1222
- });
1223
- });
1224
- });
1225
-
1226
- // =====================
1227
- // 11. platform_create_scenario
1228
- // =====================
1229
- describe("platform_create_scenario", () => {
1230
- describe("when valid data is provided", () => {
1231
- it("returns confirmation with new scenario ID", async () => {
1232
- const { handleCreateScenario } = await import(
1233
- "../tools/create-scenario.js"
1234
- );
1235
- const result = await handleCreateScenario({
1236
- name: "New Scenario",
1237
- situation: "User does something",
1238
- criteria: ["Agent responds correctly"],
1239
- labels: ["test"],
1240
- });
1241
-
1242
- expect(result).toContain("created successfully");
1243
- expect(result).toContain("scen_new789");
1244
- });
1245
- });
1246
- });
1247
-
1248
- // =====================
1249
- // 12. platform_list_scenarios
1250
- // =====================
1251
- describe("platform_list_scenarios", () => {
1252
- describe("when scenarios exist", () => {
1253
- it("returns formatted scenario list", async () => {
1254
- const { handleListScenarios } = await import(
1255
- "../tools/list-scenarios.js"
1256
- );
1257
- const result = await handleListScenarios({});
1258
-
1259
- expect(result).toContain("# Scenarios (2 total)");
1260
- expect(result).toContain("Login Flow Happy Path");
1261
- expect(result).toContain("Password Reset");
1262
- });
1263
- });
1264
-
1265
- describe("when format is json", () => {
1266
- it("returns parseable JSON matching API response", async () => {
1267
- const { handleListScenarios } = await import(
1268
- "../tools/list-scenarios.js"
1269
- );
1270
- const result = await handleListScenarios({ format: "json" });
1271
-
1272
- expect(JSON.parse(result)).toEqual(CANNED_SCENARIOS_LIST);
1273
- });
1274
- });
1275
- });
1276
-
1277
- // =====================
1278
- // 13. platform_get_scenario
1279
- // =====================
1280
- describe("platform_get_scenario", () => {
1281
- describe("when the scenario exists", () => {
1282
- it("returns formatted scenario details", async () => {
1283
- const { handleGetScenario } = await import(
1284
- "../tools/get-scenario.js"
1285
- );
1286
- const result = await handleGetScenario({
1287
- scenarioId: "scen_abc123",
1288
- });
1289
-
1290
- expect(result).toContain("# Scenario: Login Flow Happy Path");
1291
- expect(result).toContain("User attempts to log in");
1292
- expect(result).toContain("Responds with a welcome message");
1293
- });
1294
- });
1295
-
1296
- describe("when the scenario does not exist", () => {
1297
- it("propagates the 404 error", async () => {
1298
- const { handleGetScenario } = await import(
1299
- "../tools/get-scenario.js"
1300
- );
1301
-
1302
- await expect(
1303
- handleGetScenario({ scenarioId: "scen_nonexistent" }),
1304
- ).rejects.toThrow("404");
1305
- });
1306
- });
1307
-
1308
- describe("when format is json", () => {
1309
- it("returns parseable JSON", async () => {
1310
- const { handleGetScenario } = await import(
1311
- "../tools/get-scenario.js"
1312
- );
1313
- const result = await handleGetScenario({
1314
- scenarioId: "scen_abc123",
1315
- format: "json",
1316
- });
1317
-
1318
- expect(JSON.parse(result)).toEqual(CANNED_SCENARIO_DETAIL);
1319
- });
1320
- });
1321
- });
1322
-
1323
- // =====================
1324
- // 14. platform_update_scenario
1325
- // =====================
1326
- describe("platform_update_scenario", () => {
1327
- describe("when the scenario exists", () => {
1328
- it("returns update confirmation with updated details", async () => {
1329
- const { handleUpdateScenario } = await import(
1330
- "../tools/update-scenario.js"
1331
- );
1332
- const result = await handleUpdateScenario({
1333
- scenarioId: "scen_abc123",
1334
- name: "Login Flow - Updated",
1335
- });
1336
-
1337
- expect(result).toContain("updated successfully");
1338
- expect(result).toContain("scen_abc123");
1339
- });
1340
- });
1341
- });
1342
-
1343
- // =====================
1344
- // 15. platform_archive_scenario
1345
- // =====================
1346
- describe("platform_archive_scenario", () => {
1347
- describe("when the scenario exists", () => {
1348
- it("returns confirmation that scenario was archived", async () => {
1349
- const { handleArchiveScenario } = await import(
1350
- "../tools/archive-scenario.js"
1351
- );
1352
- const result = await handleArchiveScenario({
1353
- scenarioId: "scen_abc123",
1354
- });
1355
-
1356
- expect(result).toContain("archived successfully");
1357
- expect(result).toContain("scen_abc123");
1358
- expect(result).toContain("archived");
1359
- });
1360
- });
1361
- });
1362
-
1363
- // =====================
1364
- // 16. platform_create_evaluator
1365
- // =====================
1366
- describe("platform_create_evaluator", () => {
1367
- describe("when valid data is provided", () => {
1368
- it("returns success confirmation with evaluator details", async () => {
1369
- const { handleCreateEvaluator } = await import(
1370
- "../tools/create-evaluator.js"
1371
- );
1372
- const result = await handleCreateEvaluator({
1373
- name: "My LLM Judge",
1374
- config: { evaluatorType: "langevals/llm_boolean" },
1375
- });
1376
-
1377
- expect(result).toContain("Evaluator created successfully!");
1378
- expect(result).toContain("evaluator_new123");
1379
- expect(result).toContain("my-llm-judge");
1380
- expect(result).toContain("langevals/llm_boolean");
1381
- });
1382
- });
1383
- });
1384
-
1385
- // =====================
1386
- // 17. platform_list_evaluators
1387
- // =====================
1388
- describe("platform_list_evaluators", () => {
1389
- describe("when evaluators exist", () => {
1390
- it("returns formatted evaluator list", async () => {
1391
- const { handleListEvaluators } = await import(
1392
- "../tools/list-evaluators.js"
1393
- );
1394
- const result = await handleListEvaluators();
1395
-
1396
- expect(result).toContain("# Evaluators (2 total)");
1397
- expect(result).toContain("Toxicity Check");
1398
- expect(result).toContain("toxicity-check");
1399
- expect(result).toContain("openai/moderation");
1400
- expect(result).toContain("Exact Match");
1401
- expect(result).toContain("exact-match");
1402
- });
1403
- });
1404
- });
1405
-
1406
- // =====================
1407
- // 18. platform_get_evaluator
1408
- // =====================
1409
- describe("platform_get_evaluator", () => {
1410
- describe("when the evaluator exists", () => {
1411
- it("returns formatted evaluator details with config and fields", async () => {
1412
- const { handleGetEvaluator } = await import(
1413
- "../tools/get-evaluator.js"
1414
- );
1415
- const result = await handleGetEvaluator({
1416
- idOrSlug: "evaluator_abc123",
1417
- });
1418
-
1419
- expect(result).toContain("# Evaluator: Toxicity Check");
1420
- expect(result).toContain("openai/moderation");
1421
- expect(result).toContain("text-moderation-stable");
1422
- expect(result).toContain("## Input Fields");
1423
- expect(result).toContain("**input** (str)");
1424
- expect(result).toContain("## Output Fields");
1425
- expect(result).toContain("**passed** (bool)");
1426
- expect(result).toContain("**score** (float)");
1427
- });
1428
- });
1429
-
1430
- describe("when the evaluator does not exist", () => {
1431
- it("propagates the 404 error", async () => {
1432
- const { handleGetEvaluator } = await import(
1433
- "../tools/get-evaluator.js"
1434
- );
1435
-
1436
- await expect(
1437
- handleGetEvaluator({ idOrSlug: "evaluator_nonexistent" }),
1438
- ).rejects.toThrow("404");
1439
- });
1440
- });
1441
- });
1442
-
1443
- // =====================
1444
- // 19. platform_update_evaluator
1445
- // =====================
1446
- describe("platform_update_evaluator", () => {
1447
- describe("when the evaluator exists", () => {
1448
- it("returns update confirmation", async () => {
1449
- const { handleUpdateEvaluator } = await import(
1450
- "../tools/update-evaluator.js"
1451
- );
1452
- const result = await handleUpdateEvaluator({
1453
- evaluatorId: "evaluator_abc123",
1454
- name: "Updated Toxicity",
1455
- });
1456
-
1457
- expect(result).toContain("Evaluator updated successfully!");
1458
- expect(result).toContain("evaluator_abc123");
1459
- expect(result).toContain("Updated Toxicity");
1460
- });
1461
- });
1462
- });
1463
-
1464
- // =====================
1465
- // 20. platform_set_model_provider
1466
- // =====================
1467
- describe("platform_set_model_provider", () => {
1468
- describe("when setting a provider with API key", () => {
1469
- it("returns success confirmation with provider details", async () => {
1470
- const { handleSetModelProvider } = await import(
1471
- "../tools/set-model-provider.js"
1472
- );
1473
- const result = await handleSetModelProvider({
1474
- provider: "openai",
1475
- enabled: true,
1476
- customKeys: { OPENAI_API_KEY: "sk-test123" },
1477
- });
1478
-
1479
- expect(result).toContain("Model provider updated successfully!");
1480
- expect(result).toContain("**Provider**: openai");
1481
- expect(result).toContain("**Status**: enabled");
1482
- expect(result).toContain("OPENAI_API_KEY: set");
1483
- });
1484
- });
1485
-
1486
- describe("when setting a default model", () => {
1487
- it("shows the normalized model name with provider prefix", async () => {
1488
- const { handleSetModelProvider } = await import(
1489
- "../tools/set-model-provider.js"
1490
- );
1491
- const result = await handleSetModelProvider({
1492
- provider: "openai",
1493
- enabled: true,
1494
- defaultModel: "gpt-4o",
1495
- });
1496
-
1497
- expect(result).toContain("**Default Model**: openai/gpt-4o");
1498
- });
1499
- });
1500
- });
1501
-
1502
- // =====================
1503
- // 21. platform_list_model_providers
1504
- // =====================
1505
- describe("platform_list_model_providers", () => {
1506
- describe("when providers exist", () => {
1507
- it("returns formatted provider list with status and key info", async () => {
1508
- const { handleListModelProviders } = await import(
1509
- "../tools/list-model-providers.js"
1510
- );
1511
- const result = await handleListModelProviders();
1512
-
1513
- expect(result).toContain("# Model Providers (2 total)");
1514
- expect(result).toContain("## openai");
1515
- expect(result).toContain("**Status**: enabled");
1516
- expect(result).toContain("## anthropic");
1517
- expect(result).toContain("**Status**: disabled");
1518
- expect(result).toContain("OPENAI_API_KEY: set");
1519
- expect(result).toContain("2 available");
1520
- });
1521
- });
1522
- });
1523
-
1524
- // =====================
1525
- // Cross-cutting: authentication
1526
- // =====================
1527
- describe("when API key is invalid", () => {
1528
- afterAll(() => {
1529
- initConfig({
1530
- apiKey: "test-integration-key",
1531
- endpoint: `http://localhost:${port}`,
1532
- });
1533
- });
1534
-
1535
- it("throws an error with 401 status for trace search", async () => {
1536
- initConfig({
1537
- apiKey: "bad-key",
1538
- endpoint: `http://localhost:${port}`,
1539
- });
1540
-
1541
- const { handleSearchTraces } = await import(
1542
- "../tools/search-traces.js"
1543
- );
1544
- await expect(
1545
- handleSearchTraces({ startDate: "24h" }),
1546
- ).rejects.toThrow("401");
1547
- });
1548
-
1549
- it("throws an error with 401 status for evaluator list", async () => {
1550
- initConfig({
1551
- apiKey: "bad-key",
1552
- endpoint: `http://localhost:${port}`,
1553
- });
1554
-
1555
- const { handleListEvaluators } = await import(
1556
- "../tools/list-evaluators.js"
1557
- );
1558
- await expect(handleListEvaluators()).rejects.toThrow("401");
1559
- });
1560
-
1561
- it("throws an error with 401 status for model providers list", async () => {
1562
- initConfig({
1563
- apiKey: "bad-key",
1564
- endpoint: `http://localhost:${port}`,
1565
- });
1566
-
1567
- const { handleListModelProviders } = await import(
1568
- "../tools/list-model-providers.js"
1569
- );
1570
- await expect(handleListModelProviders()).rejects.toThrow("401");
1571
- });
1572
- });
1573
-
1574
- // =====================
1575
- // Run Plan Tools
1576
- // =====================
1577
- describe("platform_run_plan", () => {
1578
- it("runs a configuration and reports the plan it created", async () => {
1579
- const { handleRunPlan } = await import("../tools/run-plan.js");
1580
- const result = await handleRunPlan({
1581
- name: "Regression Plan",
1582
- scope: { mode: "labels", labels: ["auth"] },
1583
- targets: [{ type: "http", referenceId: "agent_abc" }],
1584
- repeatCount: 2,
1585
- note: "nightly regression",
1586
- });
1587
-
1588
- expect(result).toContain("Regression Plan");
1589
- expect(result).toContain("created and started");
1590
- expect(result).toContain("batch_123");
1591
- expect(result).toContain("**Jobs**: 2");
1592
- });
1593
- });
1594
-
1595
- describe("platform_list_run_plans", () => {
1596
- it("returns formatted run plan list", async () => {
1597
- const { handleListRunPlans } = await import("../tools/list-run-plans.js");
1598
- const result = await handleListRunPlans({});
1599
-
1600
- expect(result).toContain("Run Plans (1 total)");
1601
- expect(result).toContain("Regression Plan");
1602
- });
1603
- });
1604
-
1605
- describe("platform_get_run_plan", () => {
1606
- it("returns the plan configuration", async () => {
1607
- const { handleGetRunPlan } = await import("../tools/get-run-plan.js");
1608
- const result = await handleGetRunPlan({ id: "plan_abc" });
1609
-
1610
- expect(result).toContain("Regression Plan");
1611
- expect(result).toContain("plan_abc");
1612
- expect(result).toContain("labels: auth");
1613
- });
1614
- });
1615
-
1616
- describe("platform_rerun_run_plan", () => {
1617
- it("runs the stored configuration again and reports it joined the plan", async () => {
1618
- const { handleRerunRunPlan } = await import(
1619
- "../tools/rerun-run-plan.js"
1620
- );
1621
- const result = await handleRerunRunPlan({ id: "plan_abc" });
1622
-
1623
- expect(result).toContain("Regression Plan");
1624
- expect(result).toContain("configuration of this run");
1625
- expect(result).toContain("batch_123");
1626
- });
1627
- });
1628
-
1629
- describe("platform_archive_run_plan", () => {
1630
- it("archives the run plan", async () => {
1631
- const { handleArchiveRunPlan } = await import(
1632
- "../tools/archive-run-plan.js"
1633
- );
1634
- const result = await handleArchiveRunPlan({ id: "plan_abc" });
1635
-
1636
- expect(result).toContain("archived");
1637
- expect(result).toContain("plan_abc");
1638
- });
1639
- });
1640
-
1641
- // =====================
1642
- // Test Suite Tools
1643
- // =====================
1644
- describe("platform_list_test_suites", () => {
1645
- it("returns formatted test suite list", async () => {
1646
- const { handleListTestSuites } = await import(
1647
- "../tools/list-test-suites.js"
1648
- );
1649
- const result = await handleListTestSuites({});
1650
-
1651
- expect(result).toContain("Test Suites (1 total)");
1652
- expect(result).toContain("Checkout");
1653
- });
1654
- });
1655
-
1656
- describe("platform_create_test_suite", () => {
1657
- it("creates a test suite and returns confirmation", async () => {
1658
- const { handleCreateTestSuite } = await import(
1659
- "../tools/create-test-suite.js"
1660
- );
1661
- const result = await handleCreateTestSuite({ name: "New Suite" });
1662
-
1663
- expect(result).toContain("created");
1664
- expect(result).toContain("New Suite");
1665
- expect(result).toContain("suite_new");
1666
- });
1667
- });
1668
-
1669
- describe("platform_get_test_suite", () => {
1670
- it("returns the suite with the scenarios filed in it", async () => {
1671
- const { handleGetTestSuite } = await import(
1672
- "../tools/get-test-suite.js"
1673
- );
1674
- const result = await handleGetTestSuite({ id: "suite_abc" });
1675
-
1676
- expect(result).toContain("Checkout");
1677
- expect(result).toContain("Login Flow Happy Path");
1678
- });
1679
- });
1680
-
1681
- describe("platform_rename_test_suite", () => {
1682
- it("renames the test suite", async () => {
1683
- const { handleRenameTestSuite } = await import(
1684
- "../tools/rename-test-suite.js"
1685
- );
1686
- const result = await handleRenameTestSuite({
1687
- id: "suite_abc",
1688
- name: "Checkout v2",
1689
- });
1690
-
1691
- expect(result).toContain("Checkout v2");
1692
- });
1693
- });
1694
-
1695
- describe("platform_archive_test_suite", () => {
1696
- it("archives the suite and says the filed scenarios went with it", async () => {
1697
- const { handleArchiveTestSuite } = await import(
1698
- "../tools/archive-test-suite.js"
1699
- );
1700
- const result = await handleArchiveTestSuite({ id: "suite_abc" });
1701
-
1702
- expect(result).toContain("archived");
1703
- expect(result).toContain("scenarios filed in it");
1704
- });
1705
- });
1706
-
1707
- describe("platform_run_test_suite", () => {
1708
- it("runs the suite against a target and reports the derived plan", async () => {
1709
- const { handleRunTestSuite } = await import(
1710
- "../tools/run-test-suite.js"
1711
- );
1712
- const result = await handleRunTestSuite({
1713
- id: "suite_abc",
1714
- targets: [{ type: "http", referenceId: "agent_abc" }],
1715
- });
1716
-
1717
- expect(result).toContain("Regression Plan");
1718
- expect(result).toContain("batch_123");
1719
- });
1720
- });
1721
-
1722
- // =====================
1723
- // Simulation Run Tools
1724
- // =====================
1725
- describe("platform_list_simulation_runs", () => {
1726
- it("returns formatted run list", async () => {
1727
- const { handleListSimulationRuns } = await import("../tools/list-simulation-runs.js");
1728
- const result = await handleListSimulationRuns({});
1729
-
1730
- expect(result).toContain("Simulation Runs");
1731
- expect(result).toContain("Login Flow");
1732
- });
1733
- });
1734
-
1735
- describe("platform_get_simulation_run", () => {
1736
- it("returns run details with conversation", async () => {
1737
- const { handleGetSimulationRun } = await import("../tools/get-simulation-run.js");
1738
- const result = await handleGetSimulationRun({ scenarioRunId: "run_abc" });
1739
-
1740
- expect(result).toContain("Login Flow");
1741
- expect(result).toContain("passed");
1742
- expect(result).toContain("Hello");
1743
- });
1744
- });
1745
-
1746
- // =====================
1747
- // Monitor Tools
1748
- // =====================
1749
- describe("platform_list_monitors", () => {
1750
- it("returns formatted monitor list", async () => {
1751
- const { listMonitors } = await import("../langwatch-api-monitors.js");
1752
- const monitors = await listMonitors();
1753
- expect(monitors).toHaveLength(1);
1754
- expect(monitors[0]!.name).toBe("Toxicity Check");
1755
- expect(monitors[0]!.checkType).toBe("ragas/toxicity");
1756
- });
1757
- });
1758
-
1759
- describe("platform_get_monitor", () => {
1760
- it("returns monitor details", async () => {
1761
- const { getMonitor } = await import("../langwatch-api-monitors.js");
1762
- const monitor = await getMonitor("mon_abc");
1763
- expect(monitor.id).toBe("mon_abc");
1764
- expect(monitor.name).toBe("Toxicity Check");
1765
- expect(monitor.enabled).toBe(true);
1766
- });
1767
- });
1768
-
1769
- describe("platform_create_monitor", () => {
1770
- it("creates a monitor and returns metadata", async () => {
1771
- const { createMonitor } = await import("../langwatch-api-monitors.js");
1772
- const monitor = await createMonitor({ name: "New Monitor", checkType: "ragas/toxicity" });
1773
- expect(monitor.id).toBe("mon_new");
1774
- expect(monitor.name).toBe("New Monitor");
1775
- });
1776
- });
1777
-
1778
- describe("platform_update_monitor", () => {
1779
- it("updates a monitor", async () => {
1780
- const { updateMonitor } = await import("../langwatch-api-monitors.js");
1781
- const monitor = await updateMonitor({ id: "mon_abc", enabled: false });
1782
- expect(monitor.id).toBe("mon_abc");
1783
- expect(monitor.enabled).toBe(false);
1784
- });
1785
- });
1786
-
1787
- describe("platform_delete_monitor", () => {
1788
- it("deletes a monitor", async () => {
1789
- const { deleteMonitor } = await import("../langwatch-api-monitors.js");
1790
- const result = await deleteMonitor("mon_abc");
1791
- expect(result.id).toBe("mon_abc");
1792
- expect(result.deleted).toBe(true);
1793
- });
1794
- });
1795
-
1796
- // =====================
1797
- // Secret Tools
1798
- // =====================
1799
- describe("platform_list_secrets", () => {
1800
- it("returns formatted secret list", async () => {
1801
- const { listSecrets } = await import("../langwatch-api-secrets.js");
1802
- const secrets = await listSecrets();
1803
- expect(secrets).toHaveLength(2);
1804
- expect(secrets[0]!.name).toBe("MY_API_KEY");
1805
- });
1806
- });
1807
-
1808
- describe("platform_create_secret", () => {
1809
- it("creates a secret and returns metadata", async () => {
1810
- const { createSecret } = await import("../langwatch-api-secrets.js");
1811
- const secret = await createSecret({ name: "NEW_SECRET", value: "sk-123" });
1812
- expect(secret.id).toBe("secret_new");
1813
- expect(secret.name).toBe("NEW_SECRET");
1814
- });
1815
- });
1816
-
1817
- describe("platform_update_secret", () => {
1818
- it("updates a secret value", async () => {
1819
- const { updateSecret } = await import("../langwatch-api-secrets.js");
1820
- const secret = await updateSecret({ id: "secret_abc", value: "new-val" });
1821
- expect(secret.id).toBe("secret_abc");
1822
- expect(secret.name).toBe("MY_API_KEY");
1823
- });
1824
- });
1825
-
1826
- describe("platform_delete_secret", () => {
1827
- it("deletes a secret", async () => {
1828
- const { deleteSecret } = await import("../langwatch-api-secrets.js");
1829
- const result = await deleteSecret("secret_abc");
1830
- expect(result.id).toBe("secret_abc");
1831
- expect(result.deleted).toBe(true);
1832
- });
1833
- });
1834
-
1835
- // =====================
1836
- // Agent Tools
1837
- // =====================
1838
- describe("platform_get_agent", () => {
1839
- it("returns one agent by id", async () => {
1840
- const agent = await getAgent("agent_abc");
1841
- expect(agent.id).toBe("agent_abc");
1842
- expect(agent.name).toBe("Test Agent");
1843
- });
1844
- });
1845
-
1846
- describe("platform_update_agent", () => {
1847
- it("updates an agent and returns it", async () => {
1848
- const agent = await updateAgent({ id: "agent_abc", name: "Test Agent" });
1849
- expect(agent.id).toBe("agent_abc");
1850
- });
1851
- });
1852
-
1853
- describe("platform_delete_agent", () => {
1854
- it("deletes an agent", async () => {
1855
- const result = await deleteAgent("agent_abc");
1856
- expect(result.id).toBe("agent_abc");
1857
- });
1858
- });
1859
-
1860
- // =====================
1861
- // Agent Run Tool
1862
- // =====================
1863
- describe("platform_run_workflow", () => {
1864
- it("executes a workflow and returns result", async () => {
1865
- const { handleRunWorkflow } = await import("../tools/run-workflow.js");
1866
- const result = await handleRunWorkflow({ id: "wf_abc" });
1867
-
1868
- expect(result).toContain("executed successfully");
1869
- expect(result).toContain("workflow result");
1870
- });
1871
- });
1872
- });