@langwatch/mcp-server 0.6.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.release-please-shim +1 -0
- package/CHANGELOG.md +89 -0
- package/README.md +51 -8
- package/dist/{archive-scenario-YFD5THOR.js → archive-scenario-YKGLJ4MZ.js} +4 -4
- package/dist/archive-suite-QFNDO3Y3.js +15 -0
- package/dist/assign-prompt-tag-Y2B4CBBT.js +19 -0
- package/dist/chunk-B57MZ4WI.js +1573 -0
- package/dist/{chunk-IX6QJKAD.js → chunk-CPUPGDNA.js} +2 -2
- package/dist/chunk-FBRKHHJV.js +48 -0
- package/dist/{chunk-5UOPNRXW.js → chunk-HDJDFKZR.js} +10 -3
- package/dist/chunk-KOO5733S.js +72 -0
- package/dist/chunk-KZNBCKWG.js +39 -0
- package/dist/chunk-L6QENXYA.js +42 -0
- package/dist/{chunk-6U4TCGFC.js → chunk-NACSR6B7.js} +2 -2
- package/dist/chunk-NUFPTHKU.js +9 -0
- package/dist/chunk-OVQF3D5R.js +86 -0
- package/dist/chunk-PO7FM7IX.js +35 -0
- package/dist/chunk-PZUWCWOS.js +25 -0
- package/dist/chunk-WVI5CJ6S.js +21 -0
- package/dist/{chunk-LLRQIF52.js → chunk-YH2IBTEL.js} +54 -6
- package/dist/chunk-YI3OG4PI.js +71 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.js +13 -0
- package/dist/create-agent-YRSDH7P7.js +23 -0
- package/dist/create-annotation-FYQNKMVJ.js +24 -0
- package/dist/create-dashboard-CORBR4YQ.js +18 -0
- package/dist/create-dataset-ZC63T6BB.js +22 -0
- package/dist/create-dataset-records-ILVNVKVR.js +22 -0
- package/dist/{create-evaluator-E5X5ZP3B.js → create-evaluator-2AU3PXFB.js} +4 -4
- package/dist/create-mcp-server.d.ts +8 -0
- package/dist/create-mcp-server.js +8 -0
- package/dist/{create-prompt-7Z35MIL6.js → create-prompt-NKRJ47YY.js} +7 -4
- package/dist/create-prompt-tag-S4JX3ULL.js +17 -0
- package/dist/{create-scenario-DIMPJRPY.js → create-scenario-BPRF75KF.js} +4 -4
- package/dist/create-suite-4YN7D6K2.js +38 -0
- package/dist/delete-agent-Y72R2YQA.js +18 -0
- package/dist/delete-annotation-UHWDMHSH.js +17 -0
- package/dist/delete-dashboard-LPWT36KN.js +18 -0
- package/dist/delete-dataset-GGPQRHKX.js +20 -0
- package/dist/delete-dataset-records-LTG5LT26.js +21 -0
- package/dist/delete-evaluator-F7QLXDHU.js +15 -0
- package/dist/delete-prompt-tag-B5UKZL45.js +17 -0
- package/dist/delete-workflow-4DGUDIDE.js +17 -0
- package/dist/get-agent-YEVICUX4.js +28 -0
- package/dist/{get-analytics-4YJW4S5L.js → get-analytics-3W5CMU3F.js} +22 -5
- package/dist/get-dashboard-NCQHN6MW.js +29 -0
- package/dist/get-dataset-KBK6PH36.js +48 -0
- package/dist/{get-evaluator-WDEH2F7M.js → get-evaluator-QDUL5LXR.js} +4 -4
- package/dist/{get-prompt-F6PDVC76.js → get-prompt-XCSSNRKR.js} +4 -4
- package/dist/{get-scenario-H24ZYNT5.js → get-scenario-FNRKOY7E.js} +4 -4
- package/dist/get-simulation-run-WOEKUF7K.js +56 -0
- package/dist/get-suite-U5AOD5TC.js +44 -0
- package/dist/{get-trace-27USKGO7.js → get-trace-MUWEJM2Z.js} +3 -3
- package/dist/get-workflow-SOA7MT7L.js +25 -0
- package/dist/http-server-7DMOCB75.js +224 -0
- package/dist/index.js +61640 -31868
- package/dist/langwatch-api-annotations-Q57NDUVR.js +17 -0
- package/dist/langwatch-api-dashboards-VBWWXV3Y.js +17 -0
- package/dist/langwatch-api-monitors-L3IY7FDT.js +52 -0
- package/dist/langwatch-api-secrets-S5HHF3XR.js +40 -0
- package/dist/langwatch-api-triggers-ME33QE26.js +30 -0
- package/dist/langwatch-api-workflows-4CJQAYP2.js +15 -0
- package/dist/list-agents-3HGIBJFM.js +32 -0
- package/dist/list-annotations-KWXYVEPI.js +33 -0
- package/dist/list-dashboards-VQ2LH666.js +32 -0
- package/dist/list-dataset-records-RNRGROJP.js +39 -0
- package/dist/list-datasets-I4A2LT2S.js +60 -0
- package/dist/{list-evaluators-KRGI72EH.js → list-evaluators-AKKRJ5MX.js} +4 -4
- package/dist/{list-model-providers-A5YCFTPI.js → list-model-providers-DVFEHFHZ.js} +4 -4
- package/dist/list-prompt-tags-PL5OISUA.js +23 -0
- package/dist/{list-prompts-LKJSE7XN.js → list-prompts-7VTFWE2Z.js} +3 -3
- package/dist/{list-scenarios-ZK5CMGC4.js → list-scenarios-5BZWZ6ZN.js} +4 -4
- package/dist/list-simulation-runs-WCZ6RLPE.js +43 -0
- package/dist/list-suites-RRQ2JX5Y.js +42 -0
- package/dist/list-workflows-UQB5FCX5.js +35 -0
- package/dist/rename-prompt-tag-TURD6KYU.js +18 -0
- package/dist/run-agent-BEJ4K4CM.js +36 -0
- package/dist/run-evaluation-AJ7WZNQD.js +64 -0
- package/dist/run-suite-U7V432G2.js +29 -0
- package/dist/run-workflow-5WX7DK5T.js +36 -0
- package/dist/{search-traces-SOKAAMAR.js → search-traces-ZCHNOMTX.js} +3 -3
- package/dist/{set-model-provider-7MGULZDH.js → set-model-provider-6CFHMCNE.js} +4 -4
- package/dist/update-agent-UE6UWLZP.js +20 -0
- package/dist/update-dataset-4XQZZE4S.js +22 -0
- package/dist/update-dataset-record-GNNM5TKM.js +19 -0
- package/dist/{update-evaluator-A3XINFLJ.js → update-evaluator-LHVDXIZD.js} +4 -4
- package/dist/{update-prompt-IW7X2UQM.js → update-prompt-3XEHECJ7.js} +5 -3
- package/dist/{update-scenario-ZT7TOBFR.js → update-scenario-DFPREYCD.js} +4 -4
- package/dist/update-suite-RATU5THO.js +39 -0
- package/package.json +22 -8
- package/src/__tests__/all-tools.integration.test.ts +353 -0
- package/src/__tests__/config.unit.test.ts +81 -1
- package/src/__tests__/datasets.integration.test.ts +620 -0
- package/src/__tests__/datasets.unit.test.ts +244 -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__/scenario-tools.unit.test.ts +3 -0
- package/src/__tests__/tools.unit.test.ts +177 -0
- package/src/config.d.ts +15 -0
- package/src/config.ts +85 -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 +65 -3
- 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/uv.lock +188 -530
- package/build.js +0 -12
- package/dist/archive-scenario-YFD5THOR.js.map +0 -1
- package/dist/chunk-5UOPNRXW.js.map +0 -1
- package/dist/chunk-6U4TCGFC.js.map +0 -1
- package/dist/chunk-AAQNA53E.js +0 -28
- package/dist/chunk-AAQNA53E.js.map +0 -1
- package/dist/chunk-IX6QJKAD.js.map +0 -1
- package/dist/chunk-LLRQIF52.js.map +0 -1
- package/dist/chunk-ZXKLPC2E.js.map +0 -1
- package/dist/config-FIQWQRUB.js +0 -11
- package/dist/config-FIQWQRUB.js.map +0 -1
- package/dist/create-evaluator-E5X5ZP3B.js.map +0 -1
- package/dist/create-prompt-7Z35MIL6.js.map +0 -1
- package/dist/create-scenario-DIMPJRPY.js.map +0 -1
- package/dist/discover-evaluator-schema-H23XCLNE.js.map +0 -1
- package/dist/discover-scenario-schema-MEEEVND7.js.map +0 -1
- package/dist/discover-schema-3T52ORPB.js.map +0 -1
- package/dist/get-analytics-4YJW4S5L.js.map +0 -1
- package/dist/get-evaluator-WDEH2F7M.js.map +0 -1
- package/dist/get-prompt-F6PDVC76.js.map +0 -1
- package/dist/get-scenario-H24ZYNT5.js.map +0 -1
- package/dist/get-trace-27USKGO7.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/list-evaluators-KRGI72EH.js.map +0 -1
- package/dist/list-model-providers-A5YCFTPI.js.map +0 -1
- package/dist/list-prompts-LKJSE7XN.js.map +0 -1
- package/dist/list-scenarios-ZK5CMGC4.js.map +0 -1
- package/dist/search-traces-SOKAAMAR.js.map +0 -1
- package/dist/set-model-provider-7MGULZDH.js.map +0 -1
- package/dist/update-evaluator-A3XINFLJ.js.map +0 -1
- package/dist/update-prompt-IW7X2UQM.js.map +0 -1
- package/dist/update-scenario-ZT7TOBFR.js.map +0 -1
- package/tests/evaluations.ipynb +0 -649
- package/tests/fixtures/azure/azure_openai_stream_bot_expected.py +0 -102
- package/tests/fixtures/azure/azure_openai_stream_bot_input.py +0 -78
- package/tests/fixtures/dspy/dspy_bot_expected.py +0 -61
- package/tests/fixtures/dspy/dspy_bot_input.py +0 -53
- package/tests/fixtures/fastapi/fastapi_app_expected.py +0 -68
- package/tests/fixtures/fastapi/fastapi_app_input.py +0 -60
- package/tests/fixtures/fastapi/prompt_management_fastapi_expected.py +0 -114
- package/tests/fixtures/fastapi/prompt_management_fastapi_input.py +0 -88
- package/tests/fixtures/haystack/haystack_bot_expected.py +0 -141
- package/tests/fixtures/haystack/haystack_bot_input.py +0 -69
- package/tests/fixtures/langchain/langchain_bot_expected.py +0 -53
- package/tests/fixtures/langchain/langchain_bot_input.py +0 -45
- package/tests/fixtures/langchain/langchain_bot_with_memory_expected.py +0 -69
- package/tests/fixtures/langchain/langchain_bot_with_memory_input.py +0 -61
- package/tests/fixtures/langchain/langchain_rag_bot_expected.py +0 -97
- package/tests/fixtures/langchain/langchain_rag_bot_input.py +0 -77
- package/tests/fixtures/langchain/langchain_rag_bot_vertex_ai_expected.py +0 -116
- package/tests/fixtures/langchain/langchain_rag_bot_vertex_ai_input.py +0 -81
- package/tests/fixtures/langchain/langgraph_rag_bot_with_threads_expected.py +0 -331
- package/tests/fixtures/langchain/langgraph_rag_bot_with_threads_input.py +0 -106
- package/tests/fixtures/litellm/litellm_bot_expected.py +0 -40
- package/tests/fixtures/litellm/litellm_bot_input.py +0 -35
- package/tests/fixtures/openai/openai_bot_expected.py +0 -43
- package/tests/fixtures/openai/openai_bot_function_call_expected.py +0 -91
- package/tests/fixtures/openai/openai_bot_function_call_input.py +0 -82
- package/tests/fixtures/openai/openai_bot_input.py +0 -36
- package/tests/fixtures/openai/openai_bot_rag_expected.py +0 -73
- package/tests/fixtures/openai/openai_bot_rag_input.py +0 -51
- package/tests/fixtures/opentelemetry/openinference_dspy_bot_expected.py +0 -63
- package/tests/fixtures/opentelemetry/openinference_dspy_bot_input.py +0 -58
- package/tests/fixtures/opentelemetry/openinference_langchain_bot_expected.py +0 -53
- package/tests/fixtures/opentelemetry/openinference_langchain_bot_input.py +0 -52
- package/tests/fixtures/opentelemetry/openinference_openai_bot_expected.py +0 -49
- package/tests/fixtures/opentelemetry/openinference_openai_bot_input.py +0 -41
- package/tests/fixtures/opentelemetry/openllmetry_openai_bot_expected.py +0 -44
- package/tests/fixtures/opentelemetry/openllmetry_openai_bot_input.py +0 -40
- package/tests/fixtures/strands/strands_bot_expected.py +0 -84
- package/tests/fixtures/strands/strands_bot_input.py +0 -52
- package/tests/scenario-openai.test.ts +0 -160
- package/tsup.config.ts +0 -10
- package/vitest.config.js +0 -7
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getSuite
|
|
3
|
+
} from "./chunk-FBRKHHJV.js";
|
|
4
|
+
import "./chunk-YH2IBTEL.js";
|
|
5
|
+
import "./chunk-KOO5733S.js";
|
|
6
|
+
|
|
7
|
+
// src/tools/get-suite.ts
|
|
8
|
+
async function handleGetSuite(params) {
|
|
9
|
+
const suite = await getSuite(params.id);
|
|
10
|
+
if (params.format === "json") {
|
|
11
|
+
return JSON.stringify(suite, null, 2);
|
|
12
|
+
}
|
|
13
|
+
const lines = [];
|
|
14
|
+
lines.push(`# Suite: ${suite.name}
|
|
15
|
+
`);
|
|
16
|
+
lines.push(`**ID**: ${suite.id}`);
|
|
17
|
+
lines.push(`**Slug**: ${suite.slug}`);
|
|
18
|
+
lines.push(`**Description**: ${suite.description ?? "\u2014"}`);
|
|
19
|
+
lines.push(`**Repeat Count**: ${suite.repeatCount}`);
|
|
20
|
+
lines.push(`**Created**: ${suite.createdAt}`);
|
|
21
|
+
lines.push(`**Updated**: ${suite.updatedAt}`);
|
|
22
|
+
if (suite.labels.length > 0) {
|
|
23
|
+
lines.push(`**Labels**: ${suite.labels.join(", ")}`);
|
|
24
|
+
}
|
|
25
|
+
lines.push("\n## Scenarios");
|
|
26
|
+
for (const id of suite.scenarioIds) {
|
|
27
|
+
lines.push(`- ${id}`);
|
|
28
|
+
}
|
|
29
|
+
lines.push("\n## Targets");
|
|
30
|
+
for (const t of suite.targets) {
|
|
31
|
+
lines.push(`- ${t.type}:${t.referenceId}`);
|
|
32
|
+
}
|
|
33
|
+
const totalJobs = suite.scenarioIds.length * suite.targets.length * suite.repeatCount;
|
|
34
|
+
lines.push(`
|
|
35
|
+
**Total jobs per run**: ${totalJobs}`);
|
|
36
|
+
lines.push(
|
|
37
|
+
"\n> Use `platform_run_suite` to trigger a run, or `platform_update_suite` to modify."
|
|
38
|
+
);
|
|
39
|
+
return lines.join("\n");
|
|
40
|
+
}
|
|
41
|
+
export {
|
|
42
|
+
handleGetSuite
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=get-suite-U5AOD5TC.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getTraceById
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-YH2IBTEL.js";
|
|
4
|
+
import "./chunk-KOO5733S.js";
|
|
5
5
|
|
|
6
6
|
// src/tools/get-trace.ts
|
|
7
7
|
async function handleGetTrace(params) {
|
|
@@ -47,4 +47,4 @@ ${result.formatted_trace}`);
|
|
|
47
47
|
export {
|
|
48
48
|
handleGetTrace
|
|
49
49
|
};
|
|
50
|
-
//# sourceMappingURL=get-trace-
|
|
50
|
+
//# sourceMappingURL=get-trace-MUWEJM2Z.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getWorkflow
|
|
3
|
+
} from "./chunk-PO7FM7IX.js";
|
|
4
|
+
import "./chunk-YH2IBTEL.js";
|
|
5
|
+
import "./chunk-KOO5733S.js";
|
|
6
|
+
|
|
7
|
+
// src/tools/get-workflow.ts
|
|
8
|
+
async function handleGetWorkflow(params) {
|
|
9
|
+
const workflow = await getWorkflow(params.id);
|
|
10
|
+
const lines = [];
|
|
11
|
+
lines.push(`# ${workflow.name}
|
|
12
|
+
`);
|
|
13
|
+
lines.push(`**ID**: ${workflow.id}`);
|
|
14
|
+
if (workflow.description) lines.push(`**Description**: ${workflow.description}`);
|
|
15
|
+
if (workflow.icon) lines.push(`**Icon**: ${workflow.icon}`);
|
|
16
|
+
lines.push(`**Evaluator**: ${workflow.isEvaluator ? "yes" : "no"}`);
|
|
17
|
+
lines.push(`**Component**: ${workflow.isComponent ? "yes" : "no"}`);
|
|
18
|
+
lines.push(`**Created**: ${workflow.createdAt}`);
|
|
19
|
+
lines.push(`**Updated**: ${workflow.updatedAt}`);
|
|
20
|
+
return lines.join("\n");
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
handleGetWorkflow
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=get-workflow-SOA7MT7L.js.map
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createMcpServer
|
|
3
|
+
} from "./chunk-B57MZ4WI.js";
|
|
4
|
+
import {
|
|
5
|
+
getConfig,
|
|
6
|
+
runWithConfig
|
|
7
|
+
} from "./chunk-KOO5733S.js";
|
|
8
|
+
|
|
9
|
+
// src/http-server.ts
|
|
10
|
+
import express from "express";
|
|
11
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
12
|
+
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
13
|
+
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
14
|
+
import { randomUUID, createHash } from "crypto";
|
|
15
|
+
var oauthTokens = /* @__PURE__ */ new Map();
|
|
16
|
+
function extractBearerToken(req) {
|
|
17
|
+
const authHeader = req.headers.authorization;
|
|
18
|
+
if (!authHeader?.startsWith("Bearer ")) return null;
|
|
19
|
+
const token = authHeader.slice(7) || null;
|
|
20
|
+
if (!token) return null;
|
|
21
|
+
const oauthEntry = oauthTokens.get(token);
|
|
22
|
+
if (oauthEntry) {
|
|
23
|
+
if (Date.now() < oauthEntry.expiresAt) {
|
|
24
|
+
return oauthEntry.apiKey;
|
|
25
|
+
}
|
|
26
|
+
oauthTokens.delete(token);
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return token;
|
|
30
|
+
}
|
|
31
|
+
function generateAccessToken() {
|
|
32
|
+
return createHash("sha256").update(randomUUID()).digest("hex");
|
|
33
|
+
}
|
|
34
|
+
async function handleWithSessionConfig(apiKey, fn) {
|
|
35
|
+
const baseConfig = getConfig();
|
|
36
|
+
return runWithConfig({ ...baseConfig, apiKey }, fn);
|
|
37
|
+
}
|
|
38
|
+
async function startHttpServer({
|
|
39
|
+
port
|
|
40
|
+
}) {
|
|
41
|
+
const app = express();
|
|
42
|
+
app.set("trust proxy", true);
|
|
43
|
+
app.use(express.json());
|
|
44
|
+
app.use((_req, res, next) => {
|
|
45
|
+
res.header("Access-Control-Allow-Origin", "*");
|
|
46
|
+
res.header(
|
|
47
|
+
"Access-Control-Allow-Methods",
|
|
48
|
+
"GET, POST, DELETE, OPTIONS"
|
|
49
|
+
);
|
|
50
|
+
res.header(
|
|
51
|
+
"Access-Control-Allow-Headers",
|
|
52
|
+
"Content-Type, Authorization, mcp-session-id, MCP-Protocol-Version"
|
|
53
|
+
);
|
|
54
|
+
if (_req.method === "OPTIONS") {
|
|
55
|
+
res.sendStatus(200);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
next();
|
|
59
|
+
});
|
|
60
|
+
app.get("/health", (_req, res) => {
|
|
61
|
+
res.json({ status: "ok" });
|
|
62
|
+
});
|
|
63
|
+
app.get(
|
|
64
|
+
"/.well-known/oauth-authorization-server",
|
|
65
|
+
(_req, res) => {
|
|
66
|
+
const baseUrl = `${_req.protocol}://${_req.get("host")}`;
|
|
67
|
+
res.json({
|
|
68
|
+
issuer: baseUrl,
|
|
69
|
+
token_endpoint: `${baseUrl}/oauth/token`,
|
|
70
|
+
token_endpoint_auth_methods_supported: ["client_secret_post"],
|
|
71
|
+
grant_types_supported: ["client_credentials"],
|
|
72
|
+
response_types_supported: [],
|
|
73
|
+
scopes_supported: ["mcp:tools"]
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
app.post(
|
|
78
|
+
"/oauth/token",
|
|
79
|
+
express.urlencoded({ extended: false }),
|
|
80
|
+
(req, res) => {
|
|
81
|
+
const grantType = req.body.grant_type;
|
|
82
|
+
if (grantType !== "client_credentials") {
|
|
83
|
+
res.status(400).json({
|
|
84
|
+
error: "unsupported_grant_type",
|
|
85
|
+
error_description: "Only client_credentials grant type is supported"
|
|
86
|
+
});
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const clientSecret = req.body.client_secret;
|
|
90
|
+
if (!clientSecret || typeof clientSecret !== "string") {
|
|
91
|
+
res.status(400).json({
|
|
92
|
+
error: "invalid_request",
|
|
93
|
+
error_description: "client_secret is required (use your LangWatch API key)"
|
|
94
|
+
});
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const expiresIn = 3600;
|
|
98
|
+
const accessToken = generateAccessToken();
|
|
99
|
+
oauthTokens.set(accessToken, {
|
|
100
|
+
apiKey: clientSecret,
|
|
101
|
+
expiresAt: Date.now() + expiresIn * 1e3
|
|
102
|
+
});
|
|
103
|
+
res.json({
|
|
104
|
+
access_token: accessToken,
|
|
105
|
+
token_type: "Bearer",
|
|
106
|
+
expires_in: expiresIn,
|
|
107
|
+
scope: "mcp:tools"
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
);
|
|
111
|
+
const sessions = {};
|
|
112
|
+
app.post("/mcp", async (req, res) => {
|
|
113
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
114
|
+
if (sessionId && sessions[sessionId]) {
|
|
115
|
+
const session = sessions[sessionId];
|
|
116
|
+
await handleWithSessionConfig(
|
|
117
|
+
session.apiKey,
|
|
118
|
+
() => session.transport.handleRequest(req, res, req.body)
|
|
119
|
+
);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (!sessionId && isInitializeRequest(req.body)) {
|
|
123
|
+
const apiKey = extractBearerToken(req);
|
|
124
|
+
if (!apiKey) {
|
|
125
|
+
res.status(401).json({
|
|
126
|
+
error: "Authorization: Bearer <LANGWATCH_API_KEY> header required"
|
|
127
|
+
});
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const transport = new StreamableHTTPServerTransport({
|
|
131
|
+
sessionIdGenerator: () => randomUUID(),
|
|
132
|
+
onsessioninitialized: (id) => {
|
|
133
|
+
sessions[id] = { transport, apiKey };
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
transport.onclose = () => {
|
|
137
|
+
if (transport.sessionId) {
|
|
138
|
+
delete sessions[transport.sessionId];
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
const sessionServer = createMcpServer();
|
|
142
|
+
await handleWithSessionConfig(
|
|
143
|
+
apiKey,
|
|
144
|
+
() => sessionServer.connect(transport)
|
|
145
|
+
);
|
|
146
|
+
await handleWithSessionConfig(
|
|
147
|
+
apiKey,
|
|
148
|
+
() => transport.handleRequest(req, res, req.body)
|
|
149
|
+
);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
res.status(400).json({
|
|
153
|
+
error: "Invalid request \u2014 no session ID or not an initialize request"
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
app.get("/mcp", async (req, res) => {
|
|
157
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
158
|
+
if (sessionId && sessions[sessionId]) {
|
|
159
|
+
const session = sessions[sessionId];
|
|
160
|
+
await handleWithSessionConfig(
|
|
161
|
+
session.apiKey,
|
|
162
|
+
() => session.transport.handleRequest(req, res)
|
|
163
|
+
);
|
|
164
|
+
} else {
|
|
165
|
+
res.status(400).json({ error: "Invalid request \u2014 no valid session ID" });
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
app.delete("/mcp", async (req, res) => {
|
|
169
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
170
|
+
if (sessionId && sessions[sessionId]) {
|
|
171
|
+
const session = sessions[sessionId];
|
|
172
|
+
await session.transport.close();
|
|
173
|
+
delete sessions[sessionId];
|
|
174
|
+
res.status(200).json({ status: "session closed" });
|
|
175
|
+
} else {
|
|
176
|
+
res.status(404).json({ error: "Session not found" });
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
const sseSessions = {};
|
|
180
|
+
app.get("/sse", async (req, res) => {
|
|
181
|
+
const apiKey = extractBearerToken(req) || req.query["apiKey"] || null;
|
|
182
|
+
if (!apiKey) {
|
|
183
|
+
res.status(401).json({
|
|
184
|
+
error: "Authorization: Bearer <LANGWATCH_API_KEY> header or ?apiKey= query parameter required"
|
|
185
|
+
});
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const transport = new SSEServerTransport("/messages", res);
|
|
189
|
+
sseSessions[transport.sessionId] = { transport, apiKey };
|
|
190
|
+
const sessionServer = createMcpServer();
|
|
191
|
+
res.on("close", () => {
|
|
192
|
+
delete sseSessions[transport.sessionId];
|
|
193
|
+
});
|
|
194
|
+
await handleWithSessionConfig(
|
|
195
|
+
apiKey,
|
|
196
|
+
() => sessionServer.connect(transport)
|
|
197
|
+
);
|
|
198
|
+
});
|
|
199
|
+
const handleSseMessage = async (req, res) => {
|
|
200
|
+
const sessionId = req.query["sessionId"];
|
|
201
|
+
if (!sessionId || !sseSessions[sessionId]) {
|
|
202
|
+
res.status(400).json({ error: "Invalid or missing session ID" });
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const session = sseSessions[sessionId];
|
|
206
|
+
await handleWithSessionConfig(
|
|
207
|
+
session.apiKey,
|
|
208
|
+
() => session.transport.handlePostMessage(req, res, req.body)
|
|
209
|
+
);
|
|
210
|
+
};
|
|
211
|
+
app.post("/messages", handleSseMessage);
|
|
212
|
+
app.post("/sse/messages", handleSseMessage);
|
|
213
|
+
return new Promise((resolve) => {
|
|
214
|
+
const server = app.listen(port, "0.0.0.0", () => {
|
|
215
|
+
const addr = server.address();
|
|
216
|
+
const resolvedPort = typeof addr === "object" && addr ? addr.port : port;
|
|
217
|
+
resolve({ server, port: resolvedPort });
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
export {
|
|
222
|
+
startHttpServer
|
|
223
|
+
};
|
|
224
|
+
//# sourceMappingURL=http-server-7DMOCB75.js.map
|