@silkweave/nestjs 2.0.0 → 2.3.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
@@ -117,6 +117,7 @@ Exposes the decorated controller route as an MCP tool. Every option is optional.
117
117
  | `description` | `string` | `@ApiOperation` summary/description, else generated | Tool description |
118
118
  | `input` | `Record<string, z.ZodType>` | - | Zod raw-shape override merged over the reflected fields (per-field). The escape hatch for shapes reflection can't express - discriminated unions, custom validators, `@Transform` |
119
119
  | `pipes` | `'apply' \| 'skip'` | `'apply'` | Whether to run parameter-bound pipes (`@Param('id', ParseIntPipe)`) when re-binding |
120
+ | `result` | `'json' \| 'smart'` | `'smart'` | Default MCP result format - `'json'` returns compact JSON text (`jsonToolResult`); `'smart'` inlines small payloads and offloads large ones to an embedded resource (`smartToolResult`). A client that sends `_meta.disposition` on the call overrides it |
120
121
 
121
122
  ## How reflection works
122
123
 
@@ -174,15 +175,27 @@ Native NestJS `@UseGuards()` on `@Mcp` methods run before the handler is invoked
174
175
 
175
176
  **Headers over MCP.** The inbound tool-call request is surfaced as a stand-in `{ headers, url, params, query }` object built from the MCP SDK's `extra.requestInfo`, so a header-based guard - e.g. one reading `getRequest().headers['x-api-key']` - works unchanged. `ExecutionContext.getType()` is `'http'` whenever a request is available (and `'rpc'` for transports with none, e.g. MCP stdio). Caveats:
176
177
 
177
- - Only **headers** (and the request `url`) cross the MCP boundary - `getRequest().params`/`.query` are empty objects.
178
+ - **Headers**, the request `url`, and **URL path params** cross the MCP boundary. `getRequest().params` is populated from the route's reflected `@Param` fields (as raw strings, like Express), so a path-scoped guard - e.g. one reading `getRequest().params['id']` to fence a key to one resource - works the same over MCP as over REST. `getRequest().query` is still empty (no query string over MCP).
178
179
  - A guard that denies (returns `false` or throws) produces a clean MCP tool error (`ForbiddenException`), not an HTTP 500.
179
180
  - For OAuth 2.1 / bearer-token MCP auth, prefer `mcp({ auth })` and read the resolved identity from the silkweave context (`ctx.get('auth')`); the header stand-in is for custom request-reading guards.
180
181
 
182
+ **Global guards (opt-in).** App-global guards - registered via `app.useGlobalGuards(new X())` or `{ provide: APP_GUARD, useClass }` - do **not** run on tool calls by default. Opt them in by class with `globalGuards`:
183
+
184
+ ```ts
185
+ SilkweaveModule.forRoot({
186
+ silkweave: { name: 'app', description: 'My App', version: '1.0.0' },
187
+ adapters: [mcp({ basePath: '/mcp' })],
188
+ globalGuards: [ApiKeyGuard] // runs before each method/class @UseGuards; throttler etc. deliberately excluded
189
+ })
190
+ ```
191
+
192
+ The allow-list is explicit-by-class on purpose - a blanket "run every global" would also fire unrelated globals (e.g. a `ThrottlerGuard`, which assumes a writable response MCP doesn't provide). Listed guards run **before** the method/class `@UseGuards`, mirroring Nest's request pipeline. They see the same request stand-in as method guards: headers, `url`, and path `params` (populated from reflected `@Param` fields); only `query`/IP-derived logic won't apply over MCP.
193
+
181
194
  Controllers are normal Nest providers - inject services via the constructor as usual.
182
195
 
183
196
  ## What does *not* run
184
197
 
185
- Because the handler is invoked directly (not through Nest's HTTP request pipeline), the following do **not** apply on a tool call - only `@UseGuards()` and parameter-bound pipes do:
198
+ Because the handler is invoked directly (not through Nest's HTTP request pipeline), the following do **not** apply on a tool call - only `@UseGuards()` (plus any opted-in `globalGuards`) and parameter-bound pipes do:
186
199
 
187
200
  - Globally-registered `ValidationPipe` / interceptors / exception filters (MCP input is instead validated against the reflected Zod schema).
188
201
  - DTO class instantiation - whole-DTO `@Body()`/`@Query()` arguments arrive as plain objects, so `@Transform` and DTO methods do not fire.
package/build/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { CanActivate, DynamicModule, MiddlewareConsumer, NestModule, Type } from "@nestjs/common";
2
- import { DiscoveryService, HttpAdapterHost, MetadataScanner, ModuleRef, Reflector } from "@nestjs/core";
2
+ import { ApplicationConfig, DiscoveryService, HttpAdapterHost, MetadataScanner, ModuleRef, Reflector } from "@nestjs/core";
3
3
  import { Action, SilkweaveContext, SilkweaveOptions } from "@silkweave/core";
4
4
  import { z } from "zod/v4";
5
5
  import { AuthConfig } from "@silkweave/auth";
@@ -137,6 +137,19 @@ interface SilkweaveModuleOptions {
137
137
  * reflection when an operation or field isn't present.
138
138
  */
139
139
  openapi?: OpenApiDocument;
140
+ /**
141
+ * Opt-in allow-list of app-global guard classes (registered via
142
+ * `app.useGlobalGuards()` or `{ provide: APP_GUARD, useClass }`) to run on
143
+ * every MCP tool call, before each method/class `@UseGuards`. Listed by
144
+ * class - a blanket "run all globals" is deliberately not offered, since
145
+ * unrelated globals (e.g. a `ThrottlerGuard` that needs a writable response)
146
+ * would misbehave over MCP. Empty/omitted ⇒ no global guards run.
147
+ *
148
+ * Note: over MCP the request stand-in is headers-only (`params`/`query` are
149
+ * empty), so per-session or IP-derived guard logic won't apply; header-based
150
+ * authentication still works.
151
+ */
152
+ globalGuards?: Type<CanActivate>[];
140
153
  }
141
154
  declare const SILKWEAVE_MODULE_OPTIONS = "__silkweave_module_options__";
142
155
  //#endregion
@@ -204,6 +217,14 @@ interface McpMetadata {
204
217
  * the method is invoked directly, not through Nest's HTTP request pipeline.
205
218
  */
206
219
  pipes?: 'apply' | 'skip';
220
+ /**
221
+ * Default MCP result format for this tool. `'json'` returns compact JSON text
222
+ * (`jsonToolResult`); `'smart'` (the default when unset) inlines small
223
+ * payloads and offloads large ones to an embedded resource (`smartToolResult`).
224
+ * This is only a default - a client that sends `_meta.disposition` on the tool
225
+ * call overrides it.
226
+ */
227
+ result?: 'json' | 'smart';
207
228
  }
208
229
  //#endregion
209
230
  //#region src/decorator/mcp.d.ts
@@ -248,15 +269,17 @@ declare class ControllerDiscovery {
248
269
  private readonly scanner;
249
270
  private readonly reflector;
250
271
  private readonly moduleRef;
251
- constructor(discovery: DiscoveryService, scanner: MetadataScanner, reflector: Reflector, moduleRef: ModuleRef);
272
+ private readonly appConfig;
273
+ constructor(discovery: DiscoveryService, scanner: MetadataScanner, reflector: Reflector, moduleRef: ModuleRef, appConfig: ApplicationConfig);
252
274
  /**
253
275
  * Walk every Nest provider/controller, find methods annotated with `@Mcp`,
254
276
  * and build a core `Action` per method whose input schema is reflected from
255
277
  * the route + parameter decorators (+ optional OpenAPI document) and whose
256
278
  * `run` re-binds the validated input back into the method's positional
257
- * arguments (with `@UseGuards` guards applied first).
279
+ * arguments (with `@UseGuards` guards - and any opted-in `globalGuards` -
280
+ * applied first).
258
281
  */
259
- discover(openapi?: OpenApiDocument): Action[];
282
+ discover(openapi?: OpenApiDocument, globalGuards?: Type<CanActivate>[]): Action[];
260
283
  private toAction;
261
284
  /** Build the merged Zod input shape and the per-argument re-bind plan. */
262
285
  private buildInput;
@@ -264,6 +287,23 @@ declare class ControllerDiscovery {
264
287
  //#endregion
265
288
  //#region src/lib/guards.d.ts
266
289
  type GuardRef = Type<CanActivate> | CanActivate;
290
+ /**
291
+ * Collect the app's global guards that match an opt-in allow-list of classes.
292
+ *
293
+ * Reads both registration styles Nest exposes via `ApplicationConfig`:
294
+ * `useGlobalGuards(new X())` instances (`getGlobalGuards()`) and
295
+ * `{ provide: APP_GUARD, useClass }` DI guards (`getGlobalRequestGuards()`,
296
+ * which yields `InstanceWrapper`s - we read `.instance` off each).
297
+ *
298
+ * The allow-list is intentionally explicit-by-class: a blanket "run every
299
+ * global" would also fire unrelated globals (e.g. a `ThrottlerGuard` that
300
+ * assumes a writable response) on every tool call. An empty allow-list runs
301
+ * no globals, preserving the prior behavior.
302
+ *
303
+ * Call this at tool-call time, not at discovery time: `APP_GUARD` instances
304
+ * aren't populated until `app.init()` finishes.
305
+ */
306
+ declare function collectGlobalGuards(appConfig: ApplicationConfig, allowList: Type<CanActivate>[]): CanActivate[];
267
307
  /**
268
308
  * Read `@UseGuards(...)` metadata for both the method and its class and merge
269
309
  * the two lists. Method-level guards run AFTER class-level guards (matching
@@ -372,5 +412,5 @@ declare class SilkweaveModule implements NestModule {
372
412
  configure(_consumer: MiddlewareConsumer): void;
373
413
  }
374
414
  //#endregion
375
- export { Binding, ControllerDiscovery, FieldDesc, MCP_METADATA, Mcp, McpAdapterOptions, McpMetadata, NestAdapterRegisterContext, NestSilkweaveAdapter, OpenApiDocument, OpenApiLookup, SILKWEAVE_MODULE_OPTIONS, SilkweaveModule, SilkweaveModuleOptions, apiPropertyToField, buildOpenApiLookup, classValidatorToField, collectGuards, designTypeToField, fieldToZod, invokeRebound, mcp, mergeField, normalizeEnum, openApiFields, openapiSchemaToField, reflectDtoFields, runGuards, specialBinding, swaggerParamToField, typeTokenToBase };
415
+ export { Binding, ControllerDiscovery, FieldDesc, MCP_METADATA, Mcp, McpAdapterOptions, McpMetadata, NestAdapterRegisterContext, NestSilkweaveAdapter, OpenApiDocument, OpenApiLookup, SILKWEAVE_MODULE_OPTIONS, SilkweaveModule, SilkweaveModuleOptions, apiPropertyToField, buildOpenApiLookup, classValidatorToField, collectGlobalGuards, collectGuards, designTypeToField, fieldToZod, invokeRebound, mcp, mergeField, normalizeEnum, openApiFields, openapiSchemaToField, reflectDtoFields, runGuards, specialBinding, swaggerParamToField, typeTokenToBase };
376
416
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/lib/reflect/schema.ts","../src/lib/reflect/openapi.ts","../src/lib/types.ts","../src/adapter/mcp.ts","../src/lib/metadata.ts","../src/decorator/mcp.ts","../src/lib/controllerDiscovery.ts","../src/lib/guards.ts","../src/lib/rebind.ts","../src/lib/silkweave.module.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/lib/reflect/schema.ts","../src/lib/reflect/openapi.ts","../src/lib/types.ts","../src/adapter/mcp.ts","../src/lib/metadata.ts","../src/decorator/mcp.ts","../src/lib/controllerDiscovery.ts","../src/lib/guards.ts","../src/lib/rebind.ts","../src/lib/silkweave.module.ts"],"mappings":"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"}
package/build/index.mjs CHANGED
@@ -2,7 +2,7 @@ import { createRequire } from "node:module";
2
2
  import { authMiddleware, mcpCors, mcpTransport, oauthRoutes, protectedResourceMetadata, sideloadResource } from "@silkweave/mcp";
3
3
  import express from "express";
4
4
  import { ForbiddenException, Inject, Injectable, Module, SetMetadata } from "@nestjs/common";
5
- import { DiscoveryModule, DiscoveryService, HttpAdapterHost, MetadataScanner, ModuleRef, Reflector } from "@nestjs/core";
5
+ import { ApplicationConfig, DiscoveryModule, DiscoveryService, HttpAdapterHost, MetadataScanner, ModuleRef, Reflector } from "@nestjs/core";
6
6
  import { createAction, createContext } from "@silkweave/core";
7
7
  import { z } from "zod/v4";
8
8
  import { GUARDS_METADATA, METHOD_METADATA, PATH_METADATA, ROUTE_ARGS_METADATA } from "@nestjs/common/constants.js";
@@ -8761,6 +8761,26 @@ var SilkweaveExecutionContext = class {
8761
8761
  //#endregion
8762
8762
  //#region src/lib/guards.ts
8763
8763
  /**
8764
+ * Collect the app's global guards that match an opt-in allow-list of classes.
8765
+ *
8766
+ * Reads both registration styles Nest exposes via `ApplicationConfig`:
8767
+ * `useGlobalGuards(new X())` instances (`getGlobalGuards()`) and
8768
+ * `{ provide: APP_GUARD, useClass }` DI guards (`getGlobalRequestGuards()`,
8769
+ * which yields `InstanceWrapper`s - we read `.instance` off each).
8770
+ *
8771
+ * The allow-list is intentionally explicit-by-class: a blanket "run every
8772
+ * global" would also fire unrelated globals (e.g. a `ThrottlerGuard` that
8773
+ * assumes a writable response) on every tool call. An empty allow-list runs
8774
+ * no globals, preserving the prior behavior.
8775
+ *
8776
+ * Call this at tool-call time, not at discovery time: `APP_GUARD` instances
8777
+ * aren't populated until `app.init()` finishes.
8778
+ */
8779
+ function collectGlobalGuards(appConfig, allowList) {
8780
+ if (allowList.length === 0) return [];
8781
+ return [...appConfig.getGlobalGuards(), ...appConfig.getGlobalRequestGuards().map((w) => w.instance)].filter((g) => g != null).filter((g) => allowList.some((c) => g instanceof c));
8782
+ }
8783
+ /**
8764
8784
  * Read `@UseGuards(...)` metadata for both the method and its class and merge
8765
8785
  * the two lists. Method-level guards run AFTER class-level guards (matching
8766
8786
  * Nest's own behavior).
@@ -9340,22 +9360,24 @@ function __decorate(decorators, target, key, desc) {
9340
9360
  }
9341
9361
  //#endregion
9342
9362
  //#region src/lib/controllerDiscovery.ts
9343
- var _ref$1, _ref2$1, _ref3, _ref4;
9363
+ var _ref$1, _ref2$1, _ref3, _ref4, _ref5;
9344
9364
  let ControllerDiscovery = class ControllerDiscovery {
9345
- constructor(discovery, scanner, reflector, moduleRef) {
9365
+ constructor(discovery, scanner, reflector, moduleRef, appConfig) {
9346
9366
  this.discovery = discovery;
9347
9367
  this.scanner = scanner;
9348
9368
  this.reflector = reflector;
9349
9369
  this.moduleRef = moduleRef;
9370
+ this.appConfig = appConfig;
9350
9371
  }
9351
9372
  /**
9352
9373
  * Walk every Nest provider/controller, find methods annotated with `@Mcp`,
9353
9374
  * and build a core `Action` per method whose input schema is reflected from
9354
9375
  * the route + parameter decorators (+ optional OpenAPI document) and whose
9355
9376
  * `run` re-binds the validated input back into the method's positional
9356
- * arguments (with `@UseGuards` guards applied first).
9377
+ * arguments (with `@UseGuards` guards - and any opted-in `globalGuards` -
9378
+ * applied first).
9357
9379
  */
9358
- discover(openapi) {
9380
+ discover(openapi, globalGuards = []) {
9359
9381
  const lookup = openapi ? buildOpenApiLookup(openapi) : void 0;
9360
9382
  const discovered = [];
9361
9383
  for (const wrapper of this.discovery.getProviders().concat(this.discovery.getControllers())) {
@@ -9378,9 +9400,9 @@ let ControllerDiscovery = class ControllerDiscovery {
9378
9400
  });
9379
9401
  }
9380
9402
  }
9381
- return discovered.map((d) => this.toAction(d, lookup));
9403
+ return discovered.map((d) => this.toAction(d, lookup, globalGuards));
9382
9404
  }
9383
- toAction(d, lookup) {
9405
+ toAction(d, lookup, globalGuards) {
9384
9406
  const proto = Object.getPrototypeOf(d.instance);
9385
9407
  const route = reflectRoute(d.classRef, d.method);
9386
9408
  const slots = readParamSlots(d.classRef, d.methodName, proto);
@@ -9392,28 +9414,33 @@ let ControllerDiscovery = class ControllerDiscovery {
9392
9414
  const description = d.meta.description ?? operation.description ?? `${d.methodName} (${route.method} /${route.path})`;
9393
9415
  const applyParamPipes = d.meta.pipes !== "skip";
9394
9416
  const guards = collectGuards(this.reflector, d.classRef, d.method);
9395
- const { moduleRef, reflector } = this;
9417
+ const pathFields = pathParamFields(bindings);
9418
+ const { moduleRef, reflector, appConfig } = this;
9396
9419
  const classRef = d.classRef;
9397
9420
  const method = d.method;
9398
9421
  const instance = d.instance;
9399
- const applyGuards = async (context) => {
9400
- if (guards.length === 0) return;
9422
+ const applyGuards = async (context, input) => {
9423
+ const all = [...collectGlobalGuards(appConfig, globalGuards), ...guards];
9424
+ if (all.length === 0) return;
9401
9425
  const request = context.getOptional("request");
9402
9426
  const response = context.getOptional("response") ?? null;
9403
9427
  const hasRequest = request != null;
9404
- await runGuards(guards, moduleRef, reflector, classRef, method, hasRequest ? request : {
9428
+ const guardRequest = hasRequest ? request : {
9405
9429
  headers: {},
9406
9430
  params: {},
9407
9431
  query: {}
9408
- }, response, hasRequest ? "http" : "rpc");
9432
+ };
9433
+ populatePathParams(guardRequest, pathFields, input);
9434
+ await runGuards(all, moduleRef, reflector, classRef, method, guardRequest, response, hasRequest ? "http" : "rpc");
9409
9435
  };
9410
9436
  return createAction({
9411
9437
  name,
9412
9438
  description,
9413
9439
  input: z.object(shape),
9440
+ ...d.meta.result ? { disposition: d.meta.result } : {},
9414
9441
  isEnabled: (ctx) => ctx.getOptional("adapter") === "mcp",
9415
9442
  run: async (input, context) => {
9416
- await applyGuards(context);
9443
+ await applyGuards(context, input);
9417
9444
  return await invokeRebound(method, instance, input, bindings, context.getOptional("request"), applyParamPipes) ?? {};
9418
9445
  }
9419
9446
  });
@@ -9448,8 +9475,26 @@ ControllerDiscovery = __decorate([Injectable(), __decorateMetadata("design:param
9448
9475
  typeof (_ref$1 = typeof DiscoveryService !== "undefined" && DiscoveryService) === "function" ? _ref$1 : Object,
9449
9476
  typeof (_ref2$1 = typeof MetadataScanner !== "undefined" && MetadataScanner) === "function" ? _ref2$1 : Object,
9450
9477
  typeof (_ref3 = typeof Reflector !== "undefined" && Reflector) === "function" ? _ref3 : Object,
9451
- typeof (_ref4 = typeof ModuleRef !== "undefined" && ModuleRef) === "function" ? _ref4 : Object
9478
+ typeof (_ref4 = typeof ModuleRef !== "undefined" && ModuleRef) === "function" ? _ref4 : Object,
9479
+ typeof (_ref5 = typeof ApplicationConfig !== "undefined" && ApplicationConfig) === "function" ? _ref5 : Object
9452
9480
  ])], ControllerDiscovery);
9481
+ /** Input field names that originate from the URL path (`@Param`), per the re-bind plan. */
9482
+ function pathParamFields(bindings) {
9483
+ const fields = [];
9484
+ for (const b of bindings) if (b.kind === "params") fields.push(...b.fields);
9485
+ else if (b.kind === "value" && b.source === "path") fields.push(b.field);
9486
+ return fields;
9487
+ }
9488
+ /**
9489
+ * Fill `request.params` with the reflected path fields from the validated input,
9490
+ * matching what Express would populate over REST (raw string values). Only adds
9491
+ * keys that are absent, so a real REST request's params are never overwritten.
9492
+ */
9493
+ function populatePathParams(request, pathFields, input) {
9494
+ if (pathFields.length === 0 || typeof request !== "object" || request === null) return;
9495
+ const params = request.params ??= {};
9496
+ for (const field of pathFields) if (!(field in params) && input[field] !== void 0) params[field] = String(input[field]);
9497
+ }
9453
9498
  function designTypeAt(designTypes, index) {
9454
9499
  const ctor = designTypes[index];
9455
9500
  if (ctor === String) return { type: "string" };
@@ -9555,7 +9600,7 @@ let SilkweaveModule = _SilkweaveModule = class SilkweaveModule {
9555
9600
  configure(_consumer) {
9556
9601
  const httpAdapter = this.httpAdapterHost.httpAdapter;
9557
9602
  if (!httpAdapter) throw new Error("@silkweave/nestjs: HttpAdapterHost.httpAdapter is not available.");
9558
- const allActions = this.discovery.discover(this.options.openapi);
9603
+ const allActions = this.discovery.discover(this.options.openapi, this.options.globalGuards);
9559
9604
  for (const adapter of this.options.adapters) {
9560
9605
  const baseContext = createContext({
9561
9606
  ...this.options.context ?? {},
@@ -9581,6 +9626,6 @@ SilkweaveModule = _SilkweaveModule = __decorate([
9581
9626
  ])
9582
9627
  ], SilkweaveModule);
9583
9628
  //#endregion
9584
- export { ControllerDiscovery, MCP_METADATA, Mcp, SILKWEAVE_MODULE_OPTIONS, SilkweaveModule, apiPropertyToField, buildOpenApiLookup, classValidatorToField, collectGuards, designTypeToField, fieldToZod, invokeRebound, mcp, mergeField, normalizeEnum, openApiFields, openapiSchemaToField, reflectDtoFields, runGuards, specialBinding, swaggerParamToField, typeTokenToBase };
9629
+ export { ControllerDiscovery, MCP_METADATA, Mcp, SILKWEAVE_MODULE_OPTIONS, SilkweaveModule, apiPropertyToField, buildOpenApiLookup, classValidatorToField, collectGlobalGuards, collectGuards, designTypeToField, fieldToZod, invokeRebound, mcp, mergeField, normalizeEnum, openApiFields, openapiSchemaToField, reflectDtoFields, runGuards, specialBinding, swaggerParamToField, typeTokenToBase };
9585
9630
 
9586
9631
  //# sourceMappingURL=index.mjs.map