@langwatch/mcp-server 0.6.0 → 0.7.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 +30 -0
- package/Dockerfile +22 -0
- package/README.md +51 -8
- package/dist/{archive-scenario-YFD5THOR.js → archive-scenario-WJ2A7ETN.js} +4 -4
- package/dist/archive-suite-4JOM4SSL.js +15 -0
- package/dist/archive-suite-4JOM4SSL.js.map +1 -0
- package/dist/assign-prompt-tag-GL7EAAF3.js +19 -0
- package/dist/assign-prompt-tag-GL7EAAF3.js.map +1 -0
- package/dist/{chunk-6U4TCGFC.js → chunk-2GT7IFWK.js} +2 -2
- package/dist/{chunk-LLRQIF52.js → chunk-2WPLXEKF.js} +49 -5
- package/dist/chunk-2WPLXEKF.js.map +1 -0
- package/dist/chunk-4AUB2BIA.js +71 -0
- package/dist/chunk-4AUB2BIA.js.map +1 -0
- package/dist/chunk-BWWY5KEF.js +71 -0
- package/dist/chunk-BWWY5KEF.js.map +1 -0
- package/dist/{chunk-IX6QJKAD.js → chunk-CCWCIJJ3.js} +2 -2
- package/dist/chunk-E5JYYU3J.js +1567 -0
- package/dist/chunk-E5JYYU3J.js.map +1 -0
- package/dist/chunk-EGH5RPXM.js +25 -0
- package/dist/chunk-EGH5RPXM.js.map +1 -0
- package/dist/chunk-EUQDVOSU.js +39 -0
- package/dist/chunk-EUQDVOSU.js.map +1 -0
- package/dist/{chunk-5UOPNRXW.js → chunk-HYMIWO3U.js} +10 -3
- package/dist/{chunk-5UOPNRXW.js.map → chunk-HYMIWO3U.js.map} +1 -1
- package/dist/chunk-IE4M5OGE.js +42 -0
- package/dist/chunk-IE4M5OGE.js.map +1 -0
- package/dist/chunk-LUMEZEHL.js +48 -0
- package/dist/chunk-LUMEZEHL.js.map +1 -0
- package/dist/chunk-NUFPTHKU.js +9 -0
- package/dist/chunk-NUFPTHKU.js.map +1 -0
- package/dist/chunk-WQG2DFHX.js +86 -0
- package/dist/chunk-WQG2DFHX.js.map +1 -0
- package/dist/chunk-WVI5CJ6S.js +21 -0
- package/dist/chunk-WVI5CJ6S.js.map +1 -0
- package/dist/chunk-ZVS4UKN2.js +35 -0
- package/dist/chunk-ZVS4UKN2.js.map +1 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.js +13 -0
- package/dist/create-agent-MBOOT3XK.js +23 -0
- package/dist/create-agent-MBOOT3XK.js.map +1 -0
- package/dist/create-annotation-XMV6A7ED.js +24 -0
- package/dist/create-annotation-XMV6A7ED.js.map +1 -0
- package/dist/create-dashboard-VRKLFVZJ.js +18 -0
- package/dist/create-dashboard-VRKLFVZJ.js.map +1 -0
- package/dist/create-dataset-7HQPZCRE.js +22 -0
- package/dist/create-dataset-7HQPZCRE.js.map +1 -0
- package/dist/create-dataset-records-2NIVFYTV.js +22 -0
- package/dist/create-dataset-records-2NIVFYTV.js.map +1 -0
- package/dist/{create-evaluator-E5X5ZP3B.js → create-evaluator-WYFRFLRC.js} +4 -4
- package/dist/create-mcp-server.d.ts +8 -0
- package/dist/create-mcp-server.js +8 -0
- package/dist/create-mcp-server.js.map +1 -0
- package/dist/{create-prompt-7Z35MIL6.js → create-prompt-IEN7O6JT.js} +7 -4
- package/dist/create-prompt-IEN7O6JT.js.map +1 -0
- package/dist/create-prompt-tag-CAEZ45VO.js +17 -0
- package/dist/create-prompt-tag-CAEZ45VO.js.map +1 -0
- package/dist/{create-scenario-DIMPJRPY.js → create-scenario-6YHSMGLP.js} +4 -4
- package/dist/create-suite-H5XOFDE5.js +38 -0
- package/dist/create-suite-H5XOFDE5.js.map +1 -0
- package/dist/delete-agent-JKTNBWAA.js +18 -0
- package/dist/delete-agent-JKTNBWAA.js.map +1 -0
- package/dist/delete-annotation-ACMDV6QO.js +17 -0
- package/dist/delete-annotation-ACMDV6QO.js.map +1 -0
- package/dist/delete-dashboard-VJSR7CRU.js +18 -0
- package/dist/delete-dashboard-VJSR7CRU.js.map +1 -0
- package/dist/delete-dataset-RTTTWP4D.js +20 -0
- package/dist/delete-dataset-RTTTWP4D.js.map +1 -0
- package/dist/delete-dataset-records-KNQTIPQK.js +21 -0
- package/dist/delete-dataset-records-KNQTIPQK.js.map +1 -0
- package/dist/delete-evaluator-7FVC5WM4.js +15 -0
- package/dist/delete-evaluator-7FVC5WM4.js.map +1 -0
- package/dist/delete-prompt-tag-ZMZUAGFW.js +17 -0
- package/dist/delete-prompt-tag-ZMZUAGFW.js.map +1 -0
- package/dist/delete-workflow-EYQ3T4RQ.js +17 -0
- package/dist/delete-workflow-EYQ3T4RQ.js.map +1 -0
- package/dist/get-agent-E3QSTVKG.js +28 -0
- package/dist/get-agent-E3QSTVKG.js.map +1 -0
- package/dist/{get-analytics-4YJW4S5L.js → get-analytics-2OHV763I.js} +22 -5
- package/dist/get-analytics-2OHV763I.js.map +1 -0
- package/dist/get-dashboard-PC7JDHH6.js +29 -0
- package/dist/get-dashboard-PC7JDHH6.js.map +1 -0
- package/dist/get-dataset-H5IK5FVU.js +48 -0
- package/dist/get-dataset-H5IK5FVU.js.map +1 -0
- package/dist/{get-evaluator-WDEH2F7M.js → get-evaluator-UWVUPYTM.js} +4 -4
- package/dist/{get-prompt-F6PDVC76.js → get-prompt-WZUOLLQC.js} +4 -4
- package/dist/get-prompt-WZUOLLQC.js.map +1 -0
- package/dist/{get-scenario-H24ZYNT5.js → get-scenario-Q6LCNUYN.js} +4 -4
- package/dist/get-simulation-run-Z2XKO256.js +56 -0
- package/dist/get-simulation-run-Z2XKO256.js.map +1 -0
- package/dist/get-suite-BXK5TN7P.js +44 -0
- package/dist/get-suite-BXK5TN7P.js.map +1 -0
- package/dist/{get-trace-27USKGO7.js → get-trace-4EJAYVBG.js} +3 -3
- package/dist/get-workflow-O34QJLHD.js +25 -0
- package/dist/get-workflow-O34QJLHD.js.map +1 -0
- package/dist/http-server-ISHF7NJX.js +224 -0
- package/dist/http-server-ISHF7NJX.js.map +1 -0
- package/dist/index.js +61109 -31519
- package/dist/index.js.map +1 -1
- package/dist/langwatch-api-annotations-ERBG27RF.js +17 -0
- package/dist/langwatch-api-annotations-ERBG27RF.js.map +1 -0
- package/dist/langwatch-api-dashboards-G6TR7VVO.js +17 -0
- package/dist/langwatch-api-dashboards-G6TR7VVO.js.map +1 -0
- package/dist/langwatch-api-monitors-WSXZLS2B.js +52 -0
- package/dist/langwatch-api-monitors-WSXZLS2B.js.map +1 -0
- package/dist/langwatch-api-secrets-RR5DKG2X.js +40 -0
- package/dist/langwatch-api-secrets-RR5DKG2X.js.map +1 -0
- package/dist/langwatch-api-triggers-2RTNSWQY.js +30 -0
- package/dist/langwatch-api-triggers-2RTNSWQY.js.map +1 -0
- package/dist/langwatch-api-workflows-VJRIIDZ7.js +15 -0
- package/dist/langwatch-api-workflows-VJRIIDZ7.js.map +1 -0
- package/dist/list-agents-AW3P6FJV.js +32 -0
- package/dist/list-agents-AW3P6FJV.js.map +1 -0
- package/dist/list-annotations-QNCGWPQ3.js +33 -0
- package/dist/list-annotations-QNCGWPQ3.js.map +1 -0
- package/dist/list-dashboards-R7RK2IXL.js +32 -0
- package/dist/list-dashboards-R7RK2IXL.js.map +1 -0
- package/dist/list-dataset-records-WVCSQP7P.js +39 -0
- package/dist/list-dataset-records-WVCSQP7P.js.map +1 -0
- package/dist/list-datasets-KEXRLDEU.js +60 -0
- package/dist/list-datasets-KEXRLDEU.js.map +1 -0
- package/dist/{list-evaluators-KRGI72EH.js → list-evaluators-7R2LB4JH.js} +4 -4
- package/dist/{list-model-providers-A5YCFTPI.js → list-model-providers-OBYJCKBX.js} +4 -4
- package/dist/list-prompt-tags-6LY3ZPYI.js +23 -0
- package/dist/list-prompt-tags-6LY3ZPYI.js.map +1 -0
- package/dist/{list-prompts-LKJSE7XN.js → list-prompts-VWEZ4CDE.js} +3 -3
- package/dist/{list-scenarios-ZK5CMGC4.js → list-scenarios-TXD4W2ZJ.js} +4 -4
- package/dist/list-simulation-runs-RHPOLMFI.js +43 -0
- package/dist/list-simulation-runs-RHPOLMFI.js.map +1 -0
- package/dist/list-suites-FSGBX27Z.js +42 -0
- package/dist/list-suites-FSGBX27Z.js.map +1 -0
- package/dist/list-workflows-VRGEZ3EN.js +35 -0
- package/dist/list-workflows-VRGEZ3EN.js.map +1 -0
- package/dist/rename-prompt-tag-VOJJLX4M.js +18 -0
- package/dist/rename-prompt-tag-VOJJLX4M.js.map +1 -0
- package/dist/run-agent-KTCC7TB7.js +36 -0
- package/dist/run-agent-KTCC7TB7.js.map +1 -0
- package/dist/run-evaluation-2X3V3Q6L.js +64 -0
- package/dist/run-evaluation-2X3V3Q6L.js.map +1 -0
- package/dist/run-suite-BTMHGI2D.js +29 -0
- package/dist/run-suite-BTMHGI2D.js.map +1 -0
- package/dist/run-workflow-IGAYGKJL.js +36 -0
- package/dist/run-workflow-IGAYGKJL.js.map +1 -0
- package/dist/{search-traces-SOKAAMAR.js → search-traces-VYFUIMXH.js} +3 -3
- package/dist/{set-model-provider-7MGULZDH.js → set-model-provider-W5IUI326.js} +4 -4
- package/dist/update-agent-4BB7EYLR.js +20 -0
- package/dist/update-agent-4BB7EYLR.js.map +1 -0
- package/dist/update-dataset-CXBKZ4W3.js +22 -0
- package/dist/update-dataset-CXBKZ4W3.js.map +1 -0
- package/dist/update-dataset-record-HOHAMHXG.js +19 -0
- package/dist/update-dataset-record-HOHAMHXG.js.map +1 -0
- package/dist/{update-evaluator-A3XINFLJ.js → update-evaluator-IVOR6FTR.js} +4 -4
- package/dist/{update-prompt-IW7X2UQM.js → update-prompt-OWQAKE7K.js} +5 -3
- package/dist/update-prompt-OWQAKE7K.js.map +1 -0
- package/dist/{update-scenario-ZT7TOBFR.js → update-scenario-GDFVILPN.js} +4 -4
- package/dist/update-suite-BXYUYSPT.js +39 -0
- package/dist/update-suite-BXYUYSPT.js.map +1 -0
- package/package.json +17 -3
- package/src/__tests__/all-tools.integration.test.ts +344 -0
- package/src/__tests__/config.unit.test.ts +81 -1
- package/src/__tests__/datasets.integration.test.ts +603 -0
- package/src/__tests__/datasets.unit.test.ts +240 -0
- package/src/__tests__/docker.integration.test.ts +234 -0
- package/src/__tests__/http-transport.integration.test.ts +397 -0
- package/src/__tests__/langwatch-api.unit.test.ts +174 -0
- package/src/__tests__/tools.unit.test.ts +177 -0
- package/src/config.d.ts +15 -0
- package/src/config.ts +83 -7
- package/src/create-mcp-server.d.ts +6 -0
- package/src/create-mcp-server.ts +1799 -0
- package/src/http-server.ts +329 -0
- package/src/index.ts +22 -593
- package/src/langwatch-api-agents.ts +108 -0
- package/src/langwatch-api-annotations.ts +49 -0
- package/src/langwatch-api-dashboards.ts +48 -0
- package/src/langwatch-api-datasets.ts +191 -0
- package/src/langwatch-api-evaluators.ts +10 -0
- package/src/langwatch-api-monitors.ts +82 -0
- package/src/langwatch-api-secrets.ts +48 -0
- package/src/langwatch-api-simulation-runs.ts +51 -0
- package/src/langwatch-api-suites.ts +118 -0
- package/src/langwatch-api-triggers.ts +48 -0
- package/src/langwatch-api-workflows.ts +41 -0
- package/src/langwatch-api.ts +60 -2
- package/src/schemas/create-dataset.ts +48 -0
- package/src/schemas/index.ts +1 -0
- package/src/tools/archive-suite.ts +12 -0
- package/src/tools/assign-prompt-tag.ts +16 -0
- package/src/tools/create-agent.ts +15 -0
- package/src/tools/create-annotation.ts +18 -0
- package/src/tools/create-dashboard.ts +7 -0
- package/src/tools/create-dataset-records.ts +23 -0
- package/src/tools/create-dataset.ts +22 -0
- package/src/tools/create-prompt-tag.ts +12 -0
- package/src/tools/create-prompt.ts +4 -0
- package/src/tools/create-suite.ts +42 -0
- package/src/tools/delete-agent.ts +7 -0
- package/src/tools/delete-annotation.ts +7 -0
- package/src/tools/delete-dashboard.ts +7 -0
- package/src/tools/delete-dataset-records.ts +22 -0
- package/src/tools/delete-dataset.ts +20 -0
- package/src/tools/delete-evaluator.ts +12 -0
- package/src/tools/delete-prompt-tag.ts +12 -0
- package/src/tools/delete-workflow.ts +7 -0
- package/src/tools/format-dataset-mutation.ts +25 -0
- package/src/tools/get-agent.ts +21 -0
- package/src/tools/get-analytics.ts +30 -3
- package/src/tools/get-dashboard.ts +22 -0
- package/src/tools/get-dataset.ts +63 -0
- package/src/tools/get-prompt.ts +2 -1
- package/src/tools/get-simulation-run.ts +60 -0
- package/src/tools/get-suite.ts +47 -0
- package/src/tools/get-workflow.ts +17 -0
- package/src/tools/list-agents.ts +27 -0
- package/src/tools/list-annotations.ts +33 -0
- package/src/tools/list-dashboards.ts +27 -0
- package/src/tools/list-dataset-records.ts +46 -0
- package/src/tools/list-datasets.ts +80 -0
- package/src/tools/list-prompt-tags.ts +21 -0
- package/src/tools/list-simulation-runs.ts +49 -0
- package/src/tools/list-suites.ts +43 -0
- package/src/tools/list-workflows.ts +31 -0
- package/src/tools/rename-prompt-tag.ts +14 -0
- package/src/tools/run-agent.ts +36 -0
- package/src/tools/run-evaluation.ts +87 -0
- package/src/tools/run-suite.ts +28 -0
- package/src/tools/run-workflow.ts +36 -0
- package/src/tools/update-agent.ts +13 -0
- package/src/tools/update-dataset-record.ts +21 -0
- package/src/tools/update-dataset.ts +23 -0
- package/src/tools/update-prompt.ts +3 -0
- package/src/tools/update-suite.ts +44 -0
- package/src/utils/escape-markdown.ts +12 -0
- package/tsup.config.ts +5 -1
- package/dist/chunk-AAQNA53E.js +0 -28
- package/dist/chunk-AAQNA53E.js.map +0 -1
- package/dist/chunk-LLRQIF52.js.map +0 -1
- package/dist/config-FIQWQRUB.js +0 -11
- package/dist/create-prompt-7Z35MIL6.js.map +0 -1
- package/dist/get-analytics-4YJW4S5L.js.map +0 -1
- package/dist/get-prompt-F6PDVC76.js.map +0 -1
- package/dist/update-prompt-IW7X2UQM.js.map +0 -1
- /package/dist/{archive-scenario-YFD5THOR.js.map → archive-scenario-WJ2A7ETN.js.map} +0 -0
- /package/dist/{chunk-6U4TCGFC.js.map → chunk-2GT7IFWK.js.map} +0 -0
- /package/dist/{chunk-IX6QJKAD.js.map → chunk-CCWCIJJ3.js.map} +0 -0
- /package/dist/{config-FIQWQRUB.js.map → config.js.map} +0 -0
- /package/dist/{create-evaluator-E5X5ZP3B.js.map → create-evaluator-WYFRFLRC.js.map} +0 -0
- /package/dist/{create-scenario-DIMPJRPY.js.map → create-scenario-6YHSMGLP.js.map} +0 -0
- /package/dist/{get-evaluator-WDEH2F7M.js.map → get-evaluator-UWVUPYTM.js.map} +0 -0
- /package/dist/{get-scenario-H24ZYNT5.js.map → get-scenario-Q6LCNUYN.js.map} +0 -0
- /package/dist/{get-trace-27USKGO7.js.map → get-trace-4EJAYVBG.js.map} +0 -0
- /package/dist/{list-evaluators-KRGI72EH.js.map → list-evaluators-7R2LB4JH.js.map} +0 -0
- /package/dist/{list-model-providers-A5YCFTPI.js.map → list-model-providers-OBYJCKBX.js.map} +0 -0
- /package/dist/{list-prompts-LKJSE7XN.js.map → list-prompts-VWEZ4CDE.js.map} +0 -0
- /package/dist/{list-scenarios-ZK5CMGC4.js.map → list-scenarios-TXD4W2ZJ.js.map} +0 -0
- /package/dist/{search-traces-SOKAAMAR.js.map → search-traces-VYFUIMXH.js.map} +0 -0
- /package/dist/{set-model-provider-7MGULZDH.js.map → set-model-provider-W5IUI326.js.map} +0 -0
- /package/dist/{update-evaluator-A3XINFLJ.js.map → update-evaluator-IVOR6FTR.js.map} +0 -0
- /package/dist/{update-scenario-ZT7TOBFR.js.map → update-scenario-GDFVILPN.js.map} +0 -0
|
@@ -8,6 +8,11 @@ vi.mock("../langwatch-api.js", () => ({
|
|
|
8
8
|
getPrompt: vi.fn(),
|
|
9
9
|
createPrompt: vi.fn(),
|
|
10
10
|
updatePrompt: vi.fn(),
|
|
11
|
+
assignPromptTag: vi.fn(),
|
|
12
|
+
listPromptTags: vi.fn(),
|
|
13
|
+
createPromptTag: vi.fn(),
|
|
14
|
+
renamePromptTag: vi.fn(),
|
|
15
|
+
deletePromptTag: vi.fn(),
|
|
11
16
|
}));
|
|
12
17
|
|
|
13
18
|
import {
|
|
@@ -18,6 +23,11 @@ import {
|
|
|
18
23
|
getPrompt,
|
|
19
24
|
createPrompt,
|
|
20
25
|
updatePrompt,
|
|
26
|
+
assignPromptTag,
|
|
27
|
+
listPromptTags,
|
|
28
|
+
createPromptTag,
|
|
29
|
+
renamePromptTag,
|
|
30
|
+
deletePromptTag,
|
|
21
31
|
type PromptSummary,
|
|
22
32
|
} from "../langwatch-api.js";
|
|
23
33
|
|
|
@@ -28,6 +38,11 @@ import { handleListPrompts } from "../tools/list-prompts.js";
|
|
|
28
38
|
import { handleGetPrompt } from "../tools/get-prompt.js";
|
|
29
39
|
import { handleCreatePrompt } from "../tools/create-prompt.js";
|
|
30
40
|
import { handleUpdatePrompt } from "../tools/update-prompt.js";
|
|
41
|
+
import { handleAssignPromptTag } from "../tools/assign-prompt-tag.js";
|
|
42
|
+
import { handleListPromptTags } from "../tools/list-prompt-tags.js";
|
|
43
|
+
import { handleCreatePromptTag } from "../tools/create-prompt-tag.js";
|
|
44
|
+
import { handleRenamePromptTag } from "../tools/rename-prompt-tag.js";
|
|
45
|
+
import { handleDeletePromptTag } from "../tools/delete-prompt-tag.js";
|
|
31
46
|
|
|
32
47
|
const mockSearchTraces = vi.mocked(searchTraces);
|
|
33
48
|
const mockGetTraceById = vi.mocked(getTraceById);
|
|
@@ -36,6 +51,11 @@ const mockListPrompts = vi.mocked(listPrompts);
|
|
|
36
51
|
const mockGetPrompt = vi.mocked(getPrompt);
|
|
37
52
|
const mockCreatePrompt = vi.mocked(createPrompt);
|
|
38
53
|
const mockUpdatePrompt = vi.mocked(updatePrompt);
|
|
54
|
+
const mockAssignPromptTag = vi.mocked(assignPromptTag);
|
|
55
|
+
const mockListPromptTags = vi.mocked(listPromptTags);
|
|
56
|
+
const mockCreatePromptTag = vi.mocked(createPromptTag);
|
|
57
|
+
const mockRenamePromptTag = vi.mocked(renamePromptTag);
|
|
58
|
+
const mockDeletePromptTag = vi.mocked(deletePromptTag);
|
|
39
59
|
|
|
40
60
|
beforeEach(() => {
|
|
41
61
|
vi.clearAllMocks();
|
|
@@ -390,6 +410,31 @@ describe("handleGetAnalytics()", () => {
|
|
|
390
410
|
expect(result).toContain("Grouped by: metadata.model");
|
|
391
411
|
});
|
|
392
412
|
|
|
413
|
+
it("formats grouped data with group column", async () => {
|
|
414
|
+
mockGetAnalytics.mockResolvedValue({
|
|
415
|
+
currentPeriod: [
|
|
416
|
+
{
|
|
417
|
+
date: "2024-01-01",
|
|
418
|
+
"sentiment.thumbs_up_down": {
|
|
419
|
+
"Thumbs Up": { "0/metadata.trace_id/cardinality": 10 },
|
|
420
|
+
"Thumbs Down": { "0/metadata.trace_id/cardinality": 5 },
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
],
|
|
424
|
+
previousPeriod: [],
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
const result = await handleGetAnalytics({
|
|
428
|
+
metric: "metadata.trace_id",
|
|
429
|
+
aggregation: "cardinality",
|
|
430
|
+
groupBy: "sentiment.thumbs_up_down",
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
expect(result).toContain("| Date | Group | Value |");
|
|
434
|
+
expect(result).toContain("| 2024-01-01 | Thumbs Up | 10 |");
|
|
435
|
+
expect(result).toContain("| 2024-01-01 | Thumbs Down | 5 |");
|
|
436
|
+
});
|
|
437
|
+
|
|
393
438
|
it("uses the specified aggregation", async () => {
|
|
394
439
|
mockGetAnalytics.mockResolvedValue({ currentPeriod: [], previousPeriod: [] });
|
|
395
440
|
|
|
@@ -721,3 +766,135 @@ describe("handleUpdatePrompt()", () => {
|
|
|
721
766
|
});
|
|
722
767
|
});
|
|
723
768
|
});
|
|
769
|
+
|
|
770
|
+
describe("handleGetPrompt() with tag options", () => {
|
|
771
|
+
describe("when called with a tag", () => {
|
|
772
|
+
it("passes tag option to the API", async () => {
|
|
773
|
+
mockGetPrompt.mockResolvedValue({ name: "Test" });
|
|
774
|
+
|
|
775
|
+
await handleGetPrompt({ idOrHandle: "pizza-prompt", tag: "production" });
|
|
776
|
+
|
|
777
|
+
expect(mockGetPrompt).toHaveBeenCalledWith("pizza-prompt", { version: undefined, tag: "production" });
|
|
778
|
+
});
|
|
779
|
+
});
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
describe("handleCreatePrompt() with tags", () => {
|
|
783
|
+
describe("when called with tags", () => {
|
|
784
|
+
it("includes tags in the output", async () => {
|
|
785
|
+
mockCreatePrompt.mockResolvedValue({ id: "p1", handle: "test" });
|
|
786
|
+
|
|
787
|
+
const result = await handleCreatePrompt({
|
|
788
|
+
name: "Test",
|
|
789
|
+
messages: [{ role: "system", content: "hi" }],
|
|
790
|
+
model: "openai/gpt-5-mini",
|
|
791
|
+
tags: ["production"],
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
expect(result).toContain("**Tags**: production");
|
|
795
|
+
});
|
|
796
|
+
});
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
describe("handleUpdatePrompt() with tags", () => {
|
|
800
|
+
describe("when called with tags", () => {
|
|
801
|
+
it("includes tags in the output", async () => {
|
|
802
|
+
mockUpdatePrompt.mockResolvedValue({ id: "p1", handle: "test", latestVersionNumber: 2 });
|
|
803
|
+
|
|
804
|
+
const result = await handleUpdatePrompt({
|
|
805
|
+
idOrHandle: "test",
|
|
806
|
+
commitMessage: "add tags",
|
|
807
|
+
tags: ["staging"],
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
expect(result).toContain("**Tags**: staging");
|
|
811
|
+
});
|
|
812
|
+
});
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
describe("handleAssignPromptTag()", () => {
|
|
816
|
+
describe("when tag is assigned successfully", () => {
|
|
817
|
+
it("returns a confirmation with prompt, tag, and version", async () => {
|
|
818
|
+
mockAssignPromptTag.mockResolvedValue({ success: true });
|
|
819
|
+
|
|
820
|
+
const result = await handleAssignPromptTag({
|
|
821
|
+
idOrHandle: "pizza-prompt",
|
|
822
|
+
tag: "production",
|
|
823
|
+
versionId: "v123",
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
expect(result).toContain("Tag assigned successfully!");
|
|
827
|
+
expect(result).toContain("**Prompt**: pizza-prompt");
|
|
828
|
+
expect(result).toContain("**Tag**: production");
|
|
829
|
+
expect(result).toContain("**Version ID**: v123");
|
|
830
|
+
});
|
|
831
|
+
});
|
|
832
|
+
});
|
|
833
|
+
|
|
834
|
+
describe("handleListPromptTags()", () => {
|
|
835
|
+
describe("when tags exist", () => {
|
|
836
|
+
it("formats tags as a markdown list", async () => {
|
|
837
|
+
mockListPromptTags.mockResolvedValue([
|
|
838
|
+
{ id: "1", name: "production", createdAt: "2024-01-01" },
|
|
839
|
+
{ id: "2", name: "staging" },
|
|
840
|
+
]);
|
|
841
|
+
|
|
842
|
+
const result = await handleListPromptTags();
|
|
843
|
+
|
|
844
|
+
expect(result).toContain("# Prompt Tags");
|
|
845
|
+
expect(result).toContain("**production**");
|
|
846
|
+
expect(result).toContain("**staging**");
|
|
847
|
+
});
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
describe("when no tags exist", () => {
|
|
851
|
+
it("returns an empty-state message mentioning latest", async () => {
|
|
852
|
+
mockListPromptTags.mockResolvedValue([]);
|
|
853
|
+
|
|
854
|
+
const result = await handleListPromptTags();
|
|
855
|
+
|
|
856
|
+
expect(result).toContain("No prompt tags found");
|
|
857
|
+
expect(result).toContain("`latest`");
|
|
858
|
+
});
|
|
859
|
+
});
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
describe("handleCreatePromptTag()", () => {
|
|
863
|
+
describe("when tag is created successfully", () => {
|
|
864
|
+
it("returns a confirmation with the tag name", async () => {
|
|
865
|
+
mockCreatePromptTag.mockResolvedValue({ id: "t1", name: "canary" });
|
|
866
|
+
|
|
867
|
+
const result = await handleCreatePromptTag({ name: "canary" });
|
|
868
|
+
|
|
869
|
+
expect(result).toContain("Tag created successfully!");
|
|
870
|
+
expect(result).toContain("**Name**: canary");
|
|
871
|
+
});
|
|
872
|
+
});
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
describe("handleRenamePromptTag()", () => {
|
|
876
|
+
describe("when tag is renamed successfully", () => {
|
|
877
|
+
it("returns a confirmation with old and new names", async () => {
|
|
878
|
+
mockRenamePromptTag.mockResolvedValue({ id: "t1", name: "preview" });
|
|
879
|
+
|
|
880
|
+
const result = await handleRenamePromptTag({ tag: "canary", name: "preview" });
|
|
881
|
+
|
|
882
|
+
expect(result).toContain("Tag renamed successfully!");
|
|
883
|
+
expect(result).toContain("**Old name**: canary");
|
|
884
|
+
expect(result).toContain("**New name**: preview");
|
|
885
|
+
});
|
|
886
|
+
});
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
describe("handleDeletePromptTag()", () => {
|
|
890
|
+
describe("when tag is deleted successfully", () => {
|
|
891
|
+
it("returns a confirmation with the tag name", async () => {
|
|
892
|
+
mockDeletePromptTag.mockResolvedValue(null);
|
|
893
|
+
|
|
894
|
+
const result = await handleDeletePromptTag({ tag: "canary" });
|
|
895
|
+
|
|
896
|
+
expect(result).toContain("Tag deleted successfully!");
|
|
897
|
+
expect(result).toContain("**Tag**: canary");
|
|
898
|
+
});
|
|
899
|
+
});
|
|
900
|
+
});
|
package/src/config.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface McpConfig {
|
|
2
|
+
apiKey: string | undefined;
|
|
3
|
+
endpoint: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export declare function initConfig(args: {
|
|
7
|
+
apiKey?: string;
|
|
8
|
+
endpoint?: string;
|
|
9
|
+
}): void;
|
|
10
|
+
|
|
11
|
+
export declare function getConfig(): McpConfig;
|
|
12
|
+
|
|
13
|
+
export declare function requireApiKey(): string;
|
|
14
|
+
|
|
15
|
+
export declare function runWithConfig<T>(config: McpConfig, fn: () => T): T;
|
package/src/config.ts
CHANGED
|
@@ -1,12 +1,56 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
|
|
1
3
|
export interface McpConfig {
|
|
2
4
|
apiKey: string | undefined;
|
|
3
5
|
endpoint: string;
|
|
4
6
|
}
|
|
5
7
|
|
|
6
|
-
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Singleton storage on globalThis
|
|
10
|
+
//
|
|
11
|
+
// When the langwatch app (CJS, no "type": "module") imports this module,
|
|
12
|
+
// tsx creates a CJS-cached copy. When mcp-server's own ESM dist chunks
|
|
13
|
+
// do `await import("./search-traces-*.js")`, Node creates a separate
|
|
14
|
+
// ESM-cached copy. Module-level variables are NOT shared between them.
|
|
15
|
+
//
|
|
16
|
+
// By storing config on globalThis, both CJS and ESM instances read/write
|
|
17
|
+
// the same object, fixing the "Config not initialized" error that occurs
|
|
18
|
+
// when initConfig() runs on the CJS side but tool handlers execute on
|
|
19
|
+
// the ESM side.
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
const GLOBAL_KEY = "__langwatch_mcp_config" as const;
|
|
23
|
+
const STORAGE_KEY = "__langwatch_mcp_config_storage" as const;
|
|
24
|
+
|
|
25
|
+
interface McpGlobalState {
|
|
26
|
+
globalConfig: McpConfig | undefined;
|
|
27
|
+
configStorage: AsyncLocalStorage<McpConfig>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function getGlobalState(): McpGlobalState {
|
|
31
|
+
const g = globalThis as unknown as Record<string, unknown>;
|
|
32
|
+
if (!g[GLOBAL_KEY]) {
|
|
33
|
+
g[GLOBAL_KEY] = undefined;
|
|
34
|
+
}
|
|
35
|
+
if (!g[STORAGE_KEY]) {
|
|
36
|
+
g[STORAGE_KEY] = new AsyncLocalStorage<McpConfig>();
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
get globalConfig() {
|
|
40
|
+
return g[GLOBAL_KEY] as McpConfig | undefined;
|
|
41
|
+
},
|
|
42
|
+
set globalConfig(val: McpConfig | undefined) {
|
|
43
|
+
g[GLOBAL_KEY] = val;
|
|
44
|
+
},
|
|
45
|
+
get configStorage() {
|
|
46
|
+
return g[STORAGE_KEY] as AsyncLocalStorage<McpConfig>;
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
7
50
|
|
|
8
51
|
export function initConfig(args: { apiKey?: string; endpoint?: string }): void {
|
|
9
|
-
|
|
52
|
+
const state = getGlobalState();
|
|
53
|
+
state.globalConfig = {
|
|
10
54
|
apiKey: args.apiKey || process.env.LANGWATCH_API_KEY,
|
|
11
55
|
endpoint:
|
|
12
56
|
args.endpoint ||
|
|
@@ -15,17 +59,49 @@ export function initConfig(args: { apiKey?: string; endpoint?: string }): void {
|
|
|
15
59
|
};
|
|
16
60
|
}
|
|
17
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Returns the current config: the per-request scoped config if inside
|
|
64
|
+
* a `runWithConfig()` callback, otherwise the global config.
|
|
65
|
+
*/
|
|
18
66
|
export function getConfig(): McpConfig {
|
|
19
|
-
|
|
20
|
-
|
|
67
|
+
const state = getGlobalState();
|
|
68
|
+
const scoped = state.configStorage.getStore();
|
|
69
|
+
if (scoped) return scoped;
|
|
70
|
+
if (!state.globalConfig) {
|
|
71
|
+
console.error(
|
|
72
|
+
"[MCP config] getConfig() failed: globalConfig is null, no scoped config active. " +
|
|
73
|
+
"Was initConfig() called? Stack:",
|
|
74
|
+
new Error().stack,
|
|
75
|
+
);
|
|
76
|
+
throw new Error("Config not initialized");
|
|
77
|
+
}
|
|
78
|
+
return state.globalConfig;
|
|
21
79
|
}
|
|
22
80
|
|
|
23
81
|
export function requireApiKey(): string {
|
|
24
|
-
const
|
|
25
|
-
if (!apiKey) {
|
|
82
|
+
const config = getConfig();
|
|
83
|
+
if (!config.apiKey) {
|
|
84
|
+
const state = getGlobalState();
|
|
85
|
+
const hasScoped = !!state.configStorage.getStore();
|
|
86
|
+
console.error(
|
|
87
|
+
"[MCP config] requireApiKey() failed: apiKey is undefined. " +
|
|
88
|
+
`scopedConfig=${hasScoped}, endpoint=${config.endpoint}. ` +
|
|
89
|
+
"In HTTP mode, the API key should be set per-session via runWithConfig(). Stack:",
|
|
90
|
+
new Error().stack,
|
|
91
|
+
);
|
|
26
92
|
throw new Error(
|
|
27
93
|
"LANGWATCH_API_KEY is required. Set it via --apiKey flag or LANGWATCH_API_KEY environment variable."
|
|
28
94
|
);
|
|
29
95
|
}
|
|
30
|
-
return apiKey;
|
|
96
|
+
return config.apiKey;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Runs `fn` with a per-request scoped config. All calls to `getConfig()`
|
|
101
|
+
* and `requireApiKey()` within `fn` (including async continuations) will
|
|
102
|
+
* see the provided config instead of the global one.
|
|
103
|
+
*/
|
|
104
|
+
export function runWithConfig<T>(config: McpConfig, fn: () => T): T {
|
|
105
|
+
const state = getGlobalState();
|
|
106
|
+
return state.configStorage.run(config, fn);
|
|
31
107
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Minimal type declaration — avoids pulling mcp-server's full dependency tree
|
|
2
|
+
// into the langwatch app's typecheck scope. The actual return type is McpServer,
|
|
3
|
+
// but we only expose the connect() method that handler.ts uses.
|
|
4
|
+
export declare function createMcpServer(): {
|
|
5
|
+
connect(transport: unknown): Promise<void>;
|
|
6
|
+
};
|