@daloyjs/core 1.0.0-rc.6 → 1.0.0-rc.7
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 +3 -2
- package/dist/auto-ban.d.ts +16 -0
- package/dist/auto-ban.js +19 -9
- package/dist/bot-guard.d.ts +14 -0
- package/dist/bot-guard.js +12 -12
- package/dist/concurrency-limit.d.ts +14 -0
- package/dist/concurrency-limit.js +18 -9
- package/dist/conn-info.d.ts +65 -0
- package/dist/conn-info.js +99 -4
- package/dist/geo-block.d.ts +15 -0
- package/dist/geo-block.js +11 -11
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/ip-reputation.d.ts +14 -0
- package/dist/ip-reputation.js +10 -10
- package/dist/ip-restriction.d.ts +14 -0
- package/dist/ip-restriction.js +5 -7
- package/dist/mcp.d.ts +305 -34
- package/dist/mcp.js +554 -49
- package/dist/middleware.d.ts +31 -1
- package/dist/middleware.js +21 -19
- package/dist/sbom.cdx.json +9 -9
- package/dist/sbom.spdx.json +5 -5
- package/package.json +1 -1
package/dist/ip-restriction.d.ts
CHANGED
|
@@ -42,8 +42,22 @@ export interface IpRestrictionOptions {
|
|
|
42
42
|
* to `false` because those headers are client-spoofable unless every
|
|
43
43
|
* request reaches Daloy through a proxy chain you control. Pair with
|
|
44
44
|
* `new App({ trustProxy: true })` in production.
|
|
45
|
+
*
|
|
46
|
+
* When enabled, the resolver reads the **rightmost** `X-Forwarded-For`
|
|
47
|
+
* entry — the one your immediate proxy appended — never the
|
|
48
|
+
* attacker-influenceable leftmost one, so a spoofed left entry cannot
|
|
49
|
+
* bypass an allow-list or dodge a deny. Behind more than one proxy hop,
|
|
50
|
+
* set {@link trustedHops} instead.
|
|
45
51
|
*/
|
|
46
52
|
trustProxyHeaders?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Declare exactly how many proxy hops sit between Daloy and the public
|
|
55
|
+
* internet. Implies proxy-header trust and reads the client IP that many
|
|
56
|
+
* entries from the right of `X-Forwarded-For` via
|
|
57
|
+
* {@link "./conn-info.js".resolveForwardedClientIp}. Must be an integer in
|
|
58
|
+
* [1, 64]; validated at construction.
|
|
59
|
+
*/
|
|
60
|
+
trustedHops?: number;
|
|
47
61
|
/**
|
|
48
62
|
* Response message when a request is rejected. Defaults to
|
|
49
63
|
* `"IP address not permitted"`. Avoid echoing the client IP back —
|
package/dist/ip-restriction.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* @since 0.19.0
|
|
9
9
|
*/
|
|
10
10
|
import { ForbiddenError } from "./errors.js";
|
|
11
|
+
import { resolveForwardedClientIp, resolveForwardedTrust } from "./conn-info.js";
|
|
11
12
|
/**
|
|
12
13
|
* Block or allow requests by source IP / CIDR range. In direct Web-standard
|
|
13
14
|
* runtimes, pass `resolveIp` from the adapter-specific connection metadata.
|
|
@@ -40,7 +41,8 @@ export function ipRestriction(opts) {
|
|
|
40
41
|
}
|
|
41
42
|
const allow = (opts.allow ?? []).map(compileCidrMatcher);
|
|
42
43
|
const deny = (opts.deny ?? []).map(compileCidrMatcher);
|
|
43
|
-
const
|
|
44
|
+
const hops = resolveForwardedTrust("ipRestriction()", opts);
|
|
45
|
+
const resolveIp = opts.resolveIp ?? (hops !== undefined ? forwardedIpResolver(hops) : noIpResolver);
|
|
44
46
|
const message = opts.message ?? "IP address not permitted";
|
|
45
47
|
return {
|
|
46
48
|
beforeHandle(ctx) {
|
|
@@ -62,12 +64,8 @@ export function ipRestriction(opts) {
|
|
|
62
64
|
function noIpResolver(_ctx) {
|
|
63
65
|
return undefined;
|
|
64
66
|
}
|
|
65
|
-
function forwardedIpResolver(
|
|
66
|
-
|
|
67
|
-
const forwarded = headers.get("x-forwarded-for");
|
|
68
|
-
if (forwarded)
|
|
69
|
-
return forwarded.split(",")[0]?.trim();
|
|
70
|
-
return headers.get("x-real-ip") ?? undefined;
|
|
67
|
+
function forwardedIpResolver(hops) {
|
|
68
|
+
return (ctx) => resolveForwardedClientIp(ctx.request, hops);
|
|
71
69
|
}
|
|
72
70
|
/**
|
|
73
71
|
* Test whether a parsed IP falls inside a compiled CIDR matcher, comparing
|
package/dist/mcp.d.ts
CHANGED
|
@@ -2,17 +2,65 @@ import type { PathString, RouteDefinition } from "./types.js";
|
|
|
2
2
|
/**
|
|
3
3
|
* Latest MCP protocol version DaloyJS negotiates by default.
|
|
4
4
|
*
|
|
5
|
-
* @see https://modelcontextprotocol.io/specification/
|
|
5
|
+
* @see https://modelcontextprotocol.io/specification/2026-07-28
|
|
6
6
|
* @since 1.0.0
|
|
7
7
|
*/
|
|
8
|
-
export declare const MCP_PROTOCOL_VERSION = "
|
|
8
|
+
export declare const MCP_PROTOCOL_VERSION = "2026-07-28";
|
|
9
|
+
/**
|
|
10
|
+
* First MCP revision of the *stateless* ("modern") era: version, client
|
|
11
|
+
* identity, and client capabilities travel in each request's `_meta` instead
|
|
12
|
+
* of being established once by an `initialize` handshake.
|
|
13
|
+
*
|
|
14
|
+
* Revisions are `YYYY-MM-DD` strings, so a lexicographic comparison against
|
|
15
|
+
* this constant correctly classifies every past and future revision.
|
|
16
|
+
*
|
|
17
|
+
* @since 1.0.0
|
|
18
|
+
*/
|
|
19
|
+
export declare const MCP_MODERN_ERA_MIN_VERSION = "2026-07-28";
|
|
9
20
|
/**
|
|
10
21
|
* Protocol revisions accepted by {@link createMcpHandler} unless the caller
|
|
11
22
|
* provides an explicit `protocolVersions` list.
|
|
12
23
|
*
|
|
24
|
+
* The list spans both protocol eras: the stateless `2026-07-28` revision and
|
|
25
|
+
* the older handshake-based revisions, so one endpoint serves modern and
|
|
26
|
+
* legacy MCP clients at the same time.
|
|
27
|
+
*
|
|
13
28
|
* @since 1.0.0
|
|
14
29
|
*/
|
|
15
30
|
export declare const MCP_PROTOCOL_VERSIONS: readonly string[];
|
|
31
|
+
/**
|
|
32
|
+
* Reserved `_meta` keys defined by MCP `2026-07-28` for per-request protocol
|
|
33
|
+
* metadata. Exported so applications and tests can build spec-compliant
|
|
34
|
+
* requests without hard-coding string literals.
|
|
35
|
+
*
|
|
36
|
+
* @since 1.0.0
|
|
37
|
+
*/
|
|
38
|
+
export declare const MCP_META_KEYS: Readonly<{
|
|
39
|
+
/** Protocol version for this request. Required on every modern request. */
|
|
40
|
+
readonly protocolVersion: "io.modelcontextprotocol/protocolVersion";
|
|
41
|
+
/** Self-reported client name/version. Advisory only; never a security input. */
|
|
42
|
+
readonly clientInfo: "io.modelcontextprotocol/clientInfo";
|
|
43
|
+
/** Client capabilities relevant to this request. Required on every modern request. */
|
|
44
|
+
readonly clientCapabilities: "io.modelcontextprotocol/clientCapabilities";
|
|
45
|
+
/** Minimum log level the server should emit for this request. */
|
|
46
|
+
readonly logLevel: "io.modelcontextprotocol/logLevel";
|
|
47
|
+
/** Self-reported server name/version, returned in each modern result's `_meta`. */
|
|
48
|
+
readonly serverInfo: "io.modelcontextprotocol/serverInfo";
|
|
49
|
+
}>;
|
|
50
|
+
/**
|
|
51
|
+
* JSON-RPC error codes defined by the MCP specification in its reserved
|
|
52
|
+
* `-32020`..`-32099` sub-range.
|
|
53
|
+
*
|
|
54
|
+
* @since 1.0.0
|
|
55
|
+
*/
|
|
56
|
+
export declare const MCP_ERROR_CODES: Readonly<{
|
|
57
|
+
/** HTTP headers disagree with the request body, or a required header is missing. */
|
|
58
|
+
readonly headerMismatch: -32020;
|
|
59
|
+
/** The request needs a client capability the client did not declare. */
|
|
60
|
+
readonly missingRequiredClientCapability: -32021;
|
|
61
|
+
/** The requested protocol version is not implemented by this server. */
|
|
62
|
+
readonly unsupportedProtocolVersion: -32022;
|
|
63
|
+
}>;
|
|
16
64
|
/**
|
|
17
65
|
* Default maximum accepted JSON-RPC request body for a DaloyJS MCP endpoint.
|
|
18
66
|
* The cap is intentionally small because MCP calls should carry parameters,
|
|
@@ -21,6 +69,30 @@ export declare const MCP_PROTOCOL_VERSIONS: readonly string[];
|
|
|
21
69
|
* @since 1.0.0
|
|
22
70
|
*/
|
|
23
71
|
export declare const MCP_DEFAULT_MAX_BODY_BYTES: number;
|
|
72
|
+
/**
|
|
73
|
+
* Maximum accepted length of a client-supplied `params.requestState` string.
|
|
74
|
+
*
|
|
75
|
+
* `requestState` is opaque server state that round-trips through an untrusted
|
|
76
|
+
* client during a multi round-trip request, so it is bounded independently of
|
|
77
|
+
* the body cap to keep a hostile client from forcing large state parsing.
|
|
78
|
+
*
|
|
79
|
+
* @since 1.0.0
|
|
80
|
+
*/
|
|
81
|
+
export declare const MCP_MAX_REQUEST_STATE_LENGTH = 8192;
|
|
82
|
+
/**
|
|
83
|
+
* Report whether a protocol revision belongs to the stateless ("modern") MCP
|
|
84
|
+
* era introduced by {@link MCP_MODERN_ERA_MIN_VERSION}.
|
|
85
|
+
*
|
|
86
|
+
* Modern requests carry their protocol version, client identity, and client
|
|
87
|
+
* capabilities in `_meta` and are validated against the standard
|
|
88
|
+
* `MCP-Protocol-Version` / `Mcp-Method` / `Mcp-Name` HTTP headers. Older
|
|
89
|
+
* revisions keep the `initialize` handshake instead.
|
|
90
|
+
*
|
|
91
|
+
* @param version - A protocol revision string such as `"2026-07-28"`.
|
|
92
|
+
* @returns `true` when the revision uses per-request metadata.
|
|
93
|
+
* @since 1.0.0
|
|
94
|
+
*/
|
|
95
|
+
export declare function isModernProtocolVersion(version: string): boolean;
|
|
24
96
|
/**
|
|
25
97
|
* JSON value accepted in MCP schemas, structured results, and metadata.
|
|
26
98
|
*
|
|
@@ -94,6 +166,34 @@ export interface McpServerInfo {
|
|
|
94
166
|
/** Optional icons clients may display for this server (MCP 2025-11-25). */
|
|
95
167
|
icons?: McpIcon[];
|
|
96
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Self-reported implementation identity (`serverInfo` / `clientInfo`).
|
|
171
|
+
*
|
|
172
|
+
* Values are supplied by the peer and are **not** verified by the protocol.
|
|
173
|
+
* Use them for display, logging, and debugging only — never for authorization
|
|
174
|
+
* or any other security decision.
|
|
175
|
+
*
|
|
176
|
+
* @since 1.0.0
|
|
177
|
+
*/
|
|
178
|
+
export interface McpImplementation {
|
|
179
|
+
/** Stable machine-readable implementation name. */
|
|
180
|
+
name: string;
|
|
181
|
+
/** Implementation version string. */
|
|
182
|
+
version: string;
|
|
183
|
+
/** Optional human-readable display title. */
|
|
184
|
+
title?: string;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Protocol era a request was served under.
|
|
188
|
+
*
|
|
189
|
+
* - `"modern"`: MCP `2026-07-28` and later. Stateless; version, identity, and
|
|
190
|
+
* capabilities arrive in `_meta` and are mirrored into HTTP headers.
|
|
191
|
+
* - `"legacy"`: MCP `2025-11-25` and earlier. Established by an `initialize`
|
|
192
|
+
* handshake.
|
|
193
|
+
*
|
|
194
|
+
* @since 1.0.0
|
|
195
|
+
*/
|
|
196
|
+
export type McpProtocolEra = "modern" | "legacy";
|
|
97
197
|
/**
|
|
98
198
|
* Per-request context passed to tool, resource, and prompt handlers.
|
|
99
199
|
*
|
|
@@ -103,16 +203,57 @@ export interface McpRequestContext {
|
|
|
103
203
|
/** The original HTTP request received by the DaloyJS route. */
|
|
104
204
|
request: Request;
|
|
105
205
|
/**
|
|
106
|
-
* Protocol version selected for this call.
|
|
107
|
-
* `
|
|
108
|
-
*
|
|
109
|
-
*
|
|
206
|
+
* Protocol version selected for this call. On a modern request this is the
|
|
207
|
+
* verified `io.modelcontextprotocol/protocolVersion` from `_meta`. On a
|
|
208
|
+
* legacy request, `initialize` negotiates it from `params.protocolVersion`
|
|
209
|
+
* and other calls take the `MCP-Protocol-Version` header, falling back to
|
|
210
|
+
* `2025-03-26` (the spec's assumption for headerless requests) when
|
|
211
|
+
* supported, otherwise the preferred version.
|
|
110
212
|
*/
|
|
111
213
|
protocolVersion: string;
|
|
214
|
+
/**
|
|
215
|
+
* Protocol era this request was served under. Handlers that emit multi
|
|
216
|
+
* round-trip results must check this: `input_required` only exists in the
|
|
217
|
+
* `"modern"` era.
|
|
218
|
+
*/
|
|
219
|
+
era: McpProtocolEra;
|
|
112
220
|
/** JSON-RPC id for request/response correlation. */
|
|
113
221
|
id: McpJsonRpcId;
|
|
114
222
|
/** Raw MCP method name, such as `"tools/call"` or `"resources/read"`. */
|
|
115
223
|
method: string;
|
|
224
|
+
/**
|
|
225
|
+
* Capabilities the client declared for this request
|
|
226
|
+
* (`io.modelcontextprotocol/clientCapabilities`). Empty on legacy requests,
|
|
227
|
+
* which declare capabilities once during `initialize` instead.
|
|
228
|
+
*/
|
|
229
|
+
clientCapabilities: McpJsonObject;
|
|
230
|
+
/**
|
|
231
|
+
* Self-reported client identity (`io.modelcontextprotocol/clientInfo`), when
|
|
232
|
+
* the client sent one. Advisory metadata — never a security input.
|
|
233
|
+
*/
|
|
234
|
+
clientInfo?: McpImplementation;
|
|
235
|
+
/**
|
|
236
|
+
* Minimum log level the client asked the server to emit for this request
|
|
237
|
+
* (`io.modelcontextprotocol/logLevel`), when supplied.
|
|
238
|
+
*/
|
|
239
|
+
logLevel?: string;
|
|
240
|
+
/**
|
|
241
|
+
* Client answers to a previous {@link McpInputRequiredResult}, keyed by the
|
|
242
|
+
* identifiers the server assigned in `inputRequests`. Present only on a
|
|
243
|
+
* multi round-trip retry.
|
|
244
|
+
*/
|
|
245
|
+
inputResponses?: McpInputResponses;
|
|
246
|
+
/**
|
|
247
|
+
* Opaque state the server emitted on a previous
|
|
248
|
+
* {@link McpInputRequiredResult} and the client echoed back.
|
|
249
|
+
*
|
|
250
|
+
* Security: this value round-trips through an untrusted client. If it
|
|
251
|
+
* influences authorization, resource access, or business logic, integrity-
|
|
252
|
+
* protect it (HMAC or AEAD), bind it to the authenticated principal and the
|
|
253
|
+
* originating request, give it a short expiry, and reject anything that
|
|
254
|
+
* fails verification.
|
|
255
|
+
*/
|
|
256
|
+
requestState?: string;
|
|
116
257
|
}
|
|
117
258
|
/**
|
|
118
259
|
* Text content block returned from an MCP tool, resource, or prompt.
|
|
@@ -179,6 +320,69 @@ export interface McpToolResult {
|
|
|
179
320
|
/** Set to `true` for domain/tool errors the model may recover from. */
|
|
180
321
|
isError?: boolean;
|
|
181
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* Server-to-client request embedded in an {@link McpInputRequiredResult}.
|
|
325
|
+
*
|
|
326
|
+
* MCP `2026-07-28` removed server-initiated JSON-RPC requests. A server that
|
|
327
|
+
* needs elicitation, sampling, or the client's roots returns them here and the
|
|
328
|
+
* client supplies the answers on a retry of the original request.
|
|
329
|
+
*
|
|
330
|
+
* @since 1.0.0
|
|
331
|
+
*/
|
|
332
|
+
export interface McpInputRequest {
|
|
333
|
+
/** The client-side method being requested. */
|
|
334
|
+
method: "elicitation/create" | "sampling/createMessage" | "roots/list";
|
|
335
|
+
/** Method parameters, as defined by the MCP client-features specification. */
|
|
336
|
+
params?: McpJsonObject;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Map of server-assigned identifiers to server-to-client requests.
|
|
340
|
+
*
|
|
341
|
+
* @since 1.0.0
|
|
342
|
+
*/
|
|
343
|
+
export type McpInputRequests = {
|
|
344
|
+
[id: string]: McpInputRequest;
|
|
345
|
+
};
|
|
346
|
+
/**
|
|
347
|
+
* Map of the same identifiers to the client's answers, echoed back on the
|
|
348
|
+
* retry of the original request.
|
|
349
|
+
*
|
|
350
|
+
* @since 1.0.0
|
|
351
|
+
*/
|
|
352
|
+
export type McpInputResponses = {
|
|
353
|
+
[id: string]: McpJsonValue;
|
|
354
|
+
};
|
|
355
|
+
/**
|
|
356
|
+
* Interim result telling the client that more input is required before the
|
|
357
|
+
* call can complete (MCP `2026-07-28` multi round-trip requests).
|
|
358
|
+
*
|
|
359
|
+
* Return this from a tool, resource, or prompt handler instead of a final
|
|
360
|
+
* result. The client gathers the requested input and retries the original
|
|
361
|
+
* request — with a **new** JSON-RPC id — carrying `inputResponses` and, when
|
|
362
|
+
* present, the exact `requestState` string it received.
|
|
363
|
+
*
|
|
364
|
+
* At least one of `inputRequests` or `requestState` must be set. DaloyJS
|
|
365
|
+
* refuses to emit an `inputRequests` entry whose method the client did not
|
|
366
|
+
* declare support for, answering `-32021` instead of leaking a request the
|
|
367
|
+
* client cannot fulfil.
|
|
368
|
+
*
|
|
369
|
+
* @since 1.0.0
|
|
370
|
+
*/
|
|
371
|
+
export interface McpInputRequiredResult {
|
|
372
|
+
/** Discriminator literal identifying this as a multi round-trip result. */
|
|
373
|
+
resultType: "input_required";
|
|
374
|
+
/** Requests the client must fulfil before retrying. */
|
|
375
|
+
inputRequests?: McpInputRequests;
|
|
376
|
+
/**
|
|
377
|
+
* Opaque state the client must echo back verbatim on the retry.
|
|
378
|
+
*
|
|
379
|
+
* Security: it passes through an untrusted client. Integrity-protect it
|
|
380
|
+
* (HMAC or AEAD) whenever it influences authorization, resource access, or
|
|
381
|
+
* business logic, bind it to the authenticated principal and originating
|
|
382
|
+
* request, and give it a short expiry.
|
|
383
|
+
*/
|
|
384
|
+
requestState?: string;
|
|
385
|
+
}
|
|
182
386
|
/**
|
|
183
387
|
* Behavioral hints a tool can advertise to MCP clients. Hints are untrusted
|
|
184
388
|
* metadata for UX decisions (confirmation prompts, badges); clients must not
|
|
@@ -208,13 +412,15 @@ export interface McpToolAnnotations {
|
|
|
208
412
|
* declared shape holds at runtime. Constraints expressed only through
|
|
209
413
|
* unsupported schema keywords (e.g. `pattern`) remain the handler's job.
|
|
210
414
|
* @param ctx - Request metadata and the original HTTP request.
|
|
211
|
-
* @returns Text shorthand
|
|
415
|
+
* @returns Text shorthand, a full {@link McpToolResult}, or an
|
|
416
|
+
* {@link McpInputRequiredResult} to ask the client for more input first
|
|
417
|
+
* (modern protocol era only).
|
|
212
418
|
* @throws {McpToolError} for caller-correctable failures that should be
|
|
213
419
|
* returned as an MCP tool error result.
|
|
214
420
|
*
|
|
215
421
|
* @since 1.0.0
|
|
216
422
|
*/
|
|
217
|
-
export type McpToolHandler<TArgs extends Record<string, unknown> = Record<string, unknown>> = (args: TArgs, ctx: McpRequestContext) => string | McpToolResult | Promise<string | McpToolResult>;
|
|
423
|
+
export type McpToolHandler<TArgs extends Record<string, unknown> = Record<string, unknown>> = (args: TArgs, ctx: McpRequestContext) => string | McpToolResult | McpInputRequiredResult | Promise<string | McpToolResult | McpInputRequiredResult>;
|
|
218
424
|
/**
|
|
219
425
|
* Definition of a callable MCP tool.
|
|
220
426
|
*
|
|
@@ -301,9 +507,11 @@ export interface McpResourceDefinition extends McpResource {
|
|
|
301
507
|
* Read the resource contents for `resources/read`.
|
|
302
508
|
*
|
|
303
509
|
* @param ctx - Request metadata and the original HTTP request.
|
|
304
|
-
* @returns One or more content entries for this resource
|
|
510
|
+
* @returns One or more content entries for this resource, or an
|
|
511
|
+
* {@link McpInputRequiredResult} to ask the client for more input first
|
|
512
|
+
* (modern protocol era only).
|
|
305
513
|
*/
|
|
306
|
-
read: (ctx: McpRequestContext) => McpResourceContents | McpResourceContents[] | Promise<McpResourceContents | McpResourceContents[]>;
|
|
514
|
+
read: (ctx: McpRequestContext) => McpResourceContents | McpResourceContents[] | McpInputRequiredResult | Promise<McpResourceContents | McpResourceContents[] | McpInputRequiredResult>;
|
|
307
515
|
}
|
|
308
516
|
/**
|
|
309
517
|
* Resource template metadata returned from `resources/templates/list`.
|
|
@@ -344,11 +552,13 @@ export interface McpResourceTemplateDefinition extends McpResourceTemplate {
|
|
|
344
552
|
* @param uri - The full resource URI requested by the client.
|
|
345
553
|
* @param variables - Template variable values extracted from `uri`.
|
|
346
554
|
* @param ctx - Request metadata and the original HTTP request.
|
|
347
|
-
* @returns One or more content entries for this resource
|
|
555
|
+
* @returns One or more content entries for this resource, or an
|
|
556
|
+
* {@link McpInputRequiredResult} to ask the client for more input first
|
|
557
|
+
* (modern protocol era only).
|
|
348
558
|
* @throws {McpToolError} for caller-correctable failures such as an unknown
|
|
349
559
|
* record id; these become JSON-RPC invalid-params errors.
|
|
350
560
|
*/
|
|
351
|
-
read: (uri: string, variables: Record<string, string>, ctx: McpRequestContext) => McpResourceContents | McpResourceContents[] | Promise<McpResourceContents | McpResourceContents[]>;
|
|
561
|
+
read: (uri: string, variables: Record<string, string>, ctx: McpRequestContext) => McpResourceContents | McpResourceContents[] | McpInputRequiredResult | Promise<McpResourceContents | McpResourceContents[] | McpInputRequiredResult>;
|
|
352
562
|
}
|
|
353
563
|
/**
|
|
354
564
|
* Argument metadata for an MCP prompt.
|
|
@@ -417,9 +627,10 @@ export interface McpPromptDefinition extends McpPrompt {
|
|
|
417
627
|
*
|
|
418
628
|
* @param args - Prompt arguments supplied by the MCP client.
|
|
419
629
|
* @param ctx - Request metadata and the original HTTP request.
|
|
420
|
-
* @returns Prompt messages
|
|
630
|
+
* @returns Prompt messages, or an {@link McpInputRequiredResult} to ask the
|
|
631
|
+
* client for more input first (modern protocol era only).
|
|
421
632
|
*/
|
|
422
|
-
get: (args: Record<string, unknown>, ctx: McpRequestContext) => McpPromptResult | Promise<McpPromptResult>;
|
|
633
|
+
get: (args: Record<string, unknown>, ctx: McpRequestContext) => McpPromptResult | McpInputRequiredResult | Promise<McpPromptResult | McpInputRequiredResult>;
|
|
423
634
|
}
|
|
424
635
|
/**
|
|
425
636
|
* Caller-correctable MCP tool/resource/prompt error.
|
|
@@ -440,6 +651,31 @@ export declare class McpToolError extends Error {
|
|
|
440
651
|
*/
|
|
441
652
|
constructor(message: string);
|
|
442
653
|
}
|
|
654
|
+
/**
|
|
655
|
+
* Client-side caching hints attached to every cacheable modern result
|
|
656
|
+
* (`server/discover`, the four list methods, and `resources/read`).
|
|
657
|
+
*
|
|
658
|
+
* @since 1.0.0
|
|
659
|
+
*/
|
|
660
|
+
export interface McpCacheHints {
|
|
661
|
+
/**
|
|
662
|
+
* Freshness hint in milliseconds. `0` (the default) tells clients to
|
|
663
|
+
* revalidate on every call.
|
|
664
|
+
*
|
|
665
|
+
* @defaultValue 0
|
|
666
|
+
*/
|
|
667
|
+
ttlMs?: number;
|
|
668
|
+
/**
|
|
669
|
+
* Whether shared intermediaries may cache the response. DaloyJS defaults to
|
|
670
|
+
* `"private"` because MCP list results legitimately vary by the credential
|
|
671
|
+
* presented on the request — a `"public"` scope on an authorization-scoped
|
|
672
|
+
* tool list would let a proxy serve one caller's tools to another. Only set
|
|
673
|
+
* `"public"` for a server whose results are identical for every caller.
|
|
674
|
+
*
|
|
675
|
+
* @defaultValue "private"
|
|
676
|
+
*/
|
|
677
|
+
scope?: "public" | "private";
|
|
678
|
+
}
|
|
443
679
|
/**
|
|
444
680
|
* Options for {@link createMcpHandler}.
|
|
445
681
|
*
|
|
@@ -473,6 +709,21 @@ export interface McpHandlerOptions {
|
|
|
473
709
|
* other origin is rejected with `403` unless listed here.
|
|
474
710
|
*/
|
|
475
711
|
allowedOrigins?: readonly string[];
|
|
712
|
+
/**
|
|
713
|
+
* Optional extensions advertised in `capabilities.extensions`, keyed by
|
|
714
|
+
* extension identifier (for example `"io.modelcontextprotocol/tasks"`), with
|
|
715
|
+
* each value the extension's settings object. Identifiers must carry a
|
|
716
|
+
* reverse-DNS prefix, per the `_meta` key naming rules.
|
|
717
|
+
*
|
|
718
|
+
* DaloyJS core implements no extension itself; declaring one here advertises
|
|
719
|
+
* that *your* handlers implement it.
|
|
720
|
+
*/
|
|
721
|
+
extensions?: Record<string, McpJsonObject>;
|
|
722
|
+
/**
|
|
723
|
+
* Caching hints returned on cacheable modern results. Defaults to
|
|
724
|
+
* `{ ttlMs: 0, scope: "private" }` — no caching, no sharing.
|
|
725
|
+
*/
|
|
726
|
+
cache?: McpCacheHints;
|
|
476
727
|
/** Accepted MCP protocol versions. Defaults to {@link MCP_PROTOCOL_VERSIONS}. */
|
|
477
728
|
protocolVersions?: readonly string[];
|
|
478
729
|
/**
|
|
@@ -532,31 +783,51 @@ export declare function validateMcpInput(schema: McpJsonSchema, value: unknown):
|
|
|
532
783
|
/**
|
|
533
784
|
* Create a dependency-free MCP Streamable HTTP endpoint handler.
|
|
534
785
|
*
|
|
535
|
-
* The handler
|
|
536
|
-
*
|
|
537
|
-
*
|
|
538
|
-
*
|
|
539
|
-
*
|
|
540
|
-
*
|
|
541
|
-
*
|
|
542
|
-
*
|
|
543
|
-
*
|
|
544
|
-
*
|
|
545
|
-
*
|
|
546
|
-
*
|
|
547
|
-
*
|
|
548
|
-
*
|
|
549
|
-
*
|
|
550
|
-
*
|
|
551
|
-
*
|
|
552
|
-
*
|
|
786
|
+
* The handler serves **both MCP protocol eras** on one endpoint:
|
|
787
|
+
*
|
|
788
|
+
* - **Modern (`2026-07-28`+, stateless).** No handshake. Every request carries
|
|
789
|
+
* its protocol version, client identity, and client capabilities in `_meta`,
|
|
790
|
+
* mirrored into the required `MCP-Protocol-Version`, `Mcp-Method`, and
|
|
791
|
+
* `Mcp-Name` headers. Methods: `server/discover`, `tools/list`,
|
|
792
|
+
* `tools/call`, `resources/list`, `resources/templates/list`,
|
|
793
|
+
* `resources/read`, `prompts/list`, `prompts/get`. Every result carries
|
|
794
|
+
* `resultType`, the server identity in `_meta`, and — on cacheable methods —
|
|
795
|
+
* `ttlMs` / `cacheScope`. Handlers may return an
|
|
796
|
+
* {@link McpInputRequiredResult} to run a multi round-trip request.
|
|
797
|
+
* - **Legacy (`2025-11-25` and earlier).** The `initialize` / `ping` handshake
|
|
798
|
+
* protocol, unchanged, so existing clients keep working.
|
|
799
|
+
*
|
|
800
|
+
* A request is served as modern when its `_meta` protocol version (or the
|
|
801
|
+
* `MCP-Protocol-Version` header) is `2026-07-28` or later; otherwise it takes
|
|
802
|
+
* the legacy path.
|
|
803
|
+
*
|
|
804
|
+
* Security, on top of the era-independent body cap, prototype-pollution-safe
|
|
805
|
+
* parsing, and `inputSchema` enforcement:
|
|
806
|
+
*
|
|
807
|
+
* - Per the Streamable HTTP spec's DNS-rebinding guidance, every request
|
|
808
|
+
* bearing an `Origin` header is validated. Loopback origins pass; anything
|
|
809
|
+
* else is rejected with `403` unless listed in
|
|
810
|
+
* {@link McpHandlerOptions.allowedOrigins}.
|
|
811
|
+
* - Modern requests are rejected with `400` and `-32020` (`HeaderMismatch`)
|
|
812
|
+
* when a required standard header is missing or disagrees with the body.
|
|
813
|
+
* This closes the header/body confusion gap that lets a gateway route on one
|
|
814
|
+
* value while the server executes another.
|
|
815
|
+
* - `Mcp-Session-Id` and `Last-Event-ID` are ignored; no session is ever minted
|
|
816
|
+
* or echoed.
|
|
817
|
+
*
|
|
818
|
+
* It intentionally does not spawn stdio servers, manage OAuth metadata, open
|
|
819
|
+
* `subscriptions/listen` notification streams, or implement the tasks
|
|
820
|
+
* extension. Use DaloyJS middleware for authentication and authorization, and
|
|
821
|
+
* run this on a dedicated Daloy app when your MCP server has a different trust
|
|
822
|
+
* boundary than your REST API.
|
|
553
823
|
*
|
|
554
824
|
* @param options - Server identity, capabilities, limits, and response headers.
|
|
555
825
|
* @returns A Fetch-compatible request handler suitable for {@link mcpRoutes}
|
|
556
826
|
* or for direct use in any web-standard runtime.
|
|
557
827
|
* @throws {TypeError} at construction for invalid serverInfo, protocol
|
|
558
|
-
* versions, body limits,
|
|
559
|
-
*
|
|
828
|
+
* versions, body limits, cache hints, extension identifiers, duplicate
|
|
829
|
+
* names/URIs, malformed `allowedOrigins` entries, invalid `x-mcp-header`
|
|
830
|
+
* annotations, or unsupported URI template expressions.
|
|
560
831
|
*
|
|
561
832
|
* @example
|
|
562
833
|
* ```ts
|