@langwatch/mcp-server 1.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.
- package/.python-version +1 -0
- package/CHANGELOG.md +110 -0
- package/CONTRIBUTING.md +1 -1
- package/README.md +86 -4
- package/dist/{archive-project-MK3673A7.js → archive-project-W3IKI7UL.js} +5 -5
- package/dist/archive-run-plan-474OKHHU.js +16 -0
- package/dist/{archive-scenario-KATSXRIN.js → archive-scenario-V5UNYK26.js} +5 -5
- package/dist/archive-test-suite-O7FLDMXK.js +16 -0
- package/dist/{assign-prompt-tag-E3C5W6PI.js → assign-prompt-tag-Q46WRB6P.js} +4 -4
- package/dist/chunk-56OGTS24.js +44 -0
- package/dist/chunk-6DJDEXET.js +63 -0
- package/dist/chunk-7E4FZIYJ.js +15 -0
- package/dist/{chunk-J45SVWPR.js → chunk-AN4OUA4D.js} +2 -2
- package/dist/chunk-CKNAH5TG.js +74 -0
- package/dist/{chunk-CE6QMZOP.js → chunk-EYXMLE5X.js} +86 -11
- package/dist/{chunk-XY4STOBE.js → chunk-G4ZAZTZW.js} +2 -2
- package/dist/{chunk-5UWY24RP.js → chunk-GELJ2MQR.js} +12 -3
- package/dist/chunk-HV4DWKBI.js +46 -0
- package/dist/chunk-JR6DJZOU.js +49 -0
- package/dist/{chunk-DAIQKS27.js → chunk-JTQJRNFJ.js} +2 -2
- package/dist/{chunk-WWMDHZZV.js → chunk-K37ZNPXK.js} +2 -2
- package/dist/{chunk-EU22L52K.js → chunk-OJLYYJWI.js} +2 -2
- package/dist/{chunk-Y3HDZ6OW.js → chunk-QAMP3CGR.js} +2 -2
- package/dist/{chunk-VYPSKRPN.js → chunk-QRO35SOM.js} +347 -146
- package/dist/{chunk-O3LWVN67.js → chunk-RVAE2MEA.js} +2 -2
- package/dist/{chunk-UXXA4D6K.js → chunk-S4GVX36W.js} +2 -2
- package/dist/{chunk-BJ7B4R26.js → chunk-SAS5OB7F.js} +18 -2
- package/dist/{chunk-RYRB2YVJ.js → chunk-SR6QPRIE.js} +2 -2
- package/dist/chunk-UF6XIGYR.js +58 -0
- package/dist/{chunk-T7WJVS2N.js → chunk-UFOYFYI7.js} +2 -2
- package/dist/chunk-UJPXFA26.js +267 -0
- package/dist/{chunk-KOO5733S.js → chunk-WT4PO4A3.js} +8 -2
- package/dist/config.js +2 -2
- package/dist/{create-agent-7W57J7MT.js → create-agent-JFJ2K6X3.js} +5 -5
- package/dist/{create-annotation-W2XELBCQ.js → create-annotation-EUXXQ6NS.js} +5 -5
- package/dist/{create-api-key-MKRXKFJM.js → create-api-key-BGBB76JX.js} +5 -5
- package/dist/{create-dashboard-QGL7IODC.js → create-dashboard-Z76AIPLS.js} +5 -5
- package/dist/{create-dataset-3PH7A4HG.js → create-dataset-FD5VAXU4.js} +5 -5
- package/dist/{create-dataset-records-4DZS3VRL.js → create-dataset-records-WQUXXOHI.js} +5 -5
- package/dist/{create-evaluator-YWH6NC4M.js → create-evaluator-6NV33Z52.js} +5 -5
- package/dist/create-mcp-server.js +9 -4
- package/dist/{create-project-BOBQ32C5.js → create-project-ZQ7TFIO2.js} +5 -5
- package/dist/{create-prompt-FLCFGITE.js → create-prompt-BS6QRCUU.js} +4 -4
- package/dist/{create-prompt-tag-EMPNIKEA.js → create-prompt-tag-FCQ5ELSQ.js} +4 -4
- package/dist/{create-scenario-BSBIHUL6.js → create-scenario-DRPTPXU3.js} +12 -5
- package/dist/create-test-suite-ZIZCCFJ4.js +37 -0
- package/dist/{delete-agent-7MSYNY5E.js → delete-agent-SA4JMUZF.js} +5 -5
- package/dist/{delete-annotation-ZT5IGN24.js → delete-annotation-X3KKK7QY.js} +5 -5
- package/dist/{delete-dashboard-BTIY5S3L.js → delete-dashboard-LYMRPF27.js} +5 -5
- package/dist/{delete-dataset-U45HQHPR.js → delete-dataset-W6EZ44HY.js} +5 -5
- package/dist/{delete-dataset-records-R43IMAWB.js → delete-dataset-records-7CNOTALB.js} +5 -5
- package/dist/{delete-evaluator-U6JMQ6C7.js → delete-evaluator-RKYC4TNB.js} +5 -5
- package/dist/{delete-prompt-tag-4EV4TDHB.js → delete-prompt-tag-QB3OCZUV.js} +4 -4
- package/dist/{delete-workflow-5QYALXEL.js → delete-workflow-NG2VXYYM.js} +5 -5
- package/dist/{discover-evaluator-schema-MRGHVNTT.js → discover-evaluator-schema-ESSHQGPY.js} +58 -321
- package/dist/{discover-scenario-schema-UPBTCWPG.js → discover-scenario-schema-W7KBXA4J.js} +54 -2
- package/dist/{discover-schema-ICGXPHUW.js → discover-schema-L6QDPON3.js} +2 -2
- package/dist/get-agent-7THF7HCU.js +66 -0
- package/dist/{get-analytics-PVERWOA5.js → get-analytics-R6AZD3AW.js} +4 -4
- package/dist/{get-dashboard-ZZPC3WMD.js → get-dashboard-P4WWVHFU.js} +5 -5
- package/dist/{get-dataset-25TB2LQH.js → get-dataset-AKTYSQDI.js} +5 -5
- package/dist/{get-evaluator-3FTRQKEC.js → get-evaluator-RZ44UIN6.js} +5 -5
- package/dist/{get-project-5OAXYC57.js → get-project-QSQEAB33.js} +5 -5
- package/dist/get-prompt-GVKXF5NL.js +100 -0
- package/dist/get-run-plan-NY7J5GRE.js +61 -0
- package/dist/{get-scenario-DSSTR6FI.js → get-scenario-Q3LLQE4T.js} +9 -5
- package/dist/{get-simulation-run-RQPMS2MC.js → get-simulation-run-WINEYDMK.js} +14 -5
- package/dist/get-test-suite-4HKA7OKS.js +49 -0
- package/dist/{get-trace-O5VT3NAC.js → get-trace-RCUV7N4O.js} +4 -4
- package/dist/{get-workflow-3EZDOEIF.js → get-workflow-EEOC7Y45.js} +5 -5
- package/dist/http-security-EU3CHTY7.js +26 -0
- package/dist/{http-server-2ACJ3CXW.js → http-server-WCDUARTH.js} +1038 -693
- package/dist/index.js +6437 -4020
- package/dist/{langwatch-api-annotations-E65MI3WZ.js → langwatch-api-annotations-6DHVZWUK.js} +5 -5
- package/dist/{langwatch-api-dashboards-QCDECRCY.js → langwatch-api-dashboards-2KJL2M42.js} +5 -5
- package/dist/{langwatch-api-monitors-UIO2KTDV.js → langwatch-api-monitors-6ISFJ7WX.js} +4 -4
- package/dist/{langwatch-api-secrets-4T2EKSJ7.js → langwatch-api-secrets-OEBD6GBU.js} +4 -4
- package/dist/{langwatch-api-triggers-MPOEKPUY.js → langwatch-api-triggers-5E6DDGX6.js} +4 -4
- package/dist/langwatch-api-workflows-6UJL5U4Z.js +16 -0
- package/dist/list-agents-AMVDQPQL.js +43 -0
- package/dist/{list-annotations-CGX5BT7E.js → list-annotations-6PR5NLIC.js} +5 -5
- package/dist/{list-api-keys-JLWJPFRX.js → list-api-keys-GVPNBIHQ.js} +5 -5
- package/dist/{list-dashboards-RNIWX3TX.js → list-dashboards-Z3BZKDGF.js} +5 -5
- package/dist/{list-dataset-records-B5MHZR66.js → list-dataset-records-O67HZ35Y.js} +5 -5
- package/dist/{list-datasets-BVCAMQCP.js → list-datasets-WXZL6UMP.js} +5 -5
- package/dist/{list-evaluators-65FBTL2U.js → list-evaluators-PAX72WCN.js} +5 -5
- package/dist/{list-model-providers-GRK3I3VV.js → list-model-providers-VVLZMLCX.js} +5 -5
- package/dist/{list-projects-JLMBFMW3.js → list-projects-AQVI6SUK.js} +5 -5
- package/dist/{list-prompt-tags-UHNWDGQI.js → list-prompt-tags-WZFBJOHG.js} +4 -4
- package/dist/{list-prompts-7254B3U7.js → list-prompts-BQOHRHYT.js} +4 -4
- package/dist/list-run-plans-UBAU2DGH.js +46 -0
- package/dist/{list-scenarios-46YNICKM.js → list-scenarios-MS4IUIVM.js} +10 -7
- package/dist/{list-simulation-runs-LGNEFNPN.js → list-simulation-runs-D6YF4ZOU.js} +13 -7
- package/dist/list-test-suites-L7R2O6DR.js +35 -0
- package/dist/{list-workflows-MLQDM6CP.js → list-workflows-6MV4XJHF.js} +5 -5
- package/dist/{rename-prompt-tag-RHQMNEQL.js → rename-prompt-tag-K5LQ4CY6.js} +4 -4
- package/dist/rename-test-suite-NTYHAO5X.js +21 -0
- package/dist/report-issue-RHZZ33IS.js +885 -0
- package/dist/rerun-run-plan-MUDWJBE4.js +22 -0
- package/dist/{revoke-api-key-3Y35EMBT.js → revoke-api-key-6LAZ3QZZ.js} +5 -5
- package/dist/run-agent-CAGWGTYT.js +72 -0
- package/dist/run-plan-T4ZYOZGA.js +39 -0
- package/dist/run-test-suite-L4MZ7G4D.js +26 -0
- package/dist/{run-workflow-5LVBZYLS.js → run-workflow-DFIS23AP.js} +5 -5
- package/dist/{search-traces-U4HLOCZ2.js → search-traces-4XTJYXIK.js} +4 -4
- package/dist/{set-model-provider-V3M5CDIY.js → set-model-provider-XRNMFOVR.js} +5 -5
- package/dist/{update-agent-47RZIHS7.js → update-agent-DUWNYBNA.js} +5 -5
- package/dist/{update-dataset-G6QJJGS5.js → update-dataset-YSOMDCOB.js} +5 -5
- package/dist/{update-dataset-record-RMWLSWCS.js → update-dataset-record-DEA2ITYR.js} +5 -5
- package/dist/{update-evaluator-SFRRI5Q3.js → update-evaluator-NH7Q5GWV.js} +5 -5
- package/dist/{update-project-PI5DWBVK.js → update-project-Q26FBSYH.js} +5 -5
- package/dist/update-prompt-ZQQ2C3KU.js +143 -0
- package/dist/{update-scenario-G3IU2UIB.js → update-scenario-WUX34JAR.js} +12 -5
- package/package.json +8 -9
- package/pyproject.toml +5 -5
- package/src/__tests__/agent-tools.unit.test.ts +257 -0
- package/src/__tests__/all-tools.integration.test.ts +321 -75
- package/src/__tests__/config.unit.test.ts +33 -0
- package/src/__tests__/docker.integration.test.ts +91 -7
- package/src/__tests__/documentation-fetch-security.integration.test.ts +8 -2
- package/src/__tests__/feature-map-tool-drift.unit.test.ts +93 -0
- package/src/__tests__/http-security-origin.unit.test.ts +97 -0
- package/src/__tests__/http-security-sessions.unit.test.ts +209 -0
- package/src/__tests__/http-security-verifier.unit.test.ts +248 -0
- package/src/__tests__/http-server-allocation.integration.test.ts +279 -0
- package/src/__tests__/http-server-origin.integration.test.ts +169 -0
- package/src/__tests__/http-server-session-auth.integration.test.ts +341 -0
- package/src/__tests__/http-transport.integration.test.ts +85 -87
- package/src/__tests__/integration.integration.test.ts +7 -11
- package/src/__tests__/prompt-response-types.unit.test.ts +51 -0
- package/src/__tests__/prompts.integration.test.ts +736 -0
- package/src/__tests__/run-plan-tools.unit.test.ts +565 -0
- package/src/__tests__/scenario-tools.unit.test.ts +19 -0
- package/src/__tests__/simulation-run-tools.unit.test.ts +116 -0
- package/src/__tests__/support/http-server-harness.ts +138 -0
- package/src/__tests__/test-suite-tools.unit.test.ts +615 -0
- package/src/__tests__/{tools.unit.test.ts → tools.integration.test.ts} +31 -59
- package/src/config.ts +17 -2
- package/src/create-mcp-server.ts +334 -55
- package/src/http-security.ts +492 -0
- package/src/http-server.ts +536 -152
- package/src/index.ts +49 -2
- package/src/langwatch-api-agents.ts +196 -12
- package/src/langwatch-api-run-plans.ts +157 -0
- package/src/langwatch-api-scenarios.ts +9 -0
- package/src/langwatch-api-simulation-runs.ts +5 -0
- package/src/langwatch-api-test-suites.ts +135 -0
- package/src/langwatch-api.ts +62 -12
- package/src/schemas/index.ts +5 -0
- package/src/schemas/run-plan.ts +92 -0
- package/src/schemas/suite-fields.ts +149 -0
- package/src/tools/archive-run-plan.ts +12 -0
- package/src/tools/archive-test-suite.ts +12 -0
- package/src/tools/create-scenario.ts +8 -0
- package/src/tools/create-test-suite.ts +43 -0
- package/src/tools/discover-evaluator-schema.ts +2 -2
- package/src/tools/discover-scenario-schema.ts +54 -0
- package/src/tools/format-run-plan.ts +68 -0
- package/src/tools/format-scenario.ts +14 -0
- package/src/tools/format-suite-details.ts +96 -0
- package/src/tools/get-agent.ts +45 -3
- package/src/tools/get-prompt.ts +108 -24
- package/src/tools/get-run-plan.ts +62 -0
- package/src/tools/get-scenario.ts +3 -0
- package/src/tools/get-simulation-run.ts +9 -0
- package/src/tools/get-test-suite.ts +49 -0
- package/src/tools/list-agents.ts +20 -3
- package/src/tools/list-run-plans.ts +47 -0
- package/src/tools/list-scenarios.ts +12 -2
- package/src/tools/list-simulation-runs.ts +6 -2
- package/src/tools/list-test-suites.ts +35 -0
- package/src/tools/rename-test-suite.ts +18 -0
- package/src/tools/rerun-run-plan.ts +21 -0
- package/src/tools/run-agent.ts +54 -6
- package/src/tools/run-plan.ts +49 -0
- package/src/tools/run-test-suite.ts +30 -0
- package/src/tools/test-agent.ts +18 -0
- package/src/tools/update-prompt.ts +195 -10
- package/src/tools/update-scenario.ts +8 -0
- package/src/tools/update-test-suite.ts +45 -0
- package/tsconfig.json +7 -2
- package/uv.lock +418 -298
- package/dist/archive-suite-C3KX6IIZ.js +0 -16
- package/dist/chunk-WDH5P5RF.js +0 -48
- package/dist/create-suite-TWF6BBR4.js +0 -39
- package/dist/get-agent-7QJVPH72.js +0 -29
- package/dist/get-prompt-I753LZKZ.js +0 -46
- package/dist/get-suite-GTOCY5PL.js +0 -45
- package/dist/langwatch-api-workflows-5HRZ7HR4.js +0 -16
- package/dist/list-agents-TIJTP566.js +0 -33
- package/dist/list-suites-5VK4HXEK.js +0 -43
- package/dist/report-issue-DXJ6TPTC.js +0 -412
- package/dist/run-agent-RBSIFKRT.js +0 -37
- package/dist/run-suite-K3DNQ3SE.js +0 -30
- package/dist/update-prompt-RQFTEO7Y.js +0 -25
- package/dist/update-suite-TRGGLNR7.js +0 -40
- package/pnpm-workspace.yaml +0 -34
- package/src/langwatch-api-suites.ts +0 -118
- package/src/tools/archive-suite.ts +0 -12
- package/src/tools/create-suite.ts +0 -42
- package/src/tools/get-suite.ts +0 -47
- package/src/tools/list-suites.ts +0 -43
- package/src/tools/run-suite.ts +0 -28
- package/src/tools/update-suite.ts +0 -44
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { renameTestSuite as apiRenameTestSuite } from "../langwatch-api-test-suites.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Handles the platform_rename_test_suite MCP tool invocation.
|
|
5
|
+
*/
|
|
6
|
+
export async function handleRenameTestSuite(params: {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
}): Promise<string> {
|
|
10
|
+
const suite = await apiRenameTestSuite(params);
|
|
11
|
+
|
|
12
|
+
return [
|
|
13
|
+
`Test suite ${suite.id} is now named "${suite.name}".`,
|
|
14
|
+
"",
|
|
15
|
+
`**Slug**: ${suite.slug}`,
|
|
16
|
+
`**Scenarios**: ${suite.scenarioCount}`,
|
|
17
|
+
].join("\n");
|
|
18
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {
|
|
2
|
+
rerunRunPlan as apiRerunRunPlan,
|
|
3
|
+
type RunParameters,
|
|
4
|
+
} from "../langwatch-api-run-plans.js";
|
|
5
|
+
import { formatRunPlanRun } from "./format-run-plan.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Handles the platform_rerun_run_plan MCP tool invocation.
|
|
9
|
+
*/
|
|
10
|
+
export async function handleRerunRunPlan(params: {
|
|
11
|
+
id: string;
|
|
12
|
+
parameters?: RunParameters;
|
|
13
|
+
note?: string;
|
|
14
|
+
}): Promise<string> {
|
|
15
|
+
const result = await apiRerunRunPlan(params.id, {
|
|
16
|
+
parameters: params.parameters,
|
|
17
|
+
note: params.note,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
return formatRunPlanRun(result);
|
|
21
|
+
}
|
package/src/tools/run-agent.ts
CHANGED
|
@@ -1,22 +1,56 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import {
|
|
3
|
+
runAgent as apiRunAgent,
|
|
4
|
+
type AgentCallParams,
|
|
5
|
+
} from "../langwatch-api-agents.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A scalar and an array both parse as JSON, and either one reaches the agent
|
|
9
|
+
* as a body it cannot read, so only an object passes.
|
|
10
|
+
*/
|
|
11
|
+
const jsonObjectSchema = z.looseObject({});
|
|
12
|
+
type JsonObject = z.infer<typeof jsonObjectSchema>;
|
|
2
13
|
|
|
3
14
|
/**
|
|
4
15
|
* Handles the platform_run_agent MCP tool invocation.
|
|
16
|
+
*
|
|
17
|
+
* @see specs/mcp-server/agent-tools.feature
|
|
5
18
|
*/
|
|
6
|
-
export async function handleRunAgent(
|
|
19
|
+
export async function handleRunAgent({
|
|
20
|
+
id,
|
|
21
|
+
input,
|
|
22
|
+
message,
|
|
23
|
+
parameters,
|
|
24
|
+
threadId,
|
|
25
|
+
}: {
|
|
7
26
|
id: string;
|
|
8
27
|
input?: string;
|
|
28
|
+
message?: string;
|
|
29
|
+
parameters?: AgentCallParams;
|
|
30
|
+
threadId?: string;
|
|
9
31
|
}): Promise<string> {
|
|
10
|
-
let parsedInput:
|
|
11
|
-
if (
|
|
32
|
+
let parsedInput: JsonObject = {};
|
|
33
|
+
if (input) {
|
|
34
|
+
let decoded: unknown;
|
|
12
35
|
try {
|
|
13
|
-
|
|
36
|
+
decoded = JSON.parse(input);
|
|
14
37
|
} catch {
|
|
15
38
|
return "Error: `input` must be a valid JSON object.";
|
|
16
39
|
}
|
|
40
|
+
const result = jsonObjectSchema.safeParse(decoded);
|
|
41
|
+
if (!result.success) {
|
|
42
|
+
return "Error: `input` must be a valid JSON object.";
|
|
43
|
+
}
|
|
44
|
+
parsedInput = result.data;
|
|
17
45
|
}
|
|
18
46
|
|
|
19
|
-
const { agentType, result } = await apiRunAgent(
|
|
47
|
+
const { agentType, result } = await apiRunAgent({
|
|
48
|
+
id,
|
|
49
|
+
input: parsedInput,
|
|
50
|
+
message,
|
|
51
|
+
parameters,
|
|
52
|
+
threadId,
|
|
53
|
+
});
|
|
20
54
|
|
|
21
55
|
const lines: string[] = [];
|
|
22
56
|
lines.push(`Agent executed successfully (type: ${agentType}).\n`);
|
|
@@ -30,6 +64,20 @@ export async function handleRunAgent(params: {
|
|
|
30
64
|
lines.push("**Result:**");
|
|
31
65
|
lines.push(JSON.stringify(result, null, 2));
|
|
32
66
|
}
|
|
67
|
+
if (agentType === "connected") {
|
|
68
|
+
const instance = (result as { instance?: { hostname?: string; label?: string | null } }).instance;
|
|
69
|
+
const durationMs = (result as { durationMs?: number }).durationMs;
|
|
70
|
+
if (instance?.hostname) {
|
|
71
|
+
const label = instance.label ? ` (${instance.label})` : "";
|
|
72
|
+
lines.push(`\n**Instance:** ${instance.hostname}${label}`);
|
|
73
|
+
}
|
|
74
|
+
if (typeof durationMs === "number") lines.push(`**Duration:** ${durationMs} ms`);
|
|
75
|
+
const session = (result as { session?: unknown }).session;
|
|
76
|
+
if (session !== undefined && session !== null) {
|
|
77
|
+
lines.push("**Session:**");
|
|
78
|
+
lines.push(JSON.stringify(session, null, 2));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
33
81
|
}
|
|
34
82
|
|
|
35
83
|
return lines.join("\n");
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {
|
|
2
|
+
runRunPlan as apiRunRunPlan,
|
|
3
|
+
type RunParameters,
|
|
4
|
+
type RunPlanScope,
|
|
5
|
+
type RunPlanTarget,
|
|
6
|
+
} from "../langwatch-api-run-plans.js";
|
|
7
|
+
import { toWireTargets } from "../schemas/run-plan.js";
|
|
8
|
+
import {
|
|
9
|
+
type EvaluatorAttachmentInput,
|
|
10
|
+
toWireAttachments,
|
|
11
|
+
} from "../schemas/suite-fields.js";
|
|
12
|
+
import { formatRunPlanRun } from "./format-run-plan.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Handles the platform_run_plan MCP tool invocation.
|
|
16
|
+
*/
|
|
17
|
+
export async function handleRunPlan(params: {
|
|
18
|
+
name?: string;
|
|
19
|
+
scope: RunPlanScope;
|
|
20
|
+
scenarioIds?: string[];
|
|
21
|
+
targets: RunPlanTarget[];
|
|
22
|
+
repeatCount?: number;
|
|
23
|
+
simulatorModel?: string;
|
|
24
|
+
judgeModel?: string;
|
|
25
|
+
evaluators?: EvaluatorAttachmentInput[];
|
|
26
|
+
parameters?: RunParameters;
|
|
27
|
+
note?: string;
|
|
28
|
+
idempotencyKey?: string;
|
|
29
|
+
}): Promise<string> {
|
|
30
|
+
const result = await apiRunRunPlan({
|
|
31
|
+
name: params.name,
|
|
32
|
+
config: {
|
|
33
|
+
scope: params.scope,
|
|
34
|
+
targets: toWireTargets(params.targets),
|
|
35
|
+
repeatCount: params.repeatCount,
|
|
36
|
+
simulatorModel: params.simulatorModel,
|
|
37
|
+
judgeModel: params.judgeModel,
|
|
38
|
+
scenarioIds: params.scenarioIds,
|
|
39
|
+
...(params.evaluators !== undefined
|
|
40
|
+
? { evaluators: toWireAttachments(params.evaluators) }
|
|
41
|
+
: {}),
|
|
42
|
+
},
|
|
43
|
+
idempotencyKey: params.idempotencyKey,
|
|
44
|
+
parameters: params.parameters,
|
|
45
|
+
note: params.note,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
return formatRunPlanRun(result);
|
|
49
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
RunParameters,
|
|
3
|
+
RunPlanTarget,
|
|
4
|
+
} from "../langwatch-api-run-plans.js";
|
|
5
|
+
import { toWireTargets } from "../schemas/run-plan.js";
|
|
6
|
+
import { runTestSuite as apiRunTestSuite } from "../langwatch-api-test-suites.js";
|
|
7
|
+
import { formatRunPlanRun } from "./format-run-plan.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Handles the platform_run_test_suite MCP tool invocation.
|
|
11
|
+
*/
|
|
12
|
+
export async function handleRunTestSuite(params: {
|
|
13
|
+
id: string;
|
|
14
|
+
targets: RunPlanTarget[];
|
|
15
|
+
name?: string;
|
|
16
|
+
repeatCount?: number;
|
|
17
|
+
simulatorModel?: string;
|
|
18
|
+
judgeModel?: string;
|
|
19
|
+
parameters?: RunParameters;
|
|
20
|
+
note?: string;
|
|
21
|
+
idempotencyKey?: string;
|
|
22
|
+
}): Promise<string> {
|
|
23
|
+
const { targets, ...rest } = params;
|
|
24
|
+
const result = await apiRunTestSuite({
|
|
25
|
+
...rest,
|
|
26
|
+
targets: toWireTargets(targets),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
return formatRunPlanRun(result);
|
|
30
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { testAgent as apiTestAgent } from "../langwatch-api-agents.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Handles the platform_test_agent MCP tool invocation.
|
|
5
|
+
*
|
|
6
|
+
* @see specs/agents/agent-test-run.feature
|
|
7
|
+
*/
|
|
8
|
+
export async function handleTestAgent(params: { id: string }): Promise<string> {
|
|
9
|
+
const run = await apiTestAgent(params.id);
|
|
10
|
+
return [
|
|
11
|
+
"Test run scheduled. The user sends \"ping\"; the run succeeds when the agent answers.",
|
|
12
|
+
"",
|
|
13
|
+
`**Scenario run ID:** ${run.scenarioRunId}`,
|
|
14
|
+
`**Batch run ID:** ${run.batchRunId}`,
|
|
15
|
+
"",
|
|
16
|
+
`Follow it with platform_get_simulation_run using scenarioRunId "${run.scenarioRunId}".`,
|
|
17
|
+
].join("\n");
|
|
18
|
+
}
|
|
@@ -1,10 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
getPrompt as apiGetPrompt,
|
|
3
|
+
getPromptVersions as apiGetPromptVersions,
|
|
4
|
+
updatePrompt as apiUpdatePrompt,
|
|
5
|
+
type PromptDetailResponse,
|
|
6
|
+
type PromptTag,
|
|
7
|
+
} from "../langwatch-api.js";
|
|
2
8
|
|
|
3
9
|
/**
|
|
4
10
|
* Handles the platform_update_prompt MCP tool invocation.
|
|
5
11
|
*
|
|
6
|
-
* Updates an existing prompt via the PUT endpoint.
|
|
7
|
-
*
|
|
12
|
+
* Updates an existing prompt via the PUT endpoint. Every update with a
|
|
13
|
+
* commitMessage creates a new version automatically. The mutation response
|
|
14
|
+
* does not carry the tags the server actually applied, so this re-fetches
|
|
15
|
+
* the prompt via getPrompt: the GET response is the prompt's latest version
|
|
16
|
+
* flattened to the top level (version, versionId, commitMessage) plus a
|
|
17
|
+
* `tags` array of { name, versionId } naming which version each tag points
|
|
18
|
+
* to. The new version is identified by matching the request's commitMessage
|
|
19
|
+
* against that top level (falling back to the versions listing), and
|
|
20
|
+
* deployment state is derived from the tags that point at it — never from
|
|
21
|
+
* the request's tags directly.
|
|
8
22
|
*/
|
|
9
23
|
export async function handleUpdatePrompt(params: {
|
|
10
24
|
idOrHandle: string;
|
|
@@ -15,17 +29,188 @@ export async function handleUpdatePrompt(params: {
|
|
|
15
29
|
}): Promise<string> {
|
|
16
30
|
const { idOrHandle, ...data } = params;
|
|
17
31
|
|
|
18
|
-
|
|
32
|
+
let updated;
|
|
33
|
+
try {
|
|
34
|
+
updated = await apiUpdatePrompt(idOrHandle, data);
|
|
35
|
+
} catch (err) {
|
|
36
|
+
// The platform commits the version before assigning tags, so a
|
|
37
|
+
// rejection while tags were requested may still have created a version.
|
|
38
|
+
// Re-fetch to find out rather than reporting a bare failure.
|
|
39
|
+
if (params.tags && params.tags.length > 0) {
|
|
40
|
+
return renderTagFailure({ idOrHandle, params });
|
|
41
|
+
}
|
|
42
|
+
throw err;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return renderUpdateSuccess({ idOrHandle, params, updated });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface IdentifiedVersion {
|
|
49
|
+
version?: number;
|
|
50
|
+
versionId?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Finds the version created by this update. The GET detail response IS the
|
|
55
|
+
* latest version, so a top-level commitMessage match identifies it directly;
|
|
56
|
+
* otherwise (e.g. a concurrent update landed after ours) the versions
|
|
57
|
+
* listing is consulted, best-effort.
|
|
58
|
+
*/
|
|
59
|
+
async function identifyNewVersion({
|
|
60
|
+
idOrHandle,
|
|
61
|
+
prompt,
|
|
62
|
+
commitMessage,
|
|
63
|
+
}: {
|
|
64
|
+
idOrHandle: string;
|
|
65
|
+
prompt: PromptDetailResponse;
|
|
66
|
+
commitMessage: string;
|
|
67
|
+
}): Promise<IdentifiedVersion | undefined> {
|
|
68
|
+
if (prompt.commitMessage === commitMessage) {
|
|
69
|
+
return { version: prompt.version, versionId: prompt.versionId };
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
const versions = await apiGetPromptVersions(idOrHandle);
|
|
73
|
+
const match = (Array.isArray(versions) ? versions : []).find(
|
|
74
|
+
(v) => v.commitMessage === commitMessage
|
|
75
|
+
);
|
|
76
|
+
if (match) return { version: match.version, versionId: match.versionId };
|
|
77
|
+
} catch {
|
|
78
|
+
// Identification failure is reported by the caller.
|
|
79
|
+
}
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** The prompt's deployment tags, excluding the built-in "latest" tag. */
|
|
84
|
+
function deploymentTagsOf(prompt: PromptDetailResponse): PromptTag[] {
|
|
85
|
+
return (prompt.tags ?? []).filter((tag) => tag?.name !== "latest");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function renderUpdateSuccess({
|
|
89
|
+
idOrHandle,
|
|
90
|
+
params,
|
|
91
|
+
updated,
|
|
92
|
+
}: {
|
|
93
|
+
idOrHandle: string;
|
|
94
|
+
params: { commitMessage: string; tags?: string[] };
|
|
95
|
+
updated?: { id?: string; handle?: string };
|
|
96
|
+
}): Promise<string> {
|
|
97
|
+
let prompt;
|
|
98
|
+
try {
|
|
99
|
+
prompt = await apiGetPrompt(idOrHandle, {
|
|
100
|
+
version: undefined,
|
|
101
|
+
tag: undefined,
|
|
102
|
+
});
|
|
103
|
+
} catch {
|
|
104
|
+
// The update itself succeeded; a failed confirmation read must not
|
|
105
|
+
// surface as a tool failure — that invites a dangerous retry.
|
|
106
|
+
const lines: string[] = [];
|
|
107
|
+
lines.push("Prompt updated successfully!\n");
|
|
108
|
+
if (updated?.id) lines.push(`**ID**: ${updated.id}`);
|
|
109
|
+
if (updated?.handle) lines.push(`**Handle**: ${updated.handle}`);
|
|
110
|
+
lines.push(`**Commit**: ${params.commitMessage}`);
|
|
111
|
+
lines.push(
|
|
112
|
+
`**Note**: the update succeeded, but version and deployment details are unavailable (confirmation read failed). Run platform_get_prompt to inspect the current state. Do not retry the update.`
|
|
113
|
+
);
|
|
114
|
+
return lines.join("\n");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const newVersion = await identifyNewVersion({
|
|
118
|
+
idOrHandle,
|
|
119
|
+
prompt,
|
|
120
|
+
commitMessage: params.commitMessage,
|
|
121
|
+
});
|
|
19
122
|
|
|
20
123
|
const lines: string[] = [];
|
|
21
124
|
lines.push("Prompt updated successfully!\n");
|
|
22
|
-
if (
|
|
23
|
-
if (
|
|
24
|
-
if (
|
|
25
|
-
lines.push(`**Version**: v${
|
|
125
|
+
if (prompt.id) lines.push(`**ID**: ${prompt.id}`);
|
|
126
|
+
if (prompt.handle) lines.push(`**Handle**: ${prompt.handle}`);
|
|
127
|
+
if (newVersion?.version != null)
|
|
128
|
+
lines.push(`**Version**: v${newVersion.version}`);
|
|
129
|
+
if (newVersion?.versionId)
|
|
130
|
+
lines.push(`**Version ID**: ${newVersion.versionId}`);
|
|
26
131
|
lines.push(`**Commit**: ${params.commitMessage}`);
|
|
27
|
-
|
|
28
|
-
|
|
132
|
+
|
|
133
|
+
if (!newVersion) {
|
|
134
|
+
// The update succeeded but the re-fetch could not identify the new
|
|
135
|
+
// version by commit message — say so instead of silently omitting the
|
|
136
|
+
// version and deployment lines.
|
|
137
|
+
lines.push(
|
|
138
|
+
`**Note**: update succeeded, but the new version could not be identified in the re-fetched prompt — version and deployment details are unavailable. Run platform_get_prompt to inspect the current state.`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (newVersion) {
|
|
143
|
+
const allDeployments = deploymentTagsOf(prompt);
|
|
144
|
+
const newTags = allDeployments
|
|
145
|
+
.filter(
|
|
146
|
+
(tag) =>
|
|
147
|
+
newVersion.versionId != null &&
|
|
148
|
+
tag.versionId === newVersion.versionId
|
|
149
|
+
)
|
|
150
|
+
.map((tag) => tag.name);
|
|
151
|
+
if (newTags.length > 0) {
|
|
152
|
+
lines.push(`**Deployed to**: ${newTags.join(", ")}`);
|
|
153
|
+
} else {
|
|
154
|
+
lines.push(`**Deployment**: not deployed`);
|
|
155
|
+
|
|
156
|
+
// Tags pointing at other versions are untouched by this update —
|
|
157
|
+
// surface them on their own line so no line pairs a version number
|
|
158
|
+
// with a deployment tag name.
|
|
159
|
+
const otherTags = Array.from(
|
|
160
|
+
new Set(
|
|
161
|
+
allDeployments
|
|
162
|
+
.filter((tag) => tag.versionId !== newVersion.versionId)
|
|
163
|
+
.map((tag) => tag.name)
|
|
164
|
+
)
|
|
165
|
+
);
|
|
166
|
+
if (otherTags.length > 0) {
|
|
167
|
+
lines.push(
|
|
168
|
+
`**Existing deployments (untouched)**: ${otherTags.join(", ")}`
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
29
173
|
|
|
30
174
|
return lines.join("\n");
|
|
31
175
|
}
|
|
176
|
+
|
|
177
|
+
async function renderTagFailure({
|
|
178
|
+
idOrHandle,
|
|
179
|
+
params,
|
|
180
|
+
}: {
|
|
181
|
+
idOrHandle: string;
|
|
182
|
+
params: { commitMessage: string; tags?: string[] };
|
|
183
|
+
}): Promise<string> {
|
|
184
|
+
const failedTags = params.tags?.join(", ") ?? "";
|
|
185
|
+
let prompt;
|
|
186
|
+
try {
|
|
187
|
+
prompt = await apiGetPrompt(idOrHandle, {
|
|
188
|
+
version: undefined,
|
|
189
|
+
tag: undefined,
|
|
190
|
+
});
|
|
191
|
+
} catch {
|
|
192
|
+
// The confirmation read failed too — report the tag-assignment failure
|
|
193
|
+
// without claiming whether a version was created.
|
|
194
|
+
return `Prompt update failed: could not assign tag(s) ${failedTags} to "${idOrHandle}". Whether a new version was created could not be confirmed (confirmation read failed) — run platform_get_prompt before retrying.`;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const matched = await identifyNewVersion({
|
|
198
|
+
idOrHandle,
|
|
199
|
+
prompt,
|
|
200
|
+
commitMessage: params.commitMessage,
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
if (matched) {
|
|
204
|
+
const lines: string[] = [];
|
|
205
|
+
lines.push("Prompt update partially failed.\n");
|
|
206
|
+
lines.push(
|
|
207
|
+
"A new version was created, but assigning the requested tag(s) failed."
|
|
208
|
+
);
|
|
209
|
+
if (matched.versionId) lines.push(`**Version ID**: ${matched.versionId}`);
|
|
210
|
+
lines.push(`**Status**: created, untagged`);
|
|
211
|
+
lines.push(`**Failed tag(s)**: ${failedTags}`);
|
|
212
|
+
return lines.join("\n");
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return `Prompt update failed: could not assign tag(s) ${failedTags} to "${idOrHandle}".`;
|
|
216
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { updateScenario as apiUpdateScenario } from "../langwatch-api-scenarios.js";
|
|
2
|
+
import type { ScenarioFieldValues } from "../schemas/suite-fields.js";
|
|
3
|
+
import { formatScenarioFields } from "./format-scenario.js";
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Handles the platform_update_scenario MCP tool invocation.
|
|
@@ -12,6 +14,8 @@ export async function handleUpdateScenario(params: {
|
|
|
12
14
|
situation?: string;
|
|
13
15
|
criteria?: string[];
|
|
14
16
|
labels?: string[];
|
|
17
|
+
testSuiteId?: string | null;
|
|
18
|
+
fields?: ScenarioFieldValues;
|
|
15
19
|
}): Promise<string> {
|
|
16
20
|
const { scenarioId, ...data } = params;
|
|
17
21
|
const result = await apiUpdateScenario({ id: scenarioId, ...data });
|
|
@@ -27,6 +31,10 @@ export async function handleUpdateScenario(params: {
|
|
|
27
31
|
if (Array.isArray(result.labels) && result.labels.length > 0) {
|
|
28
32
|
lines.push(`**Labels**: ${result.labels.join(", ")}`);
|
|
29
33
|
}
|
|
34
|
+
if (result.testSuiteId) {
|
|
35
|
+
lines.push(`**Test suite**: ${result.testSuiteId}`);
|
|
36
|
+
}
|
|
37
|
+
lines.push(...formatScenarioFields(result.fields));
|
|
30
38
|
|
|
31
39
|
return lines.join("\n");
|
|
32
40
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { updateTestSuite as apiUpdateTestSuite } from "../langwatch-api-test-suites.js";
|
|
2
|
+
import {
|
|
3
|
+
type EvaluatorAttachmentInput,
|
|
4
|
+
type SuiteField,
|
|
5
|
+
toWireAttachments,
|
|
6
|
+
} from "../schemas/suite-fields.js";
|
|
7
|
+
import {
|
|
8
|
+
formatEvaluatorAttachments,
|
|
9
|
+
formatSuiteFields,
|
|
10
|
+
} from "./format-suite-details.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Handles the platform_update_test_suite MCP tool invocation.
|
|
14
|
+
*
|
|
15
|
+
* Any of the name, the fields and the evaluators. A field list or an
|
|
16
|
+
* evaluator list replaces the one the suite holds; a key left out keeps
|
|
17
|
+
* what the suite has.
|
|
18
|
+
*/
|
|
19
|
+
export async function handleUpdateTestSuite(params: {
|
|
20
|
+
id: string;
|
|
21
|
+
name?: string;
|
|
22
|
+
fields?: SuiteField[];
|
|
23
|
+
evaluators?: EvaluatorAttachmentInput[];
|
|
24
|
+
}): Promise<string> {
|
|
25
|
+
const suite = await apiUpdateTestSuite({
|
|
26
|
+
id: params.id,
|
|
27
|
+
...(params.name !== undefined ? { name: params.name } : {}),
|
|
28
|
+
...(params.fields !== undefined ? { fields: params.fields } : {}),
|
|
29
|
+
...(params.evaluators !== undefined
|
|
30
|
+
? { evaluators: toWireAttachments(params.evaluators) }
|
|
31
|
+
: {}),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
return [
|
|
35
|
+
`Test suite "${suite.name}" updated.`,
|
|
36
|
+
"",
|
|
37
|
+
`**ID**: ${suite.id}`,
|
|
38
|
+
`**Slug**: ${suite.slug}`,
|
|
39
|
+
`**Scenarios**: ${suite.scenarioCount}`,
|
|
40
|
+
...formatSuiteFields(suite.fields),
|
|
41
|
+
...formatEvaluatorAttachments(suite.evaluators),
|
|
42
|
+
"",
|
|
43
|
+
`**View**: ${suite.platformUrl}`,
|
|
44
|
+
].join("\n");
|
|
45
|
+
}
|
package/tsconfig.json
CHANGED
|
@@ -13,13 +13,18 @@
|
|
|
13
13
|
"skipLibCheck": true,
|
|
14
14
|
"forceConsistentCasingInFileNames": true,
|
|
15
15
|
"moduleResolution": "bundler",
|
|
16
|
-
"rootDir": "
|
|
17
|
-
// The evaluator catalog we import (langevals/ts-integration/evaluators.generated.ts)
|
|
16
|
+
"rootDir": "../..",
|
|
17
|
+
// The evaluator catalog we import (services/langevals/ts-integration/evaluators.generated.ts)
|
|
18
18
|
// is Zod-first and imports `zod`, but lives outside this package's
|
|
19
19
|
// node_modules tree, so plain resolution cannot find it. Point `zod` at this
|
|
20
20
|
// package's own copy (a declared dependency) so the type-checker resolves it.
|
|
21
21
|
// Paths are resolved relative to this tsconfig (no baseUrl — tsgo removed it).
|
|
22
22
|
"paths": { "zod": ["./node_modules/zod"] },
|
|
23
|
+
// tsup's dts worker sets `baseUrl` itself, and TypeScript 6 makes that a
|
|
24
|
+
// hard error rather than a warning — this package cannot build without the
|
|
25
|
+
// transition flag until a d.ts bundler exists that speaks the TypeScript 7
|
|
26
|
+
// API. See ADR-099.
|
|
27
|
+
"ignoreDeprecations": "6.0",
|
|
23
28
|
"isolatedModules": true,
|
|
24
29
|
"jsx": "preserve",
|
|
25
30
|
"incremental": true,
|