@decocms/runtime 1.6.0 → 1.6.1
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/package.json +1 -1
- package/src/tools.ts +53 -9
package/package.json
CHANGED
package/src/tools.ts
CHANGED
|
@@ -7,14 +7,16 @@ import {
|
|
|
7
7
|
} from "@decocms/bindings";
|
|
8
8
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
9
9
|
import { WebStandardStreamableHTTPServerTransport as HttpServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
import {
|
|
11
|
+
ListToolsRequestSchema,
|
|
12
|
+
type CallToolResult,
|
|
13
|
+
type GetPromptResult,
|
|
14
|
+
type Implementation,
|
|
15
|
+
type ListToolsResult,
|
|
16
|
+
type ToolAnnotations,
|
|
15
17
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
16
18
|
import { z } from "zod";
|
|
17
|
-
import type {
|
|
19
|
+
import type { ZodSchema, ZodTypeAny } from "zod";
|
|
18
20
|
import { BindingRegistry, injectBindingSchemas } from "./bindings.ts";
|
|
19
21
|
import { Event, type EventHandlers } from "./events.ts";
|
|
20
22
|
import type { DefaultEnv, User } from "./index.ts";
|
|
@@ -823,6 +825,12 @@ export const createMCPServer = <
|
|
|
823
825
|
let cached: Registrations | null = null;
|
|
824
826
|
let inflightResolve: Promise<Registrations> | null = null;
|
|
825
827
|
|
|
828
|
+
// The MCP SDK's `tools/list` handler runs `toJsonSchemaCompat()` for every
|
|
829
|
+
// registered tool on every request. For MCPs with hundreds of tools that
|
|
830
|
+
// dominates per-request latency (seconds, not ms). Cache the rendered
|
|
831
|
+
// payload across requests within the isolate.
|
|
832
|
+
let cachedListToolsResult: ListToolsResult | null = null;
|
|
833
|
+
|
|
826
834
|
let _warnedFactoryDeprecation = false;
|
|
827
835
|
const warnFactoryDeprecation = () => {
|
|
828
836
|
if (!_warnedFactoryDeprecation) {
|
|
@@ -940,15 +948,19 @@ export const createMCPServer = <
|
|
|
940
948
|
_meta: tool._meta,
|
|
941
949
|
description: tool.description,
|
|
942
950
|
annotations: tool.annotations,
|
|
951
|
+
// Pass the full ZodObject (not its `.shape`) so the SDK skips
|
|
952
|
+
// `objectFromShape(...)` (a fresh `z.object(shape)` per tool) inside
|
|
953
|
+
// `_createRegisteredTool`. The SDK's `getZodSchemaObject` returns
|
|
954
|
+
// an already-built object as-is.
|
|
943
955
|
inputSchema:
|
|
944
956
|
tool.inputSchema && "shape" in tool.inputSchema
|
|
945
|
-
? (tool.inputSchema
|
|
946
|
-
: z.object({})
|
|
957
|
+
? (tool.inputSchema as ZodTypeAny)
|
|
958
|
+
: z.object({}),
|
|
947
959
|
outputSchema:
|
|
948
960
|
tool.outputSchema &&
|
|
949
961
|
typeof tool.outputSchema === "object" &&
|
|
950
962
|
"shape" in tool.outputSchema
|
|
951
|
-
? (tool.outputSchema
|
|
963
|
+
? (tool.outputSchema as ZodTypeAny)
|
|
952
964
|
: undefined,
|
|
953
965
|
},
|
|
954
966
|
async (args) => {
|
|
@@ -1078,6 +1090,38 @@ export const createMCPServer = <
|
|
|
1078
1090
|
const registrations = await resolveRegistrations(bindings);
|
|
1079
1091
|
registerAll(server, registrations);
|
|
1080
1092
|
|
|
1093
|
+
// Wrap the SDK-installed `tools/list` handler so the rendered payload is
|
|
1094
|
+
// computed once per isolate and reused across requests. The MCP Server
|
|
1095
|
+
// itself can't be shared across requests (its transport is single-use, see
|
|
1096
|
+
// `Protocol.connect`), so each request still spins up a fresh Server +
|
|
1097
|
+
// Transport — but the listTools render is by far the dominant cost for
|
|
1098
|
+
// large tool surfaces, and it's pure of request-scoped state.
|
|
1099
|
+
const innerHandlers = (
|
|
1100
|
+
server.server as unknown as {
|
|
1101
|
+
_requestHandlers: Map<
|
|
1102
|
+
string,
|
|
1103
|
+
(req: unknown, extra: unknown) => Promise<unknown>
|
|
1104
|
+
>;
|
|
1105
|
+
}
|
|
1106
|
+
)._requestHandlers;
|
|
1107
|
+
const sdkListToolsHandler = innerHandlers.get(
|
|
1108
|
+
ListToolsRequestSchema.shape.method.value,
|
|
1109
|
+
);
|
|
1110
|
+
if (sdkListToolsHandler) {
|
|
1111
|
+
innerHandlers.set(
|
|
1112
|
+
ListToolsRequestSchema.shape.method.value,
|
|
1113
|
+
async (req, extra) => {
|
|
1114
|
+
if (!cachedListToolsResult) {
|
|
1115
|
+
cachedListToolsResult = (await sdkListToolsHandler(
|
|
1116
|
+
req,
|
|
1117
|
+
extra,
|
|
1118
|
+
)) as ListToolsResult;
|
|
1119
|
+
}
|
|
1120
|
+
return cachedListToolsResult;
|
|
1121
|
+
},
|
|
1122
|
+
);
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1081
1125
|
return { server, ...registrations };
|
|
1082
1126
|
};
|
|
1083
1127
|
|