@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 +27 -0
- package/dist/index.cjs +122 -0
- package/dist/index.d.cts +119 -1
- package/dist/index.d.mts +119 -1
- package/dist/index.mjs +119 -1
- package/package.json +5 -5
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.
|
|
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.
|
|
41
|
-
"@ttoss/http-server": "^0.
|
|
42
|
-
"@ttoss/i18n-core": "^0.8.
|
|
43
|
-
"@ttoss/http-server-auth": "^0.8.
|
|
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",
|