@lovable.dev/mcp-js 0.29.0-rc.4 → 0.30.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.
Files changed (57) hide show
  1. package/README.internal.md +3 -3
  2. package/README.md +25 -2
  3. package/dist/{authorize-DIYS7zBP.d.cts → authorize-9hTTG-qJ.d.cts} +1 -1
  4. package/dist/{authorize-CqYsQYfa.d.ts → authorize-DNUlPT7k.d.ts} +1 -1
  5. package/dist/base-7cq6HCPO.d.ts +36 -0
  6. package/dist/base-CHNFFrMG.d.cts +36 -0
  7. package/dist/cli/extract-manifest.cjs +7 -12
  8. package/dist/cli/extract-manifest.js +7 -12
  9. package/dist/{authorize-b3VqTC_7.js → cors-5P69YRDK.js} +79 -28
  10. package/dist/{authorize-CKMlIxm9.cjs → cors-DIQIXIHY.cjs} +90 -33
  11. package/dist/{errors-DSe96Ffd.js → errors-Bwd1L0Rf.js} +23 -8
  12. package/dist/{errors-CN6Da8iz.cjs → errors-Cr95rSkB.cjs} +23 -8
  13. package/dist/forwarded-DG83ubwT.js +26 -0
  14. package/dist/forwarded-zNAIofM2.cjs +31 -0
  15. package/dist/index.cjs +38 -1
  16. package/dist/index.d.cts +3 -3
  17. package/dist/index.d.ts +3 -3
  18. package/dist/index.js +38 -1
  19. package/dist/{io-Dx0nPc1-.d.cts → io-CDV3mud0.d.cts} +1 -1
  20. package/dist/{io-DP3icuxa.d.ts → io-D_5cuaEL.d.ts} +1 -1
  21. package/dist/mcp-Cb5SavFT.js +348 -0
  22. package/dist/mcp-Dq6dXUKT.cjs +353 -0
  23. package/dist/{package-Xk8p8Q_E.cjs → package-QViIguII.cjs} +1 -1
  24. package/dist/{package-DMs4EVwG.js → package-ZFJI6B7S.js} +1 -1
  25. package/dist/protocols/mcp/index.cjs +1 -1
  26. package/dist/protocols/mcp/index.d.cts +3 -33
  27. package/dist/protocols/mcp/index.d.ts +3 -33
  28. package/dist/protocols/mcp/index.js +1 -1
  29. package/dist/protocols/oauth-metadata.cjs +27 -10
  30. package/dist/protocols/oauth-metadata.d.cts +4 -3
  31. package/dist/protocols/oauth-metadata.d.ts +4 -3
  32. package/dist/protocols/oauth-metadata.js +21 -4
  33. package/dist/schema-CW1jKvio.js +65 -0
  34. package/dist/schema-D3vClxb7.cjs +76 -0
  35. package/dist/stacks/supabase/index.cjs +6 -6
  36. package/dist/stacks/supabase/index.d.cts +1 -1
  37. package/dist/stacks/supabase/index.d.ts +1 -1
  38. package/dist/stacks/supabase/index.js +4 -4
  39. package/dist/stacks/supabase/vite.cjs +1 -1
  40. package/dist/stacks/supabase/vite.d.cts +1 -1
  41. package/dist/stacks/supabase/vite.d.ts +1 -1
  42. package/dist/stacks/supabase/vite.js +1 -1
  43. package/dist/stacks/tanstack/index.cjs +5 -5
  44. package/dist/stacks/tanstack/index.d.cts +2 -2
  45. package/dist/stacks/tanstack/index.d.ts +2 -2
  46. package/dist/stacks/tanstack/index.js +4 -4
  47. package/dist/stacks/tanstack/vite.cjs +8 -4
  48. package/dist/stacks/tanstack/vite.d.cts +4 -1
  49. package/dist/stacks/tanstack/vite.d.ts +4 -1
  50. package/dist/stacks/tanstack/vite.js +8 -4
  51. package/dist/{types-C0Wgm9zp.d.cts → types-3CZPz364.d.cts} +65 -29
  52. package/dist/{types-C0Wgm9zp.d.ts → types-3CZPz364.d.ts} +65 -29
  53. package/package.json +5 -1
  54. package/dist/forwarded-Bhwcj22u.cjs +0 -48
  55. package/dist/forwarded-BlLF3dfc.js +0 -43
  56. package/dist/mcp-BLs6_g8i.js +0 -174
  57. package/dist/mcp-DtHM-41c.cjs +0 -179
@@ -1,15 +1,30 @@
1
1
  //#region src/auth/context.ts
2
- /**
3
- * The auth surface passed to every tool handler as its second argument. It wraps
4
- * the verified per-request auth context in a private field, so the context and
5
- * the bearer token inside it — can't be read, enumerated, spread, or
6
- * `JSON.stringify`'d off the instance; the accessors below are the only way out.
7
- * `getToken()` is the single intentional escape hatch for the raw bearer.
8
- */
2
+ const NEVER_ABORTED = new AbortController().signal;
3
+ /** The per-request context passed to every tool handler as its second argument. The
4
+ * auth context lives in a private field so the accessors are the only way out; `getToken()` is the sole bearer escape hatch. */
9
5
  var ToolContext = class {
10
6
  #auth;
11
- constructor(auth) {
7
+ #signal;
8
+ #client;
9
+ #sendProgress;
10
+ constructor(auth, request = {}) {
12
11
  this.#auth = auth;
12
+ this.#signal = request.signal ?? NEVER_ABORTED;
13
+ this.#client = request.client ?? { protocol: "legacy" };
14
+ this.#sendProgress = request.sendProgress;
15
+ }
16
+ /** Aborts when the client cancels the request or disconnects. */
17
+ get signal() {
18
+ return this.#signal;
19
+ }
20
+ /** What the client declared about itself on this request. Unverified input. */
21
+ get client() {
22
+ return this.#client;
23
+ }
24
+ /** Send a progress notification for this call. A no-op when the client
25
+ * didn't request progress (no `progressToken` on the request). */
26
+ async progress(update) {
27
+ await this.#sendProgress?.(update);
13
28
  }
14
29
  /** Whether the in-flight tool call carries a verified auth context. */
15
30
  isAuthenticated() {
@@ -0,0 +1,26 @@
1
+ //#region src/core/forwarded.ts
2
+ /** Rewrites the request URL to the proxy-forwarded public origin so derived OAuth
3
+ * URLs name the client-facing host. Trust model: AGENTS.md § Forwarded-header trust model. */
4
+ function applyForwardedOrigin(request, options) {
5
+ const proto = options.trustForwardedProto ? firstForwardedValue(request, "x-forwarded-proto") : void 0;
6
+ const host = options.trustForwardedHost ? firstForwardedValue(request, "x-forwarded-host") : void 0;
7
+ if (proto === void 0 && host === void 0) return request;
8
+ const url = new URL(request.url);
9
+ let changed = false;
10
+ if (proto !== void 0 && `${proto}:` !== url.protocol) {
11
+ url.protocol = `${proto}:`;
12
+ changed = true;
13
+ }
14
+ if (host !== void 0 && host !== url.host) {
15
+ url.host = host;
16
+ if (url.host !== host) url.port = "";
17
+ changed = true;
18
+ }
19
+ return changed ? new Request(url.href, request) : request;
20
+ }
21
+ function firstForwardedValue(request, header) {
22
+ const value = request.headers.get(header)?.split(",")[0]?.trim();
23
+ return value ? value : void 0;
24
+ }
25
+ //#endregion
26
+ export { applyForwardedOrigin as t };
@@ -0,0 +1,31 @@
1
+ //#region src/core/forwarded.ts
2
+ /** Rewrites the request URL to the proxy-forwarded public origin so derived OAuth
3
+ * URLs name the client-facing host. Trust model: AGENTS.md § Forwarded-header trust model. */
4
+ function applyForwardedOrigin(request, options) {
5
+ const proto = options.trustForwardedProto ? firstForwardedValue(request, "x-forwarded-proto") : void 0;
6
+ const host = options.trustForwardedHost ? firstForwardedValue(request, "x-forwarded-host") : void 0;
7
+ if (proto === void 0 && host === void 0) return request;
8
+ const url = new URL(request.url);
9
+ let changed = false;
10
+ if (proto !== void 0 && `${proto}:` !== url.protocol) {
11
+ url.protocol = `${proto}:`;
12
+ changed = true;
13
+ }
14
+ if (host !== void 0 && host !== url.host) {
15
+ url.host = host;
16
+ if (url.host !== host) url.port = "";
17
+ changed = true;
18
+ }
19
+ return changed ? new Request(url.href, request) : request;
20
+ }
21
+ function firstForwardedValue(request, header) {
22
+ const value = request.headers.get(header)?.split(",")[0]?.trim();
23
+ return value ? value : void 0;
24
+ }
25
+ //#endregion
26
+ Object.defineProperty(exports, "applyForwardedOrigin", {
27
+ enumerable: true,
28
+ get: function() {
29
+ return applyForwardedOrigin;
30
+ }
31
+ });
package/dist/index.cjs CHANGED
@@ -1,6 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_logger = require("./logger-CN1q-KNn.cjs");
3
- const require_errors = require("./errors-CN6Da8iz.cjs");
3
+ const require_schema = require("./schema-D3vClxb7.cjs");
4
+ const require_errors = require("./errors-Cr95rSkB.cjs");
4
5
  //#region src/core/define.ts
5
6
  function assertUniqueNames(mcp) {
6
7
  const seen = /* @__PURE__ */ new Set();
@@ -79,6 +80,39 @@ function freezeAuth(auth) {
79
80
  if (auth.accessTokenTyp) Object.freeze(auth.accessTokenTyp);
80
81
  Object.freeze(auth);
81
82
  }
83
+ function assertAllowedOrigins(allowedOrigins) {
84
+ if (allowedOrigins === void 0 || allowedOrigins === "any") return;
85
+ if (!Array.isArray(allowedOrigins)) throw new Error(`@lovable.dev/mcp-js: allowedOrigins must be "any" or an array of exact origins`);
86
+ for (const origin of allowedOrigins) {
87
+ if (typeof origin !== "string") throw new Error(`@lovable.dev/mcp-js: allowedOrigins must contain strings`);
88
+ let parsed;
89
+ try {
90
+ parsed = new URL(origin);
91
+ } catch {
92
+ throw new Error(`@lovable.dev/mcp-js: allowedOrigins must contain valid exact origins`);
93
+ }
94
+ 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`);
95
+ }
96
+ }
97
+ function assertToolSchemas(mcp) {
98
+ for (const tool of mcp.tools) for (const [name, definition, strategy] of [[
99
+ "inputSchema",
100
+ tool.inputSchema,
101
+ "input"
102
+ ], [
103
+ "outputSchema",
104
+ tool.outputSchema,
105
+ "output"
106
+ ]]) {
107
+ if (!definition) continue;
108
+ try {
109
+ require_schema.jsonSchemaFromDefinition(definition, strategy);
110
+ } catch (error) {
111
+ const detail = error instanceof Error ? `: ${error.message}` : "";
112
+ throw new Error(`@lovable.dev/mcp-js: tool "${tool.name}" ${name} cannot be represented as JSON Schema${detail}`);
113
+ }
114
+ }
115
+ }
82
116
  /**
83
117
  * Declare a single MCP tool. Import the result into your MCP definition's
84
118
  * `tools` array — `defineMcp({ tools: [echoTool, ...] })`.
@@ -101,8 +135,11 @@ function defineMcp(def) {
101
135
  assertNonEmptyString("version", def.version);
102
136
  assertUniqueNames(def);
103
137
  if (def.auth) assertOAuthConfig(def.auth);
138
+ assertAllowedOrigins(def.allowedOrigins);
139
+ assertToolSchemas(def);
104
140
  require_logger.resolveMetricsConfig(def.metrics);
105
141
  freezeAuth(def.auth);
142
+ if (Array.isArray(def.allowedOrigins)) Object.freeze(def.allowedOrigins);
106
143
  Object.freeze(def.tools);
107
144
  for (const tool of def.tools) Object.freeze(tool);
108
145
  return def;
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
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.cjs";
1
+ import { A as McpAuthConfig, C as MetricsConfig, D as ProgressUpdate, E as McpProtocolEra, O as ToolContext, S as ZodSchema, T as McpClientInfo, _ as ToolContent, a as EmbeddedResource, b as ZodOutputSchema, c as JsonValue, d as McpDefinition, f as McpDefinitionInput, g as ToolAnnotations, h as TextContent, i as EmbeddedBlobResource, k as JwtClaims, l as JsonValueInput, m as ResourceLinkIcon, n as ContentAnnotations, o as EmbeddedTextResource, p as ResourceLink, r as ContentBlock, s as ImageContent, t as AudioContent, u as McpAllowedOrigins, v as ToolDefinition, w as MetricsOptions, x as ZodRawShape, y as ToolHandlerResult } from "./types-3CZPz364.cjs";
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 McpAuthConfig, S as JwtClaims, _ as ZodRawShape, a as EmbeddedReso
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 ZodRawShape | undefined = undefined>(def: ToolDefinition<TInput, TOutput>): ToolDefinition<TInput, TOutput>;
11
+ declare function defineTool<TInput extends ZodRawShape | undefined = undefined, TOutput extends ZodOutputSchema | 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 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 };
74
+ export { type AudioContent, type ContentAnnotations, type ContentBlock, type EmbeddedBlobResource, type EmbeddedResource, type EmbeddedTextResource, type ImageContent, type IssuerOAuthOptions, type JsonValue, type JsonValueInput, type JwtClaims, type LogLevel, type McpAllowedOrigins, type McpClientInfo, type McpDefinitionInput, type McpProtocolEra, type MetricsConfig, type MetricsOptions, type ProgressUpdate, type ResourceLink, type ResourceLinkIcon, type TextContent, type ToolAnnotations, type ToolContent, ToolContext, type ToolDefinition, ToolError, type ToolHandlerResult, type ZodOutputSchema, type ZodRawShape, type ZodSchema, auth, defineMcp, defineTool, setLogLevel };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
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";
1
+ import { A as McpAuthConfig, C as MetricsConfig, D as ProgressUpdate, E as McpProtocolEra, O as ToolContext, S as ZodSchema, T as McpClientInfo, _ as ToolContent, a as EmbeddedResource, b as ZodOutputSchema, c as JsonValue, d as McpDefinition, f as McpDefinitionInput, g as ToolAnnotations, h as TextContent, i as EmbeddedBlobResource, k as JwtClaims, l as JsonValueInput, m as ResourceLinkIcon, n as ContentAnnotations, o as EmbeddedTextResource, p as ResourceLink, r as ContentBlock, s as ImageContent, t as AudioContent, u as McpAllowedOrigins, v as ToolDefinition, w as MetricsOptions, x as ZodRawShape, y as ToolHandlerResult } from "./types-3CZPz364.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 McpAuthConfig, S as JwtClaims, _ as ZodRawShape, a as EmbeddedReso
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 ZodRawShape | undefined = undefined>(def: ToolDefinition<TInput, TOutput>): ToolDefinition<TInput, TOutput>;
11
+ declare function defineTool<TInput extends ZodRawShape | undefined = undefined, TOutput extends ZodOutputSchema | 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 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 };
74
+ export { type AudioContent, type ContentAnnotations, type ContentBlock, type EmbeddedBlobResource, type EmbeddedResource, type EmbeddedTextResource, type ImageContent, type IssuerOAuthOptions, type JsonValue, type JsonValueInput, type JwtClaims, type LogLevel, type McpAllowedOrigins, type McpClientInfo, type McpDefinitionInput, type McpProtocolEra, type MetricsConfig, type MetricsOptions, type ProgressUpdate, type ResourceLink, type ResourceLinkIcon, type TextContent, type ToolAnnotations, type ToolContent, ToolContext, type ToolDefinition, ToolError, type ToolHandlerResult, type ZodOutputSchema, type ZodRawShape, type ZodSchema, auth, defineMcp, defineTool, setLogLevel };
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { a as setLogLevel, o as parseSafeUrl, s as trimTrailingSlash, u as resolveMetricsConfig } from "./logger-Ctv5xUSD.js";
2
- import { i as ToolContext, t as ToolError } from "./errors-DSe96Ffd.js";
2
+ import { t as jsonSchemaFromDefinition } from "./schema-CW1jKvio.js";
3
+ import { i as ToolContext, t as ToolError } from "./errors-Bwd1L0Rf.js";
3
4
  //#region src/core/define.ts
4
5
  function assertUniqueNames(mcp) {
5
6
  const seen = /* @__PURE__ */ new Set();
@@ -78,6 +79,39 @@ function freezeAuth(auth) {
78
79
  if (auth.accessTokenTyp) Object.freeze(auth.accessTokenTyp);
79
80
  Object.freeze(auth);
80
81
  }
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
+ }
81
115
  /**
82
116
  * Declare a single MCP tool. Import the result into your MCP definition's
83
117
  * `tools` array — `defineMcp({ tools: [echoTool, ...] })`.
@@ -100,8 +134,11 @@ function defineMcp(def) {
100
134
  assertNonEmptyString("version", def.version);
101
135
  assertUniqueNames(def);
102
136
  if (def.auth) assertOAuthConfig(def.auth);
137
+ assertAllowedOrigins(def.allowedOrigins);
138
+ assertToolSchemas(def);
103
139
  resolveMetricsConfig(def.metrics);
104
140
  freezeAuth(def.auth);
141
+ if (Array.isArray(def.allowedOrigins)) Object.freeze(def.allowedOrigins);
105
142
  Object.freeze(def.tools);
106
143
  for (const tool of def.tools) Object.freeze(tool);
107
144
  return def;
@@ -1,4 +1,4 @@
1
- import "./types-C0Wgm9zp.cjs";
1
+ import "./types-3CZPz364.cjs";
2
2
  //#region src/manifest/io.d.ts
3
3
  /**
4
4
  * Shape every MCP Vite plugin exposes on its `api` so `runExtract` can read
@@ -1,4 +1,4 @@
1
- import "./types-C0Wgm9zp.js";
1
+ import "./types-3CZPz364.js";
2
2
  //#region src/manifest/io.d.ts
3
3
  /**
4
4
  * Shape every MCP Vite plugin exposes on its `api` so `runExtract` can read
@@ -0,0 +1,348 @@
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-Bwd1L0Rf.js";
4
+ import { f as finalizeWireResponse, i as normalizeHttpMethod, l as createNoopRecorder, n as createRequestAuthorizer, t as evaluateCors, u as nowMs } from "./cors-5P69YRDK.js";
5
+ import { getParseErrorMessage, safeParseAsync } from "@modelcontextprotocol/sdk/server/zod-compat.js";
6
+ import { CLIENT_CAPABILITIES_META_KEY, CLIENT_INFO_META_KEY, McpServer, createMcpHandler } from "@modelcontextprotocol/server";
7
+ //#region src/core/content.ts
8
+ /** Join the text of a result's `type: "text"` blocks into one string, dropping
9
+ * binary/resource blocks. Used to surface a `tool_error` message in the local
10
+ * log; never sent over the telemetry wire. Returns undefined when there is no
11
+ * text to show. */
12
+ function extractTextContent(content) {
13
+ if (!content) return void 0;
14
+ const text = content.filter((block) => block.type === "text").map((block) => block.text).join("\n").trim();
15
+ return text.length > 0 ? text : void 0;
16
+ }
17
+ //#endregion
18
+ //#region src/core/tool-result.ts
19
+ function normalizeResourceLinkIcon(icon) {
20
+ if (typeof icon.sizes !== "string") return icon;
21
+ const { sizes, ...rest } = icon;
22
+ const normalized = sizes.trim();
23
+ return normalized === "" ? rest : {
24
+ ...rest,
25
+ sizes: normalized.split(/\s+/)
26
+ };
27
+ }
28
+ function normalizeToolContent(content) {
29
+ return (content ?? []).map((block) => {
30
+ if (block.type !== "resource_link" || block.icons === void 0) return block;
31
+ return {
32
+ ...block,
33
+ icons: block.icons.map(normalizeResourceLinkIcon)
34
+ };
35
+ });
36
+ }
37
+ function outputValidationError(text) {
38
+ return {
39
+ content: [{
40
+ type: "text",
41
+ text
42
+ }],
43
+ isError: true
44
+ };
45
+ }
46
+ function isJsonValue(value, ancestors = /* @__PURE__ */ new Set()) {
47
+ if (value === null || typeof value === "boolean" || typeof value === "string") return true;
48
+ if (typeof value === "number") return Number.isFinite(value);
49
+ if (typeof value !== "object" || ancestors.has(value)) return false;
50
+ const prototype = Object.getPrototypeOf(value);
51
+ if (!Array.isArray(value) && prototype !== Object.prototype && prototype !== null) return false;
52
+ ancestors.add(value);
53
+ const valid = (Array.isArray(value) ? value : Object.values(value).filter((entry) => entry !== void 0)).every((entry) => isJsonValue(entry, ancestors));
54
+ ancestors.delete(value);
55
+ return valid;
56
+ }
57
+ async function finalizeToolResult(tool, result) {
58
+ const content = normalizeToolContent(result.content);
59
+ let structuredContent = result.structuredContent;
60
+ if (tool.outputSchema && !result.isError) {
61
+ if (structuredContent === void 0) return outputValidationError(`Output validation error: Tool ${tool.name} has an output schema but no structured content was provided`);
62
+ try {
63
+ const parsed = await safeParseAsync(schemaFromDefinition(tool.outputSchema), structuredContent);
64
+ if (!parsed.success) return outputValidationError(`Output validation error: Invalid structured content for tool ${tool.name}: ${getParseErrorMessage(parsed.error)}`);
65
+ structuredContent = parsed.data;
66
+ } catch {
67
+ return outputValidationError(`Output validation error: Could not validate structured content for tool ${tool.name}`);
68
+ }
69
+ }
70
+ if (structuredContent !== void 0 && !isJsonValue(structuredContent)) {
71
+ if (!result.isError) return outputValidationError(`Output validation error: Tool ${tool.name} returned a non-JSON structured value`);
72
+ structuredContent = void 0;
73
+ }
74
+ if (structuredContent !== void 0 && (structuredContent === null || typeof structuredContent !== "object" || Array.isArray(structuredContent)) && !content.some((block) => block.type === "text")) content.push({
75
+ type: "text",
76
+ text: JSON.stringify(structuredContent)
77
+ });
78
+ return {
79
+ content,
80
+ structuredContent,
81
+ isError: result.isError
82
+ };
83
+ }
84
+ //#endregion
85
+ //#region src/protocols/gate.ts
86
+ function createGatedHandler(gate) {
87
+ const allow = [...gate.methods, "OPTIONS"].join(", ");
88
+ const respond = async (request, recorder) => {
89
+ const authResult = await gate.authorizer.authorize(request, recorder);
90
+ if (!authResult.ok) return authResult.response;
91
+ if (!gate.methods.includes(request.method)) return gate.methodNotAllowed(allow);
92
+ return gate.handle(request, authResult, recorder);
93
+ };
94
+ return async (request, recorder = createNoopRecorder()) => {
95
+ const cors = evaluateCors(request, gate.allowedOrigins);
96
+ if (!cors.ok) {
97
+ const httpMethod = normalizeHttpMethod(request.method);
98
+ log.info("cors.origin_rejected", {
99
+ origin: request.headers.get("Origin"),
100
+ httpMethod
101
+ });
102
+ await recorder.emit({
103
+ tool: null,
104
+ method: "authorize",
105
+ outcome: "origin_rejected",
106
+ httpMethod,
107
+ durationMs: 0
108
+ });
109
+ return finalizeWireResponse(request, cors.response);
110
+ }
111
+ if (request.method === "OPTIONS") return cors.preflight(allow);
112
+ const final = finalizeWireResponse(request, cors.withHeaders(await respond(request, recorder)));
113
+ if (!final.headers.has("Cache-Control")) final.headers.set("Cache-Control", "no-store");
114
+ return final;
115
+ };
116
+ }
117
+ //#endregion
118
+ //#region src/protocols/mcp/protocol.ts
119
+ const MODERN_PROTOCOL_VERSION = "2026-07-28";
120
+ function statelessMethodNotAllowed(allow) {
121
+ return Response.json({
122
+ jsonrpc: "2.0",
123
+ id: null,
124
+ error: {
125
+ code: -32e3,
126
+ message: "Method not allowed: stateless server offers no standalone SSE stream (GET) or session (DELETE)."
127
+ }
128
+ }, {
129
+ status: 405,
130
+ headers: { Allow: allow }
131
+ });
132
+ }
133
+ function zodIssues(error) {
134
+ const issues = error?.issues;
135
+ if (!Array.isArray(issues)) return [{ message: String(error) }];
136
+ return issues.map((issue) => {
137
+ const value = issue;
138
+ return {
139
+ message: typeof value.message === "string" ? value.message : String(value.message),
140
+ ...Array.isArray(value.path) ? { path: value.path } : {}
141
+ };
142
+ });
143
+ }
144
+ function standardSchemaFromDefinition(definition) {
145
+ const schema = schemaFromDefinition(definition);
146
+ return { "~standard": {
147
+ version: 1,
148
+ vendor: "zod",
149
+ validate: async (value) => {
150
+ const parsed = await safeParseAsync(schema, value);
151
+ return parsed.success ? { value: parsed.data } : { issues: zodIssues(parsed.error) };
152
+ },
153
+ jsonSchema: {
154
+ input: () => jsonSchemaFromDefinition(definition, "input"),
155
+ output: () => jsonSchemaFromDefinition(definition, "output")
156
+ }
157
+ } };
158
+ }
159
+ function isPlainObject(value) {
160
+ return typeof value === "object" && value !== null && !Array.isArray(value);
161
+ }
162
+ function toolRequestContext(sdkCtx, protocol) {
163
+ const mcpReq = sdkCtx?.mcpReq;
164
+ const client = { protocol };
165
+ const envelope = mcpReq?.envelope;
166
+ const info = envelope?.[CLIENT_INFO_META_KEY];
167
+ if (isPlainObject(info) && typeof info.name === "string" && typeof info.version === "string") client.info = info;
168
+ const capabilities = envelope?.[CLIENT_CAPABILITIES_META_KEY];
169
+ if (isPlainObject(capabilities)) {
170
+ client.capabilities = capabilities;
171
+ if (isPlainObject(capabilities.extensions)) client.extensions = capabilities.extensions;
172
+ }
173
+ const progressToken = (mcpReq?._meta)?.progressToken;
174
+ const notify = mcpReq?.notify;
175
+ const sendProgress = progressToken !== void 0 && notify ? async (update) => {
176
+ try {
177
+ await notify({
178
+ method: "notifications/progress",
179
+ params: {
180
+ progressToken,
181
+ ...update
182
+ }
183
+ });
184
+ } catch (err) {
185
+ log.debug("mcp.progress_notify_failed", describeError(err));
186
+ }
187
+ } : void 0;
188
+ return {
189
+ signal: mcpReq?.signal,
190
+ client,
191
+ sendProgress
192
+ };
193
+ }
194
+ async function invokeTool(tool, auth, recorder, args, protocol, sdkCtx) {
195
+ const endUserId = auth?.principal.sub;
196
+ const start = nowMs();
197
+ let result;
198
+ try {
199
+ result = await tool.handler(args, new ToolContext(auth, toolRequestContext(sdkCtx, protocol)));
200
+ } catch (err) {
201
+ const callerMessage = resolveToolErrorMessage(err);
202
+ if (callerMessage === void 0) {
203
+ await recorder.emit({
204
+ tool: tool.name,
205
+ method: "tools/call",
206
+ outcome: "handler_error",
207
+ durationMs: nowMs() - start,
208
+ errorText: errorSummary(err),
209
+ endUserId,
210
+ protocol
211
+ });
212
+ return finalizeToolResult(tool, {
213
+ content: [{
214
+ type: "text",
215
+ text: "tool execution failed"
216
+ }],
217
+ isError: true
218
+ });
219
+ }
220
+ result = {
221
+ content: [{
222
+ type: "text",
223
+ text: callerMessage
224
+ }],
225
+ isError: true
226
+ };
227
+ }
228
+ if (result == null) {
229
+ await recorder.emit({
230
+ tool: tool.name,
231
+ method: "tools/call",
232
+ outcome: "handler_error",
233
+ durationMs: nowMs() - start,
234
+ endUserId,
235
+ protocol
236
+ });
237
+ return finalizeToolResult(tool, {
238
+ content: [{
239
+ type: "text",
240
+ text: `tool "${tool.name}" returned no result`
241
+ }],
242
+ isError: true
243
+ });
244
+ }
245
+ result = await finalizeToolResult(tool, result);
246
+ await recorder.emit({
247
+ tool: tool.name,
248
+ method: "tools/call",
249
+ outcome: result.isError ? "tool_error" : "ok",
250
+ durationMs: nowMs() - start,
251
+ errorText: result.isError ? extractTextContent(result.content) : void 0,
252
+ endUserId,
253
+ protocol
254
+ });
255
+ return result;
256
+ }
257
+ function adaptToolCallback(tool, auth, recorder, protocol) {
258
+ return async (first, second) => {
259
+ const sdkCtx = tool.inputSchema ? second : first;
260
+ const result = await invokeTool(tool, auth, recorder, tool.inputSchema ? first ?? {} : {}, protocol, sdkCtx);
261
+ return {
262
+ content: result.content ?? [],
263
+ structuredContent: result.structuredContent,
264
+ isError: result.isError
265
+ };
266
+ };
267
+ }
268
+ function buildServer(mcp, auth, recorder, era) {
269
+ const server = new McpServer({
270
+ name: mcp.name,
271
+ version: mcp.version,
272
+ title: mcp.title
273
+ }, {
274
+ instructions: mcp.instructions,
275
+ capabilities: { tools: { listChanged: false } }
276
+ });
277
+ for (const tool of mcp.tools) {
278
+ const config = {
279
+ title: tool.title,
280
+ description: tool.description,
281
+ annotations: tool.annotations,
282
+ ...tool.outputSchema ? { outputSchema: standardSchemaFromDefinition(tool.outputSchema) } : {}
283
+ };
284
+ const callback = adaptToolCallback(tool, auth, recorder, era === "legacy" ? "legacy" : MODERN_PROTOCOL_VERSION);
285
+ if (tool.inputSchema) server.registerTool(tool.name, {
286
+ ...config,
287
+ inputSchema: standardSchemaFromDefinition(tool.inputSchema)
288
+ }, callback);
289
+ else server.registerTool(tool.name, config, callback);
290
+ }
291
+ return server;
292
+ }
293
+ /**
294
+ * Build a Web-Standard request handler that serves the MCP protocol over
295
+ * streamable HTTP. A fresh `McpServer` + transport is constructed per
296
+ * request — the transport is stateless, so this is cheap and there is no
297
+ * cross-request state to leak. The per-request `recorder` is supplied by the
298
+ * caller (the stack handler) so telemetry reads the runtime env in-flight.
299
+ */
300
+ function createMcpProtocolHandler(mcp, options = {}) {
301
+ const authorizer = createRequestAuthorizer(mcp, options);
302
+ const handle = async (request, authResult, recorder) => {
303
+ let protocolError;
304
+ const reportTransportError = async (error) => {
305
+ await recorder.emit({
306
+ tool: null,
307
+ method: "transport",
308
+ outcome: "transport_error",
309
+ durationMs: 0,
310
+ endUserId: authResult.auth?.principal.sub
311
+ });
312
+ log.error("mcp.transport_error", {
313
+ ...error === void 0 ? {} : describeError(error),
314
+ outcome: "500 internal error"
315
+ });
316
+ };
317
+ try {
318
+ const response = await createMcpHandler(({ era }) => buildServer(mcp, authResult.auth, recorder, era), {
319
+ legacy: "stateless",
320
+ maxSubscriptions: 0,
321
+ onerror: (error) => {
322
+ protocolError = error;
323
+ }
324
+ }).fetch(request);
325
+ if (response.status >= 500) await reportTransportError(protocolError);
326
+ return response;
327
+ } catch (err) {
328
+ await reportTransportError(err);
329
+ return Response.json({
330
+ jsonrpc: "2.0",
331
+ id: null,
332
+ error: {
333
+ code: -32603,
334
+ message: "internal error"
335
+ }
336
+ }, { status: 500 });
337
+ }
338
+ };
339
+ return createGatedHandler({
340
+ authorizer,
341
+ allowedOrigins: mcp.allowedOrigins,
342
+ methods: ["POST"],
343
+ methodNotAllowed: statelessMethodNotAllowed,
344
+ handle
345
+ });
346
+ }
347
+ //#endregion
348
+ export { createMcpProtocolHandler as t };