@ttoss/http-server-mcp 0.34.4 → 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1130,6 +1130,33 @@ Either way the authorization flow works. Driving the official MCP client SDK aga
1130
1130
 
1131
1131
  **Whether the handshake should be public at all is a separate question.** `publicMethods: []` closes both eras, and the authorization flow still works — RFC 9728 discovery is driven by the challenge itself, so a client authenticates from its very first request instead of one step later.
1132
1132
 
1133
+ ### Holding the surface to a budget
1134
+
1135
+ Every tool's name, description and input schema, and the server's instructions, are loaded into every session before the first call. `checkMcpSurface` compares them with a budget and answers the limits they exceed, so a test fails naming the tool that grew:
1136
+
1137
+ ```typescript
1138
+ import {
1139
+ checkMcpSurface,
1140
+ findUnknownToolMentions,
1141
+ } from '@ttoss/http-server-mcp';
1142
+
1143
+ test('the surface stays inside its budget', () => {
1144
+ expect(
1145
+ checkMcpSurface({
1146
+ tools, // { name, description?, inputSchema? }[], e.g. from openApiToToolDefinitions
1147
+ instructions,
1148
+ budget: { total: 70_000, perTool: 10_000 },
1149
+ })
1150
+ ).toEqual([]);
1151
+ });
1152
+
1153
+ test('every tool the instructions name exists', () => {
1154
+ expect(findUnknownToolMentions({ instructions, tools })).toEqual([]);
1155
+ });
1156
+ ```
1157
+
1158
+ Sizes are characters of `JSON.stringify({ name, description, inputSchema })`; `measureMcpSurface` answers them without judging. `total` and `perTool` have no default — they are the product's choice — while `instructions` defaults to `2048`, about what Claude Code keeps before cutting the rest silently. `findUnknownToolMentions` reads backticked names with a `-` or `_` (`` `get-next` ``); pass `pattern` when the instructions name tools another way.
1159
+
1133
1160
  ## AWS Lambda Deployment
1134
1161
 
1135
1162
  The default **stateless** mode (see [Stateless vs stateful mode](#stateless-vs-stateful-mode)) is built for serverless: a fresh transport is created per request, nothing is kept in memory between invocations, and responses are plain JSON (no SSE) — exactly the request/response shape API Gateway and Lambda Function URLs expect.
package/dist/index.cjs CHANGED
@@ -212,6 +212,124 @@ var createMcpRequestServer = ({
212
212
  };
213
213
  };
214
214
 
215
+ //#endregion
216
+ //#region src/mcpSurface.ts
217
+ /**
218
+ * Claude Code keeps about this many characters of a server's instructions and
219
+ * drops the rest without telling anyone, so a rule past it never reaches an
220
+ * agent.
221
+ */
222
+ var DEFAULT_INSTRUCTIONS_LIMIT = 2048;
223
+ var sizeOf = tool => {
224
+ return JSON.stringify({
225
+ name: tool.name,
226
+ description: tool.description,
227
+ inputSchema: tool.inputSchema
228
+ }).length;
229
+ };
230
+ /**
231
+ * Measures what an MCP server costs every session that connects to it: each
232
+ * tool's name, description and input schema, and the instructions.
233
+ *
234
+ * It does not judge; {@link checkMcpSurface} compares the same numbers with a
235
+ * budget.
236
+ */
237
+ var measureMcpSurface = ({
238
+ tools,
239
+ instructions
240
+ }) => {
241
+ const sizes = tools.map(tool => {
242
+ return {
243
+ name: tool.name,
244
+ size: sizeOf(tool)
245
+ };
246
+ }).sort((a, b) => {
247
+ return b.size - a.size;
248
+ });
249
+ return {
250
+ total: sizes.reduce((sum, entry) => {
251
+ return sum + entry.size;
252
+ }, 0),
253
+ tools: sizes,
254
+ instructions: instructions?.length ?? 0
255
+ };
256
+ };
257
+ /**
258
+ * Every limit an MCP surface exceeds, or `[]` when it fits.
259
+ *
260
+ * Returns rather than throws, so a test's `expect(...).toEqual([])` prints
261
+ * which tool went over and by how much. Meant for a test, not for boot: a
262
+ * server refusing to start over a few hundred bytes is worse than a red test.
263
+ *
264
+ * @example
265
+ * ```typescript
266
+ * expect(
267
+ * checkMcpSurface({
268
+ * tools,
269
+ * instructions,
270
+ * budget: { total: 70_000, perTool: 10_000 },
271
+ * })
272
+ * ).toEqual([]);
273
+ * ```
274
+ */
275
+ var checkMcpSurface = ({
276
+ tools,
277
+ instructions,
278
+ budget = {}
279
+ }) => {
280
+ const report = measureMcpSurface({
281
+ tools,
282
+ instructions
283
+ });
284
+ const violations = [];
285
+ if (budget.total !== void 0 && report.total > budget.total) violations.push({
286
+ kind: "total",
287
+ size: report.total,
288
+ limit: budget.total
289
+ });
290
+ if (budget.perTool !== void 0) {
291
+ for (const {
292
+ name,
293
+ size
294
+ } of report.tools) if (size > budget.perTool) violations.push({
295
+ kind: "per_tool",
296
+ name,
297
+ size,
298
+ limit: budget.perTool
299
+ });
300
+ }
301
+ const instructionsLimit = budget.instructions ?? 2048;
302
+ if (report.instructions > instructionsLimit) violations.push({
303
+ kind: "instructions",
304
+ size: report.instructions,
305
+ limit: instructionsLimit
306
+ });
307
+ return violations;
308
+ };
309
+ var TOOL_MENTION = /`([a-z][a-z0-9]*(?:[-_][a-z0-9]+)+)`/g;
310
+ /**
311
+ * The tool names the instructions mention that the surface does not have.
312
+ *
313
+ * Instructions are read once, at connect: a renamed tool they still name is
314
+ * one every connected agent is taught to call and then told is "not found".
315
+ * Pass `pattern` (a global RegExp whose first group is the name) when the
316
+ * instructions name tools some other way.
317
+ */
318
+ var findUnknownToolMentions = ({
319
+ instructions,
320
+ tools,
321
+ pattern = TOOL_MENTION
322
+ }) => {
323
+ const known = new Set(tools.map(tool => {
324
+ return tool.name;
325
+ }));
326
+ return [...new Set([...instructions.matchAll(pattern)].map(([, name]) => {
327
+ return name;
328
+ }))].filter(name => {
329
+ return !known.has(name);
330
+ });
331
+ };
332
+
215
333
  //#endregion
216
334
  //#region src/registerAppResource.ts
217
335
  /**
@@ -1571,6 +1689,7 @@ var createMcpRouter = (server, options = {}) => {
1571
1689
  };
1572
1690
 
1573
1691
  //#endregion
1692
+ exports.DEFAULT_INSTRUCTIONS_LIMIT = DEFAULT_INSTRUCTIONS_LIMIT;
1574
1693
  Object.defineProperty(exports, 'InMemoryServerEventBus', {
1575
1694
  enumerable: true,
1576
1695
  get: function () {
@@ -1586,14 +1705,17 @@ Object.defineProperty(exports, 'McpServer', {
1586
1705
  exports.UI_EXTENSION_ID = UI_EXTENSION_ID;
1587
1706
  exports.UI_RESOURCE_MIME_TYPE = UI_RESOURCE_MIME_TYPE;
1588
1707
  exports.apiCall = apiCall;
1708
+ exports.checkMcpSurface = checkMcpSurface;
1589
1709
  exports.checkScopes = checkScopes;
1590
1710
  exports.createMcpRouter = createMcpRouter;
1591
1711
  exports.createToolGate = createToolGate;
1592
1712
  exports.defineTool = defineTool;
1593
1713
  exports.errorBodyMessage = errorBodyMessage;
1714
+ exports.findUnknownToolMentions = findUnknownToolMentions;
1594
1715
  exports.getApiHeaders = getApiHeaders;
1595
1716
  exports.getIdentity = getIdentity;
1596
1717
  exports.getRequestLocale = getRequestLocale;
1718
+ exports.measureMcpSurface = measureMcpSurface;
1597
1719
  exports.rankTools = rankTools;
1598
1720
  exports.registerAppResource = registerAppResource;
1599
1721
  exports.registerTools = registerTools;
package/dist/index.d.cts CHANGED
@@ -886,6 +886,124 @@ interface DeferToolsOptions {
886
886
  }) => boolean;
887
887
  }
888
888
  //#endregion
889
+ //#region src/mcpSurface.d.ts
890
+ /**
891
+ * What a client loads for one tool before the first call: the three fields a
892
+ * `tools/list` answers. `openApiToToolDefinitions` returns this shape, and so
893
+ * does a `tools/list` result.
894
+ */
895
+ interface McpSurfaceTool {
896
+ name: string;
897
+ description?: string;
898
+ inputSchema?: unknown;
899
+ }
900
+ /** The size of each part of an MCP surface, in characters of JSON. */
901
+ interface McpSurfaceReport {
902
+ /** Sum of every tool's size. Instructions are not included. */
903
+ total: number;
904
+ /** One entry per tool, largest first. */
905
+ tools: Array<{
906
+ name: string;
907
+ size: number;
908
+ }>;
909
+ /** Length of the server instructions, or `0` when there are none. */
910
+ instructions: number;
911
+ }
912
+ /**
913
+ * The limits a surface is checked against. Only `instructions` has a default:
914
+ * the others are a product's own choice, not a property of any client.
915
+ */
916
+ interface McpSurfaceBudget {
917
+ /** Ceiling on the sum of every tool's size. */
918
+ total?: number;
919
+ /** Ceiling on any one tool's size. */
920
+ perTool?: number;
921
+ /**
922
+ * Ceiling on the instructions' length.
923
+ * @default 2048
924
+ */
925
+ instructions?: number;
926
+ }
927
+ /** One limit a surface exceeds. */
928
+ type McpSurfaceViolation = {
929
+ kind: 'total';
930
+ size: number;
931
+ limit: number;
932
+ } | {
933
+ kind: 'per_tool';
934
+ name: string;
935
+ size: number;
936
+ limit: number;
937
+ } | {
938
+ kind: 'instructions';
939
+ size: number;
940
+ limit: number;
941
+ };
942
+ /**
943
+ * Claude Code keeps about this many characters of a server's instructions and
944
+ * drops the rest without telling anyone, so a rule past it never reaches an
945
+ * agent.
946
+ */
947
+ declare const DEFAULT_INSTRUCTIONS_LIMIT = 2048;
948
+ /**
949
+ * Measures what an MCP server costs every session that connects to it: each
950
+ * tool's name, description and input schema, and the instructions.
951
+ *
952
+ * It does not judge; {@link checkMcpSurface} compares the same numbers with a
953
+ * budget.
954
+ */
955
+ declare const measureMcpSurface: ({
956
+ tools,
957
+ instructions
958
+ }: {
959
+ tools: McpSurfaceTool[];
960
+ instructions?: string;
961
+ }) => McpSurfaceReport;
962
+ /**
963
+ * Every limit an MCP surface exceeds, or `[]` when it fits.
964
+ *
965
+ * Returns rather than throws, so a test's `expect(...).toEqual([])` prints
966
+ * which tool went over and by how much. Meant for a test, not for boot: a
967
+ * server refusing to start over a few hundred bytes is worse than a red test.
968
+ *
969
+ * @example
970
+ * ```typescript
971
+ * expect(
972
+ * checkMcpSurface({
973
+ * tools,
974
+ * instructions,
975
+ * budget: { total: 70_000, perTool: 10_000 },
976
+ * })
977
+ * ).toEqual([]);
978
+ * ```
979
+ */
980
+ declare const checkMcpSurface: ({
981
+ tools,
982
+ instructions,
983
+ budget
984
+ }: {
985
+ tools: McpSurfaceTool[];
986
+ instructions?: string;
987
+ budget?: McpSurfaceBudget;
988
+ }) => McpSurfaceViolation[];
989
+ /**
990
+ * The tool names the instructions mention that the surface does not have.
991
+ *
992
+ * Instructions are read once, at connect: a renamed tool they still name is
993
+ * one every connected agent is taught to call and then told is "not found".
994
+ * Pass `pattern` (a global RegExp whose first group is the name) when the
995
+ * instructions name tools some other way.
996
+ */
997
+ declare const findUnknownToolMentions: ({
998
+ instructions,
999
+ tools,
1000
+ pattern
1001
+ }: {
1002
+ instructions: string;
1003
+ tools: Array<Pick<McpSurfaceTool, "name">>;
1004
+ pattern?: RegExp;
1005
+ }) => string[];
1006
+ //#endregion
889
1007
  //#region src/registerAppResource.d.ts
890
1008
  /**
891
1009
  * Identifier of the MCP Apps extension (`io.modelcontextprotocol/ui`), used as
@@ -1711,4 +1829,4 @@ interface McpRouterOptions {
1711
1829
  */
1712
1830
  declare const createMcpRouter: (server: McpServer$1, options?: McpRouterOptions) => McpRouter;
1713
1831
  //#endregion
1714
- export { ApiCallOptions, CognitoUserPoolConfig, type CreateToolGateOptions, type DeferToolsOptions, type DefineToolParams, type GateToolParams, InMemoryServerEventBus, type JsonObjectSchema, McpAuthOptions, McpRouter, McpRouterOptions, McpServer, type RegisterAppResourceParams, type RegisterToolsParams, type RegisteredAppResource, type ServerEvent, type ServerEventBus, type ServerNotifier, type Tool, type ToolCallContext, type ToolCallGate, type ToolGate, type ToolIdentity, type ToolMetaParams, type ToolSchema, type ToolSearchArgs, UI_EXTENSION_ID, UI_RESOURCE_MIME_TYPE, type UiResourceCsp, type UiResourceMeta, type UiResourcePermissions, type UiToolVisibility, apiCall, checkScopes, createMcpRouter, createToolGate, defineTool, errorBodyMessage, getApiHeaders, getIdentity, getRequestLocale, rankTools, registerAppResource, registerTools, z };
1832
+ export { ApiCallOptions, CognitoUserPoolConfig, type CreateToolGateOptions, DEFAULT_INSTRUCTIONS_LIMIT, type DeferToolsOptions, type DefineToolParams, type GateToolParams, InMemoryServerEventBus, type JsonObjectSchema, McpAuthOptions, McpRouter, McpRouterOptions, McpServer, type McpSurfaceBudget, type McpSurfaceReport, type McpSurfaceTool, type McpSurfaceViolation, type RegisterAppResourceParams, type RegisterToolsParams, type RegisteredAppResource, type ServerEvent, type ServerEventBus, type ServerNotifier, type Tool, type ToolCallContext, type ToolCallGate, type ToolGate, type ToolIdentity, type ToolMetaParams, type ToolSchema, type ToolSearchArgs, UI_EXTENSION_ID, UI_RESOURCE_MIME_TYPE, type UiResourceCsp, type UiResourceMeta, type UiResourcePermissions, type UiToolVisibility, apiCall, checkMcpSurface, checkScopes, createMcpRouter, createToolGate, defineTool, errorBodyMessage, findUnknownToolMentions, getApiHeaders, getIdentity, getRequestLocale, measureMcpSurface, rankTools, registerAppResource, registerTools, z };
package/dist/index.d.mts CHANGED
@@ -887,6 +887,124 @@ interface DeferToolsOptions {
887
887
  }) => boolean;
888
888
  }
889
889
  //#endregion
890
+ //#region src/mcpSurface.d.ts
891
+ /**
892
+ * What a client loads for one tool before the first call: the three fields a
893
+ * `tools/list` answers. `openApiToToolDefinitions` returns this shape, and so
894
+ * does a `tools/list` result.
895
+ */
896
+ interface McpSurfaceTool {
897
+ name: string;
898
+ description?: string;
899
+ inputSchema?: unknown;
900
+ }
901
+ /** The size of each part of an MCP surface, in characters of JSON. */
902
+ interface McpSurfaceReport {
903
+ /** Sum of every tool's size. Instructions are not included. */
904
+ total: number;
905
+ /** One entry per tool, largest first. */
906
+ tools: Array<{
907
+ name: string;
908
+ size: number;
909
+ }>;
910
+ /** Length of the server instructions, or `0` when there are none. */
911
+ instructions: number;
912
+ }
913
+ /**
914
+ * The limits a surface is checked against. Only `instructions` has a default:
915
+ * the others are a product's own choice, not a property of any client.
916
+ */
917
+ interface McpSurfaceBudget {
918
+ /** Ceiling on the sum of every tool's size. */
919
+ total?: number;
920
+ /** Ceiling on any one tool's size. */
921
+ perTool?: number;
922
+ /**
923
+ * Ceiling on the instructions' length.
924
+ * @default 2048
925
+ */
926
+ instructions?: number;
927
+ }
928
+ /** One limit a surface exceeds. */
929
+ type McpSurfaceViolation = {
930
+ kind: 'total';
931
+ size: number;
932
+ limit: number;
933
+ } | {
934
+ kind: 'per_tool';
935
+ name: string;
936
+ size: number;
937
+ limit: number;
938
+ } | {
939
+ kind: 'instructions';
940
+ size: number;
941
+ limit: number;
942
+ };
943
+ /**
944
+ * Claude Code keeps about this many characters of a server's instructions and
945
+ * drops the rest without telling anyone, so a rule past it never reaches an
946
+ * agent.
947
+ */
948
+ declare const DEFAULT_INSTRUCTIONS_LIMIT = 2048;
949
+ /**
950
+ * Measures what an MCP server costs every session that connects to it: each
951
+ * tool's name, description and input schema, and the instructions.
952
+ *
953
+ * It does not judge; {@link checkMcpSurface} compares the same numbers with a
954
+ * budget.
955
+ */
956
+ declare const measureMcpSurface: ({
957
+ tools,
958
+ instructions
959
+ }: {
960
+ tools: McpSurfaceTool[];
961
+ instructions?: string;
962
+ }) => McpSurfaceReport;
963
+ /**
964
+ * Every limit an MCP surface exceeds, or `[]` when it fits.
965
+ *
966
+ * Returns rather than throws, so a test's `expect(...).toEqual([])` prints
967
+ * which tool went over and by how much. Meant for a test, not for boot: a
968
+ * server refusing to start over a few hundred bytes is worse than a red test.
969
+ *
970
+ * @example
971
+ * ```typescript
972
+ * expect(
973
+ * checkMcpSurface({
974
+ * tools,
975
+ * instructions,
976
+ * budget: { total: 70_000, perTool: 10_000 },
977
+ * })
978
+ * ).toEqual([]);
979
+ * ```
980
+ */
981
+ declare const checkMcpSurface: ({
982
+ tools,
983
+ instructions,
984
+ budget
985
+ }: {
986
+ tools: McpSurfaceTool[];
987
+ instructions?: string;
988
+ budget?: McpSurfaceBudget;
989
+ }) => McpSurfaceViolation[];
990
+ /**
991
+ * The tool names the instructions mention that the surface does not have.
992
+ *
993
+ * Instructions are read once, at connect: a renamed tool they still name is
994
+ * one every connected agent is taught to call and then told is "not found".
995
+ * Pass `pattern` (a global RegExp whose first group is the name) when the
996
+ * instructions name tools some other way.
997
+ */
998
+ declare const findUnknownToolMentions: ({
999
+ instructions,
1000
+ tools,
1001
+ pattern
1002
+ }: {
1003
+ instructions: string;
1004
+ tools: Array<Pick<McpSurfaceTool, "name">>;
1005
+ pattern?: RegExp;
1006
+ }) => string[];
1007
+ //#endregion
890
1008
  //#region src/registerAppResource.d.ts
891
1009
  /**
892
1010
  * Identifier of the MCP Apps extension (`io.modelcontextprotocol/ui`), used as
@@ -1712,4 +1830,4 @@ interface McpRouterOptions {
1712
1830
  */
1713
1831
  declare const createMcpRouter: (server: McpServer$1, options?: McpRouterOptions) => McpRouter;
1714
1832
  //#endregion
1715
- export { ApiCallOptions, CognitoUserPoolConfig, type CreateToolGateOptions, type DeferToolsOptions, type DefineToolParams, type GateToolParams, InMemoryServerEventBus, type JsonObjectSchema, McpAuthOptions, McpRouter, McpRouterOptions, McpServer, type RegisterAppResourceParams, type RegisterToolsParams, type RegisteredAppResource, type ServerEvent, type ServerEventBus, type ServerNotifier, type Tool, type ToolCallContext, type ToolCallGate, type ToolGate, type ToolIdentity, type ToolMetaParams, type ToolSchema, type ToolSearchArgs, UI_EXTENSION_ID, UI_RESOURCE_MIME_TYPE, type UiResourceCsp, type UiResourceMeta, type UiResourcePermissions, type UiToolVisibility, apiCall, checkScopes, createMcpRouter, createToolGate, defineTool, errorBodyMessage, getApiHeaders, getIdentity, getRequestLocale, rankTools, registerAppResource, registerTools, z };
1833
+ export { ApiCallOptions, CognitoUserPoolConfig, type CreateToolGateOptions, DEFAULT_INSTRUCTIONS_LIMIT, type DeferToolsOptions, type DefineToolParams, type GateToolParams, InMemoryServerEventBus, type JsonObjectSchema, McpAuthOptions, McpRouter, McpRouterOptions, McpServer, type McpSurfaceBudget, type McpSurfaceReport, type McpSurfaceTool, type McpSurfaceViolation, type RegisterAppResourceParams, type RegisterToolsParams, type RegisteredAppResource, type ServerEvent, type ServerEventBus, type ServerNotifier, type Tool, type ToolCallContext, type ToolCallGate, type ToolGate, type ToolIdentity, type ToolMetaParams, type ToolSchema, type ToolSearchArgs, UI_EXTENSION_ID, UI_RESOURCE_MIME_TYPE, type UiResourceCsp, type UiResourceMeta, type UiResourcePermissions, type UiToolVisibility, apiCall, checkMcpSurface, checkScopes, createMcpRouter, createToolGate, defineTool, errorBodyMessage, findUnknownToolMentions, getApiHeaders, getIdentity, getRequestLocale, measureMcpSurface, rankTools, registerAppResource, registerTools, z };
package/dist/index.mjs CHANGED
@@ -209,6 +209,124 @@ var createMcpRequestServer = ({
209
209
  };
210
210
  };
211
211
 
212
+ //#endregion
213
+ //#region src/mcpSurface.ts
214
+ /**
215
+ * Claude Code keeps about this many characters of a server's instructions and
216
+ * drops the rest without telling anyone, so a rule past it never reaches an
217
+ * agent.
218
+ */
219
+ var DEFAULT_INSTRUCTIONS_LIMIT = 2048;
220
+ var sizeOf = tool => {
221
+ return JSON.stringify({
222
+ name: tool.name,
223
+ description: tool.description,
224
+ inputSchema: tool.inputSchema
225
+ }).length;
226
+ };
227
+ /**
228
+ * Measures what an MCP server costs every session that connects to it: each
229
+ * tool's name, description and input schema, and the instructions.
230
+ *
231
+ * It does not judge; {@link checkMcpSurface} compares the same numbers with a
232
+ * budget.
233
+ */
234
+ var measureMcpSurface = ({
235
+ tools,
236
+ instructions
237
+ }) => {
238
+ const sizes = tools.map(tool => {
239
+ return {
240
+ name: tool.name,
241
+ size: sizeOf(tool)
242
+ };
243
+ }).sort((a, b) => {
244
+ return b.size - a.size;
245
+ });
246
+ return {
247
+ total: sizes.reduce((sum, entry) => {
248
+ return sum + entry.size;
249
+ }, 0),
250
+ tools: sizes,
251
+ instructions: instructions?.length ?? 0
252
+ };
253
+ };
254
+ /**
255
+ * Every limit an MCP surface exceeds, or `[]` when it fits.
256
+ *
257
+ * Returns rather than throws, so a test's `expect(...).toEqual([])` prints
258
+ * which tool went over and by how much. Meant for a test, not for boot: a
259
+ * server refusing to start over a few hundred bytes is worse than a red test.
260
+ *
261
+ * @example
262
+ * ```typescript
263
+ * expect(
264
+ * checkMcpSurface({
265
+ * tools,
266
+ * instructions,
267
+ * budget: { total: 70_000, perTool: 10_000 },
268
+ * })
269
+ * ).toEqual([]);
270
+ * ```
271
+ */
272
+ var checkMcpSurface = ({
273
+ tools,
274
+ instructions,
275
+ budget = {}
276
+ }) => {
277
+ const report = measureMcpSurface({
278
+ tools,
279
+ instructions
280
+ });
281
+ const violations = [];
282
+ if (budget.total !== void 0 && report.total > budget.total) violations.push({
283
+ kind: "total",
284
+ size: report.total,
285
+ limit: budget.total
286
+ });
287
+ if (budget.perTool !== void 0) {
288
+ for (const {
289
+ name,
290
+ size
291
+ } of report.tools) if (size > budget.perTool) violations.push({
292
+ kind: "per_tool",
293
+ name,
294
+ size,
295
+ limit: budget.perTool
296
+ });
297
+ }
298
+ const instructionsLimit = budget.instructions ?? 2048;
299
+ if (report.instructions > instructionsLimit) violations.push({
300
+ kind: "instructions",
301
+ size: report.instructions,
302
+ limit: instructionsLimit
303
+ });
304
+ return violations;
305
+ };
306
+ var TOOL_MENTION = /`([a-z][a-z0-9]*(?:[-_][a-z0-9]+)+)`/g;
307
+ /**
308
+ * The tool names the instructions mention that the surface does not have.
309
+ *
310
+ * Instructions are read once, at connect: a renamed tool they still name is
311
+ * one every connected agent is taught to call and then told is "not found".
312
+ * Pass `pattern` (a global RegExp whose first group is the name) when the
313
+ * instructions name tools some other way.
314
+ */
315
+ var findUnknownToolMentions = ({
316
+ instructions,
317
+ tools,
318
+ pattern = TOOL_MENTION
319
+ }) => {
320
+ const known = new Set(tools.map(tool => {
321
+ return tool.name;
322
+ }));
323
+ return [...new Set([...instructions.matchAll(pattern)].map(([, name]) => {
324
+ return name;
325
+ }))].filter(name => {
326
+ return !known.has(name);
327
+ });
328
+ };
329
+
212
330
  //#endregion
213
331
  //#region src/registerAppResource.ts
214
332
  /**
@@ -1568,4 +1686,4 @@ var createMcpRouter = (server, options = {}) => {
1568
1686
  };
1569
1687
 
1570
1688
  //#endregion
1571
- export { InMemoryServerEventBus, McpServer, UI_EXTENSION_ID, UI_RESOURCE_MIME_TYPE, apiCall, checkScopes, createMcpRouter, createToolGate, defineTool, errorBodyMessage, getApiHeaders, getIdentity, getRequestLocale, rankTools, registerAppResource, registerTools, z };
1689
+ export { DEFAULT_INSTRUCTIONS_LIMIT, InMemoryServerEventBus, McpServer, UI_EXTENSION_ID, UI_RESOURCE_MIME_TYPE, apiCall, checkMcpSurface, checkScopes, createMcpRouter, createToolGate, defineTool, errorBodyMessage, findUnknownToolMentions, getApiHeaders, getIdentity, getRequestLocale, measureMcpSurface, rankTools, registerAppResource, registerTools, z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttoss/http-server-mcp",
3
- "version": "0.34.4",
3
+ "version": "0.35.0",
4
4
  "description": "Model Context Protocol (MCP) server integration for @ttoss/http-server",
5
5
  "keywords": [
6
6
  "ai",
@@ -37,10 +37,10 @@
37
37
  "@modelcontextprotocol/node": "^2.0.0",
38
38
  "@modelcontextprotocol/server": "^2.0.0",
39
39
  "zod": "^4.4.3",
40
- "@ttoss/auth-core": "^0.17.1",
41
- "@ttoss/http-server": "^0.12.1",
42
- "@ttoss/i18n-core": "^0.8.0",
43
- "@ttoss/http-server-auth": "^0.8.3"
40
+ "@ttoss/auth-core": "^0.17.2",
41
+ "@ttoss/http-server": "^0.13.0",
42
+ "@ttoss/i18n-core": "^0.8.1",
43
+ "@ttoss/http-server-auth": "^0.8.5"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@modelcontextprotocol/client": "^2.0.0",