@langwatch/mcp-server 2.0.0 → 2.1.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 (177) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/README.md +31 -3
  3. package/dist/{archive-project-4EQGLBJJ.js → archive-project-W3IKI7UL.js} +3 -3
  4. package/dist/archive-run-plan-474OKHHU.js +16 -0
  5. package/dist/{archive-scenario-W6BXYDZ3.js → archive-scenario-V5UNYK26.js} +3 -3
  6. package/dist/archive-test-suite-O7FLDMXK.js +16 -0
  7. package/dist/{assign-prompt-tag-VLXUWEKU.js → assign-prompt-tag-Q46WRB6P.js} +2 -2
  8. package/dist/chunk-56OGTS24.js +44 -0
  9. package/dist/chunk-6DJDEXET.js +63 -0
  10. package/dist/chunk-7E4FZIYJ.js +15 -0
  11. package/dist/{chunk-77BNWQ3U.js → chunk-AN4OUA4D.js} +2 -2
  12. package/dist/chunk-CKNAH5TG.js +74 -0
  13. package/dist/{chunk-TOXJQDIZ.js → chunk-EYXMLE5X.js} +85 -10
  14. package/dist/{chunk-7RHG56OH.js → chunk-G4ZAZTZW.js} +2 -2
  15. package/dist/{chunk-A2LEVYQ5.js → chunk-GELJ2MQR.js} +11 -2
  16. package/dist/chunk-HV4DWKBI.js +46 -0
  17. package/dist/chunk-JR6DJZOU.js +49 -0
  18. package/dist/{chunk-SFB4MU6H.js → chunk-JTQJRNFJ.js} +2 -2
  19. package/dist/{chunk-TJCXFNDT.js → chunk-K37ZNPXK.js} +2 -2
  20. package/dist/{chunk-CDYQOY5U.js → chunk-OJLYYJWI.js} +2 -2
  21. package/dist/{chunk-ZWIL4EFL.js → chunk-QAMP3CGR.js} +2 -2
  22. package/dist/{chunk-4QC7SA2L.js → chunk-QRO35SOM.js} +345 -144
  23. package/dist/{chunk-J42FWRJ2.js → chunk-RVAE2MEA.js} +2 -2
  24. package/dist/{chunk-AQJO2EI5.js → chunk-S4GVX36W.js} +2 -2
  25. package/dist/{chunk-YX3A3CFM.js → chunk-SR6QPRIE.js} +2 -2
  26. package/dist/chunk-UF6XIGYR.js +58 -0
  27. package/dist/{chunk-5CCZFUXS.js → chunk-UFOYFYI7.js} +2 -2
  28. package/dist/{create-agent-7XHO6UIS.js → create-agent-JFJ2K6X3.js} +3 -3
  29. package/dist/{create-annotation-YQXRQC6A.js → create-annotation-EUXXQ6NS.js} +3 -3
  30. package/dist/{create-api-key-YUS36KBU.js → create-api-key-BGBB76JX.js} +3 -3
  31. package/dist/{create-dashboard-4Z5JO5LB.js → create-dashboard-Z76AIPLS.js} +3 -3
  32. package/dist/{create-dataset-E6NMSVX7.js → create-dataset-FD5VAXU4.js} +3 -3
  33. package/dist/{create-dataset-records-SKHPRD2K.js → create-dataset-records-WQUXXOHI.js} +3 -3
  34. package/dist/{create-evaluator-JXF5PF33.js → create-evaluator-6NV33Z52.js} +3 -3
  35. package/dist/create-mcp-server.js +7 -2
  36. package/dist/{create-project-CS2CB57Y.js → create-project-ZQ7TFIO2.js} +3 -3
  37. package/dist/{create-prompt-F5RMX3L3.js → create-prompt-BS6QRCUU.js} +2 -2
  38. package/dist/{create-prompt-tag-GMRWTEFR.js → create-prompt-tag-FCQ5ELSQ.js} +2 -2
  39. package/dist/{create-scenario-JDR2VB2H.js → create-scenario-DRPTPXU3.js} +10 -3
  40. package/dist/create-test-suite-ZIZCCFJ4.js +37 -0
  41. package/dist/{delete-agent-O3EEH4S5.js → delete-agent-SA4JMUZF.js} +3 -3
  42. package/dist/{delete-annotation-XP6FQM7A.js → delete-annotation-X3KKK7QY.js} +3 -3
  43. package/dist/{delete-dashboard-RFUNYCNP.js → delete-dashboard-LYMRPF27.js} +3 -3
  44. package/dist/{delete-dataset-3MJFRDLP.js → delete-dataset-W6EZ44HY.js} +3 -3
  45. package/dist/{delete-dataset-records-WPMU5GL5.js → delete-dataset-records-7CNOTALB.js} +3 -3
  46. package/dist/{delete-evaluator-33SP2JFX.js → delete-evaluator-RKYC4TNB.js} +3 -3
  47. package/dist/{delete-prompt-tag-KNMZVSPB.js → delete-prompt-tag-QB3OCZUV.js} +2 -2
  48. package/dist/{delete-workflow-3NUXO46U.js → delete-workflow-NG2VXYYM.js} +3 -3
  49. package/dist/{discover-evaluator-schema-XTAD4X7D.js → discover-evaluator-schema-ESSHQGPY.js} +7 -286
  50. package/dist/{discover-scenario-schema-FE64QK2D.js → discover-scenario-schema-W7KBXA4J.js} +53 -1
  51. package/dist/get-agent-7THF7HCU.js +66 -0
  52. package/dist/{get-analytics-TETCSOK6.js → get-analytics-R6AZD3AW.js} +2 -2
  53. package/dist/{get-dashboard-KHBEGZGL.js → get-dashboard-P4WWVHFU.js} +3 -3
  54. package/dist/{get-dataset-RCMU5VAH.js → get-dataset-AKTYSQDI.js} +3 -3
  55. package/dist/{get-evaluator-ADMKVWYZ.js → get-evaluator-RZ44UIN6.js} +3 -3
  56. package/dist/{get-project-RAK7URQJ.js → get-project-QSQEAB33.js} +3 -3
  57. package/dist/get-prompt-GVKXF5NL.js +100 -0
  58. package/dist/get-run-plan-NY7J5GRE.js +61 -0
  59. package/dist/{get-scenario-ZTAJJL5T.js → get-scenario-Q3LLQE4T.js} +7 -3
  60. package/dist/{get-simulation-run-T2ZQWYH2.js → get-simulation-run-WINEYDMK.js} +12 -3
  61. package/dist/get-test-suite-4HKA7OKS.js +49 -0
  62. package/dist/{get-trace-K7U234Q4.js → get-trace-RCUV7N4O.js} +2 -2
  63. package/dist/{get-workflow-L5L2PSK3.js → get-workflow-EEOC7Y45.js} +3 -3
  64. package/dist/{http-server-YXHFPPGA.js → http-server-WCDUARTH.js} +8 -3
  65. package/dist/index.js +3366 -1910
  66. package/dist/{langwatch-api-annotations-FDAS6NQ7.js → langwatch-api-annotations-6DHVZWUK.js} +3 -3
  67. package/dist/{langwatch-api-dashboards-JTVCS7ZJ.js → langwatch-api-dashboards-2KJL2M42.js} +3 -3
  68. package/dist/{langwatch-api-monitors-3J7UIQME.js → langwatch-api-monitors-6ISFJ7WX.js} +2 -2
  69. package/dist/{langwatch-api-secrets-BUBSNCNO.js → langwatch-api-secrets-OEBD6GBU.js} +2 -2
  70. package/dist/{langwatch-api-triggers-EDFUCJN6.js → langwatch-api-triggers-5E6DDGX6.js} +2 -2
  71. package/dist/{langwatch-api-workflows-NBJX5KOG.js → langwatch-api-workflows-6UJL5U4Z.js} +3 -3
  72. package/dist/list-agents-AMVDQPQL.js +43 -0
  73. package/dist/{list-annotations-ZTVP4EEL.js → list-annotations-6PR5NLIC.js} +3 -3
  74. package/dist/{list-api-keys-CWD4UQT5.js → list-api-keys-GVPNBIHQ.js} +3 -3
  75. package/dist/{list-dashboards-PWG6P6KR.js → list-dashboards-Z3BZKDGF.js} +3 -3
  76. package/dist/{list-dataset-records-IOE7LQXS.js → list-dataset-records-O67HZ35Y.js} +3 -3
  77. package/dist/{list-datasets-ZT4JYUDT.js → list-datasets-WXZL6UMP.js} +3 -3
  78. package/dist/{list-evaluators-SJHC7VBH.js → list-evaluators-PAX72WCN.js} +3 -3
  79. package/dist/{list-model-providers-PPD2WPD2.js → list-model-providers-VVLZMLCX.js} +3 -3
  80. package/dist/{list-projects-K7QAIX2I.js → list-projects-AQVI6SUK.js} +3 -3
  81. package/dist/{list-prompt-tags-FOO6TNHB.js → list-prompt-tags-WZFBJOHG.js} +2 -2
  82. package/dist/{list-prompts-SC2HAUFG.js → list-prompts-BQOHRHYT.js} +2 -2
  83. package/dist/list-run-plans-UBAU2DGH.js +46 -0
  84. package/dist/{list-scenarios-F2WYNRIE.js → list-scenarios-MS4IUIVM.js} +8 -5
  85. package/dist/{list-simulation-runs-KF5THG5H.js → list-simulation-runs-D6YF4ZOU.js} +11 -5
  86. package/dist/list-test-suites-L7R2O6DR.js +35 -0
  87. package/dist/{list-workflows-QSJWURCK.js → list-workflows-6MV4XJHF.js} +3 -3
  88. package/dist/{rename-prompt-tag-EKWU5QF3.js → rename-prompt-tag-K5LQ4CY6.js} +2 -2
  89. package/dist/rename-test-suite-NTYHAO5X.js +21 -0
  90. package/dist/report-issue-RHZZ33IS.js +885 -0
  91. package/dist/rerun-run-plan-MUDWJBE4.js +22 -0
  92. package/dist/{revoke-api-key-FTRTZMOF.js → revoke-api-key-6LAZ3QZZ.js} +3 -3
  93. package/dist/run-agent-CAGWGTYT.js +72 -0
  94. package/dist/run-plan-T4ZYOZGA.js +39 -0
  95. package/dist/run-test-suite-L4MZ7G4D.js +26 -0
  96. package/dist/{run-workflow-QPXC627O.js → run-workflow-DFIS23AP.js} +3 -3
  97. package/dist/{search-traces-A6HFKPLJ.js → search-traces-4XTJYXIK.js} +2 -2
  98. package/dist/{set-model-provider-3WKT63ZN.js → set-model-provider-XRNMFOVR.js} +3 -3
  99. package/dist/{update-agent-SMAEHLXH.js → update-agent-DUWNYBNA.js} +3 -3
  100. package/dist/{update-dataset-BDUK7TUO.js → update-dataset-YSOMDCOB.js} +3 -3
  101. package/dist/{update-dataset-record-4ZSDRBSW.js → update-dataset-record-DEA2ITYR.js} +3 -3
  102. package/dist/{update-evaluator-LS6TIBY7.js → update-evaluator-NH7Q5GWV.js} +3 -3
  103. package/dist/{update-project-LKVYXENH.js → update-project-Q26FBSYH.js} +3 -3
  104. package/dist/update-prompt-ZQQ2C3KU.js +143 -0
  105. package/dist/{update-scenario-NEXCAQI7.js → update-scenario-WUX34JAR.js} +10 -3
  106. package/package.json +8 -9
  107. package/pyproject.toml +4 -4
  108. package/src/__tests__/agent-tools.unit.test.ts +257 -0
  109. package/src/__tests__/all-tools.integration.test.ts +321 -75
  110. package/src/__tests__/integration.integration.test.ts +7 -11
  111. package/src/__tests__/prompt-response-types.unit.test.ts +51 -0
  112. package/src/__tests__/prompts.integration.test.ts +736 -0
  113. package/src/__tests__/run-plan-tools.unit.test.ts +565 -0
  114. package/src/__tests__/scenario-tools.unit.test.ts +19 -0
  115. package/src/__tests__/simulation-run-tools.unit.test.ts +116 -0
  116. package/src/__tests__/test-suite-tools.unit.test.ts +615 -0
  117. package/src/__tests__/{tools.unit.test.ts → tools.integration.test.ts} +31 -59
  118. package/src/create-mcp-server.ts +334 -55
  119. package/src/langwatch-api-agents.ts +196 -12
  120. package/src/langwatch-api-run-plans.ts +157 -0
  121. package/src/langwatch-api-scenarios.ts +9 -0
  122. package/src/langwatch-api-simulation-runs.ts +5 -0
  123. package/src/langwatch-api-test-suites.ts +135 -0
  124. package/src/langwatch-api.ts +62 -12
  125. package/src/schemas/index.ts +5 -0
  126. package/src/schemas/run-plan.ts +92 -0
  127. package/src/schemas/suite-fields.ts +149 -0
  128. package/src/tools/archive-run-plan.ts +12 -0
  129. package/src/tools/archive-test-suite.ts +12 -0
  130. package/src/tools/create-scenario.ts +8 -0
  131. package/src/tools/create-test-suite.ts +43 -0
  132. package/src/tools/discover-scenario-schema.ts +54 -0
  133. package/src/tools/format-run-plan.ts +68 -0
  134. package/src/tools/format-scenario.ts +14 -0
  135. package/src/tools/format-suite-details.ts +96 -0
  136. package/src/tools/get-agent.ts +45 -3
  137. package/src/tools/get-prompt.ts +108 -24
  138. package/src/tools/get-run-plan.ts +62 -0
  139. package/src/tools/get-scenario.ts +3 -0
  140. package/src/tools/get-simulation-run.ts +9 -0
  141. package/src/tools/get-test-suite.ts +49 -0
  142. package/src/tools/list-agents.ts +20 -3
  143. package/src/tools/list-run-plans.ts +47 -0
  144. package/src/tools/list-scenarios.ts +12 -2
  145. package/src/tools/list-simulation-runs.ts +6 -2
  146. package/src/tools/list-test-suites.ts +35 -0
  147. package/src/tools/rename-test-suite.ts +18 -0
  148. package/src/tools/rerun-run-plan.ts +21 -0
  149. package/src/tools/run-agent.ts +54 -6
  150. package/src/tools/run-plan.ts +49 -0
  151. package/src/tools/run-test-suite.ts +30 -0
  152. package/src/tools/test-agent.ts +18 -0
  153. package/src/tools/update-prompt.ts +195 -10
  154. package/src/tools/update-scenario.ts +8 -0
  155. package/src/tools/update-test-suite.ts +45 -0
  156. package/tsconfig.json +5 -0
  157. package/uv.lock +416 -296
  158. package/dist/archive-suite-4T6BSRS7.js +0 -16
  159. package/dist/chunk-YZLYHLLI.js +0 -48
  160. package/dist/create-suite-LZS6QIAF.js +0 -39
  161. package/dist/get-agent-Y7MWZ65F.js +0 -29
  162. package/dist/get-prompt-OY5B7TRI.js +0 -46
  163. package/dist/get-suite-J7JVWO6O.js +0 -45
  164. package/dist/list-agents-KFHKGPXV.js +0 -33
  165. package/dist/list-suites-CRY4VWW6.js +0 -43
  166. package/dist/report-issue-XYVAPAYJ.js +0 -412
  167. package/dist/run-agent-6SM4XNA7.js +0 -37
  168. package/dist/run-suite-NKZCJ2IN.js +0 -30
  169. package/dist/update-prompt-ODCRQRN6.js +0 -25
  170. package/dist/update-suite-SNO3GS7T.js +0 -40
  171. package/src/langwatch-api-suites.ts +0 -118
  172. package/src/tools/archive-suite.ts +0 -12
  173. package/src/tools/create-suite.ts +0 -42
  174. package/src/tools/get-suite.ts +0 -47
  175. package/src/tools/list-suites.ts +0 -43
  176. package/src/tools/run-suite.ts +0 -28
  177. package/src/tools/update-suite.ts +0 -44
@@ -8,10 +8,22 @@ import {
8
8
  createDatasetSchema,
9
9
  datasetColumnDefinitionSchema,
10
10
  } from "./schemas/create-dataset.js";
11
+ import {
12
+ runParametersSchema,
13
+ runPlanScopeSchema,
14
+ runPlanTargetSchema,
15
+ } from "./schemas/run-plan.js";
16
+ import {
17
+ evaluatorAttachmentsSchema,
18
+ scenarioFieldValuesSchema,
19
+ suiteFieldsSchema,
20
+ } from "./schemas/suite-fields.js";
11
21
  import { handleExperimentResults } from "./tools/get-experiment-results.js";
12
22
  import { handleExperimentListRuns } from "./tools/list-experiment-runs.js";
13
23
  import { handleExperimentList } from "./tools/list-experiments.js";
14
24
  import { handleRunExperiment, handleExperimentStatus } from "./tools/run-experiment.js";
25
+ import { handleTestAgent } from "./tools/test-agent.js";
26
+ import { handleUpdateTestSuite } from "./tools/update-test-suite.js";
15
27
 
16
28
  const modelSchema = z
17
29
  .string()
@@ -360,7 +372,7 @@ NOTE: Prompts can be managed two ways. Determine which approach the user needs:
360
372
 
361
373
  server.tool(
362
374
  "platform_get_prompt",
363
- "Get a specific prompt from the LangWatch platform by ID or handle, including messages, model config, and version history.",
375
+ "Get a specific prompt from the LangWatch platform by ID or handle, including messages, model config, and version history. Use format: 'json' for the full raw API payload, or 'digest' (default) for a formatted summary.",
364
376
  {
365
377
  idOrHandle: z.string().describe("Prompt ID or handle"),
366
378
  version: z
@@ -371,6 +383,12 @@ NOTE: Prompts can be managed two ways. Determine which approach the user needs:
371
383
  'Fetch the version pointed to by this tag (e.g., "production", "staging"). ' +
372
384
  'Alternatively, use shorthand in idOrHandle: "pizza-prompt:production"'
373
385
  ),
386
+ format: z
387
+ .enum(["digest", "json"])
388
+ .optional()
389
+ .describe(
390
+ "Output format: 'digest' (default, AI-readable) or 'json' (full raw data)"
391
+ ),
374
392
  },
375
393
  withToolLogging("platform_get_prompt", async (params) => {
376
394
  if (params.version != null && params.tag) {
@@ -528,6 +546,13 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
528
546
  .array(z.string())
529
547
  .optional()
530
548
  .describe("Tags for organizing and filtering scenarios"),
549
+ testSuiteId: z
550
+ .string()
551
+ .nullish()
552
+ .describe(
553
+ "The test suite to file this scenario in. Pass a test suite ID, or null to unfile it."
554
+ ),
555
+ fields: scenarioFieldValuesSchema.optional(),
531
556
  },
532
557
  withToolLogging("platform_create_scenario", async (params) => {
533
558
  requireApiKey();
@@ -546,6 +571,10 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
546
571
  "platform_list_scenarios",
547
572
  "List all scenarios on the LangWatch platform. Returns AI-readable digest by default.",
548
573
  {
574
+ testSuiteId: z
575
+ .string()
576
+ .optional()
577
+ .describe("Only the scenarios filed in this test suite"),
549
578
  format: z
550
579
  .enum(["digest", "json"])
551
580
  .optional()
@@ -602,6 +631,13 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
602
631
  .array(z.string())
603
632
  .optional()
604
633
  .describe("Updated labels"),
634
+ testSuiteId: z
635
+ .string()
636
+ .nullish()
637
+ .describe(
638
+ "The test suite to file this scenario in. Pass a test suite ID, or null to unfile it."
639
+ ),
640
+ fields: scenarioFieldValuesSchema.optional(),
605
641
  },
606
642
  withToolLogging("platform_update_scenario", async (params) => {
607
643
  requireApiKey();
@@ -635,113 +671,325 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
635
671
  })
636
672
  );
637
673
 
638
- // --- Platform Suite / Run Plan Tools (require API key) ---
639
- // These tools manage suites (run plans) on the LangWatch platform via API.
674
+ // --- Platform Run Plan Tools (require API key) ---
675
+ // A run plan is what you run, and its NAME identifies it: running a name
676
+ // that exists replaces that plan's configuration, running a new name
677
+ // creates the plan. A test suite groups scenarios; running one is
678
+ // sugar that creates or joins the plan "<suite name> <target name>".
679
+
680
+ server.tool(
681
+ "platform_run_plan",
682
+ "Run scenarios against targets. The plan name identifies the run plan: an existing name is re-run with the configuration you send here, a new name creates the plan. Configuration is the scope, the targets, the repeat count and the models; parameters, the note and the idempotency key belong to this run alone. Send more than one target to compare them in the same run, and give a target its own parameters to compare one agent on two models.",
683
+ {
684
+ name: z
685
+ .string()
686
+ .optional()
687
+ .describe(
688
+ "The run plan to run. An existing name is replaced with this configuration, a new name creates the plan. Omit to let the server name it after the suite and the target.",
689
+ ),
690
+ scope: runPlanScopeSchema.describe(
691
+ "What the plan covers: every scenario in the project, the scenarios filed in given test suites, the scenarios carrying given labels, or the hand-picked list in scenarioIds.",
692
+ ),
693
+ scenarioIds: z
694
+ .array(z.string())
695
+ .optional()
696
+ .describe(
697
+ "The scenarios to run. Read only when scope.mode is 'scenarios'.",
698
+ ),
699
+ targets: z
700
+ .array(runPlanTargetSchema)
701
+ .describe(
702
+ "What to run the scenarios against. Every target runs every scenario, and the results show one column for each, so this is how one run compares two agents or one agent on two models.",
703
+ ),
704
+ repeatCount: z
705
+ .number()
706
+ .int()
707
+ .min(1)
708
+ .max(5)
709
+ .optional()
710
+ .describe("How many times to run each scenario against each target (1 to 5, default 1)"),
711
+ simulatorModel: z
712
+ .string()
713
+ .optional()
714
+ .describe("Model that plays the user. Omit for the project default."),
715
+ judgeModel: z
716
+ .string()
717
+ .optional()
718
+ .describe("Model that judges the criteria. Omit for the project default."),
719
+ evaluators: evaluatorAttachmentsSchema
720
+ .optional()
721
+ .describe(
722
+ "The plan's own evaluators, run beside the ones attached to the test suites its scenarios belong to. A plan evaluator reads the conversation and the trace, never a scenario field. Omit to keep what the plan already holds. See discover_schema({ category: 'scenarios' }) for the mapping paths.",
723
+ ),
724
+ parameters: runParametersSchema
725
+ .optional()
726
+ .describe(
727
+ "Values for the parameters the scenarios declare, by name. They apply to every target; a target that names the same parameter overrides them for itself.",
728
+ ),
729
+ note: z
730
+ .string()
731
+ .max(200)
732
+ .optional()
733
+ .describe("One short line saying why this run was started (max 200 characters)"),
734
+ idempotencyKey: z
735
+ .string()
736
+ .optional()
737
+ .describe("Send the same key to make a retry join the run it already started instead of starting a second one."),
738
+ },
739
+ withToolLogging("platform_run_plan", async (params) => {
740
+ requireApiKey();
741
+ const { handleRunPlan } = await import("./tools/run-plan.js");
742
+ return {
743
+ content: [{ type: "text", text: await handleRunPlan(params) }],
744
+ };
745
+ })
746
+ );
747
+
748
+ server.tool(
749
+ "platform_list_run_plans",
750
+ "List the run plans of the project. A run plan holds the configuration a run used, keyed by its name.",
751
+ {
752
+ includeArchived: z
753
+ .boolean()
754
+ .optional()
755
+ .describe("Include archived plans (default false)"),
756
+ format: z
757
+ .enum(["digest", "json"])
758
+ .optional()
759
+ .describe("Output format: 'digest' (default) or 'json' (raw data)"),
760
+ },
761
+ withToolLogging("platform_list_run_plans", async (params) => {
762
+ requireApiKey();
763
+ const { handleListRunPlans } = await import("./tools/list-run-plans.js");
764
+ return {
765
+ content: [{ type: "text", text: await handleListRunPlans(params) }],
766
+ };
767
+ })
768
+ );
769
+
770
+ server.tool(
771
+ "platform_get_run_plan",
772
+ "Get the full configuration of a run plan: what it covers, its targets, its repeat count and its models.",
773
+ {
774
+ id: z.string().describe("The run plan ID"),
775
+ format: z
776
+ .enum(["digest", "json"])
777
+ .optional()
778
+ .describe("Output format: 'digest' (default) or 'json'"),
779
+ },
780
+ withToolLogging("platform_get_run_plan", async (params) => {
781
+ requireApiKey();
782
+ const { handleGetRunPlan } = await import("./tools/get-run-plan.js");
783
+ return {
784
+ content: [{ type: "text", text: await handleGetRunPlan(params) }],
785
+ };
786
+ })
787
+ );
788
+
789
+ server.tool(
790
+ "platform_rerun_run_plan",
791
+ "Run a plan again with the configuration it already holds. Only the parameters and the note belong to the new run.",
792
+ {
793
+ id: z.string().describe("The run plan ID to run again"),
794
+ parameters: runParametersSchema
795
+ .optional()
796
+ .describe("Values for the parameters the scenarios declare, by name."),
797
+ note: z
798
+ .string()
799
+ .max(200)
800
+ .optional()
801
+ .describe("One short line saying why this run was started (max 200 characters)"),
802
+ },
803
+ withToolLogging("platform_rerun_run_plan", async (params) => {
804
+ requireApiKey();
805
+ const { handleRerunRunPlan } = await import("./tools/rerun-run-plan.js");
806
+ return {
807
+ content: [{ type: "text", text: await handleRerunRunPlan(params) }],
808
+ };
809
+ })
810
+ );
811
+
812
+ server.tool(
813
+ "platform_archive_run_plan",
814
+ "Archive a run plan. Its past runs stay readable and the name is free for a new plan.",
815
+ {
816
+ id: z.string().describe("The run plan ID to archive"),
817
+ },
818
+ withToolLogging("platform_archive_run_plan", async (params) => {
819
+ requireApiKey();
820
+ const { handleArchiveRunPlan } = await import(
821
+ "./tools/archive-run-plan.js"
822
+ );
823
+ return {
824
+ content: [{ type: "text", text: await handleArchiveRunPlan(params) }],
825
+ };
826
+ })
827
+ );
828
+
829
+ // --- Platform Test Suite Tools (require API key) ---
830
+ // A test suite groups scenarios: a name and the scenarios filed in it.
640
831
 
641
832
  server.tool(
642
- "platform_list_suites",
643
- "List all suites (run plans) on the LangWatch platform. A suite bundles scenarios with targets for batch execution.",
833
+ "platform_list_test_suites",
834
+ "List the test suites of the project. A test suite groups scenarios.",
644
835
  {
645
836
  format: z
646
837
  .enum(["digest", "json"])
647
838
  .optional()
648
839
  .describe("Output format: 'digest' (default) or 'json' (raw data)"),
649
840
  },
650
- withToolLogging("platform_list_suites", async (params) => {
841
+ withToolLogging("platform_list_test_suites", async (params) => {
842
+ requireApiKey();
843
+ const { handleListTestSuites } = await import(
844
+ "./tools/list-test-suites.js"
845
+ );
846
+ return {
847
+ content: [{ type: "text", text: await handleListTestSuites(params) }],
848
+ };
849
+ })
850
+ );
851
+
852
+ server.tool(
853
+ "platform_create_test_suite",
854
+ "Create a test suite. A test suite groups scenarios: file a scenario in it by passing the suite ID as testSuiteId on platform_create_scenario or platform_update_scenario. A suite can declare typed fields every scenario carries a value for, and attach saved evaluators that run after every scenario run with mappings from their inputs to the conversation, the scenario's fields or the trace. Call discover_schema({ category: 'scenarios' }) first for the field rules and the mapping paths.",
855
+ {
856
+ name: z.string().describe("Test suite name"),
857
+ fields: suiteFieldsSchema.optional(),
858
+ evaluators: evaluatorAttachmentsSchema.optional(),
859
+ },
860
+ withToolLogging("platform_create_test_suite", async (params) => {
651
861
  requireApiKey();
652
- const { handleListSuites } = await import("./tools/list-suites.js");
862
+ const { handleCreateTestSuite } = await import(
863
+ "./tools/create-test-suite.js"
864
+ );
653
865
  return {
654
- content: [{ type: "text", text: await handleListSuites(params) }],
866
+ content: [{ type: "text", text: await handleCreateTestSuite(params) }],
655
867
  };
656
868
  })
657
869
  );
658
870
 
659
871
  server.tool(
660
- "platform_get_suite",
661
- "Get detailed information about a specific suite (run plan) by ID.",
872
+ "platform_get_test_suite",
873
+ "Get a test suite and the scenarios filed in it.",
662
874
  {
663
- id: z.string().describe("The suite ID"),
875
+ id: z.string().describe("The test suite ID"),
664
876
  format: z
665
877
  .enum(["digest", "json"])
666
878
  .optional()
667
879
  .describe("Output format: 'digest' (default) or 'json'"),
668
880
  },
669
- withToolLogging("platform_get_suite", async (params) => {
881
+ withToolLogging("platform_get_test_suite", async (params) => {
670
882
  requireApiKey();
671
- const { handleGetSuite } = await import("./tools/get-suite.js");
883
+ const { handleGetTestSuite } = await import("./tools/get-test-suite.js");
672
884
  return {
673
- content: [{ type: "text", text: await handleGetSuite(params) }],
885
+ content: [{ type: "text", text: await handleGetTestSuite(params) }],
674
886
  };
675
887
  })
676
888
  );
677
889
 
678
890
  server.tool(
679
- "platform_create_suite",
680
- "Create a new suite (run plan) that bundles scenarios with targets for batch execution.",
891
+ "platform_update_test_suite",
892
+ "Edit a test suite: any of its name, its fields and its evaluators. A field list or an evaluator list replaces the one the suite holds; a key left out keeps what the suite has. A field an attached evaluator still reads cannot be removed. Call discover_schema({ category: 'scenarios' }) first for the field rules and the mapping paths.",
681
893
  {
682
- name: z.string().describe("Suite name"),
683
- description: z.string().optional().describe("Suite description"),
684
- scenarioIds: z.array(z.string()).describe("Array of scenario IDs to include"),
685
- targets: z.string().describe('JSON array of target objects, e.g. [{"type":"http","referenceId":"agent_abc"}]'),
686
- repeatCount: z.number().optional().describe("Number of times to repeat each scenario-target pair (default: 1)"),
687
- labels: z.array(z.string()).optional().describe("Tags for organizing suites"),
894
+ id: z.string().describe("The test suite ID"),
895
+ name: z.string().optional().describe("The new name. The slug is kept."),
896
+ fields: suiteFieldsSchema
897
+ .optional()
898
+ .describe("The full list of fields the suite declares. It replaces the list the suite holds."),
899
+ evaluators: evaluatorAttachmentsSchema
900
+ .optional()
901
+ .describe("The full list of evaluators attached to the suite. It replaces the list the suite holds."),
688
902
  },
689
- withToolLogging("platform_create_suite", async (params) => {
903
+ withToolLogging("platform_update_test_suite", async (params) => {
690
904
  requireApiKey();
691
- const { handleCreateSuite } = await import("./tools/create-suite.js");
692
905
  return {
693
- content: [{ type: "text", text: await handleCreateSuite(params) }],
906
+ content: [{ type: "text", text: await handleUpdateTestSuite(params) }],
694
907
  };
695
908
  })
696
909
  );
697
910
 
698
911
  server.tool(
699
- "platform_update_suite",
700
- "Update an existing suite (run plan).",
912
+ "platform_rename_test_suite",
913
+ "Rename a test suite.",
701
914
  {
702
- id: z.string().describe("The suite ID to update"),
703
- name: z.string().optional().describe("New suite name"),
704
- description: z.string().optional().describe("New description"),
705
- scenarioIds: z.array(z.string()).optional().describe("New array of scenario IDs"),
706
- targets: z.string().optional().describe("New JSON array of targets"),
707
- repeatCount: z.number().optional().describe("New repeat count"),
708
- labels: z.array(z.string()).optional().describe("New labels"),
915
+ id: z.string().describe("The test suite ID"),
916
+ name: z.string().describe("The new name"),
709
917
  },
710
- withToolLogging("platform_update_suite", async (params) => {
918
+ withToolLogging("platform_rename_test_suite", async (params) => {
711
919
  requireApiKey();
712
- const { handleUpdateSuite } = await import("./tools/update-suite.js");
920
+ const { handleRenameTestSuite } = await import(
921
+ "./tools/rename-test-suite.js"
922
+ );
713
923
  return {
714
- content: [{ type: "text", text: await handleUpdateSuite(params) }],
924
+ content: [{ type: "text", text: await handleRenameTestSuite(params) }],
715
925
  };
716
926
  })
717
927
  );
718
928
 
719
929
  server.tool(
720
- "platform_run_suite",
721
- "Trigger a suite run. Schedules all scenario x target x repeat jobs for execution.",
930
+ "platform_archive_test_suite",
931
+ "Archive a test suite. The scenarios filed in it are archived with it.",
722
932
  {
723
- id: z.string().describe("The suite ID to run"),
933
+ id: z.string().describe("The test suite ID to archive"),
724
934
  },
725
- withToolLogging("platform_run_suite", async (params) => {
935
+ withToolLogging("platform_archive_test_suite", async (params) => {
726
936
  requireApiKey();
727
- const { handleRunSuite } = await import("./tools/run-suite.js");
937
+ const { handleArchiveTestSuite } = await import(
938
+ "./tools/archive-test-suite.js"
939
+ );
728
940
  return {
729
- content: [{ type: "text", text: await handleRunSuite(params) }],
941
+ content: [{ type: "text", text: await handleArchiveTestSuite(params) }],
730
942
  };
731
943
  })
732
944
  );
733
945
 
734
946
  server.tool(
735
- "platform_archive_suite",
736
- "Archive (soft-delete) a suite (run plan).",
947
+ "platform_run_test_suite",
948
+ "Run every scenario of a test suite against targets. This creates or joins the run plan named '<suite name> <target name>' unless you send a name of your own. Send more than one target to compare them in the same run, and give a target its own parameters to compare one agent on two models.",
737
949
  {
738
- id: z.string().describe("The suite ID to archive"),
950
+ id: z.string().describe("The test suite ID to run"),
951
+ targets: z
952
+ .array(runPlanTargetSchema)
953
+ .describe(
954
+ "What to run the scenarios against. Every target runs every scenario, and the results show one column for each.",
955
+ ),
956
+ name: z
957
+ .string()
958
+ .optional()
959
+ .describe(
960
+ "The run plan to run. Omit to let the server name it after the suite and the target.",
961
+ ),
962
+ repeatCount: z
963
+ .number()
964
+ .int()
965
+ .min(1)
966
+ .max(5)
967
+ .optional()
968
+ .describe("How many times to run each scenario against each target (1 to 5, default 1)"),
969
+ simulatorModel: z
970
+ .string()
971
+ .optional()
972
+ .describe("Model that plays the user. Omit for the project default."),
973
+ judgeModel: z
974
+ .string()
975
+ .optional()
976
+ .describe("Model that judges the criteria. Omit for the project default."),
977
+ parameters: runParametersSchema
978
+ .optional()
979
+ .describe(
980
+ "Values for the parameters the scenarios declare, by name. They apply to every target; a target that names the same parameter overrides them for itself.",
981
+ ),
982
+ note: z
983
+ .string()
984
+ .max(200)
985
+ .optional()
986
+ .describe("One short line saying why this run was started (max 200 characters)"),
739
987
  },
740
- withToolLogging("platform_archive_suite", async (params) => {
988
+ withToolLogging("platform_run_test_suite", async (params) => {
741
989
  requireApiKey();
742
- const { handleArchiveSuite } = await import("./tools/archive-suite.js");
990
+ const { handleRunTestSuite } = await import("./tools/run-test-suite.js");
743
991
  return {
744
- content: [{ type: "text", text: await handleArchiveSuite(params) }],
992
+ content: [{ type: "text", text: await handleRunTestSuite(params) }],
745
993
  };
746
994
  })
747
995
  );
@@ -769,7 +1017,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
769
1017
 
770
1018
  server.tool(
771
1019
  "platform_get_simulation_run",
772
- "Get full details of a simulation run including conversation messages, results, costs, and verdict.",
1020
+ "Get full details of a simulation run including conversation messages, results, costs, the verdict and the result of every evaluator attached to its test suite or run plan.",
773
1021
  {
774
1022
  scenarioRunId: z.string().describe("The simulation run ID"),
775
1023
  format: z.enum(["digest", "json"]).optional().describe("Output format"),
@@ -941,7 +1189,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
941
1189
 
942
1190
  server.tool(
943
1191
  "platform_list_agents",
944
- "List all agents in the LangWatch project with their names, types, and IDs.",
1192
+ "List all agents in the LangWatch project with their names, types and IDs. A connected agent (one that registered itself from code with connectAgent or connect_agent) also shows its environment, whether it is online, how many instances are connected, and its owner.",
945
1193
  {},
946
1194
  withToolLogging("platform_list_agents", async () => {
947
1195
  requireApiKey();
@@ -954,7 +1202,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
954
1202
 
955
1203
  server.tool(
956
1204
  "platform_get_agent",
957
- "Get detailed information about a specific agent by its ID, including its configuration.",
1205
+ "Get detailed information about a specific agent by its ID, including its configuration. A connected agent also shows its environment, status, the run parameters it declared (with options and defaults) and the instances connected right now.",
958
1206
  {
959
1207
  id: z.string().describe("The agent ID"),
960
1208
  },
@@ -969,7 +1217,7 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
969
1217
 
970
1218
  server.tool(
971
1219
  "platform_create_agent",
972
- "Create a new agent. Supported types: 'signature' (LLM prompt), 'code' (Python), 'workflow' (sub-workflow), 'http' (external API).",
1220
+ "Create a new agent. Supported types: 'signature' (LLM prompt), 'code' (Python), 'workflow' (sub-workflow), 'http' (external API). A 'connected' agent is not created here: it registers itself from code, with connectAgent from langwatch/agent in TypeScript or langwatch.connect_agent in Python, and appears in the list once that process runs.",
973
1221
  {
974
1222
  name: z.string().describe("Agent name"),
975
1223
  type: z.enum(["signature", "code", "workflow", "http"]).describe("Agent type"),
@@ -1021,10 +1269,27 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
1021
1269
 
1022
1270
  server.tool(
1023
1271
  "platform_run_agent",
1024
- "Execute an agent with JSON input. HTTP agents call their configured URL directly; workflow-linked agents execute via the workflow engine.",
1272
+ "Run one turn of an agent. A connected agent runs through the platform relay on a live instance: give `message` (one user turn) or `input` with a `messages` list, plus `parameters` and `threadId` to continue a conversation. HTTP agents call their configured URL directly; workflow-linked agents execute via the workflow engine.",
1025
1273
  {
1026
1274
  id: z.string().describe("The agent ID to run"),
1027
- input: z.string().optional().describe("Input data as a JSON object string"),
1275
+ input: z
1276
+ .string()
1277
+ .optional()
1278
+ .describe(
1279
+ "Input data as a JSON object string. For a connected agent it is the relay body: messages (OpenAI style), and optionally threadId, session and params.",
1280
+ ),
1281
+ message: z
1282
+ .string()
1283
+ .optional()
1284
+ .describe("One user message to send to a connected agent, instead of input"),
1285
+ parameters: z
1286
+ .record(z.string(), z.union([z.string(), z.number(), z.boolean()]))
1287
+ .optional()
1288
+ .describe("Run parameter values for a connected agent, by name"),
1289
+ threadId: z
1290
+ .string()
1291
+ .optional()
1292
+ .describe("Continue a conversation on a connected agent"),
1028
1293
  },
1029
1294
  withToolLogging("platform_run_agent", async (params) => {
1030
1295
  requireApiKey();
@@ -1035,6 +1300,20 @@ NOTE: Scenarios can be created two ways. Determine which approach the user needs
1035
1300
  })
1036
1301
  );
1037
1302
 
1303
+ server.tool(
1304
+ "platform_test_agent",
1305
+ "Test an agent with one scripted scenario run: the user sends \"ping\", the agent answers, and the run succeeds when the answer arrives. No model is used, and no scenario, run plan or test suite is added to the project. Answers at once with the scenario run id to follow with platform_get_simulation_run; the run itself is asynchronous.",
1306
+ {
1307
+ id: z.string().describe("The agent ID to test"),
1308
+ },
1309
+ withToolLogging("platform_test_agent", async (params) => {
1310
+ requireApiKey();
1311
+ return {
1312
+ content: [{ type: "text", text: await handleTestAgent(params) }],
1313
+ };
1314
+ })
1315
+ );
1316
+
1038
1317
  // --- Platform Dashboard Tools (require API key) ---
1039
1318
 
1040
1319
  server.tool(