@lovable.dev/mcp-js 0.29.0-rc.2 → 0.29.0-rc.4
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.internal.md +3 -3
- package/README.md +2 -25
- package/dist/{cors-DNzLB6qa.cjs → authorize-CKMlIxm9.cjs} +29 -78
- package/dist/{authorize-Bpz8oxjF.d.ts → authorize-CqYsQYfa.d.ts} +1 -1
- package/dist/{authorize-Cqr7Jps0.d.cts → authorize-DIYS7zBP.d.cts} +1 -1
- package/dist/{cors-C_VffY86.js → authorize-b3VqTC_7.js} +24 -73
- package/dist/cli/extract-manifest.cjs +12 -7
- package/dist/cli/extract-manifest.js +12 -7
- package/dist/{errors-Cr95rSkB.cjs → errors-CN6Da8iz.cjs} +8 -23
- package/dist/{errors-Bwd1L0Rf.js → errors-DSe96Ffd.js} +8 -23
- package/dist/{forwarded-BC_RHnn5.cjs → forwarded-Bhwcj22u.cjs} +3 -3
- package/dist/{forwarded-B3EBjHnA.js → forwarded-BlLF3dfc.js} +3 -3
- package/dist/index.cjs +1 -38
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -38
- package/dist/{io-DrHivYt0.d.ts → io-DP3icuxa.d.ts} +1 -1
- package/dist/{io-Bh_WpbxU.d.cts → io-Dx0nPc1-.d.cts} +1 -1
- package/dist/mcp-BLs6_g8i.js +174 -0
- package/dist/mcp-DtHM-41c.cjs +179 -0
- package/dist/{package-CYA-5C24.js → package-DMs4EVwG.js} +1 -1
- package/dist/{package-DNSwCau1.cjs → package-Xk8p8Q_E.cjs} +1 -1
- package/dist/protocols/mcp/index.cjs +1 -1
- package/dist/protocols/mcp/index.d.cts +3 -6
- package/dist/protocols/mcp/index.d.ts +3 -6
- package/dist/protocols/mcp/index.js +1 -1
- package/dist/protocols/oauth-metadata.cjs +9 -11
- package/dist/protocols/oauth-metadata.d.cts +2 -2
- package/dist/protocols/oauth-metadata.d.ts +2 -2
- package/dist/protocols/oauth-metadata.js +3 -5
- package/dist/stacks/supabase/index.cjs +5 -5
- package/dist/stacks/supabase/index.d.cts +1 -1
- package/dist/stacks/supabase/index.d.ts +1 -1
- package/dist/stacks/supabase/index.js +3 -3
- 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 +4 -4
- package/dist/stacks/tanstack/index.d.cts +2 -2
- package/dist/stacks/tanstack/index.d.ts +2 -2
- package/dist/stacks/tanstack/index.js +3 -3
- package/dist/stacks/tanstack/vite.cjs +4 -8
- package/dist/stacks/tanstack/vite.d.cts +1 -4
- package/dist/stacks/tanstack/vite.d.ts +1 -4
- package/dist/stacks/tanstack/vite.js +4 -8
- package/dist/{types-DnGEKnvI.d.cts → types-C0Wgm9zp.d.cts} +26 -57
- package/dist/{types-DnGEKnvI.d.ts → types-C0Wgm9zp.d.ts} +26 -57
- package/package.json +1 -5
- package/dist/mcp-Bl7pQYdv.js +0 -346
- package/dist/mcp-CRdT2Fai.cjs +0 -351
- package/dist/schema-CW1jKvio.js +0 -65
- package/dist/schema-D3vClxb7.cjs +0 -76
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as
|
|
1
|
+
import { C as McpAuthConfig, S as JwtClaims, _ as ZodRawShape, a as EmbeddedResource, b as MetricsOptions, c as McpDefinition, d as ResourceLinkIcon, f as TextContent, g as ToolHandlerResult, h as ToolDefinition, i as EmbeddedBlobResource, l as McpDefinitionInput, m as ToolContent, n as ContentAnnotations, o as EmbeddedTextResource, p as ToolAnnotations, r as ContentBlock, s as ImageContent, t as AudioContent, u as ResourceLink, v as ZodSchema, x as ToolContext, y as MetricsConfig } from "./types-C0Wgm9zp.js";
|
|
2
2
|
//#region src/core/define.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* Declare a single MCP tool. Import the result into your MCP definition's
|
|
@@ -8,7 +8,7 @@ import { C as MetricsOptions, D as JwtClaims, E as ToolContext, O as McpAuthConf
|
|
|
8
8
|
* full `z.object(...)` — the MCP SDK builds the object wrapper internally
|
|
9
9
|
* and the raw shape is what the JSON-RPC `tools/list` payload exposes.
|
|
10
10
|
*/
|
|
11
|
-
declare function defineTool<TInput extends ZodRawShape | undefined = undefined, TOutput extends
|
|
11
|
+
declare function defineTool<TInput extends ZodRawShape | undefined = undefined, TOutput extends ZodRawShape | undefined = undefined>(def: ToolDefinition<TInput, TOutput>): ToolDefinition<TInput, TOutput>;
|
|
12
12
|
/**
|
|
13
13
|
* Declare the MCP server. `export default` the result from your MCP
|
|
14
14
|
* entrypoint (default `lib/mcp/index.ts`); the Vite plugin reads it to
|
|
@@ -71,4 +71,4 @@ type LogLevel = "silent" | "error" | "warn" | "info" | "debug";
|
|
|
71
71
|
*/
|
|
72
72
|
declare function setLogLevel(level: LogLevel): LogLevel;
|
|
73
73
|
//#endregion
|
|
74
|
-
export { type AudioContent, type ContentAnnotations, type ContentBlock, type EmbeddedBlobResource, type EmbeddedResource, type EmbeddedTextResource, type ImageContent, type IssuerOAuthOptions, type
|
|
74
|
+
export { type AudioContent, type ContentAnnotations, type ContentBlock, type EmbeddedBlobResource, type EmbeddedResource, type EmbeddedTextResource, type ImageContent, type IssuerOAuthOptions, type JwtClaims, type LogLevel, type McpDefinitionInput, type MetricsConfig, type MetricsOptions, type ResourceLink, type ResourceLinkIcon, type TextContent, type ToolAnnotations, type ToolContent, ToolContext, type ToolDefinition, ToolError, type ToolHandlerResult, type ZodRawShape, type ZodSchema, auth, defineMcp, defineTool, setLogLevel };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { a as setLogLevel, o as parseSafeUrl, s as trimTrailingSlash, u as resolveMetricsConfig } from "./logger-Ctv5xUSD.js";
|
|
2
|
-
import { t as
|
|
3
|
-
import { i as ToolContext, t as ToolError } from "./errors-Bwd1L0Rf.js";
|
|
2
|
+
import { i as ToolContext, t as ToolError } from "./errors-DSe96Ffd.js";
|
|
4
3
|
//#region src/core/define.ts
|
|
5
4
|
function assertUniqueNames(mcp) {
|
|
6
5
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -79,39 +78,6 @@ function freezeAuth(auth) {
|
|
|
79
78
|
if (auth.accessTokenTyp) Object.freeze(auth.accessTokenTyp);
|
|
80
79
|
Object.freeze(auth);
|
|
81
80
|
}
|
|
82
|
-
function assertAllowedOrigins(allowedOrigins) {
|
|
83
|
-
if (allowedOrigins === void 0 || allowedOrigins === "any") return;
|
|
84
|
-
if (!Array.isArray(allowedOrigins)) throw new Error(`@lovable.dev/mcp-js: allowedOrigins must be "any" or an array of exact origins`);
|
|
85
|
-
for (const origin of allowedOrigins) {
|
|
86
|
-
if (typeof origin !== "string") throw new Error(`@lovable.dev/mcp-js: allowedOrigins must contain strings`);
|
|
87
|
-
let parsed;
|
|
88
|
-
try {
|
|
89
|
-
parsed = new URL(origin);
|
|
90
|
-
} catch {
|
|
91
|
-
throw new Error(`@lovable.dev/mcp-js: allowedOrigins must contain valid exact origins`);
|
|
92
|
-
}
|
|
93
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.origin !== origin) throw new Error(`@lovable.dev/mcp-js: allowedOrigins entry "${origin}" must be an exact http(s) origin without a path, query, fragment, or trailing slash`);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
function assertToolSchemas(mcp) {
|
|
97
|
-
for (const tool of mcp.tools) for (const [name, definition, strategy] of [[
|
|
98
|
-
"inputSchema",
|
|
99
|
-
tool.inputSchema,
|
|
100
|
-
"input"
|
|
101
|
-
], [
|
|
102
|
-
"outputSchema",
|
|
103
|
-
tool.outputSchema,
|
|
104
|
-
"output"
|
|
105
|
-
]]) {
|
|
106
|
-
if (!definition) continue;
|
|
107
|
-
try {
|
|
108
|
-
jsonSchemaFromDefinition(definition, strategy);
|
|
109
|
-
} catch (error) {
|
|
110
|
-
const detail = error instanceof Error ? `: ${error.message}` : "";
|
|
111
|
-
throw new Error(`@lovable.dev/mcp-js: tool "${tool.name}" ${name} cannot be represented as JSON Schema${detail}`);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
81
|
/**
|
|
116
82
|
* Declare a single MCP tool. Import the result into your MCP definition's
|
|
117
83
|
* `tools` array — `defineMcp({ tools: [echoTool, ...] })`.
|
|
@@ -134,11 +100,8 @@ function defineMcp(def) {
|
|
|
134
100
|
assertNonEmptyString("version", def.version);
|
|
135
101
|
assertUniqueNames(def);
|
|
136
102
|
if (def.auth) assertOAuthConfig(def.auth);
|
|
137
|
-
assertAllowedOrigins(def.allowedOrigins);
|
|
138
|
-
assertToolSchemas(def);
|
|
139
103
|
resolveMetricsConfig(def.metrics);
|
|
140
104
|
freezeAuth(def.auth);
|
|
141
|
-
if (Array.isArray(def.allowedOrigins)) Object.freeze(def.allowedOrigins);
|
|
142
105
|
Object.freeze(def.tools);
|
|
143
106
|
for (const tool of def.tools) Object.freeze(tool);
|
|
144
107
|
return def;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { i as log, r as describeError } from "./logger-Ctv5xUSD.js";
|
|
2
|
+
import { i as ToolContext, n as errorSummary, r as resolveToolErrorMessage } from "./errors-DSe96Ffd.js";
|
|
3
|
+
import { c as nowMs, f as corsPreflightResponse, s as createNoopRecorder, t as createRequestAuthorizer, u as finalizeWireResponse } from "./authorize-b3VqTC_7.js";
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
6
|
+
//#region src/core/content.ts
|
|
7
|
+
/** Join the text of a result's `type: "text"` blocks into one string, dropping
|
|
8
|
+
* binary/resource blocks. Used to surface a `tool_error` message in the local
|
|
9
|
+
* log; never sent over the telemetry wire. Returns undefined when there is no
|
|
10
|
+
* text to show. */
|
|
11
|
+
function extractTextContent(content) {
|
|
12
|
+
if (!content) return void 0;
|
|
13
|
+
const text = content.filter((block) => block.type === "text").map((block) => block.text).join("\n").trim();
|
|
14
|
+
return text.length > 0 ? text : void 0;
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/protocols/gate.ts
|
|
18
|
+
function createGatedHandler(gate) {
|
|
19
|
+
const allow = [...gate.methods, "OPTIONS"].join(", ");
|
|
20
|
+
const respond = async (request, recorder) => {
|
|
21
|
+
const authResult = await gate.authorizer.authorize(request, recorder);
|
|
22
|
+
if (!authResult.ok) return authResult.response;
|
|
23
|
+
if (!gate.methods.includes(request.method)) return gate.methodNotAllowed(allow);
|
|
24
|
+
return gate.handle(request, authResult, recorder);
|
|
25
|
+
};
|
|
26
|
+
return async (request, recorder = createNoopRecorder()) => {
|
|
27
|
+
if (request.method === "OPTIONS") return corsPreflightResponse(allow);
|
|
28
|
+
const final = finalizeWireResponse(request, await respond(request, recorder));
|
|
29
|
+
if (!final.headers.has("Cache-Control")) final.headers.set("Cache-Control", "no-store");
|
|
30
|
+
return final;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/protocols/mcp/protocol.ts
|
|
35
|
+
function statelessMethodNotAllowed(allow) {
|
|
36
|
+
return Response.json({
|
|
37
|
+
jsonrpc: "2.0",
|
|
38
|
+
id: null,
|
|
39
|
+
error: {
|
|
40
|
+
code: -32e3,
|
|
41
|
+
message: "Method not allowed: stateless server offers no standalone SSE stream (GET) or session (DELETE)."
|
|
42
|
+
}
|
|
43
|
+
}, {
|
|
44
|
+
status: 405,
|
|
45
|
+
headers: { Allow: allow }
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Adapt a Lovable tool to the SDK's tool callback. The boundary cast bridges
|
|
50
|
+
* our SDK-decoupled content-block types to the SDK's; they match structurally.
|
|
51
|
+
*/
|
|
52
|
+
function adaptToolToSdkCallback(tool, auth, recorder) {
|
|
53
|
+
const endUserId = auth?.principal.sub;
|
|
54
|
+
return (async (first) => {
|
|
55
|
+
const args = tool.inputSchema ? first ?? {} : {};
|
|
56
|
+
const start = nowMs();
|
|
57
|
+
let result;
|
|
58
|
+
try {
|
|
59
|
+
result = await tool.handler(args, new ToolContext(auth));
|
|
60
|
+
} catch (err) {
|
|
61
|
+
const callerMessage = resolveToolErrorMessage(err);
|
|
62
|
+
if (callerMessage === void 0) {
|
|
63
|
+
await recorder.emit({
|
|
64
|
+
tool: tool.name,
|
|
65
|
+
method: "tools/call",
|
|
66
|
+
outcome: "handler_error",
|
|
67
|
+
durationMs: nowMs() - start,
|
|
68
|
+
errorText: errorSummary(err),
|
|
69
|
+
endUserId
|
|
70
|
+
});
|
|
71
|
+
return {
|
|
72
|
+
content: [{
|
|
73
|
+
type: "text",
|
|
74
|
+
text: "tool execution failed"
|
|
75
|
+
}],
|
|
76
|
+
isError: true
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
result = {
|
|
80
|
+
content: [{
|
|
81
|
+
type: "text",
|
|
82
|
+
text: callerMessage
|
|
83
|
+
}],
|
|
84
|
+
isError: true
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (result == null) {
|
|
88
|
+
await recorder.emit({
|
|
89
|
+
tool: tool.name,
|
|
90
|
+
method: "tools/call",
|
|
91
|
+
outcome: "handler_error",
|
|
92
|
+
durationMs: nowMs() - start,
|
|
93
|
+
endUserId
|
|
94
|
+
});
|
|
95
|
+
return {
|
|
96
|
+
content: [{
|
|
97
|
+
type: "text",
|
|
98
|
+
text: `tool "${tool.name}" returned no result`
|
|
99
|
+
}],
|
|
100
|
+
isError: true
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
await recorder.emit({
|
|
104
|
+
tool: tool.name,
|
|
105
|
+
method: "tools/call",
|
|
106
|
+
outcome: result.isError ? "tool_error" : "ok",
|
|
107
|
+
durationMs: nowMs() - start,
|
|
108
|
+
errorText: result.isError ? extractTextContent(result.content) : void 0,
|
|
109
|
+
endUserId
|
|
110
|
+
});
|
|
111
|
+
return {
|
|
112
|
+
content: result.content ?? [],
|
|
113
|
+
structuredContent: result.structuredContent,
|
|
114
|
+
isError: result.isError
|
|
115
|
+
};
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Build a Web-Standard request handler that serves the MCP protocol over
|
|
120
|
+
* streamable HTTP. A fresh `McpServer` + transport is constructed per
|
|
121
|
+
* request — the transport is stateless, so this is cheap and there is no
|
|
122
|
+
* cross-request state to leak. The per-request `recorder` is supplied by the
|
|
123
|
+
* caller (the stack handler) so telemetry reads the runtime env in-flight.
|
|
124
|
+
*/
|
|
125
|
+
function createMcpProtocolHandler(mcp, options = {}) {
|
|
126
|
+
const authorizer = createRequestAuthorizer(mcp, options);
|
|
127
|
+
const handle = async (request, authResult, recorder) => {
|
|
128
|
+
try {
|
|
129
|
+
const server = new McpServer({
|
|
130
|
+
name: mcp.name,
|
|
131
|
+
version: mcp.version,
|
|
132
|
+
title: mcp.title
|
|
133
|
+
}, { instructions: mcp.instructions });
|
|
134
|
+
for (const tool of mcp.tools) server.registerTool(tool.name, {
|
|
135
|
+
title: tool.title,
|
|
136
|
+
description: tool.description,
|
|
137
|
+
inputSchema: tool.inputSchema,
|
|
138
|
+
outputSchema: tool.outputSchema,
|
|
139
|
+
annotations: tool.annotations
|
|
140
|
+
}, adaptToolToSdkCallback(tool, authResult.auth, recorder));
|
|
141
|
+
const transport = new WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
|
|
142
|
+
await server.connect(transport);
|
|
143
|
+
return await transport.handleRequest(request);
|
|
144
|
+
} catch (err) {
|
|
145
|
+
await recorder.emit({
|
|
146
|
+
tool: null,
|
|
147
|
+
method: "transport",
|
|
148
|
+
outcome: "transport_error",
|
|
149
|
+
durationMs: 0,
|
|
150
|
+
endUserId: authResult.auth?.principal.sub
|
|
151
|
+
});
|
|
152
|
+
log.error("mcp.transport_error", {
|
|
153
|
+
...describeError(err),
|
|
154
|
+
outcome: "500 internal error"
|
|
155
|
+
});
|
|
156
|
+
return Response.json({
|
|
157
|
+
jsonrpc: "2.0",
|
|
158
|
+
id: null,
|
|
159
|
+
error: {
|
|
160
|
+
code: -32603,
|
|
161
|
+
message: "internal error"
|
|
162
|
+
}
|
|
163
|
+
}, { status: 500 });
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
return createGatedHandler({
|
|
167
|
+
authorizer,
|
|
168
|
+
methods: ["POST"],
|
|
169
|
+
methodNotAllowed: statelessMethodNotAllowed,
|
|
170
|
+
handle
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
//#endregion
|
|
174
|
+
export { createMcpProtocolHandler as t };
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
const require_logger = require("./logger-CN1q-KNn.cjs");
|
|
2
|
+
const require_errors = require("./errors-CN6Da8iz.cjs");
|
|
3
|
+
const require_authorize = require("./authorize-CKMlIxm9.cjs");
|
|
4
|
+
let _modelcontextprotocol_sdk_server_mcp_js = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
5
|
+
let _modelcontextprotocol_sdk_server_webStandardStreamableHttp_js = require("@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js");
|
|
6
|
+
//#region src/core/content.ts
|
|
7
|
+
/** Join the text of a result's `type: "text"` blocks into one string, dropping
|
|
8
|
+
* binary/resource blocks. Used to surface a `tool_error` message in the local
|
|
9
|
+
* log; never sent over the telemetry wire. Returns undefined when there is no
|
|
10
|
+
* text to show. */
|
|
11
|
+
function extractTextContent(content) {
|
|
12
|
+
if (!content) return void 0;
|
|
13
|
+
const text = content.filter((block) => block.type === "text").map((block) => block.text).join("\n").trim();
|
|
14
|
+
return text.length > 0 ? text : void 0;
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/protocols/gate.ts
|
|
18
|
+
function createGatedHandler(gate) {
|
|
19
|
+
const allow = [...gate.methods, "OPTIONS"].join(", ");
|
|
20
|
+
const respond = async (request, recorder) => {
|
|
21
|
+
const authResult = await gate.authorizer.authorize(request, recorder);
|
|
22
|
+
if (!authResult.ok) return authResult.response;
|
|
23
|
+
if (!gate.methods.includes(request.method)) return gate.methodNotAllowed(allow);
|
|
24
|
+
return gate.handle(request, authResult, recorder);
|
|
25
|
+
};
|
|
26
|
+
return async (request, recorder = require_authorize.createNoopRecorder()) => {
|
|
27
|
+
if (request.method === "OPTIONS") return require_authorize.corsPreflightResponse(allow);
|
|
28
|
+
const final = require_authorize.finalizeWireResponse(request, await respond(request, recorder));
|
|
29
|
+
if (!final.headers.has("Cache-Control")) final.headers.set("Cache-Control", "no-store");
|
|
30
|
+
return final;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/protocols/mcp/protocol.ts
|
|
35
|
+
function statelessMethodNotAllowed(allow) {
|
|
36
|
+
return Response.json({
|
|
37
|
+
jsonrpc: "2.0",
|
|
38
|
+
id: null,
|
|
39
|
+
error: {
|
|
40
|
+
code: -32e3,
|
|
41
|
+
message: "Method not allowed: stateless server offers no standalone SSE stream (GET) or session (DELETE)."
|
|
42
|
+
}
|
|
43
|
+
}, {
|
|
44
|
+
status: 405,
|
|
45
|
+
headers: { Allow: allow }
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Adapt a Lovable tool to the SDK's tool callback. The boundary cast bridges
|
|
50
|
+
* our SDK-decoupled content-block types to the SDK's; they match structurally.
|
|
51
|
+
*/
|
|
52
|
+
function adaptToolToSdkCallback(tool, auth, recorder) {
|
|
53
|
+
const endUserId = auth?.principal.sub;
|
|
54
|
+
return (async (first) => {
|
|
55
|
+
const args = tool.inputSchema ? first ?? {} : {};
|
|
56
|
+
const start = require_authorize.nowMs();
|
|
57
|
+
let result;
|
|
58
|
+
try {
|
|
59
|
+
result = await tool.handler(args, new require_errors.ToolContext(auth));
|
|
60
|
+
} catch (err) {
|
|
61
|
+
const callerMessage = require_errors.resolveToolErrorMessage(err);
|
|
62
|
+
if (callerMessage === void 0) {
|
|
63
|
+
await recorder.emit({
|
|
64
|
+
tool: tool.name,
|
|
65
|
+
method: "tools/call",
|
|
66
|
+
outcome: "handler_error",
|
|
67
|
+
durationMs: require_authorize.nowMs() - start,
|
|
68
|
+
errorText: require_errors.errorSummary(err),
|
|
69
|
+
endUserId
|
|
70
|
+
});
|
|
71
|
+
return {
|
|
72
|
+
content: [{
|
|
73
|
+
type: "text",
|
|
74
|
+
text: "tool execution failed"
|
|
75
|
+
}],
|
|
76
|
+
isError: true
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
result = {
|
|
80
|
+
content: [{
|
|
81
|
+
type: "text",
|
|
82
|
+
text: callerMessage
|
|
83
|
+
}],
|
|
84
|
+
isError: true
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (result == null) {
|
|
88
|
+
await recorder.emit({
|
|
89
|
+
tool: tool.name,
|
|
90
|
+
method: "tools/call",
|
|
91
|
+
outcome: "handler_error",
|
|
92
|
+
durationMs: require_authorize.nowMs() - start,
|
|
93
|
+
endUserId
|
|
94
|
+
});
|
|
95
|
+
return {
|
|
96
|
+
content: [{
|
|
97
|
+
type: "text",
|
|
98
|
+
text: `tool "${tool.name}" returned no result`
|
|
99
|
+
}],
|
|
100
|
+
isError: true
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
await recorder.emit({
|
|
104
|
+
tool: tool.name,
|
|
105
|
+
method: "tools/call",
|
|
106
|
+
outcome: result.isError ? "tool_error" : "ok",
|
|
107
|
+
durationMs: require_authorize.nowMs() - start,
|
|
108
|
+
errorText: result.isError ? extractTextContent(result.content) : void 0,
|
|
109
|
+
endUserId
|
|
110
|
+
});
|
|
111
|
+
return {
|
|
112
|
+
content: result.content ?? [],
|
|
113
|
+
structuredContent: result.structuredContent,
|
|
114
|
+
isError: result.isError
|
|
115
|
+
};
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Build a Web-Standard request handler that serves the MCP protocol over
|
|
120
|
+
* streamable HTTP. A fresh `McpServer` + transport is constructed per
|
|
121
|
+
* request — the transport is stateless, so this is cheap and there is no
|
|
122
|
+
* cross-request state to leak. The per-request `recorder` is supplied by the
|
|
123
|
+
* caller (the stack handler) so telemetry reads the runtime env in-flight.
|
|
124
|
+
*/
|
|
125
|
+
function createMcpProtocolHandler(mcp, options = {}) {
|
|
126
|
+
const authorizer = require_authorize.createRequestAuthorizer(mcp, options);
|
|
127
|
+
const handle = async (request, authResult, recorder) => {
|
|
128
|
+
try {
|
|
129
|
+
const server = new _modelcontextprotocol_sdk_server_mcp_js.McpServer({
|
|
130
|
+
name: mcp.name,
|
|
131
|
+
version: mcp.version,
|
|
132
|
+
title: mcp.title
|
|
133
|
+
}, { instructions: mcp.instructions });
|
|
134
|
+
for (const tool of mcp.tools) server.registerTool(tool.name, {
|
|
135
|
+
title: tool.title,
|
|
136
|
+
description: tool.description,
|
|
137
|
+
inputSchema: tool.inputSchema,
|
|
138
|
+
outputSchema: tool.outputSchema,
|
|
139
|
+
annotations: tool.annotations
|
|
140
|
+
}, adaptToolToSdkCallback(tool, authResult.auth, recorder));
|
|
141
|
+
const transport = new _modelcontextprotocol_sdk_server_webStandardStreamableHttp_js.WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
|
|
142
|
+
await server.connect(transport);
|
|
143
|
+
return await transport.handleRequest(request);
|
|
144
|
+
} catch (err) {
|
|
145
|
+
await recorder.emit({
|
|
146
|
+
tool: null,
|
|
147
|
+
method: "transport",
|
|
148
|
+
outcome: "transport_error",
|
|
149
|
+
durationMs: 0,
|
|
150
|
+
endUserId: authResult.auth?.principal.sub
|
|
151
|
+
});
|
|
152
|
+
require_logger.log.error("mcp.transport_error", {
|
|
153
|
+
...require_logger.describeError(err),
|
|
154
|
+
outcome: "500 internal error"
|
|
155
|
+
});
|
|
156
|
+
return Response.json({
|
|
157
|
+
jsonrpc: "2.0",
|
|
158
|
+
id: null,
|
|
159
|
+
error: {
|
|
160
|
+
code: -32603,
|
|
161
|
+
message: "internal error"
|
|
162
|
+
}
|
|
163
|
+
}, { status: 500 });
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
return createGatedHandler({
|
|
167
|
+
authorizer,
|
|
168
|
+
methods: ["POST"],
|
|
169
|
+
methodNotAllowed: statelessMethodNotAllowed,
|
|
170
|
+
handle
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
//#endregion
|
|
174
|
+
Object.defineProperty(exports, "createMcpProtocolHandler", {
|
|
175
|
+
enumerable: true,
|
|
176
|
+
get: function() {
|
|
177
|
+
return createMcpProtocolHandler;
|
|
178
|
+
}
|
|
179
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as McpRuntimeOptions } from "../../authorize-
|
|
1
|
+
import { c as McpDefinition } from "../../types-C0Wgm9zp.cjs";
|
|
2
|
+
import { t as McpRuntimeOptions } from "../../authorize-DIYS7zBP.cjs";
|
|
3
3
|
//#region src/metrics/impl/base.d.ts
|
|
4
|
-
type InvocationOutcome = "ok" | "tool_error" | "handler_error" | "transport_error" | "auth_challenge" | "
|
|
4
|
+
type InvocationOutcome = "ok" | "tool_error" | "handler_error" | "transport_error" | "auth_challenge" | "auth_discovery_config_error" | "auth_discovery_unreachable" | "auth_jwks_config_error" | "auth_jwks_unreachable";
|
|
5
5
|
/** One recorded MCP call. Carries no arguments or response payloads — only the
|
|
6
6
|
* tool name, the JSON-RPC method, the result class, timing, and byte sizes.
|
|
7
7
|
* `errorText` is the sole exception: it is logged locally for debugging and
|
|
@@ -18,9 +18,6 @@ interface InvocationRecord {
|
|
|
18
18
|
/** HTTP request method. Set on `auth_challenge` so probe traffic (GET vs
|
|
19
19
|
* POST discovery) is distinguishable; absent on tool-call records. */
|
|
20
20
|
httpMethod?: string;
|
|
21
|
-
/** Wire surface that served the call; absent on records emitted before
|
|
22
|
-
* protocol classification (auth gate, transport faults). */
|
|
23
|
-
protocol?: "2026-07-28" | "legacy";
|
|
24
21
|
/** The app end-user that made the call (the access token `sub`), or
|
|
25
22
|
* `undefined` when the request is unauthenticated. Not a Lovable identity. */
|
|
26
23
|
endUserId?: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as McpRuntimeOptions } from "../../authorize-
|
|
1
|
+
import { c as McpDefinition } from "../../types-C0Wgm9zp.js";
|
|
2
|
+
import { t as McpRuntimeOptions } from "../../authorize-CqYsQYfa.js";
|
|
3
3
|
//#region src/metrics/impl/base.d.ts
|
|
4
|
-
type InvocationOutcome = "ok" | "tool_error" | "handler_error" | "transport_error" | "auth_challenge" | "
|
|
4
|
+
type InvocationOutcome = "ok" | "tool_error" | "handler_error" | "transport_error" | "auth_challenge" | "auth_discovery_config_error" | "auth_discovery_unreachable" | "auth_jwks_config_error" | "auth_jwks_unreachable";
|
|
5
5
|
/** One recorded MCP call. Carries no arguments or response payloads — only the
|
|
6
6
|
* tool name, the JSON-RPC method, the result class, timing, and byte sizes.
|
|
7
7
|
* `errorText` is the sole exception: it is logged locally for debugging and
|
|
@@ -18,9 +18,6 @@ interface InvocationRecord {
|
|
|
18
18
|
/** HTTP request method. Set on `auth_challenge` so probe traffic (GET vs
|
|
19
19
|
* POST discovery) is distinguishable; absent on tool-call records. */
|
|
20
20
|
httpMethod?: string;
|
|
21
|
-
/** Wire surface that served the call; absent on records emitted before
|
|
22
|
-
* protocol classification (auth gate, transport faults). */
|
|
23
|
-
protocol?: "2026-07-28" | "legacy";
|
|
24
21
|
/** The app end-user that made the call (the access token `sub`), or
|
|
25
22
|
* `undefined` when the request is unauthenticated. Not a Lovable identity. */
|
|
26
23
|
endUserId?: string;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as createMcpProtocolHandler } from "../../mcp-
|
|
1
|
+
import { t as createMcpProtocolHandler } from "../../mcp-BLs6_g8i.js";
|
|
2
2
|
export { createMcpProtocolHandler };
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_logger = require("../logger-CN1q-KNn.cjs");
|
|
3
|
-
const
|
|
3
|
+
const require_authorize = require("../authorize-CKMlIxm9.cjs");
|
|
4
4
|
//#region src/protocols/oauth-metadata.ts
|
|
5
5
|
function notFound() {
|
|
6
6
|
return new Response(JSON.stringify({ error: "not found" }), {
|
|
7
7
|
status: 404,
|
|
8
8
|
headers: {
|
|
9
|
-
...
|
|
9
|
+
...require_authorize.JSON_HEADERS,
|
|
10
10
|
"Cache-Control": "no-store"
|
|
11
11
|
}
|
|
12
12
|
});
|
|
@@ -14,7 +14,7 @@ function notFound() {
|
|
|
14
14
|
async function buildProtectedResourceMetadata(mcp, auth, request, options, discovery) {
|
|
15
15
|
const issuer = await discovery.resolveIssuer();
|
|
16
16
|
const body = {
|
|
17
|
-
resource:
|
|
17
|
+
resource: require_authorize.resolveProtectedResource(auth, request, options),
|
|
18
18
|
authorization_servers: [issuer],
|
|
19
19
|
bearer_methods_supported: ["header"],
|
|
20
20
|
resource_name: auth.resourceName ?? mcp.title,
|
|
@@ -24,7 +24,7 @@ async function buildProtectedResourceMetadata(mcp, auth, request, options, disco
|
|
|
24
24
|
return body;
|
|
25
25
|
}
|
|
26
26
|
function createOAuthProtectedResourceMetadataHandler(mcp, options = {}) {
|
|
27
|
-
const runtime =
|
|
27
|
+
const runtime = require_authorize.getOAuthRuntime(mcp, options);
|
|
28
28
|
if (runtime.kind === "configured" && runtime.auth.protectedResourceMetadataUrl === void 0 && runtime.auth.resource === void 0) {
|
|
29
29
|
if (runtime.options.resourcePath === void 0) throw new Error(`@lovable.dev/mcp-js: auth.resource or a resourcePath is required so the protected-resource metadata doesn't advertise the well-known URL as the resource`);
|
|
30
30
|
if (runtime.options.trustForwardedHost === true && runtime.options.forwardedHostTrustedByPlatform !== true) require_logger.log.warn("oauth.metadata.host_derived_resource", { detail: "trustForwardedHost is on and auth.resource is unpinned; the advertised resource follows X-Forwarded-Host. Pin auth.resource unless a trusted proxy overwrites that header." });
|
|
@@ -35,9 +35,9 @@ function createOAuthProtectedResourceMetadataHandler(mcp, options = {}) {
|
|
|
35
35
|
const vary = varyHeaders.join(", ");
|
|
36
36
|
const respond = async (request) => {
|
|
37
37
|
if (runtime.kind !== "configured" || runtime.auth.protectedResourceMetadataUrl !== void 0) return notFound();
|
|
38
|
-
if (request.method !== "GET" && request.method !== "HEAD") return
|
|
38
|
+
if (request.method !== "GET" && request.method !== "HEAD") return require_authorize.methodNotAllowed("GET, HEAD, OPTIONS");
|
|
39
39
|
const headers = {
|
|
40
|
-
...
|
|
40
|
+
...require_authorize.JSON_HEADERS,
|
|
41
41
|
"Cache-Control": "public, max-age=300",
|
|
42
42
|
Vary: vary
|
|
43
43
|
};
|
|
@@ -49,14 +49,12 @@ function createOAuthProtectedResourceMetadataHandler(mcp, options = {}) {
|
|
|
49
49
|
...require_logger.describeError(err),
|
|
50
50
|
outcome: "500 oauth configuration error"
|
|
51
51
|
});
|
|
52
|
-
return
|
|
52
|
+
return require_authorize.oauthConfigurationErrorResponse();
|
|
53
53
|
}
|
|
54
54
|
};
|
|
55
55
|
return async (request) => {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (request.method === "OPTIONS") return cors.preflight("GET, HEAD, OPTIONS");
|
|
59
|
-
return require_cors.finalizeWireResponse(request, cors.withHeaders(await respond(request)));
|
|
56
|
+
if (request.method === "OPTIONS") return require_authorize.corsPreflightResponse("GET, HEAD, OPTIONS");
|
|
57
|
+
return require_authorize.finalizeWireResponse(request, await respond(request));
|
|
60
58
|
};
|
|
61
59
|
}
|
|
62
60
|
//#endregion
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as McpRuntimeOptions } from "../authorize-
|
|
1
|
+
import { c as McpDefinition } from "../types-C0Wgm9zp.cjs";
|
|
2
|
+
import { t as McpRuntimeOptions } from "../authorize-DIYS7zBP.cjs";
|
|
3
3
|
//#region src/protocols/oauth-metadata.d.ts
|
|
4
4
|
type OAuthProtectedResourceMetadataHandler = (request: Request) => Promise<Response>;
|
|
5
5
|
declare function createOAuthProtectedResourceMetadataHandler(mcp: McpDefinition, options?: McpRuntimeOptions): OAuthProtectedResourceMetadataHandler;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as McpRuntimeOptions } from "../authorize-
|
|
1
|
+
import { c as McpDefinition } from "../types-C0Wgm9zp.js";
|
|
2
|
+
import { t as McpRuntimeOptions } from "../authorize-CqYsQYfa.js";
|
|
3
3
|
//#region src/protocols/oauth-metadata.d.ts
|
|
4
4
|
type OAuthProtectedResourceMetadataHandler = (request: Request) => Promise<Response>;
|
|
5
5
|
declare function createOAuthProtectedResourceMetadataHandler(mcp: McpDefinition, options?: McpRuntimeOptions): OAuthProtectedResourceMetadataHandler;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as log, r as describeError } from "../logger-Ctv5xUSD.js";
|
|
2
|
-
import {
|
|
2
|
+
import { a as resolveProtectedResource, d as methodNotAllowed, f as corsPreflightResponse, l as JSON_HEADERS, n as getOAuthRuntime, r as oauthConfigurationErrorResponse, u as finalizeWireResponse } from "../authorize-b3VqTC_7.js";
|
|
3
3
|
//#region src/protocols/oauth-metadata.ts
|
|
4
4
|
function notFound() {
|
|
5
5
|
return new Response(JSON.stringify({ error: "not found" }), {
|
|
@@ -52,10 +52,8 @@ function createOAuthProtectedResourceMetadataHandler(mcp, options = {}) {
|
|
|
52
52
|
}
|
|
53
53
|
};
|
|
54
54
|
return async (request) => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (request.method === "OPTIONS") return cors.preflight("GET, HEAD, OPTIONS");
|
|
58
|
-
return finalizeWireResponse(request, cors.withHeaders(await respond(request)));
|
|
55
|
+
if (request.method === "OPTIONS") return corsPreflightResponse("GET, HEAD, OPTIONS");
|
|
56
|
+
return finalizeWireResponse(request, await respond(request));
|
|
59
57
|
};
|
|
60
58
|
}
|
|
61
59
|
//#endregion
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_logger = require("../../logger-CN1q-KNn.cjs");
|
|
3
|
-
const
|
|
3
|
+
const require_authorize = require("../../authorize-CKMlIxm9.cjs");
|
|
4
4
|
const require_metadata_path = require("../../metadata-path-zd7NSNoa.cjs");
|
|
5
|
-
const require_mcp = require("../../mcp-
|
|
5
|
+
const require_mcp = require("../../mcp-DtHM-41c.cjs");
|
|
6
6
|
const require_protocols_oauth_metadata = require("../../protocols/oauth-metadata.cjs");
|
|
7
|
-
const require_forwarded = require("../../forwarded-
|
|
7
|
+
const require_forwarded = require("../../forwarded-Bhwcj22u.cjs");
|
|
8
8
|
const require_paths = require("../../paths-OP318a6c.cjs");
|
|
9
9
|
//#region src/stacks/supabase/handler.ts
|
|
10
10
|
function deriveResourcePath(options) {
|
|
@@ -29,7 +29,7 @@ function isMetadataPath(pathname) {
|
|
|
29
29
|
*/
|
|
30
30
|
function createSupabaseHandler(mcp, options = {}) {
|
|
31
31
|
const resourcePath = deriveResourcePath(options);
|
|
32
|
-
if (resourcePath !== void 0)
|
|
32
|
+
if (resourcePath !== void 0) require_authorize.assertResourcePathShape(resourcePath);
|
|
33
33
|
const servesOwnPrm = !(mcp.auth?.type === "oauth" && mcp.auth.protectedResourceMetadataUrl !== void 0);
|
|
34
34
|
const metadataPath = resourcePath === void 0 || !servesOwnPrm ? void 0 : `${require_logger.trimTrailingSlash(resourcePath)}${require_metadata_path.OAUTH_PROTECTED_RESOURCE_METADATA_PATH}`;
|
|
35
35
|
const runtimeOptions = {
|
|
@@ -45,7 +45,7 @@ function createSupabaseHandler(mcp, options = {}) {
|
|
|
45
45
|
return async (request) => {
|
|
46
46
|
const req = require_forwarded.applyForwardedOrigin(request, runtimeOptions);
|
|
47
47
|
if (isMetadataPath(new URL(req.url).pathname)) return metadataHandler(req);
|
|
48
|
-
return mcpHandler(req,
|
|
48
|
+
return mcpHandler(req, require_authorize.createRecorderForRuntime(mcp, { stack: "supabase" }));
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
51
|
//#endregion
|