@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,318 +0,0 @@
1
- import { afterAll, beforeAll, describe, expect, it } from "vitest";
2
- import { execSync } from "child_process";
3
-
4
- const IMAGE_NAME = "langwatch-mcp-server-test";
5
- const CONTAINER_NAME = "langwatch-mcp-server-test-container";
6
- const HOST_PORT = 13099; // unlikely to conflict
7
-
8
- /**
9
- * HTTP mode verifies the bearer against the LangWatch API before creating a
10
- * session, so the authenticated paths need a key the API actually accepts.
11
- * Without one, this file still covers startup, CORS, and rejection.
12
- */
13
- const BEARER_TOKEN = process.env.LANGWATCH_API_KEY;
14
-
15
- /** A syntactically fine token that the LangWatch API does not know. */
16
- const BOGUS_TOKEN = "totally-not-a-real-key";
17
-
18
- /** Standard headers required by the MCP Streamable HTTP protocol */
19
- const MCP_POST_HEADERS = {
20
- "Content-Type": "application/json",
21
- Accept: "application/json, text/event-stream",
22
- };
23
-
24
- describe("Docker container", () => {
25
- let containerRunning = false;
26
-
27
- beforeAll(async () => {
28
- try {
29
- // Build from repo root (mcp-server needs langevals/ for build)
30
- const repoRoot = process.cwd().replace(/\/mcp-server.*$/, "");
31
- execSync(
32
- `docker build -t ${IMAGE_NAME} -f mcp/typescript/Dockerfile .`,
33
- {
34
- cwd: repoRoot,
35
- stdio: "pipe",
36
- timeout: 180_000,
37
- }
38
- );
39
-
40
- // Stop any previous container
41
- execSync(`docker rm -f ${CONTAINER_NAME} 2>/dev/null || true`, {
42
- stdio: "pipe",
43
- });
44
-
45
- // Start the container WITHOUT LANGWATCH_API_KEY -- clients bring their own
46
- execSync(
47
- `docker run -d --name ${CONTAINER_NAME} -p ${HOST_PORT}:3000 ${IMAGE_NAME}`,
48
- { stdio: "pipe" }
49
- );
50
-
51
- // Wait for the server to be ready
52
- let retries = 0;
53
- while (retries < 20) {
54
- try {
55
- const res = await fetch(`http://localhost:${HOST_PORT}/health`);
56
- if (res.ok) {
57
- containerRunning = true;
58
- break;
59
- }
60
- } catch {
61
- // not ready yet
62
- }
63
- await new Promise((r) => setTimeout(r, 500));
64
- retries++;
65
- }
66
-
67
- if (!containerRunning) {
68
- const logs = execSync(`docker logs ${CONTAINER_NAME}`, {
69
- encoding: "utf8",
70
- });
71
- throw new Error(`Container failed to start. Logs:\n${logs}`);
72
- }
73
- } catch (error) {
74
- console.error("Docker setup failed:", error);
75
- // Clean up on failure
76
- execSync(`docker rm -f ${CONTAINER_NAME} 2>/dev/null || true`, {
77
- stdio: "pipe",
78
- });
79
- }
80
- }, 180_000); // 3 min for build + start
81
-
82
- afterAll(() => {
83
- execSync(`docker rm -f ${CONTAINER_NAME} 2>/dev/null || true`, {
84
- stdio: "pipe",
85
- });
86
- });
87
-
88
- it("health endpoint responds without authentication", async () => {
89
- if (!containerRunning) return;
90
-
91
- const res = await fetch(`http://localhost:${HOST_PORT}/health`);
92
- expect(res.status).toBe(200);
93
- const body = await res.json();
94
- expect(body.status).toBe("ok");
95
- });
96
-
97
- it("does not answer with a wildcard CORS origin", async () => {
98
- if (!containerRunning) return;
99
-
100
- const res = await fetch(`http://localhost:${HOST_PORT}/health`);
101
- expect(res.headers.get("access-control-allow-origin")).not.toBe("*");
102
- });
103
-
104
- it("reflects an allowed origin and rejects an unlisted one", async () => {
105
- if (!containerRunning) return;
106
-
107
- const allowed = await fetch(`http://localhost:${HOST_PORT}/health`, {
108
- headers: { Origin: "http://localhost:5173" },
109
- });
110
- expect(allowed.status).toBe(200);
111
- expect(allowed.headers.get("access-control-allow-origin")).toBe(
112
- "http://localhost:5173"
113
- );
114
-
115
- const rejected = await fetch(`http://localhost:${HOST_PORT}/health`, {
116
- headers: { Origin: "https://attacker.example" },
117
- });
118
- expect(rejected.status).toBe(403);
119
- });
120
-
121
- it("rejects a bearer the LangWatch API does not recognise", async () => {
122
- if (!containerRunning) return;
123
-
124
- const res = await fetch(`http://localhost:${HOST_PORT}/mcp`, {
125
- method: "POST",
126
- headers: {
127
- ...MCP_POST_HEADERS,
128
- Authorization: `Bearer ${BOGUS_TOKEN}`,
129
- },
130
- body: JSON.stringify({
131
- jsonrpc: "2.0",
132
- id: 1,
133
- method: "initialize",
134
- params: {
135
- protocolVersion: "2025-03-26",
136
- capabilities: {},
137
- clientInfo: { name: "docker-test", version: "1.0.0" },
138
- },
139
- }),
140
- });
141
-
142
- expect(res.status).toBe(401);
143
- expect(res.headers.get("mcp-session-id")).toBeNull();
144
- });
145
-
146
- it("rejects a session id presented without a bearer token", async () => {
147
- if (!containerRunning || !BEARER_TOKEN) return;
148
-
149
- const initRes = await fetch(`http://localhost:${HOST_PORT}/mcp`, {
150
- method: "POST",
151
- headers: {
152
- ...MCP_POST_HEADERS,
153
- Authorization: `Bearer ${BEARER_TOKEN}`,
154
- },
155
- body: JSON.stringify({
156
- jsonrpc: "2.0",
157
- id: 1,
158
- method: "initialize",
159
- params: {
160
- protocolVersion: "2025-03-26",
161
- capabilities: {},
162
- clientInfo: { name: "docker-test", version: "1.0.0" },
163
- },
164
- }),
165
- });
166
- const sessionId = initRes.headers.get("mcp-session-id");
167
- expect(sessionId).toBeTruthy();
168
- await initRes.text();
169
-
170
- const res = await fetch(`http://localhost:${HOST_PORT}/mcp`, {
171
- method: "POST",
172
- headers: { ...MCP_POST_HEADERS, "mcp-session-id": sessionId! },
173
- body: JSON.stringify({ jsonrpc: "2.0", id: 2, method: "tools/list" }),
174
- });
175
-
176
- expect(res.status).toBe(401);
177
- });
178
-
179
- it("returns 401 on initialize without Bearer token", async () => {
180
- if (!containerRunning) return;
181
-
182
- const res = await fetch(`http://localhost:${HOST_PORT}/mcp`, {
183
- method: "POST",
184
- headers: MCP_POST_HEADERS,
185
- body: JSON.stringify({
186
- jsonrpc: "2.0",
187
- id: 1,
188
- method: "initialize",
189
- params: {
190
- protocolVersion: "2025-03-26",
191
- capabilities: {},
192
- clientInfo: { name: "docker-test", version: "1.0.0" },
193
- },
194
- }),
195
- });
196
-
197
- expect(res.status).toBe(401);
198
- const body = await res.json();
199
- expect(body.error).toContain("Authorization");
200
- });
201
-
202
- it("MCP initialize works with Bearer token", async () => {
203
- if (!containerRunning || !BEARER_TOKEN) return;
204
-
205
- const res = await fetch(`http://localhost:${HOST_PORT}/mcp`, {
206
- method: "POST",
207
- headers: {
208
- ...MCP_POST_HEADERS,
209
- Authorization: `Bearer ${BEARER_TOKEN}`,
210
- },
211
- body: JSON.stringify({
212
- jsonrpc: "2.0",
213
- id: 1,
214
- method: "initialize",
215
- params: {
216
- protocolVersion: "2025-03-26",
217
- capabilities: {},
218
- clientInfo: { name: "docker-test", version: "1.0.0" },
219
- },
220
- }),
221
- });
222
-
223
- expect(res.status).toBe(200);
224
- const sessionId = res.headers.get("mcp-session-id");
225
- expect(sessionId).toBeTruthy();
226
-
227
- // Parse SSE response for the initialize result
228
- const text = await res.text();
229
- expect(text).toContain("serverInfo");
230
- });
231
-
232
- it("lists tools after initialization with Bearer token", async () => {
233
- if (!containerRunning || !BEARER_TOKEN) return;
234
-
235
- // Initialize a session
236
- const initRes = await fetch(`http://localhost:${HOST_PORT}/mcp`, {
237
- method: "POST",
238
- headers: {
239
- ...MCP_POST_HEADERS,
240
- Authorization: `Bearer ${BEARER_TOKEN}`,
241
- },
242
- body: JSON.stringify({
243
- jsonrpc: "2.0",
244
- id: 1,
245
- method: "initialize",
246
- params: {
247
- protocolVersion: "2025-03-26",
248
- capabilities: {},
249
- clientInfo: { name: "docker-test", version: "1.0.0" },
250
- },
251
- }),
252
- });
253
-
254
- const sessionId = initRes.headers.get("mcp-session-id");
255
-
256
- // Send initialized notification
257
- await fetch(`http://localhost:${HOST_PORT}/mcp`, {
258
- method: "POST",
259
- headers: {
260
- ...MCP_POST_HEADERS,
261
- "mcp-session-id": sessionId!,
262
- Authorization: `Bearer ${BEARER_TOKEN}`,
263
- },
264
- body: JSON.stringify({
265
- jsonrpc: "2.0",
266
- method: "notifications/initialized",
267
- }),
268
- });
269
-
270
- // List tools
271
- const toolsRes = await fetch(`http://localhost:${HOST_PORT}/mcp`, {
272
- method: "POST",
273
- headers: {
274
- ...MCP_POST_HEADERS,
275
- "mcp-session-id": sessionId!,
276
- Authorization: `Bearer ${BEARER_TOKEN}`,
277
- },
278
- body: JSON.stringify({
279
- jsonrpc: "2.0",
280
- id: 2,
281
- method: "tools/list",
282
- }),
283
- });
284
-
285
- expect(toolsRes.status).toBe(200);
286
- const toolsText = await toolsRes.text();
287
- expect(toolsText).toContain("fetch_langwatch_docs");
288
- expect(toolsText).toContain("search_traces");
289
- });
290
-
291
- it("legacy SSE endpoint responds with Bearer token", async () => {
292
- if (!containerRunning || !BEARER_TOKEN) return;
293
-
294
- const controller = new AbortController();
295
- const timeout = setTimeout(() => controller.abort(), 5000);
296
-
297
- try {
298
- const res = await fetch(`http://localhost:${HOST_PORT}/sse`, {
299
- signal: controller.signal,
300
- headers: { Authorization: `Bearer ${BEARER_TOKEN}` },
301
- });
302
- expect(res.status).toBe(200);
303
- expect(res.headers.get("content-type")).toContain("text/event-stream");
304
- } catch (error: any) {
305
- if (error.name !== "AbortError") throw error;
306
- // AbortError is expected -- SSE stays open
307
- } finally {
308
- clearTimeout(timeout);
309
- }
310
- });
311
-
312
- it("legacy SSE endpoint returns 401 without token", async () => {
313
- if (!containerRunning) return;
314
-
315
- const res = await fetch(`http://localhost:${HOST_PORT}/sse`);
316
- expect(res.status).toBe(401);
317
- });
318
- });
@@ -1,182 +0,0 @@
1
- import { createServer, type Server } from "node:http";
2
- import { afterAll, beforeAll, describe, expect, it } from "vitest";
3
-
4
- import { initConfig } from "../config.js";
5
- import { fetchDocumentation, resolveDocumentationUrl } from "../documentation-fetch.js";
6
- import { countingVerifier } from "./support/http-server-harness.js";
7
-
8
- const TEST_API_KEY = "security-regression-test";
9
-
10
- const MCP_HEADERS = {
11
- Accept: "application/json, text/event-stream",
12
- Authorization: `Bearer ${TEST_API_KEY}`,
13
- "Content-Type": "application/json",
14
- };
15
-
16
- describe("MCP documentation fetch security", () => {
17
- describe("URL validation", () => {
18
- /** @scenario A documentation tool accepts its own trusted HTTPS pages */
19
- it.each([
20
- ["langwatch" as const, undefined, "https://langwatch.ai/docs/llms.txt"],
21
- ["scenario" as const, undefined, "https://langwatch.ai/scenario/llms.txt"],
22
- ["langwatch" as const, "observability/tracing", "https://langwatch.ai/docs/observability/tracing.md"],
23
- ["scenario" as const, "/scenario/guides/quickstart", "https://langwatch.ai/scenario/guides/quickstart.md"],
24
- [
25
- "langwatch" as const,
26
- "https://langwatch.ai/docs/llms.txt?format=raw",
27
- "https://langwatch.ai/docs/llms.txt?format=raw",
28
- ],
29
- ])("resolves trusted %s documentation URL %s", (kind, input, expected) => {
30
- expect(resolveDocumentationUrl(kind, input).toString()).toBe(expected);
31
- });
32
-
33
- /** @scenario A documentation tool rejects non-HTTPS and untrusted hosts */
34
- /** @scenario A documentation tool rejects a trusted host outside its namespace */
35
- it.each([
36
- ["langwatch" as const, "http://169.254.169.254/latest/meta-data/"],
37
- ["langwatch" as const, "https://langwatch.ai.attacker.example/docs/x"],
38
- ["langwatch" as const, "https://langwatch.ai@127.0.0.1/docs/x"],
39
- ["langwatch" as const, "https://langwatch.ai:444/docs/x"],
40
- ["langwatch" as const, "file:///etc/passwd"],
41
- ["langwatch" as const, "https://langwatch.ai/scenario/llms.txt"],
42
- ["langwatch" as const, "https://langwatch.ai/docs-evil/page.md"],
43
- ["scenario" as const, "https://langwatch.ai/docs/llms.txt"],
44
- ["scenario" as const, "https://langwatch.ai/scenario-evil/page.md"],
45
- ["langwatch" as const, "https://langwatch.ai/docs/../scenario/llms.txt"],
46
- ])("rejects untrusted %s documentation URL %s", (kind, input) => {
47
- expect(() => resolveDocumentationUrl(kind, input)).toThrow(/trusted LangWatch documentation URL/);
48
- });
49
- });
50
-
51
- /** @scenario Documentation fetches do not follow redirects */
52
- it("disables redirects and bounds trusted documentation fetches", async () => {
53
- let receivedRedirectMode: RequestRedirect | undefined;
54
- let receivedSignal: AbortSignal | null | undefined;
55
- const fetchForTest: typeof fetch = async (_input, init) => {
56
- receivedRedirectMode = init?.redirect;
57
- receivedSignal = init?.signal;
58
- return new Response("# documentation");
59
- };
60
-
61
- const text = await fetchDocumentation("langwatch", "https://langwatch.ai/docs/llms.txt", fetchForTest);
62
-
63
- expect(text).toBe("# documentation");
64
- expect(receivedRedirectMode).toBe("error");
65
- expect(receivedSignal).toBeInstanceOf(AbortSignal);
66
- });
67
-
68
- it("rejects non-document responses from the trusted origin", async () => {
69
- const fetchForTest: typeof fetch = async () =>
70
- new Response("<html>unexpected</html>", {
71
- headers: { "Content-Type": "text/html" },
72
- });
73
-
74
- await expect(fetchDocumentation("langwatch", "https://langwatch.ai/docs/llms.txt", fetchForTest)).rejects.toThrow(
75
- /unexpected content type/
76
- );
77
- });
78
-
79
- describe("HTTP tool transport", () => {
80
- let mcpServer: Server;
81
- let targetServer: Server;
82
- let mcpPort: number;
83
- let targetPort: number;
84
- let sessionId: string;
85
- let targetHits = 0;
86
- let rpcId = 10;
87
-
88
- beforeAll(async () => {
89
- targetServer = createServer((_request, response) => {
90
- targetHits += 1;
91
- response.end("internal-only response");
92
- });
93
- await new Promise<void>((resolve) => {
94
- targetServer.listen(0, "127.0.0.1", resolve);
95
- });
96
- const targetAddress = targetServer.address();
97
- targetPort = typeof targetAddress === "object" && targetAddress ? targetAddress.port : 0;
98
-
99
- initConfig({ endpoint: "https://app.langwatch.ai" });
100
- const { startHttpServer } = await import("../http-server.js");
101
- // These cases are about SSRF in the documentation tools, so the key check
102
- // is stubbed to keep the test on its own subject.
103
- const { verifier } = countingVerifier([TEST_API_KEY]);
104
- const started = await startHttpServer({ port: 0, apiKeyVerifier: verifier });
105
- mcpServer = started.server;
106
- mcpPort = started.port;
107
-
108
- const initializeResponse = await fetch(`http://127.0.0.1:${mcpPort}/mcp`, {
109
- method: "POST",
110
- headers: MCP_HEADERS,
111
- body: JSON.stringify({
112
- jsonrpc: "2.0",
113
- id: 1,
114
- method: "initialize",
115
- params: {
116
- protocolVersion: "2025-03-26",
117
- capabilities: {},
118
- clientInfo: { name: "security-test", version: "1.0.0" },
119
- },
120
- }),
121
- });
122
- sessionId = initializeResponse.headers.get("mcp-session-id") ?? "";
123
- expect(sessionId).not.toBe("");
124
-
125
- await fetch(`http://127.0.0.1:${mcpPort}/mcp`, {
126
- method: "POST",
127
- headers: { ...MCP_HEADERS, "mcp-session-id": sessionId },
128
- body: JSON.stringify({
129
- jsonrpc: "2.0",
130
- method: "notifications/initialized",
131
- }),
132
- });
133
- });
134
-
135
- afterAll(async () => {
136
- await Promise.all([
137
- new Promise<void>((resolve, reject) => {
138
- mcpServer.close((error) => (error ? reject(error) : resolve()));
139
- }),
140
- new Promise<void>((resolve, reject) => {
141
- targetServer.close((error) => (error ? reject(error) : resolve()));
142
- }),
143
- ]);
144
- });
145
-
146
- async function callDocumentationTool(
147
- name: "fetch_langwatch_docs" | "fetch_scenario_docs",
148
- url: string
149
- ): Promise<string> {
150
- const response = await fetch(`http://127.0.0.1:${mcpPort}/mcp`, {
151
- method: "POST",
152
- headers: { ...MCP_HEADERS, "mcp-session-id": sessionId },
153
- body: JSON.stringify({
154
- jsonrpc: "2.0",
155
- id: rpcId++,
156
- method: "tools/call",
157
- params: { name, arguments: { url } },
158
- }),
159
- });
160
-
161
- expect(response.status).toBe(200);
162
- return response.text();
163
- }
164
-
165
- it.each(["fetch_langwatch_docs" as const, "fetch_scenario_docs" as const])(
166
- "blocks %s from reaching a loopback HTTP server",
167
- async (tool) => {
168
- const hitsBefore = targetHits;
169
- const body = await callDocumentationTool(tool, `http://127.0.0.1:${targetPort}/secrets`);
170
-
171
- expect(body).toContain("trusted LangWatch documentation URL");
172
- expect(targetHits).toBe(hitsBefore);
173
- }
174
- );
175
-
176
- it("blocks cross-namespace fetches through the real MCP handler", async () => {
177
- const body = await callDocumentationTool("fetch_langwatch_docs", "https://langwatch.ai/scenario/llms.txt");
178
-
179
- expect(body).toContain("trusted LangWatch documentation URL");
180
- });
181
- });
182
- });