@langwatch/mcp-server 0.3.2 → 0.4.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/CHANGELOG.md +20 -0
- package/README.md +97 -25
- package/dist/chunk-AAQNA53E.js +28 -0
- package/dist/chunk-AAQNA53E.js.map +1 -0
- package/dist/chunk-HOPTUDCZ.js +90 -0
- package/dist/chunk-HOPTUDCZ.js.map +1 -0
- package/dist/chunk-ZXKLPC2E.js +27 -0
- package/dist/chunk-ZXKLPC2E.js.map +1 -0
- package/dist/config-FIQWQRUB.js +11 -0
- package/dist/config-FIQWQRUB.js.map +1 -0
- package/dist/create-prompt-UBC537BJ.js +22 -0
- package/dist/create-prompt-UBC537BJ.js.map +1 -0
- package/dist/discover-schema-3T52ORPB.js +446 -0
- package/dist/discover-schema-3T52ORPB.js.map +1 -0
- package/dist/get-analytics-3IFTN6MY.js +55 -0
- package/dist/get-analytics-3IFTN6MY.js.map +1 -0
- package/dist/get-prompt-2ZB5B3QC.js +48 -0
- package/dist/get-prompt-2ZB5B3QC.js.map +1 -0
- package/dist/get-trace-7IXKKCJJ.js +50 -0
- package/dist/get-trace-7IXKKCJJ.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +20003 -0
- package/dist/index.js.map +1 -0
- package/dist/list-prompts-J72LTP7Z.js +33 -0
- package/dist/list-prompts-J72LTP7Z.js.map +1 -0
- package/dist/search-traces-RW2NDHN5.js +72 -0
- package/dist/search-traces-RW2NDHN5.js.map +1 -0
- package/dist/update-prompt-G6HHZSUM.js +31 -0
- package/dist/update-prompt-G6HHZSUM.js.map +1 -0
- package/package.json +8 -8
- package/src/__tests__/config.unit.test.ts +89 -0
- package/src/__tests__/date-parsing.unit.test.ts +78 -0
- package/src/__tests__/discover-schema.unit.test.ts +118 -0
- package/src/__tests__/integration.integration.test.ts +313 -0
- package/src/__tests__/langwatch-api.unit.test.ts +309 -0
- package/src/__tests__/schemas.unit.test.ts +85 -0
- package/src/__tests__/tools.unit.test.ts +729 -0
- package/src/config.ts +31 -0
- package/src/index.ts +254 -0
- package/src/langwatch-api.ts +265 -0
- package/src/schemas/analytics-groups.ts +78 -0
- package/src/schemas/analytics-metrics.ts +179 -0
- package/src/schemas/filter-fields.ts +119 -0
- package/src/schemas/index.ts +3 -0
- package/src/tools/create-prompt.ts +29 -0
- package/src/tools/discover-schema.ts +106 -0
- package/src/tools/get-analytics.ts +71 -0
- package/src/tools/get-prompt.ts +56 -0
- package/src/tools/get-trace.ts +61 -0
- package/src/tools/list-prompts.ts +35 -0
- package/src/tools/search-traces.ts +91 -0
- package/src/tools/update-prompt.ts +44 -0
- package/src/utils/date-parsing.ts +31 -0
- package/tests/evaluations.ipynb +634 -634
- package/tests/scenario-openai.test.ts +3 -1
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { searchTraces as apiSearchTraces } from "../langwatch-api.js";
|
|
2
|
+
import { parseRelativeDate } from "../utils/date-parsing.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Handles the search_traces MCP tool invocation.
|
|
6
|
+
*
|
|
7
|
+
* Searches LangWatch traces with optional filters, text query, and date range.
|
|
8
|
+
* In digest mode (default), returns AI-readable formatted digests per trace.
|
|
9
|
+
* In json mode, returns the full raw JSON.
|
|
10
|
+
*/
|
|
11
|
+
export async function handleSearchTraces(params: {
|
|
12
|
+
query?: string;
|
|
13
|
+
filters?: Record<string, string[]>;
|
|
14
|
+
startDate?: string;
|
|
15
|
+
endDate?: string;
|
|
16
|
+
pageSize?: number;
|
|
17
|
+
scrollId?: string;
|
|
18
|
+
format?: "digest" | "json";
|
|
19
|
+
}): Promise<string> {
|
|
20
|
+
const now = Date.now();
|
|
21
|
+
const startDate = params.startDate
|
|
22
|
+
? parseRelativeDate(params.startDate)
|
|
23
|
+
: now - 86400000;
|
|
24
|
+
const endDate = params.endDate ? parseRelativeDate(params.endDate) : now;
|
|
25
|
+
const format = params.format ?? "digest";
|
|
26
|
+
|
|
27
|
+
const result = await apiSearchTraces({
|
|
28
|
+
query: params.query,
|
|
29
|
+
filters: params.filters,
|
|
30
|
+
startDate,
|
|
31
|
+
endDate,
|
|
32
|
+
pageSize: params.pageSize ?? 25,
|
|
33
|
+
scrollId: params.scrollId,
|
|
34
|
+
format,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const traces = result.traces ?? [];
|
|
38
|
+
if (traces.length === 0) {
|
|
39
|
+
return "No traces found matching your query.";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (format === "json") {
|
|
43
|
+
return JSON.stringify(result, null, 2);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const lines: string[] = [];
|
|
47
|
+
lines.push(
|
|
48
|
+
`Found ${result.pagination?.totalHits ?? traces.length} traces:\n`
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
for (const trace of traces) {
|
|
52
|
+
lines.push(`### Trace: ${trace.trace_id}`);
|
|
53
|
+
|
|
54
|
+
if (trace.formatted_trace) {
|
|
55
|
+
lines.push(trace.formatted_trace);
|
|
56
|
+
} else {
|
|
57
|
+
const inputStr = trace.input?.value
|
|
58
|
+
? String(trace.input.value)
|
|
59
|
+
: "N/A";
|
|
60
|
+
const outputStr = trace.output?.value
|
|
61
|
+
? String(trace.output.value)
|
|
62
|
+
: "N/A";
|
|
63
|
+
lines.push(
|
|
64
|
+
`- **Input**: ${inputStr.slice(0, 100)}${inputStr.length > 100 ? "..." : ""}`
|
|
65
|
+
);
|
|
66
|
+
lines.push(
|
|
67
|
+
`- **Output**: ${outputStr.slice(0, 100)}${outputStr.length > 100 ? "..." : ""}`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (trace.timestamps) {
|
|
72
|
+
lines.push(`- **Time**: ${trace.timestamps.started_at || "N/A"}`);
|
|
73
|
+
}
|
|
74
|
+
if (trace.error) {
|
|
75
|
+
lines.push(`- **Error**: ${JSON.stringify(trace.error)}`);
|
|
76
|
+
}
|
|
77
|
+
lines.push("");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (result.pagination?.scrollId) {
|
|
81
|
+
lines.push(
|
|
82
|
+
`\n**More results available.** Use scrollId: "${result.pagination.scrollId}" to get next page.`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
lines.push(
|
|
87
|
+
'\n> Tip: Use `get_trace` with a trace_id for full details. Use `search_traces` with `format: "json"` for raw data. Use `discover_schema` to see available filter fields.'
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
return lines.join("\n");
|
|
91
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {
|
|
2
|
+
updatePrompt as apiUpdatePrompt,
|
|
3
|
+
createPromptVersion as apiCreateVersion,
|
|
4
|
+
} from "../langwatch-api.js";
|
|
5
|
+
import type { PromptMutationResponse } from "../langwatch-api.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Handles the update_prompt MCP tool invocation.
|
|
9
|
+
*
|
|
10
|
+
* Updates an existing prompt or creates a new version, depending on the
|
|
11
|
+
* `createVersion` flag. Returns a confirmation with the updated details.
|
|
12
|
+
*/
|
|
13
|
+
export async function handleUpdatePrompt(params: {
|
|
14
|
+
idOrHandle: string;
|
|
15
|
+
messages?: Array<{ role: string; content: string }>;
|
|
16
|
+
model?: string;
|
|
17
|
+
modelProvider?: string;
|
|
18
|
+
commitMessage?: string;
|
|
19
|
+
createVersion?: boolean;
|
|
20
|
+
}): Promise<string> {
|
|
21
|
+
const { idOrHandle, createVersion, ...data } = params;
|
|
22
|
+
|
|
23
|
+
let result: PromptMutationResponse;
|
|
24
|
+
if (createVersion) {
|
|
25
|
+
result = await apiCreateVersion(idOrHandle, data);
|
|
26
|
+
} else {
|
|
27
|
+
result = await apiUpdatePrompt(idOrHandle, data);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const lines: string[] = [];
|
|
31
|
+
lines.push(
|
|
32
|
+
createVersion
|
|
33
|
+
? "New version created successfully!\n"
|
|
34
|
+
: "Prompt updated successfully!\n"
|
|
35
|
+
);
|
|
36
|
+
if (result.id) lines.push(`**ID**: ${result.id}`);
|
|
37
|
+
if (result.handle) lines.push(`**Handle**: ${result.handle}`);
|
|
38
|
+
if (result.latestVersionNumber != null)
|
|
39
|
+
lines.push(`**Version**: v${result.latestVersionNumber}`);
|
|
40
|
+
if (params.commitMessage)
|
|
41
|
+
lines.push(`**Commit**: ${params.commitMessage}`);
|
|
42
|
+
|
|
43
|
+
return lines.join("\n");
|
|
44
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const RELATIVE_UNITS: Record<string, number> = {
|
|
2
|
+
h: 3600000,
|
|
3
|
+
d: 86400000,
|
|
4
|
+
w: 604800000,
|
|
5
|
+
m: 2592000000,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Parses a date string that can be either a relative duration (e.g. "24h", "7d")
|
|
10
|
+
* or an ISO date string. Throws on invalid input rather than silently falling back.
|
|
11
|
+
*
|
|
12
|
+
* @returns epoch milliseconds
|
|
13
|
+
* @throws Error if the input is not a valid relative duration or parseable date string
|
|
14
|
+
*/
|
|
15
|
+
export function parseRelativeDate(input: string): number {
|
|
16
|
+
if (input === "now") return Date.now();
|
|
17
|
+
|
|
18
|
+
const match = input.match(/^(\d+)(h|d|w|m)$/);
|
|
19
|
+
if (match) {
|
|
20
|
+
const [, amount, unit] = match;
|
|
21
|
+
return Date.now() - parseInt(amount!) * (RELATIVE_UNITS[unit!] ?? 86400000);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const parsed = Date.parse(input);
|
|
25
|
+
if (Number.isNaN(parsed)) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
`Invalid date: "${input}". Use a relative duration (e.g. "24h", "7d", "4w") or an ISO date string.`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
return parsed;
|
|
31
|
+
}
|