@breadstone/archipel-mcp 0.0.41 → 0.0.42

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 (74) hide show
  1. package/data/guides/implementing-ports.md +2 -0
  2. package/data/guides/payments-and-feature-gating.md +76 -45
  3. package/data/packages/platform-bootstrap/api/index.md +0 -2
  4. package/data/packages/platform-core/api/Class.ProblemDetailResponse.md +8 -8
  5. package/data/packages/platform-database/api/Class.DatabaseHealthIndicator.md +4 -4
  6. package/data/packages/platform-database/api/Class.DatabaseModule.md +3 -3
  7. package/data/packages/platform-database/api/Class.DatabaseService.md +12 -11
  8. package/data/packages/platform-database/api/Class.PrismaService.md +6 -5
  9. package/data/packages/platform-database/api/Class.RepositoryBase.md +23 -23
  10. package/data/packages/platform-database/api/Class.RepositoryBase2.md +7 -7
  11. package/data/packages/platform-database/api/Class.RepositoryExceptionFilter.md +2 -2
  12. package/data/packages/platform-database/api/Function.aggregate.md +1 -1
  13. package/data/packages/platform-database/api/Function.aggregateRaw.md +1 -1
  14. package/data/packages/platform-database/api/Function.count.md +1 -1
  15. package/data/packages/platform-database/api/Function.create.md +1 -1
  16. package/data/packages/platform-database/api/Function.createMany.md +1 -1
  17. package/data/packages/platform-database/api/Function.createManyAndReturn.md +1 -1
  18. package/data/packages/platform-database/api/Function.cursorPaginator.md +1 -1
  19. package/data/packages/platform-database/api/Function.findFirst.md +1 -1
  20. package/data/packages/platform-database/api/Function.findFirstOrThrow.md +1 -1
  21. package/data/packages/platform-database/api/Function.findMany.md +1 -1
  22. package/data/packages/platform-database/api/Function.findManyPaged.md +1 -1
  23. package/data/packages/platform-database/api/Function.findRaw.md +1 -1
  24. package/data/packages/platform-database/api/Function.findUnique.md +1 -1
  25. package/data/packages/platform-database/api/Function.findUniqueOrThrow.md +1 -1
  26. package/data/packages/platform-database/api/Function.groupBy.md +1 -1
  27. package/data/packages/platform-database/api/Function.pageable.md +1 -1
  28. package/data/packages/platform-database/api/Function.paginator.md +1 -1
  29. package/data/packages/platform-database/api/Function.pipe.md +1 -1
  30. package/data/packages/platform-database/api/Function.pipeAll.md +1 -1
  31. package/data/packages/platform-database/api/Function.query.md +1 -1
  32. package/data/packages/platform-database/api/Function.query2.md +1 -1
  33. package/data/packages/platform-database/api/Function.remove.md +1 -1
  34. package/data/packages/platform-database/api/Function.removeMany.md +1 -1
  35. package/data/packages/platform-database/api/Function.skip.md +1 -1
  36. package/data/packages/platform-database/api/Function.transactionalQuery.md +1 -1
  37. package/data/packages/platform-database/api/Function.update.md +1 -1
  38. package/data/packages/platform-database/api/Function.updateMany.md +1 -1
  39. package/data/packages/platform-database/api/Function.updateManyAndReturn.md +1 -1
  40. package/data/packages/platform-database/api/Function.upsert.md +1 -1
  41. package/data/packages/platform-database/api/Interface.ICursorPaginateOptions.md +4 -4
  42. package/data/packages/platform-database/api/Interface.ICursorPaginatedResult.md +3 -3
  43. package/data/packages/platform-database/api/Interface.IDatabaseModuleConfig.md +3 -3
  44. package/data/packages/platform-database/api/Interface.IPaginateOptions.md +3 -3
  45. package/data/packages/platform-database/api/Interface.IPaginatedResult.md +3 -3
  46. package/data/packages/platform-database/api/Interface.IPrismaServiceOptions.md +2 -2
  47. package/data/packages/platform-database/api/Interface.IRepositoryQuery.md +3 -3
  48. package/data/packages/platform-database/api/Interface.IRepositoryQuery2.md +3 -3
  49. package/data/packages/platform-database/api/Interface.ITransactionalRepositoryQuery.md +4 -4
  50. package/data/packages/platform-database/api/TypeAlias.CursorPaginateFunction.md +1 -1
  51. package/data/packages/platform-database/api/TypeAlias.DelegateArgs.md +1 -1
  52. package/data/packages/platform-database/api/TypeAlias.DelegateOperations.md +1 -1
  53. package/data/packages/platform-database/api/TypeAlias.DelegateReturnTypes.md +1 -1
  54. package/data/packages/platform-database/api/TypeAlias.PaginateFunction.md +1 -1
  55. package/data/packages/platform-database/api/TypeAlias.QueryResultType.md +1 -1
  56. package/data/packages/platform-database/api/TypeAlias.RepositoryOperationsAll.md +1 -1
  57. package/data/packages/platform-database/api/TypeAlias.RepositoryOperationsBase.md +1 -1
  58. package/data/packages/platform-database/api/TypeAlias.TransactionalQueryResultType.md +1 -1
  59. package/data/packages/platform-database/api/Variable.DATABASE_MODULE_CONFIG.md +1 -1
  60. package/data/packages/platform-database/api/Variable.DB_AUTO_CONNECTION.md +1 -1
  61. package/data/packages/platform-database/api/Variable.PLATFORM_DATABASE_CONFIG_ENTRIES.md +1 -1
  62. package/data/packages/platform-payments/api/Class.FeatureAccessPort.md +13 -5
  63. package/data/packages/platform-payments/api/Class.FeatureGuard.md +3 -3
  64. package/data/packages/platform-payments/api/Class.FeatureUsageInterceptor.md +3 -3
  65. package/data/packages/platform-payments/api/Function.RequiresFeature.md +1 -1
  66. package/data/packages/platform-payments/api/Interface.IFeatureAccessContext.md +46 -0
  67. package/data/packages/platform-payments/api/Variable.FEATURE_KEY_METADATA.md +1 -1
  68. package/data/packages/platform-payments/api/index.md +1 -0
  69. package/data/packages/platform-payments/index.md +33 -12
  70. package/data/patterns/guard-pattern.md +27 -6
  71. package/data/patterns/module-pattern.md +6 -2
  72. package/package.json +1 -1
  73. package/data/packages/platform-bootstrap/api/Function.useExpressServer.md +0 -32
  74. package/data/packages/platform-bootstrap/api/Interface.IExpressServerStepOptions.md +0 -46
@@ -314,15 +314,30 @@ type SubscriptionStatus =
314
314
 
315
315
  ## FeatureAccessPort
316
316
 
317
- Abstract port for checking feature quotas and recording usage. This enables feature-gating with any billing system.
317
+ Abstract port for checking feature quotas and recording usage. This enables feature-gating with any billing system and optional request-scoped tenancy context.
318
318
 
319
319
  ```typescript
320
320
  abstract class FeatureAccessPort {
321
- abstract checkAccess(userId: string, featureKey: string): Promise<IFeatureAccessResult>;
322
- abstract recordUsage(userId: string, featureKey: string): Promise<void>;
321
+ abstract checkAccess(
322
+ userId: string,
323
+ featureKey: string,
324
+ context?: IFeatureAccessContext,
325
+ ): Promise<IFeatureAccessResult>;
326
+
327
+ abstract recordUsage(userId: string, featureKey: string, context?: IFeatureAccessContext): Promise<void>;
323
328
  }
324
329
  ```
325
330
 
331
+ `FeatureGuard` and `FeatureUsageInterceptor` create `IFeatureAccessContext` from the authenticated request subject. The guard resolves `request.user.id`, converts numeric ids to strings, forwards `request.user.tenantId` when present, and includes the original subject for adapters that need additional claims.
332
+
333
+ ### IFeatureAccessContext
334
+
335
+ | Field | Type | Description |
336
+ | ---------- | -------------------- | ---------------------------------------------------------- |
337
+ | `userId` | `string` | Authenticated user identifier resolved from `request.user` |
338
+ | `tenantId` | `string \| number` | Optional tenant identifier from the authenticated subject |
339
+ | `subject` | `unknown` | Original authenticated subject from the request |
340
+
326
341
  ### IFeatureAccessResult
327
342
 
328
343
  | Field | Type | Description |
@@ -345,9 +360,13 @@ export class PrismaFeatureAccessAdapter extends FeatureAccessPort {
345
360
  this._prisma = prisma;
346
361
  }
347
362
 
348
- public async checkAccess(userId: string, featureKey: string): Promise<IFeatureAccessResult> {
363
+ public async checkAccess(
364
+ userId: string,
365
+ featureKey: string,
366
+ context?: IFeatureAccessContext,
367
+ ): Promise<IFeatureAccessResult> {
349
368
  const subscription = await this._prisma.subscription.findFirst({
350
- where: { userId, status: 'active' },
369
+ where: { userId, tenantId: context?.tenantId, status: 'active' },
351
370
  include: { plan: { include: { features: true } } },
352
371
  });
353
372
 
@@ -360,6 +379,7 @@ export class PrismaFeatureAccessAdapter extends FeatureAccessPort {
360
379
  const usage = await this._prisma.featureUsage.count({
361
380
  where: {
362
381
  userId,
382
+ tenantId: context?.tenantId,
363
383
  featureKey,
364
384
  createdAt: { gte: periodStart },
365
385
  },
@@ -377,14 +397,14 @@ export class PrismaFeatureAccessAdapter extends FeatureAccessPort {
377
397
  };
378
398
  }
379
399
 
380
- public async recordUsage(userId: string, featureKey: string): Promise<void> {
381
- const access = await this.checkAccess(userId, featureKey);
400
+ public async recordUsage(userId: string, featureKey: string, context?: IFeatureAccessContext): Promise<void> {
401
+ const access = await this.checkAccess(userId, featureKey, context);
382
402
  if (!access.allowed) {
383
403
  throw new Error(`Feature quota exceeded for ${featureKey}`);
384
404
  }
385
405
 
386
406
  await this._prisma.featureUsage.create({
387
- data: { userId, featureKey },
407
+ data: { userId, tenantId: context?.tenantId, featureKey },
388
408
  });
389
409
  }
390
410
 
@@ -417,7 +437,7 @@ export class RecipeController {
417
437
 
418
438
  ### FeatureGuard
419
439
 
420
- Checks the `@RequiresFeature` metadata against `FeatureAccessPort.checkAccess()`. Apply it globally or per-controller:
440
+ Checks the `@RequiresFeature` metadata against `FeatureAccessPort.checkAccess()`. Metadata is resolved from route handlers first and controller classes second. Apply the guard globally or per-controller:
421
441
 
422
442
  ```typescript
423
443
  // Global registration
@@ -431,7 +451,7 @@ export class RecipeController {
431
451
  }
432
452
  ```
433
453
 
434
- The guard attaches the `IFeatureAccessResult` to the request as `request.featureAccess`, enabling downstream access to quota information.
454
+ The guard passes `IFeatureAccessContext` into the access port and attaches the `IFeatureAccessResult` to the request as `request.featureAccess`, enabling downstream access to quota information.
435
455
 
436
456
  ### FeatureUsageInterceptor
437
457
 
@@ -446,7 +466,7 @@ export class RecipeController {
446
466
  @RequiresFeature('RECIPE_CREATION')
447
467
  public async createRecipe(@Body() body: CreateRecipeDto): Promise<RecipeResponse> {
448
468
  // After successful response, FeatureUsageInterceptor calls
449
- // FeatureAccessPort.recordUsage(userId, 'RECIPE_CREATION')
469
+ // FeatureAccessPort.recordUsage(userId, 'RECIPE_CREATION', context)
450
470
  }
451
471
  }
452
472
  ```
@@ -514,7 +534,7 @@ All payment clients throw a `PaymentError` when provider operations fail (checko
514
534
  import { PaymentError } from '@breadstone/archipel-platform-payments';
515
535
 
516
536
  try {
517
- await this._paymentClient.createCheckoutSession(params);
537
+ await this._paymentClient.createCheckoutSession(priceId, successUrl, cancelUrl, userId);
518
538
  } catch (error) {
519
539
  if (error instanceof PaymentError) {
520
540
  logger.error('Payment operation failed', {
@@ -566,6 +586,7 @@ export class AppModule {}
566
586
  | `PaymentModule` | NestJS Module | Main module with `register()` |
567
587
  | `PaymentClientPort` | Abstract Port | Provider-agnostic payment contract |
568
588
  | `FeatureAccessPort` | Abstract Port | Feature quota checking |
589
+ | `IFeatureAccessContext` | Interface | Request-scoped access context |
569
590
  | `IFeatureAccessResult` | Interface | Quota check result |
570
591
  | `FeatureGuard` | Guard | Enforces `@RequiresFeature` |
571
592
  | `FeatureUsageInterceptor` | Interceptor | Records feature usage |
@@ -53,6 +53,7 @@ export const RequiresFeature = (featureKey: string) =>
53
53
  import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
54
54
  import { Reflector } from '@nestjs/core';
55
55
  import { FeatureAccessPort } from '../contracts/FeatureAccessPort';
56
+ import type { IFeatureAccessContext } from '../contracts/IFeatureAccessContext';
56
57
 
57
58
  @Injectable()
58
59
  export class FeatureGuard implements CanActivate {
@@ -76,7 +77,10 @@ export class FeatureGuard implements CanActivate {
76
77
  // #region Methods
77
78
 
78
79
  public async canActivate(context: ExecutionContext): Promise<boolean> {
79
- const featureKey = this._reflector.get<string>(FEATURE_KEY_METADATA, context.getHandler());
80
+ const featureKey = this._reflector.getAllAndOverride<string | undefined>(FEATURE_KEY_METADATA, [
81
+ context.getHandler(),
82
+ context.getClass(),
83
+ ]);
80
84
 
81
85
  if (!featureKey) {
82
86
  return true; // No feature requirement on this endpoint
@@ -84,12 +88,18 @@ export class FeatureGuard implements CanActivate {
84
88
 
85
89
  const request = context.switchToHttp().getRequest();
86
90
  const user = request.user;
91
+ const userId = typeof user?.id === 'number' ? String(user.id) : user?.id;
87
92
 
88
- if (!user || !user.id) {
93
+ if (!userId) {
89
94
  return false;
90
95
  }
91
96
 
92
- const access = await this._featureAccessPort.checkAccess(user.id, featureKey);
97
+ const accessContext: IFeatureAccessContext = {
98
+ userId,
99
+ tenantId: user?.tenantId,
100
+ subject: user,
101
+ };
102
+ const access = await this._featureAccessPort.checkAccess(userId, featureKey, accessContext);
93
103
  request.featureAccess = access; // Attach for downstream use
94
104
 
95
105
  return access.allowed;
@@ -130,7 +140,10 @@ export class FeatureUsageInterceptor implements NestInterceptor {
130
140
  // #region Methods
131
141
 
132
142
  public intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
133
- const featureKey = this._reflector.get<string>(FEATURE_KEY_METADATA, context.getHandler());
143
+ const featureKey = this._reflector.getAllAndOverride<string | undefined>(FEATURE_KEY_METADATA, [
144
+ context.getHandler(),
145
+ context.getClass(),
146
+ ]);
134
147
 
135
148
  if (!featureKey) {
136
149
  return next.handle();
@@ -138,11 +151,17 @@ export class FeatureUsageInterceptor implements NestInterceptor {
138
151
 
139
152
  const request = context.switchToHttp().getRequest();
140
153
  const user = request.user;
154
+ const userId = typeof user?.id === 'number' ? String(user.id) : user?.id;
141
155
 
142
156
  return next.handle().pipe(
143
157
  tap(async () => {
144
- if (user && user.id) {
145
- await this._featureAccessPort.recordUsage(user.id, featureKey);
158
+ if (userId) {
159
+ const accessContext: IFeatureAccessContext = {
160
+ userId,
161
+ tenantId: user?.tenantId,
162
+ subject: user,
163
+ };
164
+ await this._featureAccessPort.recordUsage(userId, featureKey, accessContext);
146
165
  }
147
166
  }),
148
167
  );
@@ -153,6 +172,8 @@ export class FeatureUsageInterceptor implements NestInterceptor {
153
172
  }
154
173
  ```
155
174
 
175
+ The context object lets adapters evaluate tenant-specific plans or inspect claims from the authenticated subject without pushing billing concerns into controllers.
176
+
156
177
  ### Step 4: Controller Usage
157
178
 
158
179
  ```typescript
@@ -24,9 +24,13 @@ full customization through dependency injection.
24
24
 
25
25
  export abstract class FeatureAccessPort {
26
26
 
27
- public abstract checkAccess(userId: string, featureKey: string): Promise<IFeatureAccessResult>;
27
+ public abstract checkAccess(
28
+ userId: string,
29
+ featureKey: string,
30
+ context?: IFeatureAccessContext,
31
+ ): Promise<IFeatureAccessResult>;
28
32
 
29
- public abstract recordUsage(userId: string, featureKey: string): Promise<void>;
33
+ public abstract recordUsage(userId: string, featureKey: string, context?: IFeatureAccessContext): Promise<void>;
30
34
 
31
35
  }
32
36
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@breadstone/archipel-mcp",
3
- "version": "0.0.41",
3
+ "version": "0.0.42",
4
4
  "description": "MCP server providing Archipel platform knowledge - documentation, query patterns, and coding conventions - to AI development tools.",
5
5
  "type": "commonjs",
6
6
  "main": "./src/main.js",
@@ -1,32 +0,0 @@
1
- ---
2
- title: 'Function: useExpressServer()'
3
- generated: true
4
- editUrl: false
5
- ---
6
- # Function: useExpressServer()
7
-
8
- ```ts
9
- function useExpressServer<TApplication>(options?): PlatformBootstrapStep<TApplication>;
10
- ```
11
-
12
- Defined in: server/express/useExpressServer.ts:14
13
-
14
- Creates an Express-compatible server bootstrap step.
15
-
16
- ## Type Parameters
17
-
18
- | Type Parameter | Default type |
19
- | ------ | ------ |
20
- | `TApplication` *extends* `INestApplication`\<`any`\> | `INestApplication`\<`any`\> |
21
-
22
- ## Parameters
23
-
24
- | Parameter | Type | Description |
25
- | ------ | ------ | ------ |
26
- | `options?` | [`IExpressServerStepOptions`](Interface.IExpressServerStepOptions) | Express server options. |
27
-
28
- ## Returns
29
-
30
- [`PlatformBootstrapStep`](TypeAlias.PlatformBootstrapStep)\<`TApplication`\>
31
-
32
- A platform bootstrap step.
@@ -1,46 +0,0 @@
1
- ---
2
- title: 'Interface: IExpressServerStepOptions'
3
- generated: true
4
- editUrl: false
5
- ---
6
- # Interface: IExpressServerStepOptions
7
-
8
- Defined in: server/express/IExpressServerStepOptions.ts:9
9
-
10
- Options for the Express-compatible server bootstrap step.
11
-
12
- ## Properties
13
-
14
- ### compression?
15
-
16
- ```ts
17
- readonly optional compression?: boolean | CompressionOptions;
18
- ```
19
-
20
- Defined in: server/express/IExpressServerStepOptions.ts:17
21
-
22
- Compression middleware options, or `false` to skip compression.
23
-
24
- ***
25
-
26
- ### json?
27
-
28
- ```ts
29
- readonly optional json?: boolean | OptionsJson;
30
- ```
31
-
32
- Defined in: server/express/IExpressServerStepOptions.ts:11
33
-
34
- JSON body parser options, or `false` to skip JSON body parsing.
35
-
36
- ***
37
-
38
- ### urlEncoded?
39
-
40
- ```ts
41
- readonly optional urlEncoded?: boolean | OptionsUrlencoded;
42
- ```
43
-
44
- Defined in: server/express/IExpressServerStepOptions.ts:14
45
-
46
- URL-encoded body parser options, or `false` to skip URL-encoded body parsing.