@langwatch/mcp-server 0.7.0 → 0.8.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 (225) hide show
  1. package/.release-please-shim +1 -0
  2. package/CHANGELOG.md +66 -0
  3. package/dist/{archive-scenario-WJ2A7ETN.js → archive-scenario-YKGLJ4MZ.js} +4 -4
  4. package/dist/{archive-suite-4JOM4SSL.js → archive-suite-QFNDO3Y3.js} +4 -4
  5. package/dist/{assign-prompt-tag-GL7EAAF3.js → assign-prompt-tag-Y2B4CBBT.js} +3 -3
  6. package/dist/{chunk-E5JYYU3J.js → chunk-B57MZ4WI.js} +89 -83
  7. package/dist/{chunk-CCWCIJJ3.js → chunk-CPUPGDNA.js} +2 -2
  8. package/dist/{chunk-LUMEZEHL.js → chunk-FBRKHHJV.js} +2 -2
  9. package/dist/{chunk-HYMIWO3U.js → chunk-HDJDFKZR.js} +2 -2
  10. package/dist/{chunk-BWWY5KEF.js → chunk-KOO5733S.js} +3 -2
  11. package/dist/{chunk-EUQDVOSU.js → chunk-KZNBCKWG.js} +2 -2
  12. package/dist/{chunk-IE4M5OGE.js → chunk-L6QENXYA.js} +2 -2
  13. package/dist/{chunk-2GT7IFWK.js → chunk-NACSR6B7.js} +2 -2
  14. package/dist/{chunk-WQG2DFHX.js → chunk-OVQF3D5R.js} +2 -2
  15. package/dist/{chunk-ZVS4UKN2.js → chunk-PO7FM7IX.js} +2 -2
  16. package/dist/{chunk-EGH5RPXM.js → chunk-PZUWCWOS.js} +2 -2
  17. package/dist/{chunk-2WPLXEKF.js → chunk-YH2IBTEL.js} +7 -3
  18. package/dist/{chunk-4AUB2BIA.js → chunk-YI3OG4PI.js} +3 -3
  19. package/dist/config.js +1 -1
  20. package/dist/{create-agent-MBOOT3XK.js → create-agent-YRSDH7P7.js} +4 -4
  21. package/dist/{create-annotation-XMV6A7ED.js → create-annotation-FYQNKMVJ.js} +4 -4
  22. package/dist/{create-dashboard-VRKLFVZJ.js → create-dashboard-CORBR4YQ.js} +4 -4
  23. package/dist/{create-dataset-7HQPZCRE.js → create-dataset-ZC63T6BB.js} +4 -4
  24. package/dist/{create-dataset-records-2NIVFYTV.js → create-dataset-records-ILVNVKVR.js} +4 -4
  25. package/dist/{create-evaluator-WYFRFLRC.js → create-evaluator-2AU3PXFB.js} +4 -4
  26. package/dist/create-mcp-server.js +2 -2
  27. package/dist/{create-prompt-IEN7O6JT.js → create-prompt-NKRJ47YY.js} +3 -3
  28. package/dist/{create-prompt-tag-CAEZ45VO.js → create-prompt-tag-S4JX3ULL.js} +3 -3
  29. package/dist/{create-scenario-6YHSMGLP.js → create-scenario-BPRF75KF.js} +4 -4
  30. package/dist/{create-suite-H5XOFDE5.js → create-suite-4YN7D6K2.js} +4 -4
  31. package/dist/{delete-agent-JKTNBWAA.js → delete-agent-Y72R2YQA.js} +4 -4
  32. package/dist/{delete-annotation-ACMDV6QO.js → delete-annotation-UHWDMHSH.js} +4 -4
  33. package/dist/{delete-dashboard-VJSR7CRU.js → delete-dashboard-LPWT36KN.js} +4 -4
  34. package/dist/{delete-dataset-RTTTWP4D.js → delete-dataset-GGPQRHKX.js} +4 -4
  35. package/dist/{delete-dataset-records-KNQTIPQK.js → delete-dataset-records-LTG5LT26.js} +4 -4
  36. package/dist/{delete-evaluator-7FVC5WM4.js → delete-evaluator-F7QLXDHU.js} +4 -4
  37. package/dist/{delete-prompt-tag-ZMZUAGFW.js → delete-prompt-tag-B5UKZL45.js} +3 -3
  38. package/dist/{delete-workflow-EYQ3T4RQ.js → delete-workflow-4DGUDIDE.js} +4 -4
  39. package/dist/{get-agent-E3QSTVKG.js → get-agent-YEVICUX4.js} +4 -4
  40. package/dist/{get-analytics-2OHV763I.js → get-analytics-3W5CMU3F.js} +3 -3
  41. package/dist/{get-dashboard-PC7JDHH6.js → get-dashboard-NCQHN6MW.js} +4 -4
  42. package/dist/{get-dataset-H5IK5FVU.js → get-dataset-KBK6PH36.js} +4 -4
  43. package/dist/{get-evaluator-UWVUPYTM.js → get-evaluator-QDUL5LXR.js} +4 -4
  44. package/dist/{get-prompt-WZUOLLQC.js → get-prompt-XCSSNRKR.js} +3 -3
  45. package/dist/{get-scenario-Q6LCNUYN.js → get-scenario-FNRKOY7E.js} +4 -4
  46. package/dist/{get-simulation-run-Z2XKO256.js → get-simulation-run-WOEKUF7K.js} +4 -4
  47. package/dist/{get-suite-BXK5TN7P.js → get-suite-U5AOD5TC.js} +4 -4
  48. package/dist/{get-trace-4EJAYVBG.js → get-trace-MUWEJM2Z.js} +3 -3
  49. package/dist/{get-workflow-O34QJLHD.js → get-workflow-SOA7MT7L.js} +4 -4
  50. package/dist/{http-server-ISHF7NJX.js → http-server-7DMOCB75.js} +3 -3
  51. package/dist/index.js +423 -241
  52. package/dist/{langwatch-api-annotations-ERBG27RF.js → langwatch-api-annotations-Q57NDUVR.js} +4 -4
  53. package/dist/{langwatch-api-dashboards-G6TR7VVO.js → langwatch-api-dashboards-VBWWXV3Y.js} +4 -4
  54. package/dist/{langwatch-api-monitors-WSXZLS2B.js → langwatch-api-monitors-L3IY7FDT.js} +3 -3
  55. package/dist/{langwatch-api-secrets-RR5DKG2X.js → langwatch-api-secrets-S5HHF3XR.js} +3 -3
  56. package/dist/{langwatch-api-triggers-2RTNSWQY.js → langwatch-api-triggers-ME33QE26.js} +3 -3
  57. package/dist/langwatch-api-workflows-4CJQAYP2.js +15 -0
  58. package/dist/{list-agents-AW3P6FJV.js → list-agents-3HGIBJFM.js} +4 -4
  59. package/dist/{list-annotations-QNCGWPQ3.js → list-annotations-KWXYVEPI.js} +4 -4
  60. package/dist/{list-dashboards-R7RK2IXL.js → list-dashboards-VQ2LH666.js} +4 -4
  61. package/dist/{list-dataset-records-WVCSQP7P.js → list-dataset-records-RNRGROJP.js} +4 -4
  62. package/dist/{list-datasets-KEXRLDEU.js → list-datasets-I4A2LT2S.js} +4 -4
  63. package/dist/{list-evaluators-7R2LB4JH.js → list-evaluators-AKKRJ5MX.js} +4 -4
  64. package/dist/{list-model-providers-OBYJCKBX.js → list-model-providers-DVFEHFHZ.js} +4 -4
  65. package/dist/{list-prompt-tags-6LY3ZPYI.js → list-prompt-tags-PL5OISUA.js} +3 -3
  66. package/dist/{list-prompts-VWEZ4CDE.js → list-prompts-7VTFWE2Z.js} +3 -3
  67. package/dist/{list-scenarios-TXD4W2ZJ.js → list-scenarios-5BZWZ6ZN.js} +4 -4
  68. package/dist/{list-simulation-runs-RHPOLMFI.js → list-simulation-runs-WCZ6RLPE.js} +4 -4
  69. package/dist/{list-suites-FSGBX27Z.js → list-suites-RRQ2JX5Y.js} +4 -4
  70. package/dist/{list-workflows-VRGEZ3EN.js → list-workflows-UQB5FCX5.js} +4 -4
  71. package/dist/{rename-prompt-tag-VOJJLX4M.js → rename-prompt-tag-TURD6KYU.js} +3 -3
  72. package/dist/{run-agent-KTCC7TB7.js → run-agent-BEJ4K4CM.js} +4 -4
  73. package/dist/{run-evaluation-2X3V3Q6L.js → run-evaluation-AJ7WZNQD.js} +3 -3
  74. package/dist/{run-suite-BTMHGI2D.js → run-suite-U7V432G2.js} +4 -4
  75. package/dist/{run-workflow-IGAYGKJL.js → run-workflow-5WX7DK5T.js} +4 -4
  76. package/dist/{search-traces-VYFUIMXH.js → search-traces-ZCHNOMTX.js} +3 -3
  77. package/dist/{set-model-provider-W5IUI326.js → set-model-provider-6CFHMCNE.js} +4 -4
  78. package/dist/{update-agent-4BB7EYLR.js → update-agent-UE6UWLZP.js} +4 -4
  79. package/dist/{update-dataset-CXBKZ4W3.js → update-dataset-4XQZZE4S.js} +4 -4
  80. package/dist/{update-dataset-record-HOHAMHXG.js → update-dataset-record-GNNM5TKM.js} +4 -4
  81. package/dist/{update-evaluator-IVOR6FTR.js → update-evaluator-LHVDXIZD.js} +4 -4
  82. package/dist/{update-prompt-OWQAKE7K.js → update-prompt-3XEHECJ7.js} +3 -3
  83. package/dist/{update-scenario-GDFVILPN.js → update-scenario-DFPREYCD.js} +4 -4
  84. package/dist/{update-suite-BXYUYSPT.js → update-suite-RATU5THO.js} +4 -4
  85. package/package.json +6 -6
  86. package/src/__tests__/all-tools.integration.test.ts +9 -0
  87. package/src/__tests__/datasets.integration.test.ts +17 -0
  88. package/src/__tests__/datasets.unit.test.ts +4 -0
  89. package/src/__tests__/scenario-tools.unit.test.ts +3 -0
  90. package/src/config.ts +2 -0
  91. package/src/langwatch-api.ts +5 -1
  92. package/uv.lock +188 -530
  93. package/Dockerfile +0 -22
  94. package/build.js +0 -12
  95. package/dist/archive-scenario-WJ2A7ETN.js.map +0 -1
  96. package/dist/archive-suite-4JOM4SSL.js.map +0 -1
  97. package/dist/assign-prompt-tag-GL7EAAF3.js.map +0 -1
  98. package/dist/chunk-2GT7IFWK.js.map +0 -1
  99. package/dist/chunk-2WPLXEKF.js.map +0 -1
  100. package/dist/chunk-4AUB2BIA.js.map +0 -1
  101. package/dist/chunk-BWWY5KEF.js.map +0 -1
  102. package/dist/chunk-CCWCIJJ3.js.map +0 -1
  103. package/dist/chunk-E5JYYU3J.js.map +0 -1
  104. package/dist/chunk-EGH5RPXM.js.map +0 -1
  105. package/dist/chunk-EUQDVOSU.js.map +0 -1
  106. package/dist/chunk-HYMIWO3U.js.map +0 -1
  107. package/dist/chunk-IE4M5OGE.js.map +0 -1
  108. package/dist/chunk-LUMEZEHL.js.map +0 -1
  109. package/dist/chunk-NUFPTHKU.js.map +0 -1
  110. package/dist/chunk-WQG2DFHX.js.map +0 -1
  111. package/dist/chunk-WVI5CJ6S.js.map +0 -1
  112. package/dist/chunk-ZVS4UKN2.js.map +0 -1
  113. package/dist/chunk-ZXKLPC2E.js.map +0 -1
  114. package/dist/config.js.map +0 -1
  115. package/dist/create-agent-MBOOT3XK.js.map +0 -1
  116. package/dist/create-annotation-XMV6A7ED.js.map +0 -1
  117. package/dist/create-dashboard-VRKLFVZJ.js.map +0 -1
  118. package/dist/create-dataset-7HQPZCRE.js.map +0 -1
  119. package/dist/create-dataset-records-2NIVFYTV.js.map +0 -1
  120. package/dist/create-evaluator-WYFRFLRC.js.map +0 -1
  121. package/dist/create-mcp-server.js.map +0 -1
  122. package/dist/create-prompt-IEN7O6JT.js.map +0 -1
  123. package/dist/create-prompt-tag-CAEZ45VO.js.map +0 -1
  124. package/dist/create-scenario-6YHSMGLP.js.map +0 -1
  125. package/dist/create-suite-H5XOFDE5.js.map +0 -1
  126. package/dist/delete-agent-JKTNBWAA.js.map +0 -1
  127. package/dist/delete-annotation-ACMDV6QO.js.map +0 -1
  128. package/dist/delete-dashboard-VJSR7CRU.js.map +0 -1
  129. package/dist/delete-dataset-RTTTWP4D.js.map +0 -1
  130. package/dist/delete-dataset-records-KNQTIPQK.js.map +0 -1
  131. package/dist/delete-evaluator-7FVC5WM4.js.map +0 -1
  132. package/dist/delete-prompt-tag-ZMZUAGFW.js.map +0 -1
  133. package/dist/delete-workflow-EYQ3T4RQ.js.map +0 -1
  134. package/dist/discover-evaluator-schema-H23XCLNE.js.map +0 -1
  135. package/dist/discover-scenario-schema-MEEEVND7.js.map +0 -1
  136. package/dist/discover-schema-3T52ORPB.js.map +0 -1
  137. package/dist/get-agent-E3QSTVKG.js.map +0 -1
  138. package/dist/get-analytics-2OHV763I.js.map +0 -1
  139. package/dist/get-dashboard-PC7JDHH6.js.map +0 -1
  140. package/dist/get-dataset-H5IK5FVU.js.map +0 -1
  141. package/dist/get-evaluator-UWVUPYTM.js.map +0 -1
  142. package/dist/get-prompt-WZUOLLQC.js.map +0 -1
  143. package/dist/get-scenario-Q6LCNUYN.js.map +0 -1
  144. package/dist/get-simulation-run-Z2XKO256.js.map +0 -1
  145. package/dist/get-suite-BXK5TN7P.js.map +0 -1
  146. package/dist/get-trace-4EJAYVBG.js.map +0 -1
  147. package/dist/get-workflow-O34QJLHD.js.map +0 -1
  148. package/dist/http-server-ISHF7NJX.js.map +0 -1
  149. package/dist/index.js.map +0 -1
  150. package/dist/langwatch-api-annotations-ERBG27RF.js.map +0 -1
  151. package/dist/langwatch-api-dashboards-G6TR7VVO.js.map +0 -1
  152. package/dist/langwatch-api-monitors-WSXZLS2B.js.map +0 -1
  153. package/dist/langwatch-api-secrets-RR5DKG2X.js.map +0 -1
  154. package/dist/langwatch-api-triggers-2RTNSWQY.js.map +0 -1
  155. package/dist/langwatch-api-workflows-VJRIIDZ7.js +0 -15
  156. package/dist/langwatch-api-workflows-VJRIIDZ7.js.map +0 -1
  157. package/dist/list-agents-AW3P6FJV.js.map +0 -1
  158. package/dist/list-annotations-QNCGWPQ3.js.map +0 -1
  159. package/dist/list-dashboards-R7RK2IXL.js.map +0 -1
  160. package/dist/list-dataset-records-WVCSQP7P.js.map +0 -1
  161. package/dist/list-datasets-KEXRLDEU.js.map +0 -1
  162. package/dist/list-evaluators-7R2LB4JH.js.map +0 -1
  163. package/dist/list-model-providers-OBYJCKBX.js.map +0 -1
  164. package/dist/list-prompt-tags-6LY3ZPYI.js.map +0 -1
  165. package/dist/list-prompts-VWEZ4CDE.js.map +0 -1
  166. package/dist/list-scenarios-TXD4W2ZJ.js.map +0 -1
  167. package/dist/list-simulation-runs-RHPOLMFI.js.map +0 -1
  168. package/dist/list-suites-FSGBX27Z.js.map +0 -1
  169. package/dist/list-workflows-VRGEZ3EN.js.map +0 -1
  170. package/dist/rename-prompt-tag-VOJJLX4M.js.map +0 -1
  171. package/dist/run-agent-KTCC7TB7.js.map +0 -1
  172. package/dist/run-evaluation-2X3V3Q6L.js.map +0 -1
  173. package/dist/run-suite-BTMHGI2D.js.map +0 -1
  174. package/dist/run-workflow-IGAYGKJL.js.map +0 -1
  175. package/dist/search-traces-VYFUIMXH.js.map +0 -1
  176. package/dist/set-model-provider-W5IUI326.js.map +0 -1
  177. package/dist/update-agent-4BB7EYLR.js.map +0 -1
  178. package/dist/update-dataset-CXBKZ4W3.js.map +0 -1
  179. package/dist/update-dataset-record-HOHAMHXG.js.map +0 -1
  180. package/dist/update-evaluator-IVOR6FTR.js.map +0 -1
  181. package/dist/update-prompt-OWQAKE7K.js.map +0 -1
  182. package/dist/update-scenario-GDFVILPN.js.map +0 -1
  183. package/dist/update-suite-BXYUYSPT.js.map +0 -1
  184. package/tests/evaluations.ipynb +0 -649
  185. package/tests/fixtures/azure/azure_openai_stream_bot_expected.py +0 -102
  186. package/tests/fixtures/azure/azure_openai_stream_bot_input.py +0 -78
  187. package/tests/fixtures/dspy/dspy_bot_expected.py +0 -61
  188. package/tests/fixtures/dspy/dspy_bot_input.py +0 -53
  189. package/tests/fixtures/fastapi/fastapi_app_expected.py +0 -68
  190. package/tests/fixtures/fastapi/fastapi_app_input.py +0 -60
  191. package/tests/fixtures/fastapi/prompt_management_fastapi_expected.py +0 -114
  192. package/tests/fixtures/fastapi/prompt_management_fastapi_input.py +0 -88
  193. package/tests/fixtures/haystack/haystack_bot_expected.py +0 -141
  194. package/tests/fixtures/haystack/haystack_bot_input.py +0 -69
  195. package/tests/fixtures/langchain/langchain_bot_expected.py +0 -53
  196. package/tests/fixtures/langchain/langchain_bot_input.py +0 -45
  197. package/tests/fixtures/langchain/langchain_bot_with_memory_expected.py +0 -69
  198. package/tests/fixtures/langchain/langchain_bot_with_memory_input.py +0 -61
  199. package/tests/fixtures/langchain/langchain_rag_bot_expected.py +0 -97
  200. package/tests/fixtures/langchain/langchain_rag_bot_input.py +0 -77
  201. package/tests/fixtures/langchain/langchain_rag_bot_vertex_ai_expected.py +0 -116
  202. package/tests/fixtures/langchain/langchain_rag_bot_vertex_ai_input.py +0 -81
  203. package/tests/fixtures/langchain/langgraph_rag_bot_with_threads_expected.py +0 -331
  204. package/tests/fixtures/langchain/langgraph_rag_bot_with_threads_input.py +0 -106
  205. package/tests/fixtures/litellm/litellm_bot_expected.py +0 -40
  206. package/tests/fixtures/litellm/litellm_bot_input.py +0 -35
  207. package/tests/fixtures/openai/openai_bot_expected.py +0 -43
  208. package/tests/fixtures/openai/openai_bot_function_call_expected.py +0 -91
  209. package/tests/fixtures/openai/openai_bot_function_call_input.py +0 -82
  210. package/tests/fixtures/openai/openai_bot_input.py +0 -36
  211. package/tests/fixtures/openai/openai_bot_rag_expected.py +0 -73
  212. package/tests/fixtures/openai/openai_bot_rag_input.py +0 -51
  213. package/tests/fixtures/opentelemetry/openinference_dspy_bot_expected.py +0 -63
  214. package/tests/fixtures/opentelemetry/openinference_dspy_bot_input.py +0 -58
  215. package/tests/fixtures/opentelemetry/openinference_langchain_bot_expected.py +0 -53
  216. package/tests/fixtures/opentelemetry/openinference_langchain_bot_input.py +0 -52
  217. package/tests/fixtures/opentelemetry/openinference_openai_bot_expected.py +0 -49
  218. package/tests/fixtures/opentelemetry/openinference_openai_bot_input.py +0 -41
  219. package/tests/fixtures/opentelemetry/openllmetry_openai_bot_expected.py +0 -44
  220. package/tests/fixtures/opentelemetry/openllmetry_openai_bot_input.py +0 -40
  221. package/tests/fixtures/strands/strands_bot_expected.py +0 -84
  222. package/tests/fixtures/strands/strands_bot_input.py +0 -52
  223. package/tests/scenario-openai.test.ts +0 -160
  224. package/tsup.config.ts +0 -14
  225. package/vitest.config.js +0 -7
@@ -781,6 +781,7 @@ describe("All MCP tools integration", () => {
781
781
  // =====================
782
782
  describe("discover_schema", () => {
783
783
  describe("when category is filters", () => {
784
+ /** @scenario Agent discovers available filter fields */
784
785
  it("returns filter field documentation", async () => {
785
786
  const { formatSchema } = await import(
786
787
  "../tools/discover-schema.js"
@@ -793,6 +794,7 @@ describe("All MCP tools integration", () => {
793
794
  });
794
795
 
795
796
  describe("when category is metrics", () => {
797
+ /** @scenario Agent discovers available metrics with allowed aggregations */
796
798
  it("returns metric documentation", async () => {
797
799
  const { formatSchema } = await import(
798
800
  "../tools/discover-schema.js"
@@ -818,6 +820,7 @@ describe("All MCP tools integration", () => {
818
820
  });
819
821
 
820
822
  describe("when category is groups", () => {
823
+ /** @scenario Agent discovers available group-by options */
821
824
  it("returns group-by options", async () => {
822
825
  const { formatSchema } = await import(
823
826
  "../tools/discover-schema.js"
@@ -876,6 +879,7 @@ describe("All MCP tools integration", () => {
876
879
  });
877
880
 
878
881
  describe("when category is all", () => {
882
+ /** @scenario Agent discovers all schema information at once */
879
883
  it("returns all schema categories", async () => {
880
884
  const { formatSchema } = await import(
881
885
  "../tools/discover-schema.js"
@@ -895,6 +899,7 @@ describe("All MCP tools integration", () => {
895
899
  // =====================
896
900
  describe("search_traces", () => {
897
901
  describe("when traces are found", () => {
902
+ /** @scenario Agent searches traces with a text query */
898
903
  it("returns formatted trace digests", async () => {
899
904
  const { handleSearchTraces } = await import(
900
905
  "../tools/search-traces.js"
@@ -924,6 +929,7 @@ describe("All MCP tools integration", () => {
924
929
  });
925
930
 
926
931
  describe("when pagination token is present", () => {
932
+ /** @scenario Agent paginates through trace results */
927
933
  it("includes scroll ID for next page", async () => {
928
934
  const { handleSearchTraces } = await import(
929
935
  "../tools/search-traces.js"
@@ -953,6 +959,7 @@ describe("All MCP tools integration", () => {
953
959
  });
954
960
 
955
961
  describe("when filters are applied", () => {
962
+ /** @scenario Agent searches traces filtered by user_id */
956
963
  it("passes filters to the API", async () => {
957
964
  const { handleSearchTraces } = await import(
958
965
  "../tools/search-traces.js"
@@ -976,6 +983,7 @@ describe("All MCP tools integration", () => {
976
983
  // =====================
977
984
  describe("get_trace", () => {
978
985
  describe("when trace exists", () => {
986
+ /** @scenario Agent gets a single trace by ID in AI-readable format */
979
987
  it("returns formatted trace with metadata and evaluations", async () => {
980
988
  const { handleGetTrace } = await import(
981
989
  "../tools/get-trace.js"
@@ -993,6 +1001,7 @@ describe("All MCP tools integration", () => {
993
1001
  });
994
1002
 
995
1003
  describe("when trace does not exist", () => {
1004
+ /** @scenario Agent gets a trace that does not exist */
996
1005
  it("propagates the 404 error", async () => {
997
1006
  const { handleGetTrace } = await import(
998
1007
  "../tools/get-trace.js"
@@ -305,6 +305,7 @@ describe("MCP dataset tools integration", () => {
305
305
 
306
306
  describe("platform_list_datasets", () => {
307
307
  describe("when the project has datasets", () => {
308
+ /** @scenario "List datasets returns a formatted summary of all datasets" */
308
309
  it("returns a formatted list showing both datasets with their names, slugs, and record counts", async () => {
309
310
  const { handleListDatasets } = await import(
310
311
  "../tools/list-datasets.js"
@@ -320,6 +321,7 @@ describe("MCP dataset tools integration", () => {
320
321
  });
321
322
 
322
323
  describe("when the project has no datasets", () => {
324
+ /** @scenario "List datasets returns a helpful message when none exist" */
323
325
  it("returns a helpful message indicating no datasets were found", async () => {
324
326
  emptyListMode = true;
325
327
  const { handleListDatasets } = await import(
@@ -356,6 +358,7 @@ describe("MCP dataset tools integration", () => {
356
358
 
357
359
  describe("platform_get_dataset", () => {
358
360
  describe("when the dataset exists", () => {
361
+ /** @scenario "Get dataset by slug returns metadata and a preview of records" */
359
362
  it("returns the dataset name, slug, and column definitions", async () => {
360
363
  const { handleGetDataset } = await import(
361
364
  "../tools/get-dataset.js"
@@ -388,6 +391,7 @@ describe("MCP dataset tools integration", () => {
388
391
  });
389
392
 
390
393
  describe("when the dataset does not exist", () => {
394
+ /** @scenario "Get dataset with non-existent slug returns an error" */
391
395
  it("propagates the 404 error", async () => {
392
396
  const { handleGetDataset } = await import(
393
397
  "../tools/get-dataset.js"
@@ -403,6 +407,7 @@ describe("MCP dataset tools integration", () => {
403
407
 
404
408
  describe("platform_create_dataset", () => {
405
409
  describe("when creating with name and columns", () => {
410
+ /** @scenario "Create a dataset with name and columns" */
406
411
  it("returns confirmation including the generated slug", async () => {
407
412
  const { handleCreateDataset } = await import(
408
413
  "../tools/create-dataset.js"
@@ -420,6 +425,7 @@ describe("MCP dataset tools integration", () => {
420
425
  });
421
426
 
422
427
  describe("when creating with only a name", () => {
428
+ /** @scenario "Create a dataset with only a name and no columns" */
423
429
  it("returns confirmation including the slug", async () => {
424
430
  const { handleCreateDataset } = await import(
425
431
  "../tools/create-dataset.js"
@@ -437,6 +443,7 @@ describe("MCP dataset tools integration", () => {
437
443
 
438
444
  describe("platform_update_dataset", () => {
439
445
  describe("when updating the dataset name", () => {
446
+ /** @scenario "Update a dataset name" */
440
447
  it("returns confirmation reflecting the new name", async () => {
441
448
  const { handleUpdateDataset } = await import(
442
449
  "../tools/update-dataset.js"
@@ -451,6 +458,7 @@ describe("MCP dataset tools integration", () => {
451
458
  });
452
459
 
453
460
  describe("when updating dataset column types", () => {
461
+ /** @scenario "Update a dataset column types" */
454
462
  it("returns confirmation reflecting the new columns", async () => {
455
463
  const { handleUpdateDataset } = await import(
456
464
  "../tools/update-dataset.js"
@@ -465,6 +473,7 @@ describe("MCP dataset tools integration", () => {
465
473
  });
466
474
 
467
475
  describe("when the dataset does not exist", () => {
476
+ /** @scenario "Update a non-existent dataset returns an error" */
468
477
  it("propagates the 404 error", async () => {
469
478
  const { handleUpdateDataset } = await import(
470
479
  "../tools/update-dataset.js"
@@ -480,6 +489,7 @@ describe("MCP dataset tools integration", () => {
480
489
 
481
490
  describe("platform_delete_dataset", () => {
482
491
  describe("when the dataset exists", () => {
492
+ /** @scenario "Delete a dataset archives it" */
483
493
  it("returns confirmation that the dataset was deleted", async () => {
484
494
  const { handleDeleteDataset } = await import(
485
495
  "../tools/delete-dataset.js"
@@ -490,6 +500,7 @@ describe("MCP dataset tools integration", () => {
490
500
  });
491
501
 
492
502
  describe("when the dataset does not exist", () => {
503
+ /** @scenario "Delete a non-existent dataset returns an error" */
493
504
  it("propagates the 404 error", async () => {
494
505
  const { handleDeleteDataset } = await import(
495
506
  "../tools/delete-dataset.js"
@@ -505,6 +516,7 @@ describe("MCP dataset tools integration", () => {
505
516
 
506
517
  describe("platform_create_dataset_records", () => {
507
518
  describe("when the dataset exists", () => {
519
+ /** @scenario "Add records to a dataset" */
508
520
  it("returns confirmation with the count of records created", async () => {
509
521
  const { handleCreateDatasetRecords } = await import(
510
522
  "../tools/create-dataset-records.js"
@@ -522,6 +534,7 @@ describe("MCP dataset tools integration", () => {
522
534
  });
523
535
 
524
536
  describe("when the dataset does not exist", () => {
537
+ /** @scenario "Add records to a non-existent dataset returns an error" */
525
538
  it("propagates the 404 error", async () => {
526
539
  const { handleCreateDatasetRecords } = await import(
527
540
  "../tools/create-dataset-records.js"
@@ -540,6 +553,7 @@ describe("MCP dataset tools integration", () => {
540
553
 
541
554
  describe("platform_update_dataset_record", () => {
542
555
  describe("when the record exists", () => {
556
+ /** @scenario "Update a single record entry" */
543
557
  it("returns confirmation that the record was updated", async () => {
544
558
  const { handleUpdateDatasetRecord } = await import(
545
559
  "../tools/update-dataset-record.js"
@@ -554,6 +568,7 @@ describe("MCP dataset tools integration", () => {
554
568
  });
555
569
 
556
570
  describe("when the dataset does not exist", () => {
571
+ /** @scenario "Update a record in a non-existent dataset returns an error" */
557
572
  it("propagates the 404 error", async () => {
558
573
  const { handleUpdateDatasetRecord } = await import(
559
574
  "../tools/update-dataset-record.js"
@@ -573,6 +588,7 @@ describe("MCP dataset tools integration", () => {
573
588
 
574
589
  describe("platform_delete_dataset_records", () => {
575
590
  describe("when the dataset exists", () => {
591
+ /** @scenario "Delete records by IDs" */
576
592
  it("returns confirmation with the count of records deleted", async () => {
577
593
  const { handleDeleteDatasetRecords } = await import(
578
594
  "../tools/delete-dataset-records.js"
@@ -587,6 +603,7 @@ describe("MCP dataset tools integration", () => {
587
603
  });
588
604
 
589
605
  describe("when the dataset does not exist", () => {
606
+ /** @scenario "Delete records from a non-existent dataset returns an error" */
590
607
  it("propagates the 404 error", async () => {
591
608
  const { handleDeleteDatasetRecords } = await import(
592
609
  "../tools/delete-dataset-records.js"
@@ -53,6 +53,7 @@ describe("formatDatasetResponse()", () => {
53
53
  expect(result).toContain("my-dataset");
54
54
  });
55
55
 
56
+ /** @scenario "formatDatasetResponse renders column table and record entries as markdown" */
56
57
  it("includes a column table", () => {
57
58
  expect(result).toContain("input");
58
59
  expect(result).toContain("output");
@@ -171,6 +172,7 @@ describe("handleGetDataset()", () => {
171
172
 
172
173
  describe("platform_create_dataset schema", () => {
173
174
  describe("when input has no name", () => {
175
+ /** @scenario "platform_create_dataset schema rejects input without a name" */
174
176
  it("rejects the input with a validation error", () => {
175
177
  const result = createDatasetSchema.safeParse({});
176
178
  expect(result.success).toBe(false);
@@ -196,6 +198,7 @@ describe("platform_create_dataset schema", () => {
196
198
 
197
199
  describe("MCP server dataset tool registration", () => {
198
200
  describe("when the MCP server is created", () => {
201
+ /** @scenario "All dataset tools are registered in the MCP server" */
199
202
  it("registers all 8 dataset tools", async () => {
200
203
  const { createMcpServer } = await import("../create-mcp-server.js");
201
204
  const server = createMcpServer();
@@ -219,6 +222,7 @@ describe("MCP server dataset tool registration", () => {
219
222
 
220
223
  describe("dataset tools API key requirement", () => {
221
224
  describe("when no API key is configured", () => {
225
+ /** @scenario "Dataset tools require an API key" */
222
226
  it("requireApiKey throws when apiKey is empty", async () => {
223
227
  const savedKey = process.env.LANGWATCH_API_KEY;
224
228
  delete process.env.LANGWATCH_API_KEY;
@@ -79,6 +79,7 @@ describe("handleListScenarios()", () => {
79
79
  expect(result).toContain("scen_def456");
80
80
  });
81
81
 
82
+ /** @scenario Agent lists all scenarios in a project */
82
83
  it("includes the total count header", () => {
83
84
  expect(result).toContain("# Scenarios (2 total)");
84
85
  });
@@ -92,6 +93,7 @@ describe("handleListScenarios()", () => {
92
93
  result = await handleListScenarios({});
93
94
  });
94
95
 
96
+ /** @scenario Agent lists scenarios when none exist */
95
97
  it("returns a no-scenarios message", () => {
96
98
  expect(result).toContain("No scenarios found");
97
99
  });
@@ -130,6 +132,7 @@ describe("handleGetScenario()", () => {
130
132
  result = await handleGetScenario({ scenarioId: "scen_abc123" });
131
133
  });
132
134
 
135
+ /** @scenario Agent gets full details of a scenario */
133
136
  it("includes the scenario name in the heading", () => {
134
137
  expect(result).toContain("# Scenario: Login Flow Happy Path");
135
138
  });
package/src/config.ts CHANGED
@@ -3,6 +3,7 @@ import { AsyncLocalStorage } from "node:async_hooks";
3
3
  export interface McpConfig {
4
4
  apiKey: string | undefined;
5
5
  endpoint: string;
6
+ projectId?: string;
6
7
  }
7
8
 
8
9
  // ---------------------------------------------------------------------------
@@ -56,6 +57,7 @@ export function initConfig(args: { apiKey?: string; endpoint?: string }): void {
56
57
  args.endpoint ||
57
58
  process.env.LANGWATCH_ENDPOINT ||
58
59
  "https://app.langwatch.ai",
60
+ projectId: process.env.LANGWATCH_PROJECT_ID,
59
61
  };
60
62
  }
61
63
 
@@ -118,10 +118,14 @@ export async function makeRequest(
118
118
  path: string,
119
119
  body?: unknown
120
120
  ): Promise<unknown> {
121
- const url = getConfig().endpoint + path;
121
+ const config = getConfig();
122
+ const url = config.endpoint + path;
122
123
  const headers: Record<string, string> = {
123
124
  "X-Auth-Token": requireApiKey(),
124
125
  };
126
+ if (config.projectId) {
127
+ headers["X-Project-Id"] = config.projectId;
128
+ }
125
129
 
126
130
  if (body !== undefined) {
127
131
  headers["Content-Type"] = "application/json";