@silkweave/nestjs 2.0.0 → 2.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.
package/README.md CHANGED
@@ -174,15 +174,27 @@ Native NestJS `@UseGuards()` on `@Mcp` methods run before the handler is invoked
174
174
 
175
175
  **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
176
 
177
- - Only **headers** (and the request `url`) cross the MCP boundary - `getRequest().params`/`.query` are empty objects.
177
+ - **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
178
  - A guard that denies (returns `false` or throws) produces a clean MCP tool error (`ForbiddenException`), not an HTTP 500.
179
179
  - 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
180
 
181
+ **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`:
182
+
183
+ ```ts
184
+ SilkweaveModule.forRoot({
185
+ silkweave: { name: 'app', description: 'My App', version: '1.0.0' },
186
+ adapters: [mcp({ basePath: '/mcp' })],
187
+ globalGuards: [ApiKeyGuard] // runs before each method/class @UseGuards; throttler etc. deliberately excluded
188
+ })
189
+ ```
190
+
191
+ 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.
192
+
181
193
  Controllers are normal Nest providers - inject services via the constructor as usual.
182
194
 
183
195
  ## What does *not* run
184
196
 
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:
197
+ 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
198
 
187
199
  - Globally-registered `ValidationPipe` / interceptors / exception filters (MCP input is instead validated against the reflected Zod schema).
188
200
  - 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
@@ -248,15 +261,17 @@ declare class ControllerDiscovery {
248
261
  private readonly scanner;
249
262
  private readonly reflector;
250
263
  private readonly moduleRef;
251
- constructor(discovery: DiscoveryService, scanner: MetadataScanner, reflector: Reflector, moduleRef: ModuleRef);
264
+ private readonly appConfig;
265
+ constructor(discovery: DiscoveryService, scanner: MetadataScanner, reflector: Reflector, moduleRef: ModuleRef, appConfig: ApplicationConfig);
252
266
  /**
253
267
  * Walk every Nest provider/controller, find methods annotated with `@Mcp`,
254
268
  * and build a core `Action` per method whose input schema is reflected from
255
269
  * the route + parameter decorators (+ optional OpenAPI document) and whose
256
270
  * `run` re-binds the validated input back into the method's positional
257
- * arguments (with `@UseGuards` guards applied first).
271
+ * arguments (with `@UseGuards` guards - and any opted-in `globalGuards` -
272
+ * applied first).
258
273
  */
259
- discover(openapi?: OpenApiDocument): Action[];
274
+ discover(openapi?: OpenApiDocument, globalGuards?: Type<CanActivate>[]): Action[];
260
275
  private toAction;
261
276
  /** Build the merged Zod input shape and the per-argument re-bind plan. */
262
277
  private buildInput;
@@ -264,6 +279,23 @@ declare class ControllerDiscovery {
264
279
  //#endregion
265
280
  //#region src/lib/guards.d.ts
266
281
  type GuardRef = Type<CanActivate> | CanActivate;
282
+ /**
283
+ * Collect the app's global guards that match an opt-in allow-list of classes.
284
+ *
285
+ * Reads both registration styles Nest exposes via `ApplicationConfig`:
286
+ * `useGlobalGuards(new X())` instances (`getGlobalGuards()`) and
287
+ * `{ provide: APP_GUARD, useClass }` DI guards (`getGlobalRequestGuards()`,
288
+ * which yields `InstanceWrapper`s - we read `.instance` off each).
289
+ *
290
+ * The allow-list is intentionally explicit-by-class: a blanket "run every
291
+ * global" would also fire unrelated globals (e.g. a `ThrottlerGuard` that
292
+ * assumes a writable response) on every tool call. An empty allow-list runs
293
+ * no globals, preserving the prior behavior.
294
+ *
295
+ * Call this at tool-call time, not at discovery time: `APP_GUARD` instances
296
+ * aren't populated until `app.init()` finishes.
297
+ */
298
+ declare function collectGlobalGuards(appConfig: ApplicationConfig, allowList: Type<CanActivate>[]): CanActivate[];
267
299
  /**
268
300
  * Read `@UseGuards(...)` metadata for both the method and its class and merge
269
301
  * the two lists. Method-level guards run AFTER class-level guards (matching
@@ -372,5 +404,5 @@ declare class SilkweaveModule implements NestModule {
372
404
  configure(_consumer: MiddlewareConsumer): void;
373
405
  }
374
406
  //#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 };
407
+ 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
408
  //# 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,20 +9414,24 @@ 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,
@@ -9413,7 +9439,7 @@ let ControllerDiscovery = class ControllerDiscovery {
9413
9439
  input: z.object(shape),
9414
9440
  isEnabled: (ctx) => ctx.getOptional("adapter") === "mcp",
9415
9441
  run: async (input, context) => {
9416
- await applyGuards(context);
9442
+ await applyGuards(context, input);
9417
9443
  return await invokeRebound(method, instance, input, bindings, context.getOptional("request"), applyParamPipes) ?? {};
9418
9444
  }
9419
9445
  });
@@ -9448,8 +9474,26 @@ ControllerDiscovery = __decorate([Injectable(), __decorateMetadata("design:param
9448
9474
  typeof (_ref$1 = typeof DiscoveryService !== "undefined" && DiscoveryService) === "function" ? _ref$1 : Object,
9449
9475
  typeof (_ref2$1 = typeof MetadataScanner !== "undefined" && MetadataScanner) === "function" ? _ref2$1 : Object,
9450
9476
  typeof (_ref3 = typeof Reflector !== "undefined" && Reflector) === "function" ? _ref3 : Object,
9451
- typeof (_ref4 = typeof ModuleRef !== "undefined" && ModuleRef) === "function" ? _ref4 : Object
9477
+ typeof (_ref4 = typeof ModuleRef !== "undefined" && ModuleRef) === "function" ? _ref4 : Object,
9478
+ typeof (_ref5 = typeof ApplicationConfig !== "undefined" && ApplicationConfig) === "function" ? _ref5 : Object
9452
9479
  ])], ControllerDiscovery);
9480
+ /** Input field names that originate from the URL path (`@Param`), per the re-bind plan. */
9481
+ function pathParamFields(bindings) {
9482
+ const fields = [];
9483
+ for (const b of bindings) if (b.kind === "params") fields.push(...b.fields);
9484
+ else if (b.kind === "value" && b.source === "path") fields.push(b.field);
9485
+ return fields;
9486
+ }
9487
+ /**
9488
+ * Fill `request.params` with the reflected path fields from the validated input,
9489
+ * matching what Express would populate over REST (raw string values). Only adds
9490
+ * keys that are absent, so a real REST request's params are never overwritten.
9491
+ */
9492
+ function populatePathParams(request, pathFields, input) {
9493
+ if (pathFields.length === 0 || typeof request !== "object" || request === null) return;
9494
+ const params = request.params ??= {};
9495
+ for (const field of pathFields) if (!(field in params) && input[field] !== void 0) params[field] = String(input[field]);
9496
+ }
9453
9497
  function designTypeAt(designTypes, index) {
9454
9498
  const ctor = designTypes[index];
9455
9499
  if (ctor === String) return { type: "string" };
@@ -9555,7 +9599,7 @@ let SilkweaveModule = _SilkweaveModule = class SilkweaveModule {
9555
9599
  configure(_consumer) {
9556
9600
  const httpAdapter = this.httpAdapterHost.httpAdapter;
9557
9601
  if (!httpAdapter) throw new Error("@silkweave/nestjs: HttpAdapterHost.httpAdapter is not available.");
9558
- const allActions = this.discovery.discover(this.options.openapi);
9602
+ const allActions = this.discovery.discover(this.options.openapi, this.options.globalGuards);
9559
9603
  for (const adapter of this.options.adapters) {
9560
9604
  const baseContext = createContext({
9561
9605
  ...this.options.context ?? {},
@@ -9581,6 +9625,6 @@ SilkweaveModule = _SilkweaveModule = __decorate([
9581
9625
  ])
9582
9626
  ], SilkweaveModule);
9583
9627
  //#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 };
9628
+ 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
9629
 
9586
9630
  //# sourceMappingURL=index.mjs.map