@lovable.dev/mcp-js 0.27.1 → 0.29.0-rc.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/README.md +13 -7
- package/dist/{authorize-Cs1yyLrq.d.ts → authorize-BjNE3LPr.d.ts} +1 -1
- package/dist/{authorize-BcNuNPtP.d.cts → authorize-BzZFS0zi.d.cts} +1 -1
- package/dist/{base-CReh7d3E.d.cts → base-DSH8NnPj.d.cts} +10 -3
- package/dist/{base-CReh7d3E.d.ts → base-DSH8NnPj.d.ts} +10 -3
- package/dist/cli/extract-manifest.cjs +2 -2
- package/dist/cli/extract-manifest.js +2 -2
- package/dist/{authorize-CB35c3Wg.cjs → cors-CmKzDN7k.cjs} +154 -59
- package/dist/{authorize-FOlzhxrM.js → cors-D4YbODs-.js} +150 -55
- package/dist/{forwarded-BlLF3dfc.js → forwarded-B3EBjHnA.js} +3 -3
- package/dist/{forwarded-Bhwcj22u.cjs → forwarded-BC_RHnn5.cjs} +3 -3
- package/dist/{gate-B9RSzvoI.cjs → gate-n_HX6eQ5.cjs} +6 -4
- package/dist/{gate-BeHmyGxk.js → gate-pGsVlY_4.js} +5 -3
- package/dist/index.cjs +38 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +38 -1
- package/dist/{io-BzTGjG_c.d.cts → io-BEwvgN8s.d.cts} +1 -1
- package/dist/{io--MeYrWG7.d.ts → io-CvALf9NE.d.ts} +1 -1
- package/dist/list-tools-Dkyyje7j.cjs +48 -0
- package/dist/{list-tools-Dt-mhh52.js → list-tools-ePP9tXND.js} +9 -13
- package/dist/{list-tools-Bku6XkVc.d.ts → list-tools-qIvVJeWZ.d.ts} +3 -3
- package/dist/{list-tools-Cn7tpE0V.d.cts → list-tools-zagoI477.d.cts} +3 -3
- package/dist/{logger-BkMMCFTv.cjs → logger-CN1q-KNn.cjs} +6 -6
- package/dist/{logger-rGakBvJ1.js → logger-Ctv5xUSD.js} +6 -6
- package/dist/mcp-Cs4zfvSi.js +336 -0
- package/dist/mcp-DQ1Vdv4v.cjs +341 -0
- package/dist/{package-BdaLbzEh.cjs → package-C3YVYgxp.cjs} +1 -1
- package/dist/{package-dtiombMt.js → package-DzB_5Hur.js} +1 -1
- package/dist/protocols/mcp/index.cjs +1 -1
- package/dist/protocols/mcp/index.d.cts +3 -3
- package/dist/protocols/mcp/index.d.ts +3 -3
- package/dist/protocols/mcp/index.js +1 -1
- package/dist/protocols/oauth-metadata.cjs +12 -10
- package/dist/protocols/oauth-metadata.d.cts +2 -2
- package/dist/protocols/oauth-metadata.d.ts +2 -2
- package/dist/protocols/oauth-metadata.js +6 -4
- package/dist/protocols/rest/index.cjs +2 -2
- package/dist/protocols/rest/index.d.cts +4 -4
- package/dist/protocols/rest/index.d.ts +4 -4
- package/dist/protocols/rest/index.js +2 -2
- package/dist/{rest-Cit2862S.js → rest-DSclWpIt.js} +13 -12
- package/dist/{rest-1Pf9sYNU.cjs → rest-P6XZWP42.cjs} +28 -27
- package/dist/schema-CW1jKvio.js +65 -0
- package/dist/schema-D3vClxb7.cjs +76 -0
- package/dist/stacks/supabase/index.cjs +8 -8
- package/dist/stacks/supabase/index.d.cts +1 -1
- package/dist/stacks/supabase/index.d.ts +1 -1
- package/dist/stacks/supabase/index.js +6 -6
- package/dist/stacks/supabase/vite.cjs +1 -1
- package/dist/stacks/supabase/vite.d.cts +1 -1
- package/dist/stacks/supabase/vite.d.ts +1 -1
- package/dist/stacks/supabase/vite.js +1 -1
- package/dist/stacks/tanstack/index.cjs +8 -8
- package/dist/stacks/tanstack/index.d.cts +2 -2
- package/dist/stacks/tanstack/index.d.ts +2 -2
- package/dist/stacks/tanstack/index.js +5 -5
- package/dist/stacks/tanstack/vite.cjs +8 -4
- package/dist/stacks/tanstack/vite.d.cts +11 -1
- package/dist/stacks/tanstack/vite.d.ts +11 -1
- package/dist/stacks/tanstack/vite.js +8 -4
- package/dist/tool-result-CK6hXO43.cjs +89 -0
- package/dist/tool-result-DfCk445b.js +78 -0
- package/dist/{types-C0Wgm9zp.d.cts → types-D1cgsnPu.d.cts} +15 -7
- package/dist/{types-C0Wgm9zp.d.ts → types-D1cgsnPu.d.ts} +15 -7
- package/package.json +2 -1
- package/dist/content-D5LJAHyM.js +0 -12
- package/dist/content-NCYIGgOb.cjs +0 -17
- package/dist/list-tools-DiMCRunS.cjs +0 -52
- package/dist/mcp-DiVQhHIw.js +0 -148
- package/dist/mcp-ECo1Ixk8.cjs +0 -153
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as MetricsRecorder } from "./base-
|
|
3
|
-
import { t as McpRuntimeOptions } from "./authorize-
|
|
1
|
+
import { u as McpDefinition } from "./types-D1cgsnPu.js";
|
|
2
|
+
import { t as MetricsRecorder } from "./base-DSH8NnPj.js";
|
|
3
|
+
import { t as McpRuntimeOptions } from "./authorize-BjNE3LPr.js";
|
|
4
4
|
//#region src/protocols/rest/list-tools.d.ts
|
|
5
5
|
type RestListToolsHandler = (request: Request, recorder?: MetricsRecorder) => Promise<Response>;
|
|
6
6
|
declare function createListToolsHandler(mcp: McpDefinition, options?: McpRuntimeOptions): RestListToolsHandler;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as MetricsRecorder } from "./base-
|
|
3
|
-
import { t as McpRuntimeOptions } from "./authorize-
|
|
1
|
+
import { u as McpDefinition } from "./types-D1cgsnPu.cjs";
|
|
2
|
+
import { t as MetricsRecorder } from "./base-DSH8NnPj.cjs";
|
|
3
|
+
import { t as McpRuntimeOptions } from "./authorize-BzZFS0zi.cjs";
|
|
4
4
|
//#region src/protocols/rest/list-tools.d.ts
|
|
5
5
|
type RestListToolsHandler = (request: Request, recorder?: MetricsRecorder) => Promise<Response>;
|
|
6
6
|
declare function createListToolsHandler(mcp: McpDefinition, options?: McpRuntimeOptions): RestListToolsHandler;
|
|
@@ -84,13 +84,12 @@ function isLogLevel(value) {
|
|
|
84
84
|
const LOG_LEVEL_ENV_VAR = "LOVABLE_MCP_LOG_LEVEL";
|
|
85
85
|
function readEnvLevel() {
|
|
86
86
|
try {
|
|
87
|
-
|
|
88
|
-
return isLogLevel(normalized) ? normalized : void 0;
|
|
87
|
+
return parseLogLevel(typeof process !== "undefined" ? process.env?.[LOG_LEVEL_ENV_VAR] : void 0);
|
|
89
88
|
} catch {
|
|
90
89
|
return;
|
|
91
90
|
}
|
|
92
91
|
}
|
|
93
|
-
let currentLevel = readEnvLevel() ?? "
|
|
92
|
+
let currentLevel = readEnvLevel() ?? "info";
|
|
94
93
|
/**
|
|
95
94
|
* Set the runtime log level programmatically. Use this when the deployment can't
|
|
96
95
|
* surface `LOVABLE_MCP_LOG_LEVEL` to the package (e.g. a Cloudflare Worker that
|
|
@@ -108,11 +107,12 @@ function parseLogLevel(raw) {
|
|
|
108
107
|
return isLogLevel(normalized) ? normalized : void 0;
|
|
109
108
|
}
|
|
110
109
|
/**
|
|
111
|
-
* Apply the log level from an env value
|
|
112
|
-
*
|
|
110
|
+
* Apply the log level from an env value when it names a valid level; otherwise
|
|
111
|
+
* leave the current level alone so a programmatic `setLogLevel` sticks.
|
|
113
112
|
*/
|
|
114
113
|
function applyLogLevelFromEnv(raw) {
|
|
115
|
-
|
|
114
|
+
const level = parseLogLevel(raw);
|
|
115
|
+
if (level !== void 0) setLogLevel(level);
|
|
116
116
|
}
|
|
117
117
|
function enabled(level) {
|
|
118
118
|
return LEVEL_RANK[level] <= LEVEL_RANK[currentLevel];
|
|
@@ -84,13 +84,12 @@ function isLogLevel(value) {
|
|
|
84
84
|
const LOG_LEVEL_ENV_VAR = "LOVABLE_MCP_LOG_LEVEL";
|
|
85
85
|
function readEnvLevel() {
|
|
86
86
|
try {
|
|
87
|
-
|
|
88
|
-
return isLogLevel(normalized) ? normalized : void 0;
|
|
87
|
+
return parseLogLevel(typeof process !== "undefined" ? process.env?.[LOG_LEVEL_ENV_VAR] : void 0);
|
|
89
88
|
} catch {
|
|
90
89
|
return;
|
|
91
90
|
}
|
|
92
91
|
}
|
|
93
|
-
let currentLevel = readEnvLevel() ?? "
|
|
92
|
+
let currentLevel = readEnvLevel() ?? "info";
|
|
94
93
|
/**
|
|
95
94
|
* Set the runtime log level programmatically. Use this when the deployment can't
|
|
96
95
|
* surface `LOVABLE_MCP_LOG_LEVEL` to the package (e.g. a Cloudflare Worker that
|
|
@@ -108,11 +107,12 @@ function parseLogLevel(raw) {
|
|
|
108
107
|
return isLogLevel(normalized) ? normalized : void 0;
|
|
109
108
|
}
|
|
110
109
|
/**
|
|
111
|
-
* Apply the log level from an env value
|
|
112
|
-
*
|
|
110
|
+
* Apply the log level from an env value when it names a valid level; otherwise
|
|
111
|
+
* leave the current level alone so a programmatic `setLogLevel` sticks.
|
|
113
112
|
*/
|
|
114
113
|
function applyLogLevelFromEnv(raw) {
|
|
115
|
-
|
|
114
|
+
const level = parseLogLevel(raw);
|
|
115
|
+
if (level !== void 0) setLogLevel(level);
|
|
116
116
|
}
|
|
117
117
|
function enabled(level) {
|
|
118
118
|
return LEVEL_RANK[level] <= LEVEL_RANK[currentLevel];
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import { i as log, r as describeError } from "./logger-Ctv5xUSD.js";
|
|
2
|
+
import { n as schemaFromDefinition, t as jsonSchemaFromDefinition } from "./schema-CW1jKvio.js";
|
|
3
|
+
import { i as ToolContext, n as errorSummary, r as resolveToolErrorMessage } from "./errors-DSe96Ffd.js";
|
|
4
|
+
import { r as createRequestAuthorizer, u as nowMs } from "./cors-D4YbODs-.js";
|
|
5
|
+
import { n as extractTextContent, t as finalizeToolResult } from "./tool-result-DfCk445b.js";
|
|
6
|
+
import { t as createGatedHandler } from "./gate-pGsVlY_4.js";
|
|
7
|
+
import { normalizeObjectSchema, safeParseAsync } from "@modelcontextprotocol/sdk/server/zod-compat.js";
|
|
8
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
9
|
+
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
10
|
+
import { McpServer as McpServer$1, classifyInboundRequest, createMcpHandler, isJsonContentType, isLegacyRequest } from "@modelcontextprotocol/server";
|
|
11
|
+
//#region src/protocols/mcp/protocol.ts
|
|
12
|
+
const MODERN_PROTOCOL_VERSION = "2026-07-28";
|
|
13
|
+
function statelessMethodNotAllowed(allow) {
|
|
14
|
+
return Response.json({
|
|
15
|
+
jsonrpc: "2.0",
|
|
16
|
+
id: null,
|
|
17
|
+
error: {
|
|
18
|
+
code: -32e3,
|
|
19
|
+
message: "Method not allowed: stateless server offers no standalone SSE stream (GET) or session (DELETE)."
|
|
20
|
+
}
|
|
21
|
+
}, {
|
|
22
|
+
status: 405,
|
|
23
|
+
headers: { Allow: allow }
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function acceptsResponseType(accept, responseType) {
|
|
27
|
+
if (accept === null) return false;
|
|
28
|
+
return accept.split(",").some((range) => {
|
|
29
|
+
const [mediaType, ...parameters] = range.split(";");
|
|
30
|
+
if (mediaType.trim().toLowerCase() !== responseType) return false;
|
|
31
|
+
const quality = parameters.find((parameter) => parameter.split("=", 1)[0]?.trim().toLowerCase() === "q");
|
|
32
|
+
if (quality === void 0) return true;
|
|
33
|
+
const separator = quality.indexOf("=");
|
|
34
|
+
if (separator === -1) return false;
|
|
35
|
+
const value = Number(quality.slice(separator + 1).trim());
|
|
36
|
+
return value > 0 && value <= 1;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function missingProtocolVersionHeader(id) {
|
|
40
|
+
return Response.json({
|
|
41
|
+
jsonrpc: "2.0",
|
|
42
|
+
id,
|
|
43
|
+
error: {
|
|
44
|
+
code: -32020,
|
|
45
|
+
message: "Bad Request: the required MCP-Protocol-Version header is absent",
|
|
46
|
+
data: { mismatch: {
|
|
47
|
+
header: "(missing)",
|
|
48
|
+
body: "the request body names protocol version 2026-07-28"
|
|
49
|
+
} }
|
|
50
|
+
}
|
|
51
|
+
}, { status: 400 });
|
|
52
|
+
}
|
|
53
|
+
function modernMessageWithoutProtocolHeader(request, body) {
|
|
54
|
+
if (request.headers.has("MCP-Protocol-Version")) return void 0;
|
|
55
|
+
const outcome = classifyInboundRequest({
|
|
56
|
+
httpMethod: request.method,
|
|
57
|
+
mcpMethodHeader: request.headers.get("Mcp-Method") ?? void 0,
|
|
58
|
+
mcpNameHeader: request.headers.get("Mcp-Name") ?? void 0,
|
|
59
|
+
body
|
|
60
|
+
});
|
|
61
|
+
if (outcome.kind !== "modern" || outcome.classification.revision !== MODERN_PROTOCOL_VERSION) return;
|
|
62
|
+
return { id: outcome.messageKind === "request" ? outcome.message.id : null };
|
|
63
|
+
}
|
|
64
|
+
function zodIssues(error) {
|
|
65
|
+
const issues = error?.issues;
|
|
66
|
+
if (!Array.isArray(issues)) return [{ message: String(error) }];
|
|
67
|
+
return issues.map((issue) => {
|
|
68
|
+
const value = issue;
|
|
69
|
+
return {
|
|
70
|
+
message: typeof value.message === "string" ? value.message : String(value.message),
|
|
71
|
+
...Array.isArray(value.path) ? { path: value.path } : {}
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function standardSchemaFromDefinition(definition) {
|
|
76
|
+
const schema = schemaFromDefinition(definition);
|
|
77
|
+
return { "~standard": {
|
|
78
|
+
version: 1,
|
|
79
|
+
vendor: "zod",
|
|
80
|
+
validate: async (value) => {
|
|
81
|
+
const parsed = await safeParseAsync(schema, value);
|
|
82
|
+
return parsed.success ? { value: parsed.data } : { issues: zodIssues(parsed.error) };
|
|
83
|
+
},
|
|
84
|
+
jsonSchema: {
|
|
85
|
+
input: () => jsonSchemaFromDefinition(definition, "input"),
|
|
86
|
+
output: () => jsonSchemaFromDefinition(definition, "output")
|
|
87
|
+
}
|
|
88
|
+
} };
|
|
89
|
+
}
|
|
90
|
+
async function invokeTool(tool, auth, recorder, first, protocol) {
|
|
91
|
+
const endUserId = auth?.principal.sub;
|
|
92
|
+
const args = tool.inputSchema ? first ?? {} : {};
|
|
93
|
+
const start = nowMs();
|
|
94
|
+
let result;
|
|
95
|
+
try {
|
|
96
|
+
result = await tool.handler(args, new ToolContext(auth));
|
|
97
|
+
} catch (err) {
|
|
98
|
+
const callerMessage = resolveToolErrorMessage(err);
|
|
99
|
+
if (callerMessage === void 0) {
|
|
100
|
+
await recorder.emit({
|
|
101
|
+
tool: tool.name,
|
|
102
|
+
method: "tools/call",
|
|
103
|
+
outcome: "handler_error",
|
|
104
|
+
durationMs: nowMs() - start,
|
|
105
|
+
errorText: errorSummary(err),
|
|
106
|
+
endUserId,
|
|
107
|
+
protocol
|
|
108
|
+
});
|
|
109
|
+
return finalizeToolResult(tool, {
|
|
110
|
+
content: [{
|
|
111
|
+
type: "text",
|
|
112
|
+
text: "tool execution failed"
|
|
113
|
+
}],
|
|
114
|
+
isError: true
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
result = {
|
|
118
|
+
content: [{
|
|
119
|
+
type: "text",
|
|
120
|
+
text: callerMessage
|
|
121
|
+
}],
|
|
122
|
+
isError: true
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if (result == null) {
|
|
126
|
+
await recorder.emit({
|
|
127
|
+
tool: tool.name,
|
|
128
|
+
method: "tools/call",
|
|
129
|
+
outcome: "handler_error",
|
|
130
|
+
durationMs: nowMs() - start,
|
|
131
|
+
endUserId,
|
|
132
|
+
protocol
|
|
133
|
+
});
|
|
134
|
+
return finalizeToolResult(tool, {
|
|
135
|
+
content: [{
|
|
136
|
+
type: "text",
|
|
137
|
+
text: `tool "${tool.name}" returned no result`
|
|
138
|
+
}],
|
|
139
|
+
isError: true
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
result = await finalizeToolResult(tool, result);
|
|
143
|
+
await recorder.emit({
|
|
144
|
+
tool: tool.name,
|
|
145
|
+
method: "tools/call",
|
|
146
|
+
outcome: result.isError ? "tool_error" : "ok",
|
|
147
|
+
durationMs: nowMs() - start,
|
|
148
|
+
errorText: result.isError ? extractTextContent(result.content) : void 0,
|
|
149
|
+
endUserId,
|
|
150
|
+
protocol
|
|
151
|
+
});
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
function adaptToolCallback(tool, auth, recorder, legacy = false, legacyOutputSchemaWrapped = false) {
|
|
155
|
+
return async (first) => {
|
|
156
|
+
const result = await invokeTool(tool, auth, recorder, first, legacy ? "legacy" : MODERN_PROTOCOL_VERSION);
|
|
157
|
+
const content = result.content ?? [];
|
|
158
|
+
const structuredContent = result.structuredContent;
|
|
159
|
+
return {
|
|
160
|
+
content,
|
|
161
|
+
structuredContent: legacy && structuredContent !== void 0 && (legacyOutputSchemaWrapped || structuredContent !== void 0 && (structuredContent === null || typeof structuredContent !== "object" || Array.isArray(structuredContent))) ? { result: structuredContent } : structuredContent,
|
|
162
|
+
isError: result.isError
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function buildServer(mcp, auth, recorder) {
|
|
167
|
+
const server = new McpServer$1({
|
|
168
|
+
name: mcp.name,
|
|
169
|
+
version: mcp.version,
|
|
170
|
+
title: mcp.title
|
|
171
|
+
}, { instructions: mcp.instructions });
|
|
172
|
+
for (const tool of mcp.tools) {
|
|
173
|
+
const config = {
|
|
174
|
+
title: tool.title,
|
|
175
|
+
description: tool.description,
|
|
176
|
+
annotations: tool.annotations,
|
|
177
|
+
...tool.outputSchema ? { outputSchema: standardSchemaFromDefinition(tool.outputSchema) } : {}
|
|
178
|
+
};
|
|
179
|
+
const callback = adaptToolCallback(tool, auth, recorder);
|
|
180
|
+
if (tool.inputSchema) server.registerTool(tool.name, {
|
|
181
|
+
...config,
|
|
182
|
+
inputSchema: standardSchemaFromDefinition(tool.inputSchema)
|
|
183
|
+
}, callback);
|
|
184
|
+
else server.registerTool(tool.name, config, callback);
|
|
185
|
+
}
|
|
186
|
+
return server;
|
|
187
|
+
}
|
|
188
|
+
function buildLegacyServer(mcp, auth, recorder) {
|
|
189
|
+
const server = new McpServer({
|
|
190
|
+
name: mcp.name,
|
|
191
|
+
version: mcp.version,
|
|
192
|
+
title: mcp.title
|
|
193
|
+
}, { instructions: mcp.instructions });
|
|
194
|
+
for (const tool of mcp.tools) {
|
|
195
|
+
const naturalOutputSchema = tool.outputSchema ? schemaFromDefinition(tool.outputSchema) : void 0;
|
|
196
|
+
const legacyOutputSchemaWrapped = naturalOutputSchema !== void 0 && normalizeObjectSchema(naturalOutputSchema) === void 0;
|
|
197
|
+
server.registerTool(tool.name, {
|
|
198
|
+
title: tool.title,
|
|
199
|
+
description: tool.description,
|
|
200
|
+
inputSchema: tool.inputSchema,
|
|
201
|
+
outputSchema: legacyOutputSchemaWrapped ? { result: naturalOutputSchema } : tool.outputSchema,
|
|
202
|
+
annotations: tool.annotations
|
|
203
|
+
}, adaptToolCallback(tool, auth, recorder, true, legacyOutputSchemaWrapped));
|
|
204
|
+
}
|
|
205
|
+
return server;
|
|
206
|
+
}
|
|
207
|
+
async function serveLegacy(request, parsedBody, mcp, auth, recorder) {
|
|
208
|
+
const server = buildLegacyServer(mcp, auth, recorder);
|
|
209
|
+
const transport = new WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
|
|
210
|
+
let closed = false;
|
|
211
|
+
const close = () => {
|
|
212
|
+
if (closed) return;
|
|
213
|
+
closed = true;
|
|
214
|
+
transport.close().catch(() => {});
|
|
215
|
+
server.close().catch(() => {});
|
|
216
|
+
};
|
|
217
|
+
try {
|
|
218
|
+
await server.connect(transport);
|
|
219
|
+
request.signal.addEventListener("abort", close, { once: true });
|
|
220
|
+
const response = await transport.handleRequest(request, { parsedBody });
|
|
221
|
+
if (response.body === null || !response.headers.get("Content-Type")?.startsWith("text/event-stream")) {
|
|
222
|
+
close();
|
|
223
|
+
return response;
|
|
224
|
+
}
|
|
225
|
+
const reader = response.body.getReader();
|
|
226
|
+
const body = new ReadableStream({
|
|
227
|
+
async pull(controller) {
|
|
228
|
+
try {
|
|
229
|
+
const { done, value } = await reader.read();
|
|
230
|
+
if (done) {
|
|
231
|
+
close();
|
|
232
|
+
controller.close();
|
|
233
|
+
} else controller.enqueue(value);
|
|
234
|
+
} catch (error) {
|
|
235
|
+
close();
|
|
236
|
+
controller.error(error);
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
cancel(reason) {
|
|
240
|
+
close();
|
|
241
|
+
return reader.cancel(reason);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
return new Response(body, response);
|
|
245
|
+
} catch (error) {
|
|
246
|
+
close();
|
|
247
|
+
throw error;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Build a Web-Standard request handler that serves the MCP protocol over
|
|
252
|
+
* streamable HTTP. A fresh `McpServer` + transport is constructed per
|
|
253
|
+
* request — the transport is stateless, so this is cheap and there is no
|
|
254
|
+
* cross-request state to leak. The per-request `recorder` is supplied by the
|
|
255
|
+
* caller (the stack handler) so telemetry reads the runtime env in-flight.
|
|
256
|
+
*/
|
|
257
|
+
function createMcpProtocolHandler(mcp, options = {}) {
|
|
258
|
+
const authorizer = createRequestAuthorizer(mcp, options);
|
|
259
|
+
const handle = async (request, authResult, recorder) => {
|
|
260
|
+
const accept = request.headers.get("Accept");
|
|
261
|
+
if (!acceptsResponseType(accept, "application/json") || !acceptsResponseType(accept, "text/event-stream")) return Response.json({
|
|
262
|
+
jsonrpc: "2.0",
|
|
263
|
+
id: null,
|
|
264
|
+
error: {
|
|
265
|
+
code: -32e3,
|
|
266
|
+
message: "Not Acceptable: Client must accept both application/json and text/event-stream"
|
|
267
|
+
}
|
|
268
|
+
}, { status: 406 });
|
|
269
|
+
let protocolError;
|
|
270
|
+
try {
|
|
271
|
+
const protocol = createMcpHandler(() => buildServer(mcp, authResult.auth, recorder), {
|
|
272
|
+
legacy: "reject",
|
|
273
|
+
onerror: (error) => {
|
|
274
|
+
protocolError = error;
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
let response;
|
|
278
|
+
if (isJsonContentType(request.headers.get("Content-Type"))) {
|
|
279
|
+
let parsedBody;
|
|
280
|
+
try {
|
|
281
|
+
parsedBody = await request.clone().json();
|
|
282
|
+
} catch {
|
|
283
|
+
const headerVersion = request.headers.get("MCP-Protocol-Version")?.trim();
|
|
284
|
+
response = headerVersion === void 0 || headerVersion < MODERN_PROTOCOL_VERSION ? await serveLegacy(request, void 0, mcp, authResult.auth, recorder) : await protocol.fetch(request);
|
|
285
|
+
}
|
|
286
|
+
if (response === void 0) {
|
|
287
|
+
const missingProtocolHeader = modernMessageWithoutProtocolHeader(request, parsedBody);
|
|
288
|
+
if (missingProtocolHeader !== void 0) return missingProtocolVersionHeader(missingProtocolHeader.id);
|
|
289
|
+
response = await isLegacyRequest(request, parsedBody) ? await serveLegacy(request, parsedBody, mcp, authResult.auth, recorder) : await protocol.fetch(request, { parsedBody });
|
|
290
|
+
}
|
|
291
|
+
} else response = await protocol.fetch(request);
|
|
292
|
+
if (response.status < 500) return response;
|
|
293
|
+
await recorder.emit({
|
|
294
|
+
tool: null,
|
|
295
|
+
method: "transport",
|
|
296
|
+
outcome: "transport_error",
|
|
297
|
+
durationMs: 0,
|
|
298
|
+
endUserId: authResult.auth?.principal.sub
|
|
299
|
+
});
|
|
300
|
+
log.error("mcp.transport_error", {
|
|
301
|
+
...protocolError ? describeError(protocolError) : {},
|
|
302
|
+
outcome: "500 internal error"
|
|
303
|
+
});
|
|
304
|
+
return response;
|
|
305
|
+
} catch (err) {
|
|
306
|
+
await recorder.emit({
|
|
307
|
+
tool: null,
|
|
308
|
+
method: "transport",
|
|
309
|
+
outcome: "transport_error",
|
|
310
|
+
durationMs: 0,
|
|
311
|
+
endUserId: authResult.auth?.principal.sub
|
|
312
|
+
});
|
|
313
|
+
log.error("mcp.transport_error", {
|
|
314
|
+
...describeError(err),
|
|
315
|
+
outcome: "500 internal error"
|
|
316
|
+
});
|
|
317
|
+
return Response.json({
|
|
318
|
+
jsonrpc: "2.0",
|
|
319
|
+
id: null,
|
|
320
|
+
error: {
|
|
321
|
+
code: -32603,
|
|
322
|
+
message: "internal error"
|
|
323
|
+
}
|
|
324
|
+
}, { status: 500 });
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
return createGatedHandler({
|
|
328
|
+
authorizer,
|
|
329
|
+
allowedOrigins: mcp.allowedOrigins,
|
|
330
|
+
methods: ["POST"],
|
|
331
|
+
methodNotAllowed: statelessMethodNotAllowed,
|
|
332
|
+
handle
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
//#endregion
|
|
336
|
+
export { createMcpProtocolHandler as t };
|