@lovable.dev/mcp-js 0.29.0-rc.0 → 0.29.0-rc.2
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 +22 -31
- package/README.md +23 -13
- package/dist/{authorize-BjNE3LPr.d.ts → authorize-Bpz8oxjF.d.ts} +2 -5
- package/dist/{authorize-BzZFS0zi.d.cts → authorize-Cqr7Jps0.d.cts} +2 -5
- package/dist/cli/extract-manifest.cjs +25 -3
- package/dist/cli/extract-manifest.js +24 -2
- package/dist/{cors-D4YbODs-.js → cors-C_VffY86.js} +15 -21
- package/dist/{cors-CmKzDN7k.cjs → cors-DNzLB6qa.cjs} +14 -26
- package/dist/{errors-DSe96Ffd.js → errors-Bwd1L0Rf.js} +23 -8
- package/dist/{errors-CN6Da8iz.cjs → errors-Cr95rSkB.cjs} +23 -8
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/{io-CvALf9NE.d.ts → io-Bh_WpbxU.d.cts} +1 -1
- package/dist/{io-BEwvgN8s.d.cts → io-DrHivYt0.d.ts} +1 -1
- package/dist/mcp-Bl7pQYdv.js +346 -0
- package/dist/mcp-CRdT2Fai.cjs +351 -0
- package/dist/{package-DzB_5Hur.js → package-CYA-5C24.js} +1 -1
- package/dist/{package-C3YVYgxp.cjs → package-DNSwCau1.cjs} +1 -1
- package/dist/protocols/mcp/index.cjs +1 -1
- package/dist/protocols/mcp/index.d.cts +36 -3
- package/dist/protocols/mcp/index.d.ts +36 -3
- package/dist/protocols/mcp/index.js +1 -1
- package/dist/protocols/oauth-metadata.cjs +1 -1
- package/dist/protocols/oauth-metadata.d.cts +2 -2
- package/dist/protocols/oauth-metadata.d.ts +2 -2
- package/dist/protocols/oauth-metadata.js +1 -1
- package/dist/stacks/supabase/index.cjs +6 -35
- package/dist/stacks/supabase/index.d.cts +1 -4
- package/dist/stacks/supabase/index.d.ts +1 -4
- package/dist/stacks/supabase/index.js +6 -35
- 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 +2 -23
- package/dist/stacks/tanstack/index.d.cts +3 -20
- package/dist/stacks/tanstack/index.d.ts +3 -20
- package/dist/stacks/tanstack/index.js +3 -22
- package/dist/stacks/tanstack/vite.cjs +5 -33
- package/dist/stacks/tanstack/vite.d.cts +5 -32
- package/dist/stacks/tanstack/vite.d.ts +5 -32
- package/dist/stacks/tanstack/vite.js +5 -33
- package/dist/{types-D1cgsnPu.d.cts → types-DnGEKnvI.d.cts} +43 -20
- package/dist/{types-D1cgsnPu.d.ts → types-DnGEKnvI.d.ts} +43 -20
- package/package.json +4 -11
- package/dist/base-DSH8NnPj.d.cts +0 -36
- package/dist/base-DSH8NnPj.d.ts +0 -36
- package/dist/gate-n_HX6eQ5.cjs +0 -26
- package/dist/gate-pGsVlY_4.js +0 -21
- package/dist/list-tools-Dkyyje7j.cjs +0 -48
- package/dist/list-tools-ePP9tXND.js +0 -37
- package/dist/list-tools-qIvVJeWZ.d.ts +0 -8
- package/dist/list-tools-zagoI477.d.cts +0 -8
- package/dist/mcp-Cs4zfvSi.js +0 -336
- package/dist/mcp-DQ1Vdv4v.cjs +0 -341
- package/dist/protocols/rest/index.cjs +0 -5
- package/dist/protocols/rest/index.d.cts +0 -9
- package/dist/protocols/rest/index.d.ts +0 -9
- package/dist/protocols/rest/index.js +0 -3
- package/dist/rest-DSclWpIt.js +0 -129
- package/dist/rest-P6XZWP42.cjs +0 -134
- package/dist/tool-result-CK6hXO43.cjs +0 -89
- package/dist/tool-result-DfCk445b.js +0 -78
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as MetricsOptions, E as McpAuthConfig, S as MetricsConfig, T as
|
|
1
|
+
import { C as MetricsOptions, D as JwtClaims, E as ToolContext, O as McpAuthConfig, S as MetricsConfig, T as ProgressUpdate, _ as ToolDefinition, a as EmbeddedResource, b as ZodRawShape, c as JsonValue, d as McpDefinitionInput, f as ResourceLink, g as ToolContent, h as ToolAnnotations, i as EmbeddedBlobResource, l as McpAllowedOrigins, m as TextContent, n as ContentAnnotations, o as EmbeddedTextResource, p as ResourceLinkIcon, r as ContentBlock, s as ImageContent, t as AudioContent, u as McpDefinition, v as ToolHandlerResult, w as McpClientInfo, x as ZodSchema, y as ZodOutputSchema } from "./types-DnGEKnvI.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
|
|
@@ -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 JsonValue, type JwtClaims, type LogLevel, type McpAllowedOrigins, type McpDefinitionInput, type MetricsConfig, type MetricsOptions, 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 };
|
|
74
|
+
export { type AudioContent, type ContentAnnotations, type ContentBlock, type EmbeddedBlobResource, type EmbeddedResource, type EmbeddedTextResource, type ImageContent, type IssuerOAuthOptions, type JsonValue, type JwtClaims, type LogLevel, type McpAllowedOrigins, type McpClientInfo, type McpDefinitionInput, 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 MetricsOptions, E as McpAuthConfig, S as MetricsConfig, T as
|
|
1
|
+
import { C as MetricsOptions, D as JwtClaims, E as ToolContext, O as McpAuthConfig, S as MetricsConfig, T as ProgressUpdate, _ as ToolDefinition, a as EmbeddedResource, b as ZodRawShape, c as JsonValue, d as McpDefinitionInput, f as ResourceLink, g as ToolContent, h as ToolAnnotations, i as EmbeddedBlobResource, l as McpAllowedOrigins, m as TextContent, n as ContentAnnotations, o as EmbeddedTextResource, p as ResourceLinkIcon, r as ContentBlock, s as ImageContent, t as AudioContent, u as McpDefinition, v as ToolHandlerResult, w as McpClientInfo, x as ZodSchema, y as ZodOutputSchema } from "./types-DnGEKnvI.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
|
|
@@ -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 JsonValue, type JwtClaims, type LogLevel, type McpAllowedOrigins, type McpDefinitionInput, type MetricsConfig, type MetricsOptions, 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 };
|
|
74
|
+
export { type AudioContent, type ContentAnnotations, type ContentBlock, type EmbeddedBlobResource, type EmbeddedResource, type EmbeddedTextResource, type ImageContent, type IssuerOAuthOptions, type JsonValue, type JwtClaims, type LogLevel, type McpAllowedOrigins, type McpClientInfo, type McpDefinitionInput, 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,6 +1,6 @@
|
|
|
1
1
|
import { a as setLogLevel, o as parseSafeUrl, s as trimTrailingSlash, u as resolveMetricsConfig } from "./logger-Ctv5xUSD.js";
|
|
2
2
|
import { t as jsonSchemaFromDefinition } from "./schema-CW1jKvio.js";
|
|
3
|
-
import { i as ToolContext, t as ToolError } from "./errors-
|
|
3
|
+
import { i as ToolContext, t as ToolError } from "./errors-Bwd1L0Rf.js";
|
|
4
4
|
//#region src/core/define.ts
|
|
5
5
|
function assertUniqueNames(mcp) {
|
|
6
6
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -0,0 +1,346 @@
|
|
|
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 { c as createNoopRecorder, d as finalizeWireResponse, l as nowMs, n as createRequestAuthorizer, t as evaluateCors } from "./cors-C_VffY86.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
|
+
log.info("cors.origin_rejected", {
|
|
98
|
+
origin: request.headers.get("Origin"),
|
|
99
|
+
httpMethod: request.method
|
|
100
|
+
});
|
|
101
|
+
await recorder.emit({
|
|
102
|
+
tool: null,
|
|
103
|
+
method: "authorize",
|
|
104
|
+
outcome: "origin_rejected",
|
|
105
|
+
httpMethod: request.method,
|
|
106
|
+
durationMs: 0
|
|
107
|
+
});
|
|
108
|
+
return finalizeWireResponse(request, cors.response);
|
|
109
|
+
}
|
|
110
|
+
if (request.method === "OPTIONS") return cors.preflight(allow);
|
|
111
|
+
const final = finalizeWireResponse(request, cors.withHeaders(await respond(request, recorder)));
|
|
112
|
+
if (!final.headers.has("Cache-Control")) final.headers.set("Cache-Control", "no-store");
|
|
113
|
+
return final;
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/protocols/mcp/protocol.ts
|
|
118
|
+
const MODERN_PROTOCOL_VERSION = "2026-07-28";
|
|
119
|
+
function statelessMethodNotAllowed(allow) {
|
|
120
|
+
return Response.json({
|
|
121
|
+
jsonrpc: "2.0",
|
|
122
|
+
id: null,
|
|
123
|
+
error: {
|
|
124
|
+
code: -32e3,
|
|
125
|
+
message: "Method not allowed: stateless server offers no standalone SSE stream (GET) or session (DELETE)."
|
|
126
|
+
}
|
|
127
|
+
}, {
|
|
128
|
+
status: 405,
|
|
129
|
+
headers: { Allow: allow }
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
function zodIssues(error) {
|
|
133
|
+
const issues = error?.issues;
|
|
134
|
+
if (!Array.isArray(issues)) return [{ message: String(error) }];
|
|
135
|
+
return issues.map((issue) => {
|
|
136
|
+
const value = issue;
|
|
137
|
+
return {
|
|
138
|
+
message: typeof value.message === "string" ? value.message : String(value.message),
|
|
139
|
+
...Array.isArray(value.path) ? { path: value.path } : {}
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
function standardSchemaFromDefinition(definition) {
|
|
144
|
+
const schema = schemaFromDefinition(definition);
|
|
145
|
+
return { "~standard": {
|
|
146
|
+
version: 1,
|
|
147
|
+
vendor: "zod",
|
|
148
|
+
validate: async (value) => {
|
|
149
|
+
const parsed = await safeParseAsync(schema, value);
|
|
150
|
+
return parsed.success ? { value: parsed.data } : { issues: zodIssues(parsed.error) };
|
|
151
|
+
},
|
|
152
|
+
jsonSchema: {
|
|
153
|
+
input: () => jsonSchemaFromDefinition(definition, "input"),
|
|
154
|
+
output: () => jsonSchemaFromDefinition(definition, "output")
|
|
155
|
+
}
|
|
156
|
+
} };
|
|
157
|
+
}
|
|
158
|
+
function isPlainObject(value) {
|
|
159
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
160
|
+
}
|
|
161
|
+
function toolRequestContext(sdkCtx, protocol) {
|
|
162
|
+
const mcpReq = sdkCtx?.mcpReq;
|
|
163
|
+
const client = { protocol };
|
|
164
|
+
const envelope = mcpReq?.envelope;
|
|
165
|
+
const info = envelope?.[CLIENT_INFO_META_KEY];
|
|
166
|
+
if (isPlainObject(info) && typeof info.name === "string" && typeof info.version === "string") client.info = info;
|
|
167
|
+
const capabilities = envelope?.[CLIENT_CAPABILITIES_META_KEY];
|
|
168
|
+
if (isPlainObject(capabilities)) {
|
|
169
|
+
client.capabilities = capabilities;
|
|
170
|
+
if (isPlainObject(capabilities.extensions)) client.extensions = capabilities.extensions;
|
|
171
|
+
}
|
|
172
|
+
const progressToken = (mcpReq?._meta)?.progressToken;
|
|
173
|
+
const notify = mcpReq?.notify;
|
|
174
|
+
const sendProgress = progressToken !== void 0 && notify ? async (update) => {
|
|
175
|
+
try {
|
|
176
|
+
await notify({
|
|
177
|
+
method: "notifications/progress",
|
|
178
|
+
params: {
|
|
179
|
+
progressToken,
|
|
180
|
+
...update
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
} catch (err) {
|
|
184
|
+
log.debug("mcp.progress_notify_failed", describeError(err));
|
|
185
|
+
}
|
|
186
|
+
} : void 0;
|
|
187
|
+
return {
|
|
188
|
+
signal: mcpReq?.signal,
|
|
189
|
+
client,
|
|
190
|
+
sendProgress
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
async function invokeTool(tool, auth, recorder, args, protocol, sdkCtx) {
|
|
194
|
+
const endUserId = auth?.principal.sub;
|
|
195
|
+
const start = nowMs();
|
|
196
|
+
let result;
|
|
197
|
+
try {
|
|
198
|
+
result = await tool.handler(args, new ToolContext(auth, toolRequestContext(sdkCtx, protocol)));
|
|
199
|
+
} catch (err) {
|
|
200
|
+
const callerMessage = resolveToolErrorMessage(err);
|
|
201
|
+
if (callerMessage === void 0) {
|
|
202
|
+
await recorder.emit({
|
|
203
|
+
tool: tool.name,
|
|
204
|
+
method: "tools/call",
|
|
205
|
+
outcome: "handler_error",
|
|
206
|
+
durationMs: nowMs() - start,
|
|
207
|
+
errorText: errorSummary(err),
|
|
208
|
+
endUserId,
|
|
209
|
+
protocol
|
|
210
|
+
});
|
|
211
|
+
return finalizeToolResult(tool, {
|
|
212
|
+
content: [{
|
|
213
|
+
type: "text",
|
|
214
|
+
text: "tool execution failed"
|
|
215
|
+
}],
|
|
216
|
+
isError: true
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
result = {
|
|
220
|
+
content: [{
|
|
221
|
+
type: "text",
|
|
222
|
+
text: callerMessage
|
|
223
|
+
}],
|
|
224
|
+
isError: true
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
if (result == null) {
|
|
228
|
+
await recorder.emit({
|
|
229
|
+
tool: tool.name,
|
|
230
|
+
method: "tools/call",
|
|
231
|
+
outcome: "handler_error",
|
|
232
|
+
durationMs: nowMs() - start,
|
|
233
|
+
endUserId,
|
|
234
|
+
protocol
|
|
235
|
+
});
|
|
236
|
+
return finalizeToolResult(tool, {
|
|
237
|
+
content: [{
|
|
238
|
+
type: "text",
|
|
239
|
+
text: `tool "${tool.name}" returned no result`
|
|
240
|
+
}],
|
|
241
|
+
isError: true
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
result = await finalizeToolResult(tool, result);
|
|
245
|
+
await recorder.emit({
|
|
246
|
+
tool: tool.name,
|
|
247
|
+
method: "tools/call",
|
|
248
|
+
outcome: result.isError ? "tool_error" : "ok",
|
|
249
|
+
durationMs: nowMs() - start,
|
|
250
|
+
errorText: result.isError ? extractTextContent(result.content) : void 0,
|
|
251
|
+
endUserId,
|
|
252
|
+
protocol
|
|
253
|
+
});
|
|
254
|
+
return result;
|
|
255
|
+
}
|
|
256
|
+
function adaptToolCallback(tool, auth, recorder, protocol) {
|
|
257
|
+
return async (first, second) => {
|
|
258
|
+
const sdkCtx = tool.inputSchema ? second : first;
|
|
259
|
+
const result = await invokeTool(tool, auth, recorder, tool.inputSchema ? first ?? {} : {}, protocol, sdkCtx);
|
|
260
|
+
return {
|
|
261
|
+
content: result.content ?? [],
|
|
262
|
+
structuredContent: result.structuredContent,
|
|
263
|
+
isError: result.isError
|
|
264
|
+
};
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
function buildServer(mcp, auth, recorder, era) {
|
|
268
|
+
const server = new McpServer({
|
|
269
|
+
name: mcp.name,
|
|
270
|
+
version: mcp.version,
|
|
271
|
+
title: mcp.title
|
|
272
|
+
}, {
|
|
273
|
+
instructions: mcp.instructions,
|
|
274
|
+
capabilities: { tools: { listChanged: false } }
|
|
275
|
+
});
|
|
276
|
+
for (const tool of mcp.tools) {
|
|
277
|
+
const config = {
|
|
278
|
+
title: tool.title,
|
|
279
|
+
description: tool.description,
|
|
280
|
+
annotations: tool.annotations,
|
|
281
|
+
...tool.outputSchema ? { outputSchema: standardSchemaFromDefinition(tool.outputSchema) } : {}
|
|
282
|
+
};
|
|
283
|
+
const callback = adaptToolCallback(tool, auth, recorder, era === "legacy" ? "legacy" : MODERN_PROTOCOL_VERSION);
|
|
284
|
+
if (tool.inputSchema) server.registerTool(tool.name, {
|
|
285
|
+
...config,
|
|
286
|
+
inputSchema: standardSchemaFromDefinition(tool.inputSchema)
|
|
287
|
+
}, callback);
|
|
288
|
+
else server.registerTool(tool.name, config, callback);
|
|
289
|
+
}
|
|
290
|
+
return server;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Build a Web-Standard request handler that serves the MCP protocol over
|
|
294
|
+
* streamable HTTP. A fresh `McpServer` + transport is constructed per
|
|
295
|
+
* request — the transport is stateless, so this is cheap and there is no
|
|
296
|
+
* cross-request state to leak. The per-request `recorder` is supplied by the
|
|
297
|
+
* caller (the stack handler) so telemetry reads the runtime env in-flight.
|
|
298
|
+
*/
|
|
299
|
+
function createMcpProtocolHandler(mcp, options = {}) {
|
|
300
|
+
const authorizer = createRequestAuthorizer(mcp, options);
|
|
301
|
+
const handle = async (request, authResult, recorder) => {
|
|
302
|
+
let protocolError;
|
|
303
|
+
const reportTransportError = async (error) => {
|
|
304
|
+
await recorder.emit({
|
|
305
|
+
tool: null,
|
|
306
|
+
method: "transport",
|
|
307
|
+
outcome: "transport_error",
|
|
308
|
+
durationMs: 0,
|
|
309
|
+
endUserId: authResult.auth?.principal.sub
|
|
310
|
+
});
|
|
311
|
+
log.error("mcp.transport_error", {
|
|
312
|
+
...error === void 0 ? {} : describeError(error),
|
|
313
|
+
outcome: "500 internal error"
|
|
314
|
+
});
|
|
315
|
+
};
|
|
316
|
+
try {
|
|
317
|
+
const response = await createMcpHandler(({ era }) => buildServer(mcp, authResult.auth, recorder, era), {
|
|
318
|
+
legacy: "stateless",
|
|
319
|
+
onerror: (error) => {
|
|
320
|
+
protocolError = error;
|
|
321
|
+
}
|
|
322
|
+
}).fetch(request);
|
|
323
|
+
if (response.status >= 500) await reportTransportError(protocolError);
|
|
324
|
+
return response;
|
|
325
|
+
} catch (err) {
|
|
326
|
+
await reportTransportError(err);
|
|
327
|
+
return Response.json({
|
|
328
|
+
jsonrpc: "2.0",
|
|
329
|
+
id: null,
|
|
330
|
+
error: {
|
|
331
|
+
code: -32603,
|
|
332
|
+
message: "internal error"
|
|
333
|
+
}
|
|
334
|
+
}, { status: 500 });
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
return createGatedHandler({
|
|
338
|
+
authorizer,
|
|
339
|
+
allowedOrigins: mcp.allowedOrigins,
|
|
340
|
+
methods: ["POST"],
|
|
341
|
+
methodNotAllowed: statelessMethodNotAllowed,
|
|
342
|
+
handle
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
//#endregion
|
|
346
|
+
export { createMcpProtocolHandler as t };
|