@sezzlee/sdk-nestjs 0.0.0-stage → 0.2.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.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +360 -2
  3. package/dist/cache.d.ts +18 -0
  4. package/dist/cache.js +55 -0
  5. package/dist/catalog.d.ts +100 -0
  6. package/dist/catalog.js +456 -0
  7. package/dist/decorators.d.ts +172 -0
  8. package/dist/decorators.js +79 -0
  9. package/dist/discovery/body-encoding.d.ts +24 -0
  10. package/dist/discovery/body-encoding.js +211 -0
  11. package/dist/discovery/diagnostics.d.ts +4 -0
  12. package/dist/discovery/diagnostics.js +49 -0
  13. package/dist/discovery/endpoint-discovery.d.ts +52 -0
  14. package/dist/discovery/endpoint-discovery.js +722 -0
  15. package/dist/discovery/type-shape.d.ts +21 -0
  16. package/dist/discovery/type-shape.js +297 -0
  17. package/dist/dispatcher.d.ts +29 -0
  18. package/dist/dispatcher.js +182 -0
  19. package/dist/extension-points.d.ts +34 -0
  20. package/dist/extension-points.js +35 -0
  21. package/dist/families.d.ts +77 -0
  22. package/dist/families.js +157 -0
  23. package/dist/files.d.ts +43 -0
  24. package/dist/files.js +13 -0
  25. package/dist/index.d.ts +48 -0
  26. package/dist/index.js +26 -0
  27. package/dist/invoke-result-mapper.d.ts +16 -0
  28. package/dist/invoke-result-mapper.js +44 -0
  29. package/dist/markers.d.ts +5 -0
  30. package/dist/markers.js +27 -0
  31. package/dist/meta-tools.d.ts +18 -0
  32. package/dist/meta-tools.js +306 -0
  33. package/dist/options-validation.d.ts +17 -0
  34. package/dist/options-validation.js +79 -0
  35. package/dist/options.d.ts +201 -0
  36. package/dist/options.js +151 -0
  37. package/dist/outer-connection.d.ts +18 -0
  38. package/dist/outer-connection.js +23 -0
  39. package/dist/sezzlee.module.d.ts +16 -0
  40. package/dist/sezzlee.module.js +180 -0
  41. package/dist/synthetic-context.d.ts +18 -0
  42. package/dist/synthetic-context.js +131 -0
  43. package/dist/transport/audience.d.ts +2 -0
  44. package/dist/transport/audience.js +21 -0
  45. package/dist/transport/protected-resource-metadata.d.ts +6 -0
  46. package/dist/transport/protected-resource-metadata.js +23 -0
  47. package/dist/transport/streamable-http.d.ts +25 -0
  48. package/dist/transport/streamable-http.js +52 -0
  49. package/dist/visibility/evaluator.d.ts +8 -0
  50. package/dist/visibility/evaluator.js +5 -0
  51. package/dist/visibility/probe.d.ts +25 -0
  52. package/dist/visibility/probe.js +104 -0
  53. package/dist/visibility/provider.d.ts +23 -0
  54. package/dist/visibility/provider.js +118 -0
  55. package/package.json +99 -3
@@ -0,0 +1,306 @@
1
+ import { catalogGenerationMetaKey, checkPinnedVersion, compose, createLoadedTool, emitGuarded as emitWithin, errorResult, invokeArgumentsDescription, invokeDescription, invokeVersionDescription, isInvokeError, knownFields, loadDescription, missingArgument, narrowingArguments, normalizeInvokeArguments, notInvocable, operationNameDescription, refuseTimedOutInvoke, refuseUnresolvedFile, resolveDeferred as resolveDeferredWith, searchCatalog, searchDescription, searchDetailDescription, searchLimitDescription, searchQueryDescription, searchTagsDescription, SezzleeArgumentError, SezzleeDispatchAborted, textResult, unknownTool, vocabularyOf, wrongArgumentType, defaultSearchLimit, describeRankerEvent, } from "@sezzlee/core";
2
+ import { Logger } from "@nestjs/common";
3
+ import { z } from "zod";
4
+ import { SezzleeFileRefused } from "./files.js";
5
+ import { callerOf } from "./options.js";
6
+ import { currentOuterConnection } from "./outer-connection.js";
7
+ export { catalogGenerationMetaKey };
8
+ const logger = new Logger("Sezzlee");
9
+ /**
10
+ * Guard: `name` binds as `unknown` and the object publishes its own `required`, so a call that
11
+ * misspells the argument reaches the handler instead of the framework's validator. The published
12
+ * schema is byte-identical to a plain `z.string()` one — `.meta` supplies the `type` the runtime
13
+ * no longer implies. Without this the MCP SDK answers with a bare text error that is not JSON, so
14
+ * the agent cannot parse it, the leak filter never runs, and the turn dies with nothing to repair
15
+ * from. Measured: a model called `load_tool` with `operation` instead of `name`. Pinned by
16
+ * test/meta-tools.spec.ts and by T19.
17
+ */
18
+ function namedArgument(description) {
19
+ return z.unknown().optional().meta({ type: "string", description });
20
+ }
21
+ function timeoutFor(deps, target) {
22
+ const invoke = deps.options.invoke;
23
+ return invoke.timeoutMsFor?.(target) ?? invoke.timeoutMs;
24
+ }
25
+ function budgetFor(deps, target) {
26
+ const invoke = deps.options.invoke;
27
+ const override = target === undefined ? undefined : invoke.maxResponseBytesFor?.(target);
28
+ return override ?? invoke.maxResponseBytes;
29
+ }
30
+ function rankerFor(deps) {
31
+ const ranker = deps.catalog.ranker ?? null;
32
+ if (ranker === null) {
33
+ return {};
34
+ }
35
+ const { rankerTimeoutMs, onRankerFailure } = deps.options.search;
36
+ return {
37
+ ranker: {
38
+ ranker,
39
+ timeoutMs: rankerTimeoutMs,
40
+ onFailure: onRankerFailure,
41
+ report: (event) => {
42
+ const message = describeRankerEvent(event, onRankerFailure);
43
+ if (event.kind === "fallback" && event.error !== undefined) {
44
+ logger.warn(message, event.error);
45
+ }
46
+ else {
47
+ logger.warn(message);
48
+ }
49
+ },
50
+ },
51
+ };
52
+ }
53
+ /**
54
+ * The MCP call's cancellation channel, read separately from {@link outerFrom}.
55
+ *
56
+ * A stdio session has no `ctx.http`, so folding the signal into `OuterRequest` would either lose it
57
+ * there or synthesize an outer request that never existed — which would start calling the host's
58
+ * identity projector on a transport that carries no headers.
59
+ */
60
+ function deadlineFrom(ctx) {
61
+ const signal = ctx?.mcpReq?.signal;
62
+ return signal === undefined ? undefined : { signal };
63
+ }
64
+ /**
65
+ * Guard: `ctx.http.req` is a Web `Request`, so its `Headers` has to be flattened into the Node
66
+ * record every identity carrier and value provider in this SDK reads. Iterating `Headers` already
67
+ * lower-cases each name and joins repeats with ", " — the same shape Node produces — so a carrier
68
+ * configured for `x-tenant-id` keeps matching across the v1 to v2 move.
69
+ */
70
+ function headersOf(headers) {
71
+ const record = {};
72
+ for (const [name, value] of headers) {
73
+ record[name] = value;
74
+ }
75
+ return record;
76
+ }
77
+ function outerFrom(ctx) {
78
+ const request = ctx?.http?.req;
79
+ if (request === undefined) {
80
+ return undefined;
81
+ }
82
+ const connection = currentOuterConnection();
83
+ const auth = ctx?.http?.authInfo;
84
+ return {
85
+ headers: headersOf(request.headers),
86
+ ...(connection === undefined ? {} : { connection }),
87
+ ...(auth === undefined ? {} : { auth }),
88
+ };
89
+ }
90
+ function emitGuarded(deps, produce) {
91
+ return emitWithin((target) => budgetFor(deps, target), produce);
92
+ }
93
+ export function registerSezzleeTools(server, deps) {
94
+ const generationMeta = () => ({
95
+ [catalogGenerationMetaKey]: deps.catalog.generation,
96
+ });
97
+ server.registerTool("search_tools", {
98
+ description: searchDescription,
99
+ _meta: generationMeta(),
100
+ inputSchema: z.object({
101
+ query: z.string().default("").describe(searchQueryDescription),
102
+ limit: z
103
+ .number()
104
+ .int()
105
+ .default(defaultSearchLimit)
106
+ .describe(searchLimitDescription),
107
+ /**
108
+ * Guard: `.catch` is what makes an unrecognised `detail` fall back to `card`
109
+ * instead of failing validation. Without it zod rejects the call while the .NET
110
+ * SDK, whose `[AllowedValues]` only decorates the published schema, answers with
111
+ * cards — two SDKs disagreeing on one argument. It does not change the published
112
+ * schema, which still carries `enum` and `default`.
113
+ */
114
+ detail: z
115
+ .enum(["card", "schema"])
116
+ .catch("card")
117
+ .default("card")
118
+ .describe(searchDetailDescription),
119
+ /**
120
+ * Guard: `meta` publishes the `default: null` that the ASP.NET SDK emits for this argument
121
+ * and cannot omit, because a C# array parameter's default has to be a compile-time
122
+ * constant and `null` is the only one. Without it the two SDKs publish different schemas
123
+ * for the same argument, which search-semantics.md makes contract. It decorates the
124
+ * schema only; `parse` still yields `undefined` for an absent value. Pinned by T16 and by
125
+ * test/meta-tools.spec.ts.
126
+ */
127
+ tags: z
128
+ .array(z.string())
129
+ .describe(searchTagsDescription)
130
+ .optional()
131
+ .meta({ default: null }),
132
+ }),
133
+ }, async ({ query, limit, detail, tags }, ctx) => emitGuarded(deps, async () => {
134
+ deps.catalog.ensureValid();
135
+ const outer = outerFrom(ctx);
136
+ const decide = await decider(deps, outer);
137
+ const visibility = deps.options.visibility;
138
+ return searchCatalog({
139
+ catalog: deps.catalog.current,
140
+ query,
141
+ limit,
142
+ detail,
143
+ tags,
144
+ decide,
145
+ visible: (decision) => deps.visibility.visible(decision),
146
+ ...rankerFor(deps),
147
+ ...deadlineFrom(ctx),
148
+ ...(visibility.tier === "probe"
149
+ ? {
150
+ probe: {
151
+ budget: visibility.probeTopK,
152
+ canProbe: (entry) => deps.visibility.canProbe(entry),
153
+ run: (entry) => deps.visibility.probe(scopeFor(deps, outer), entry, outer),
154
+ },
155
+ }
156
+ : {}),
157
+ });
158
+ }));
159
+ server.registerTool("load_tool", {
160
+ description: loadDescription,
161
+ _meta: generationMeta(),
162
+ inputSchema: z
163
+ .object({ name: namedArgument(operationNameDescription) })
164
+ .meta({ required: ["name"] }),
165
+ }, async ({ name }, ctx) => emitGuarded(deps, async () => {
166
+ if (name === undefined) {
167
+ return missingArgument("load_tool", "name");
168
+ }
169
+ if (typeof name !== "string") {
170
+ return wrongArgumentType("load_tool", "name", name);
171
+ }
172
+ deps.catalog.ensureValid();
173
+ const entry = deps.catalog.find(name);
174
+ if (entry === undefined) {
175
+ return unknownTool(name);
176
+ }
177
+ const outer = outerFrom(ctx);
178
+ const decide = await decider(deps, outer);
179
+ let decision = decide(entry);
180
+ if (decision === "unknown" &&
181
+ deps.options.visibility.tier === "probe" &&
182
+ deps.options.visibility.probeTopK > 0 &&
183
+ deps.visibility.canProbe(entry)) {
184
+ decision = await deps.visibility.probe(scopeFor(deps, outer), entry, outer);
185
+ }
186
+ if (!deps.visibility.visible(decision)) {
187
+ return unknownTool(name);
188
+ }
189
+ return textResult(createLoadedTool(entry.tool, decision), false);
190
+ }));
191
+ server.registerTool("invoke_tool", {
192
+ description: invokeDescription,
193
+ _meta: generationMeta(),
194
+ /**
195
+ * Guard: `arguments` publishes a description and no `type`, so a value that is not an object
196
+ * reaches the handler and leaves as an sezzlee envelope. A schema that constrained it was
197
+ * rejected during the framework's own argument binding, and the caller got an answer carrying
198
+ * neither the envelope nor the leak filter. Pinned by test/meta-tools.spec.ts and by T18.
199
+ */
200
+ inputSchema: z
201
+ .object({
202
+ name: namedArgument(operationNameDescription),
203
+ arguments: z.unknown().optional().meta({
204
+ description: invokeArgumentsDescription,
205
+ }),
206
+ /**
207
+ * Guard: `meta` publishes the `default: null` the ASP.NET SDK emits for this optional
208
+ * string and cannot omit, the same trap `tags` has. It binds as `unknown` so a value that
209
+ * is not a string reaches checkPinnedVersion and leaves as an envelope. Pinned by T18 and
210
+ * by test/meta-tools.spec.ts.
211
+ */
212
+ version: z.unknown().optional().meta({
213
+ type: "string",
214
+ description: invokeVersionDescription,
215
+ default: null,
216
+ }),
217
+ })
218
+ .meta({ required: ["name", "arguments"] }),
219
+ }, async ({ name, arguments: args, version }, ctx) => emitGuarded(deps, async () => {
220
+ if (name === undefined) {
221
+ return missingArgument("invoke_tool", "name");
222
+ }
223
+ if (typeof name !== "string") {
224
+ return wrongArgumentType("invoke_tool", "name", name);
225
+ }
226
+ deps.catalog.ensureValid();
227
+ const entry = deps.catalog.find(name);
228
+ if (entry === undefined) {
229
+ return unknownTool(name);
230
+ }
231
+ const stale = checkPinnedVersion(entry.tool, version);
232
+ if (stale !== undefined) {
233
+ return stale;
234
+ }
235
+ if (entry.template === undefined) {
236
+ return notInvocable(name);
237
+ }
238
+ const outer = outerFrom(ctx);
239
+ const normalized = normalizeInvokeArguments(args);
240
+ if (normalized.unwrapped) {
241
+ logger.warn(`invoke_tool received the arguments for '${name}' as JSON text instead of a JSON object; the text was parsed. A client that double-encodes this argument is defective.`);
242
+ }
243
+ /**
244
+ * Sources are resolved once per invocation and the same map feeds every composition, so a
245
+ * source that is not constant cannot make validation and dispatch disagree.
246
+ */
247
+ const target = {
248
+ tool: entry.tool.name,
249
+ method: entry.descriptor.method,
250
+ route: entry.descriptor.route,
251
+ };
252
+ const files = {
253
+ target,
254
+ maxInlineFileBytes: deps.options.invoke.maxInlineFileBytes,
255
+ maxFileBytes: deps.options.invoke.maxFileBytes,
256
+ };
257
+ let deferred;
258
+ try {
259
+ deferred = await resolveDeferredWith(entry.template, deps.options.arguments.providers, callerOf(outer));
260
+ compose(entry.template, normalized.value, deferred, {
261
+ maxInlineFileBytes: files.maxInlineFileBytes,
262
+ });
263
+ }
264
+ catch (error) {
265
+ if (error instanceof SezzleeArgumentError) {
266
+ return errorResult(error.code, error.message);
267
+ }
268
+ throw error;
269
+ }
270
+ const timeoutMs = timeoutFor(deps, target);
271
+ let result;
272
+ try {
273
+ result = await deps.dispatcher.dispatch(entry.template, normalized.value, outer, deferred, { ...deadlineFrom(ctx), timeoutMs }, files);
274
+ }
275
+ catch (error) {
276
+ if (error instanceof SezzleeDispatchAborted &&
277
+ error.reason === "timeout") {
278
+ return { payload: refuseTimedOutInvoke(timeoutMs), isError: true };
279
+ }
280
+ if (error instanceof SezzleeArgumentError) {
281
+ return errorResult(error.code, error.message);
282
+ }
283
+ if (error instanceof SezzleeFileRefused) {
284
+ return {
285
+ payload: refuseUnresolvedFile(error.field, error.reason, error.limit),
286
+ isError: true,
287
+ };
288
+ }
289
+ throw error;
290
+ }
291
+ const outcome = deps.mapper.map(result, knownFields(entry.tool), vocabularyOf(entry.template));
292
+ return textResult(outcome, isInvokeError(outcome), {
293
+ summaryOf: isInvokeError(outcome) ? outcome : outcome.body,
294
+ narrowing: narrowingArguments(entry.tool.inputSchema),
295
+ target,
296
+ });
297
+ }));
298
+ }
299
+ async function decider(deps, outer) {
300
+ const snapshot = deps.catalog.current;
301
+ const facts = await deps.visibility.facts(scopeFor(deps, outer), outer, snapshot.policyNames);
302
+ return (entry) => deps.visibility.decide(entry.descriptor.auth, facts);
303
+ }
304
+ function scopeFor(deps, outer) {
305
+ return deps.scopes.resolve(outer);
306
+ }
@@ -0,0 +1,17 @@
1
+ import type { SezzleeOptions } from "./options.js";
2
+ export declare class SezzleeConfigurationError extends Error {
3
+ readonly failures: readonly string[];
4
+ constructor(failures: readonly string[]);
5
+ }
6
+ /**
7
+ * Mirrors the ASP.NET `SezzleeOptionsValidator` so the two surfaces can be audited side by side.
8
+ *
9
+ * @param options the configured options
10
+ * @returns one message per invalid setting, empty when the configuration is usable
11
+ */
12
+ export declare function collectConfigurationFailures(options: SezzleeOptions): readonly string[];
13
+ /**
14
+ * @param options the configured options
15
+ * @throws SezzleeConfigurationError when any setting is unusable
16
+ */
17
+ export declare function validateSezzleeOptions(options: SezzleeOptions): void;
@@ -0,0 +1,79 @@
1
+ export class SezzleeConfigurationError extends Error {
2
+ failures;
3
+ constructor(failures) {
4
+ super(`sezzlee: invalid configuration.\n ${failures.join("\n ")}`);
5
+ this.failures = failures;
6
+ this.name = "SezzleeConfigurationError";
7
+ }
8
+ }
9
+ /**
10
+ * Mirrors the ASP.NET `SezzleeOptionsValidator` so the two surfaces can be audited side by side.
11
+ *
12
+ * @param options the configured options
13
+ * @returns one message per invalid setting, empty when the configuration is usable
14
+ */
15
+ export function collectConfigurationFailures(options) {
16
+ const failures = [];
17
+ if (options.cache.lifetimeMs < 0) {
18
+ failures.push("cache.lifetimeMs must be zero or positive.");
19
+ }
20
+ if (options.cache.maxCallers < 1) {
21
+ failures.push("cache.maxCallers must be at least 1.");
22
+ }
23
+ if (options.visibility.probeTopK < 0) {
24
+ failures.push("visibility.probeTopK must be zero or positive.");
25
+ }
26
+ if (options.visibility.probeConcurrency < 1) {
27
+ failures.push("visibility.probeConcurrency must be at least 1.");
28
+ }
29
+ if (options.invoke.maxResponseBytes < 1) {
30
+ failures.push("invoke.maxResponseBytes must be at least 1.");
31
+ }
32
+ if (options.invoke.timeoutMs < 0) {
33
+ failures.push("invoke.timeoutMs must be zero or positive.");
34
+ }
35
+ if (options.invoke.maxInlineFileBytes < 0) {
36
+ failures.push("invoke.maxInlineFileBytes must be zero or positive.");
37
+ }
38
+ if (options.invoke.maxFileBytes < 1) {
39
+ failures.push("invoke.maxFileBytes must be at least 1.");
40
+ }
41
+ if (!Number.isInteger(options.search.rankerTimeoutMs) ||
42
+ options.search.rankerTimeoutMs < 0) {
43
+ failures.push("search.rankerTimeoutMs must be zero or a positive integer.");
44
+ }
45
+ if (!["fallback", "error"].includes(options.search.onRankerFailure)) {
46
+ failures.push('search.onRankerFailure must be "fallback" or "error".');
47
+ }
48
+ if (!Number.isInteger(options.families.loadTimeoutMs) ||
49
+ options.families.loadTimeoutMs < 1) {
50
+ failures.push("families.loadTimeoutMs must be a positive integer.");
51
+ }
52
+ const refDescription = options.files.resolver?.refDescription;
53
+ if (refDescription !== undefined && refDescription.trim() === "") {
54
+ failures.push("files.resolver.refDescription must say what a ref is; an agent reads it to find one.");
55
+ }
56
+ /**
57
+ * A blank field is not the catch-all: the catch-all omits the field, while `route: ""` matches
58
+ * only the empty string and no composed route is empty, so the rule would decide nothing.
59
+ */
60
+ (options.selection.rules ?? []).forEach((rule, position) => {
61
+ for (const field of ["route", "method"]) {
62
+ const value = rule[field];
63
+ if (value !== undefined && value.trim() === "") {
64
+ failures.push(`selection.rules[${position}].${field} must not be blank; omit it to match every ${field}.`);
65
+ }
66
+ }
67
+ });
68
+ return failures;
69
+ }
70
+ /**
71
+ * @param options the configured options
72
+ * @throws SezzleeConfigurationError when any setting is unusable
73
+ */
74
+ export function validateSezzleeOptions(options) {
75
+ const failures = collectConfigurationFailures(options);
76
+ if (failures.length > 0) {
77
+ throw new SezzleeConfigurationError(failures);
78
+ }
79
+ }
@@ -0,0 +1,201 @@
1
+ import type { IncomingHttpHeaders } from "node:http";
2
+ import type { OAuthTokenVerifier } from "@modelcontextprotocol/express";
3
+ import type { RankerFailureMode, Recognizer, SelectionDefault, SelectionRule } from "@sezzlee/core";
4
+ import type { ArgumentRule, JsonValue } from "./decorators.js";
5
+ import type { CatalogSeverity } from "./discovery/diagnostics.js";
6
+ import type { TypeShapeBinderOptions } from "./discovery/type-shape.js";
7
+ import { ToolFamilyOptions } from "./families.js";
8
+ import type { SezzleeFileOptions } from "./files.js";
9
+ import type { OuterConnection } from "./outer-connection.js";
10
+ export interface OuterRequest {
11
+ readonly headers: IncomingHttpHeaders;
12
+ readonly protocol?: string;
13
+ readonly connection?: OuterConnection;
14
+ /**
15
+ * The MCP transport's verified token, when `resourceServer` is configured.
16
+ *
17
+ * Without it a value provider sees headers only and decodes the token itself; the C# side reads
18
+ * `HttpContext.User` and always has one. The two platforms are not symmetric here.
19
+ */
20
+ readonly auth?: VerifiedToken;
21
+ }
22
+ export interface VerifiedToken {
23
+ readonly clientId?: string;
24
+ readonly scopes?: readonly string[];
25
+ readonly resource?: string | URL;
26
+ readonly expiresAt?: number;
27
+ readonly extra?: Readonly<Record<string, unknown>>;
28
+ }
29
+ /** What a value provider sees. */
30
+ export interface McpCaller {
31
+ readonly subject?: string;
32
+ readonly clientId?: string;
33
+ readonly scopes: readonly string[];
34
+ readonly resource?: string;
35
+ readonly expiresAt?: Date;
36
+ claim(name: string): string | undefined;
37
+ header(name: string): string | undefined;
38
+ }
39
+ export declare function callerOf(outer: OuterRequest | undefined): McpCaller;
40
+ export type ArgumentValueProvider = (caller: McpCaller) => JsonValue | undefined | Promise<JsonValue | undefined>;
41
+ export interface CurationTarget {
42
+ readonly controller?: NewableFunction;
43
+ readonly handler?: string;
44
+ readonly method?: string;
45
+ /** Matched against the final descriptor route, after prefixes and versioning. */
46
+ readonly route?: string;
47
+ }
48
+ export interface CurationRule {
49
+ readonly target: CurationTarget;
50
+ readonly rules: Readonly<Record<string, ArgumentRule>>;
51
+ readonly sealed: boolean;
52
+ readonly specificity: number;
53
+ }
54
+ /**
55
+ * Central curation: rules for controllers the host cannot decorate, and rules that repeat.
56
+ *
57
+ * `seal` exists because plain "most specific wins" lets a method decorator defeat a tenancy rule.
58
+ * A sealed field cannot be overridden by any layer; attempting it is a build error.
59
+ */
60
+ export declare class ArgumentCurationOptions {
61
+ private readonly valueProviders;
62
+ private readonly declarations;
63
+ get providers(): ReadonlyMap<string, ArgumentValueProvider>;
64
+ get rules(): readonly CurationRule[];
65
+ provide(name: string, provider: ArgumentValueProvider): this;
66
+ curate(target: CurationTarget, rules: Readonly<Record<string, ArgumentRule>>): this;
67
+ everywhere(rules: Readonly<Record<string, ArgumentRule>>): this;
68
+ seal(target: CurationTarget, rules: Readonly<Record<string, ArgumentRule>>): this;
69
+ }
70
+ export type SyntheticHeaders = Record<string, string>;
71
+ export declare class IdentityForwardingOptions {
72
+ private readonly names;
73
+ projector?: (outer: OuterRequest, syntheticHeaders: SyntheticHeaders) => void;
74
+ get carriers(): ReadonlySet<string>;
75
+ forward(name: string): this;
76
+ clear(): this;
77
+ project(projector: (outer: OuterRequest, syntheticHeaders: SyntheticHeaders) => void): this;
78
+ }
79
+ export interface SyntheticRequestOptions {
80
+ host?: string;
81
+ scheme?: string;
82
+ accept?: string;
83
+ userAgent?: string;
84
+ }
85
+ export interface SezzleeCacheOptions {
86
+ lifetimeMs: number;
87
+ maxCallers: number;
88
+ }
89
+ /** What a per-endpoint budget or timeout override sees. */
90
+ export interface InvokeTarget {
91
+ readonly tool: string;
92
+ readonly method: string;
93
+ readonly route: string;
94
+ }
95
+ export interface SezzleeInvokeOptions {
96
+ /** The largest tool response, in UTF-8 bytes, that may reach the agent. */
97
+ maxResponseBytes: number;
98
+ /**
99
+ * How long an invocation waits for the backend, in whole milliseconds. Zero means no deadline.
100
+ *
101
+ * Values at or above 60000 are unreachable through a stock MCP client, whose own request timeout
102
+ * (`DEFAULT_REQUEST_TIMEOUT_MSEC` in the TypeScript client) cancels first.
103
+ */
104
+ timeoutMs: number;
105
+ maxResponseBytesFor?: (target: InvokeTarget) => number | undefined;
106
+ timeoutMsFor?: (target: InvokeTarget) => number | undefined;
107
+ /** Decoded `base64` file bytes one call may carry inline. */
108
+ maxInlineFileBytes: number;
109
+ /** The bytes one resolved `ref` file may carry. */
110
+ maxFileBytes: number;
111
+ }
112
+ export declare class ErrorMappingOptions {
113
+ private readonly items;
114
+ get recognizers(): readonly Recognizer[];
115
+ recognize(recognizer: Recognizer): this;
116
+ }
117
+ export interface SezzleeResourceServerOptions {
118
+ resource: URL;
119
+ authorizationServers: URL[];
120
+ scopesSupported?: string[];
121
+ resourceName?: string;
122
+ verifier: OAuthTokenVerifier;
123
+ mcpPath?: string;
124
+ }
125
+ /**
126
+ * Config-level selection, for routes the host cannot or will not decorate.
127
+ *
128
+ * `rules` sits below both attribute levels and above `default`; order carries no meaning, and
129
+ * equally specific rules that disagree are a build error rather than a silent first-match win.
130
+ */
131
+ export interface SezzleeSelectionOptions {
132
+ default: SelectionDefault;
133
+ rules?: readonly SelectionRule[];
134
+ }
135
+ /**
136
+ * How a named, object-typed query binding reaches the agent.
137
+ *
138
+ * `flatten` leaves today's binding alone; `group` publishes one object-valued
139
+ * argument the composer expands in the notation this SDK's backend parses. The
140
+ * default is `flatten` because switching rewrites the `inputSchema` of every
141
+ * affected tool and renames the namespace curation is keyed by.
142
+ */
143
+ export interface SezzleeQueryOptions {
144
+ grouping: "flatten" | "group";
145
+ }
146
+ export interface SezzleeNamingOptions {
147
+ prefixMode: "always" | "onCollision";
148
+ prefix?: (container: string) => string | undefined;
149
+ }
150
+ export interface SezzleeDiagnosticsOptions {
151
+ failOn?: CatalogSeverity;
152
+ readonly escalate: Set<string>;
153
+ readonly downgrade: Set<string>;
154
+ }
155
+ /**
156
+ * How `search_tools` treats a ranker bound through `extensionTokens.toolRanker`.
157
+ *
158
+ * @param rankerTimeoutMs the ranker's deadline in whole milliseconds; zero means none
159
+ * @param onRankerFailure `fallback` ranks with BM25 and logs, `error` answers
160
+ * `search_ranker_unavailable`
161
+ */
162
+ export interface SezzleeSearchOptions {
163
+ rankerTimeoutMs: number;
164
+ onRankerFailure: RankerFailureMode;
165
+ }
166
+ export type SezzleeVisibilityTier = "declarative" | "probe";
167
+ export interface SezzleeVisibilityOptions {
168
+ tier: SezzleeVisibilityTier;
169
+ onUnknown: "show" | "hide";
170
+ probeTopK: number;
171
+ probeConcurrency: number;
172
+ readonly probeValues: Map<string, string>;
173
+ }
174
+ export declare class SezzleeOptions {
175
+ readonly identity: IdentityForwardingOptions;
176
+ readonly synthetic: SyntheticRequestOptions;
177
+ readonly cache: SezzleeCacheOptions;
178
+ readonly errors: ErrorMappingOptions;
179
+ readonly selection: SezzleeSelectionOptions;
180
+ readonly query: SezzleeQueryOptions;
181
+ readonly naming: SezzleeNamingOptions;
182
+ readonly diagnostics: SezzleeDiagnosticsOptions;
183
+ readonly arguments: ArgumentCurationOptions;
184
+ readonly families: ToolFamilyOptions;
185
+ readonly visibility: SezzleeVisibilityOptions;
186
+ readonly invoke: SezzleeInvokeOptions;
187
+ readonly search: SezzleeSearchOptions;
188
+ /** Binding `files.resolver` is what makes `ref` appear in a file argument's schema. */
189
+ readonly files: SezzleeFileOptions;
190
+ /**
191
+ * Grouping labels for a container the host cannot decorate. It sits below a `@McpTool({ tags })`
192
+ * declaration and above the container-derived default, and like a declaration it replaces that
193
+ * default rather than adding to it.
194
+ */
195
+ tags?: (container: string) => readonly string[] | undefined;
196
+ /** Search vocabulary for a container the host cannot decorate, below a `@McpTool({ searchTerms })`. */
197
+ searchTerms?: (container: string) => readonly string[] | undefined;
198
+ schema?: TypeShapeBinderOptions;
199
+ resourceServer?: SezzleeResourceServerOptions;
200
+ }
201
+ export declare const SEZZLEE_OPTIONS = "SEZZLEE_OPTIONS";