@aws/nx-plugin-mcp 1.0.0-rc.96 → 1.0.0-rc.98

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 (72) hide show
  1. package/bin/aws-nx-mcp.js +90 -45
  2. package/docs/get_started/existing-project.mdx +7 -4
  3. package/docs/get_started/quick-start.mdx +58 -5
  4. package/docs/get_started/tutorials/dungeon-game/1.mdx +23 -20
  5. package/docs/get_started/tutorials/dungeon-game/2.mdx +11 -3
  6. package/docs/get_started/tutorials/dungeon-game/3.mdx +4 -0
  7. package/docs/get_started/tutorials/dungeon-game/4.mdx +7 -2
  8. package/docs/get_started/tutorials/dungeon-game/wrap-up.mdx +10 -1
  9. package/docs/guides/agentcore-gateway.mdx +4 -2
  10. package/docs/guides/agentcore-harness.mdx +2 -1
  11. package/docs/guides/astro-docs.mdx +25 -7
  12. package/docs/guides/connection/py-agent-a2a.mdx +2 -0
  13. package/docs/guides/connection/py-agent-dynamodb.mdx +1 -1
  14. package/docs/guides/connection/py-agent-gateway.mdx +3 -0
  15. package/docs/guides/connection/py-agent-mcp.mdx +18 -4
  16. package/docs/guides/connection/py-agent-rdb.mdx +5 -4
  17. package/docs/guides/connection/py-fast-api-dynamodb.mdx +1 -1
  18. package/docs/guides/connection/py-fast-api-rdb.mdx +7 -2
  19. package/docs/guides/connection/py-mcp-server-dynamodb.mdx +1 -1
  20. package/docs/guides/connection/py-mcp-server-rdb.mdx +6 -6
  21. package/docs/guides/connection/react-agui.mdx +34 -25
  22. package/docs/guides/connection/react-fastapi.mdx +114 -116
  23. package/docs/guides/connection/react-py-agent.mdx +4 -0
  24. package/docs/guides/connection/react-smithy.mdx +152 -98
  25. package/docs/guides/connection/react-trpc.mdx +13 -6
  26. package/docs/guides/connection/smithy-dynamodb.mdx +2 -8
  27. package/docs/guides/connection/smithy-rdb.mdx +3 -6
  28. package/docs/guides/connection/trpc-rdb.mdx +6 -6
  29. package/docs/guides/connection/ts-agent-a2a.mdx +4 -4
  30. package/docs/guides/connection/ts-agent-dynamodb.mdx +13 -11
  31. package/docs/guides/connection/ts-agent-gateway.mdx +2 -0
  32. package/docs/guides/connection/ts-agent-mcp.mdx +19 -7
  33. package/docs/guides/connection/ts-agent-rdb.mdx +2 -2
  34. package/docs/guides/connection/ts-mcp-server-dynamodb.mdx +11 -7
  35. package/docs/guides/connection/ts-mcp-server-rdb.mdx +4 -3
  36. package/docs/guides/docker-bundling.mdx +23 -3
  37. package/docs/guides/fastapi.mdx +16 -5
  38. package/docs/guides/license.mdx +30 -6
  39. package/docs/guides/nx-generator.mdx +11 -12
  40. package/docs/guides/py-agent.mdx +129 -54
  41. package/docs/guides/py-mcp-server.mdx +3 -1
  42. package/docs/guides/py-rdb.mdx +13 -4
  43. package/docs/guides/python-lambda-function.mdx +8 -8
  44. package/docs/guides/python-project.mdx +28 -25
  45. package/docs/guides/react-website-auth.mdx +8 -8
  46. package/docs/guides/react-website.mdx +46 -27
  47. package/docs/guides/runtime-config.mdx +24 -4
  48. package/docs/guides/security.mdx +1 -1
  49. package/docs/guides/terraform-project.mdx +8 -2
  50. package/docs/guides/trpc.mdx +96 -12
  51. package/docs/guides/ts-agent.mdx +17 -3
  52. package/docs/guides/ts-dcr-proxy.mdx +24 -6
  53. package/docs/guides/ts-lambda-function.mdx +7 -1
  54. package/docs/guides/ts-mcp-server.mdx +45 -15
  55. package/docs/guides/ts-nx-plugin.mdx +17 -7
  56. package/docs/guides/ts-rdb.mdx +9 -2
  57. package/docs/guides/ts-smithy-api.mdx +76 -7
  58. package/docs/guides/typescript-infrastructure.mdx +27 -11
  59. package/docs/guides/typescript-project.mdx +12 -5
  60. package/docs/guides/workspace.mdx +21 -9
  61. package/docs/snippets/api/type-safe-api-integrations.mdx +2 -0
  62. package/docs/snippets/connection/a2a-infrastructure.mdx +3 -0
  63. package/docs/snippets/connection/infra-project-prerequisite.mdx +8 -0
  64. package/docs/snippets/connection/strands-agent-rdb-ssl-requirements.mdx +1 -1
  65. package/docs/snippets/connection/ts-lambda-rdb-ssl-requirements.mdx +1 -1
  66. package/docs/snippets/connection/ts-mcp-server-rdb-ssl-requirements.mdx +1 -1
  67. package/docs/snippets/pdk-migration/faq/type-safe-api.mdx +1 -1
  68. package/docs/snippets/prerequisites.mdx +1 -1
  69. package/docs/snippets/required-prerequisites.mdx +1 -1
  70. package/package.json +1 -1
  71. package/src/init/schema.json +5 -0
  72. package/src/py/project/schema.json +3 -1
@@ -50,16 +50,23 @@ The generator will create the following project structure in the `<directory>/<n
50
50
  - config.ts Application configuration (eg. logo)
51
51
  - components
52
52
  - AppLayout Components for the overall layout and navigation bar
53
+ - app-sidebar.tsx Sidebar navigation (shadcn only)
54
+ - alert.tsx Alert component wrapping your UX provider's own
55
+ - spinner.tsx Spinner component wrapping your UX provider's own
53
56
  - hooks
54
57
  - useAppLayout.tsx Hook for adjusting the AppLayout from nested components (Cloudscape only)
55
58
  - routes
59
+ - \_\_root.tsx Root route, wrapping every page in the AppLayout
56
60
  - index.tsx Example route (or page) for TanStack Router
61
+ - routeTree.gen.ts Route tree, generated and maintained by TanStack Router
57
62
  - styles.css Global styles
58
63
  - vite.config.mts Vite and Vitest configuration
64
+ - project.json Nx project defining the project's targets
59
65
  - tsconfig.json Base TypeScript configuration for source and tests
60
66
  - tsconfig.app.json TypeScript configuration for source code
61
67
  - tsconfig.spec.json TypeScript configuration for tests
62
68
  - package.json Project manifest defining the project's package name and dependencies
69
+ - README.md Project README
63
70
  </FileTree>
64
71
 
65
72
  :::note[Without TanStack Router]
@@ -157,27 +164,33 @@ Your website comes with [TanStack Router](https://tanstack.com/router/v1) config
157
164
 
158
165
  You can use the `Link` component or `useNavigate` hook to navigate between pages:
159
166
 
160
- ```tsx {1, 4, 8-9, 14}
167
+ ```tsx {1, 8, 12-13, 18}
161
168
  import { Link, useNavigate } from '@tanstack/react-router';
162
169
 
163
- export const MyComponent = () => {
170
+ export const MyComponent = ({
171
+ createProduct,
172
+ }: {
173
+ createProduct: () => Promise<string>;
174
+ }) => {
164
175
  const navigate = useNavigate();
165
176
 
166
177
  const submit = async () => {
167
- const id = await ...
178
+ const id = await createProduct();
168
179
  // Use `navigate` for redirecting after some asynchronous action
169
- navigate({ to: '/products/$id', { params: { id }} });
180
+ navigate({ to: '/products/$id', params: { id } });
170
181
  };
171
182
 
172
183
  return (
173
184
  <>
174
185
  <Link to="/products">Cancel</Link>
175
- <Button onClick={submit}>Submit</Button>
186
+ <button onClick={submit}>Submit</button>
176
187
  </>
177
- )
188
+ );
178
189
  };
179
190
  ```
180
191
 
192
+ `$id` in the `to` path is a [path parameter](https://tanstack.com/router/latest/docs/framework/react/guide/path-params), supplied through `params`. Swap the plain `<button>` for your UX kit's own button component as needed.
193
+
181
194
  For more details, check out the [TanStack Router](https://tanstack.com/router/latest/docs/framework/react/overview) documentation.
182
195
 
183
196
  ## Deploying your Website
@@ -191,13 +204,13 @@ To deploy your website, we recommend using the <Link path="guides/typescript-inf
191
204
  You can use the CDK construct generated for you in `packages/common/constructs` to deploy your website.
192
205
 
193
206
  ```ts title="packages/infra/src/stacks/application-stack.ts" {3, 9}
194
- import { Stack } from 'aws-cdk-lib';
207
+ import { Stack, StackProps } from 'aws-cdk-lib';
195
208
  import { Construct } from 'constructs';
196
209
  import { MyWebsite } from '@my-scope/common-constructs';
197
210
 
198
211
  export class ApplicationStack extends Stack {
199
- constructor(scope: Construct, id: string) {
200
- super(scope, id);
212
+ constructor(scope: Construct, id: string, props?: StackProps) {
213
+ super(scope, id, props);
201
214
 
202
215
  new MyWebsite(this, 'MyWebsite');
203
216
  }
@@ -266,13 +279,13 @@ The CloudFront distribution is protected by an [AWS WAFv2](https://docs.aws.amaz
266
279
  To opt out, set `enableWaf` to `false` when you create your website:
267
280
 
268
281
  ```ts title="packages/infra/src/stacks/application-stack.ts" {10,15-18}
269
- import { Stack } from 'aws-cdk-lib';
282
+ import { Stack, StackProps } from 'aws-cdk-lib';
270
283
  import { Construct } from 'constructs';
271
284
  import { MyWebsite, suppressRules } from '@my-scope/common-constructs';
272
285
 
273
286
  export class ApplicationStack extends Stack {
274
- constructor(scope: Construct, id: string) {
275
- super(scope, id);
287
+ constructor(scope: Construct, id: string, props?: StackProps) {
288
+ super(scope, id, props);
276
289
 
277
290
  const website = new MyWebsite(this, 'MyWebsite', {
278
291
  enableWaf: false,
@@ -318,14 +331,14 @@ The website bucket, the CloudFront distribution log bucket, and the CloudWatch L
318
331
  If you want to use a different encryption configuration, pass the `encryption`, `encryptionKey` and `enableKeyRotation` props when you create your website:
319
332
 
320
333
  ```ts title="packages/infra/src/stacks/application-stack.ts" {11}
321
- import { Stack } from 'aws-cdk-lib';
334
+ import { Stack, StackProps } from 'aws-cdk-lib';
322
335
  import { Construct } from 'constructs';
323
336
  import { BucketEncryption } from 'aws-cdk-lib/aws-s3';
324
337
  import { MyWebsite } from '@my-scope/common-constructs';
325
338
 
326
339
  export class ApplicationStack extends Stack {
327
- constructor(scope: Construct, id: string) {
328
- super(scope, id);
340
+ constructor(scope: Construct, id: string, props?: StackProps) {
341
+ super(scope, id, props);
329
342
 
330
343
  new MyWebsite(this, 'MyWebsite', {
331
344
  encryption: BucketEncryption.S3_MANAGED,
@@ -401,11 +414,12 @@ module "my_website" {
401
414
  ```
402
415
 
403
416
  :::caution[Checkov: S3_MANAGED]
404
- Choosing `S3_MANAGED` fails the `checkov` security scan with `CKV_AWS_145` on the website and distribution log buckets. The vended `test` target doesn't accept a `--config-file`, but `checkov` auto-discovers a `.checkov.yaml` in its working directory, so drop one in `packages/infra/src`:
417
+ Choosing `S3_MANAGED` fails your project's `checkov` target with `CKV_AWS_145` on the website and distribution log buckets. The target runs `checkov` against your project's own `checkov.yml`, so add the check to its `skip-check` list:
405
418
 
406
- ```yaml title="packages/infra/src/.checkov.yaml"
419
+ ```yaml title="packages/infra/checkov.yml" {3}
407
420
  skip-check:
408
- - CKV_AWS_145
421
+ # ...
422
+ - CKV_AWS_145 # S3 buckets are not KMS encrypted when encryption is S3_MANAGED
409
423
  ```
410
424
  :::
411
425
 
@@ -439,11 +453,12 @@ module "my_website" {
439
453
  ```
440
454
 
441
455
  :::caution[Checkov: key rotation disabled]
442
- Disabling key rotation on the automatically created key fails the `checkov` security scan with `CKV_AWS_7`. Add it to the same `.checkov.yaml`:
456
+ Disabling key rotation on the automatically created key fails your project's `checkov` target with `CKV_AWS_7`. Add it to the same `skip-check` list in `packages/infra/checkov.yml`:
443
457
 
444
- ```yaml title="packages/infra/src/.checkov.yaml"
458
+ ```yaml title="packages/infra/checkov.yml" {3}
445
459
  skip-check:
446
- - CKV_AWS_7
460
+ # ...
461
+ - CKV_AWS_7 # Key rotation is disabled on the website's KMS key
447
462
  ```
448
463
  :::
449
464
  </Fragment>
@@ -458,14 +473,14 @@ By default the distribution uses the default CloudFront domain name (`*.cloudfro
458
473
  Pass the `certificate` and `domainNames` props when you create your website:
459
474
 
460
475
  ```ts title="packages/infra/src/stacks/application-stack.ts" {11-13}
461
- import { Stack } from 'aws-cdk-lib';
476
+ import { Stack, StackProps } from 'aws-cdk-lib';
462
477
  import { Construct } from 'constructs';
463
478
  import { Certificate } from 'aws-cdk-lib/aws-certificatemanager';
464
479
  import { MyWebsite } from '@my-scope/common-constructs';
465
480
 
466
481
  export class ApplicationStack extends Stack {
467
- constructor(scope: Construct, id: string) {
468
- super(scope, id);
482
+ constructor(scope: Construct, id: string, props?: StackProps) {
483
+ super(scope, id, props);
469
484
 
470
485
  new MyWebsite(this, 'MyWebsite', {
471
486
  domainNames: ['www.example.com'],
@@ -508,13 +523,13 @@ The `RuntimeConfig` CDK construct can be used to add and retrieve configuration
508
523
  Your website CDK construct will deploy the `connection` namespace of the runtime configuration as a `runtime-config.json` file to the root of your S3 bucket.
509
524
 
510
525
  ```ts title="packages/infra/src/stacks/application-stack.ts"
511
- import { Stack } from 'aws-cdk-lib';
526
+ import { Stack, StackProps } from 'aws-cdk-lib';
512
527
  import { Construct } from 'constructs';
513
528
  import { MyWebsite, MyApi } from '@my-scope/common-constructs';
514
529
 
515
530
  export class ApplicationStack extends Stack {
516
- constructor(scope: Construct, id: string) {
517
- super(scope, id);
531
+ constructor(scope: Construct, id: string, props?: StackProps) {
532
+ super(scope, id, props);
518
533
 
519
534
  // Website can be declared at any point, since runtime config is resolved lazily
520
535
  new MyWebsite(this, 'MyWebsite');
@@ -582,6 +597,10 @@ const MyComponent = () => {
582
597
  };
583
598
  ```
584
599
 
600
+ :::note[Added when you need it]
601
+ `src/hooks/useRuntimeConfig.tsx` and `src/components/RuntimeConfig` are added to your website by the <Link path="/guides/react-website-auth">`ts#website#auth`</Link> generator and the <Link path="guides/connection">`connection`</Link> generators, since those are what put values into the runtime configuration. A website with neither has no runtime configuration to read, so the hook is not vended until one has run.
602
+ :::
603
+
585
604
  :::note[Runtime Configuration]
586
605
  For details on how runtime configuration is stored in AWS AppConfig and how server-side consumers (Lambda functions, agents) can retrieve it, see the <Link path="guides/runtime-config">Runtime Configuration</Link> guide.
587
606
  :::
@@ -253,15 +253,27 @@ All generated API and agent constructs are automatically configured with:
253
253
 
254
254
  Use `getAppConfig` from [`@aws-lambda-powertools/parameters`](https://docs.aws.amazon.com/powertools/typescript/latest/features/parameters):
255
255
 
256
+ `getAppConfig` returns any valid JSON value, so declare the shape you expect and cast the result — otherwise the property accesses do not compile under the workspace's strict `tsconfig`:
257
+
256
258
  ```ts
257
259
  import { getAppConfig } from '@aws-lambda-powertools/parameters/appconfig';
258
260
 
261
+ interface ConnectionConfig {
262
+ apis?: Record<string, string>;
263
+ cognitoProps?: {
264
+ region: string;
265
+ userPoolId: string;
266
+ userPoolWebClientId: string;
267
+ identityPoolId: string;
268
+ };
269
+ }
270
+
259
271
  // Retrieve the 'connection' namespace as a parsed JSON object
260
- const config = await getAppConfig('connection', {
272
+ const config = (await getAppConfig('connection', {
261
273
  application: process.env.RUNTIME_CONFIG_APP_ID!,
262
274
  environment: 'default',
263
275
  transform: 'json',
264
- });
276
+ })) as ConnectionConfig;
265
277
 
266
278
  // Access values
267
279
  const apiUrl = config.apis?.MyApi;
@@ -271,16 +283,24 @@ const cognitoProps = config.cognitoProps;
271
283
  You can also retrieve custom namespaces:
272
284
 
273
285
  ```ts
286
+ interface TablesConfig {
287
+ users?: { tableName: string; tableArn: string };
288
+ }
289
+
274
290
  // Retrieve a custom 'tables' namespace
275
- const tablesConfig = await getAppConfig('tables', {
291
+ const tablesConfig = (await getAppConfig('tables', {
276
292
  application: process.env.RUNTIME_CONFIG_APP_ID!,
277
293
  environment: 'default',
278
294
  transform: 'json',
279
- });
295
+ })) as TablesConfig;
280
296
 
281
297
  const usersTableName = tablesConfig.users?.tableName;
282
298
  ```
283
299
 
300
+ :::tip[Validate at the boundary]
301
+ A cast tells the compiler the shape but does not check it at runtime. For configuration you don't control, parse it with a [Zod](https://zod.dev/) schema instead, so a malformed value fails loudly rather than surfacing as `undefined` later.
302
+ :::
303
+
284
304
  </TabItem>
285
305
  <TabItem label="Python" icon="seti:python">
286
306
 
@@ -28,7 +28,7 @@ Projects which build container images (for example agents, MCP servers, and data
28
28
 
29
29
  ### Credential Scanning
30
30
 
31
- Workspaces include [git-secrets](https://github.com/awslabs/git-secrets) pre-commit hooks which scan staged files for AWS credential patterns, preventing accidental commits of access keys and other sensitive values. See the <Link path="/guides/workspace#git-secrets">Git Secrets</Link> section of the workspace guide.
31
+ Workspaces include [git-secrets](https://github.com/awslabs/git-secrets) pre-commit hooks which scan staged files for AWS credential patterns, preventing accidental commits of access keys and other sensitive values. Both creating a workspace and adopting the plugin into an existing one set these up, unless you opt out. See the <Link path="/guides/workspace#git-secrets">Git Secrets</Link> section of the workspace guide.
32
32
 
33
33
  ### Authentication
34
34
 
@@ -111,6 +111,8 @@ You can start writing your Terraform infrastructure inside `src/main.tf`, for ex
111
111
  +}
112
112
  ```
113
113
 
114
+ Note that the S3 bucket above would fail the Checkov security scan, which checks that the bucket has the appropriate security settings enabled.
115
+
114
116
  ### Cross project dependencies
115
117
 
116
118
  If you wanted to execute a module from a separate project (lib), you could do so as follows:
@@ -134,8 +136,12 @@ To add new environments, create a new `src/env/<environment>.tfvars` file with t
134
136
  ```hcl
135
137
  # Production environment variables
136
138
  environment = "prod"
137
- region = "us-west-2"
139
+ aws_region = "us-west-2"
138
140
  ```
141
+
142
+ :::note[Variable names]
143
+ Every name you set here must be declared in `src/variables.tf` — the generator declares `aws_region` and `environment`.
144
+ :::
139
145
  </TabItem>
140
146
  <TabItem label="project.json">
141
147
  ```diff
@@ -200,7 +206,7 @@ region = "us-west-2"
200
206
  "command": "terraform plan -var-file=env/dev.tfvars -out=../../../dist/packages/infra/terraform/dev.tfplan"
201
207
  },
202
208
  + "prod": {
203
- + "command": "terraform plan -var-file=env/dev.tfvars -out=../../../dist/packages/infra/terraform/prod.tfplan"
209
+ + "command": "terraform plan -var-file=env/prod.tfvars -out=../../../dist/packages/infra/terraform/prod.tfplan"
204
210
  + }
205
211
  },
206
212
  "options": {
@@ -16,6 +16,8 @@ import NxCommands from '@components/nx-commands.astro';
16
16
  import Infrastructure from '@components/infrastructure.astro';
17
17
  import Snippet from '@components/snippet.astro';
18
18
  import OptionFilter from '@components/option-filter.astro';
19
+ import InstallCommand from '@components/install-command.astro';
20
+ import { TS_VERSIONS } from '../../../../../../packages/nx-plugin/src/utils/versions';
19
21
 
20
22
  [tRPC](https://trpc.io/) is a framework for building APIs in TypeScript with end-to-end type safety. Using tRPC, updates to API operation inputs and outputs are immediately reflected in client code and are visible in your IDE without the need to rebuild your project.
21
23
 
@@ -49,15 +51,18 @@ The generator will create the following project structure in the `<directory>/<a
49
51
 
50
52
  <FileTree>
51
53
  - src
54
+ - index.ts Package entrypoint re-exporting the router, context, client and schema
52
55
  - init.ts Backend tRPC initialisation
53
56
  - handler.ts Lambda handler entrypoint
54
57
  - router.ts tRPC router definition
55
58
  - schema Schema definitions using Zod
59
+ - index.ts Barrel re-exporting every schema
56
60
  - echo.ts Example definitions for the input and output of the "echo" procedure
57
61
  - z-async-iterable.ts Zod helper for subscriptions (REST API only)
58
62
  - procedures Procedures (or operations) exposed by your API
59
63
  - echo.ts Example procedure
60
64
  - middleware
65
+ - index.ts Barrel re-exporting the middleware, and the procedure context type
61
66
  - error.ts Middleware for error handling
62
67
  - logger.ts middleware for configuring AWS Powertools for Lambda logging
63
68
  - tracer.ts middleware for configuring AWS Powertools for Lambda tracing
@@ -65,9 +70,15 @@ The generator will create the following project structure in the `<directory>/<a
65
70
  - local-server.ts tRPC standalone adapter entrypoint for local development server
66
71
  - client
67
72
  - index.ts Type-safe client for machine-to-machine API calls
73
+ - rolldown.config.ts Bundle configuration for the Lambda deployment package
68
74
  - tsconfig.json TypeScript configuration
75
+ - tsconfig.lib.json TypeScript configuration for the library sources
76
+ - tsconfig.spec.json TypeScript configuration for the tests
77
+ - vitest.config.mts Vitest configuration
69
78
  - package.json Project manifest defining the project's package name and dependencies
70
79
  - project.json Project configuration and build targets
80
+ - README.md Project readme
81
+ - .gitignore Ignores the project's build output
71
82
 
72
83
  </FileTree>
73
84
 
@@ -297,6 +308,10 @@ As an example, let's implement some middlware to extract some details about the
297
308
  <OptionFilter when={{ auth: 'iam' }} description="Identity middleware example for IAM-authenticated APIs">
298
309
  This example walks through identity middleware for `IAM` authentication. We look up the caller in Cognito using the sub extracted from the API Gateway event.
299
310
 
311
+ The lookup uses the Cognito Identity Provider client, which is not a dependency of a generated tRPC API. Install it into your API project first:
312
+
313
+ <InstallCommand pkg={`@aws-sdk/client-cognito-identity-provider@${TS_VERSIONS['@aws-sdk/client-cognito-identity-provider']}`} project="@my-scope/my-api" />
314
+
300
315
  First, we define what we'll add to the context:
301
316
 
302
317
  ```ts
@@ -334,8 +349,8 @@ In our case, we want to extract details about the calling Cognito user. We'll do
334
349
  ```ts
335
350
  import { CognitoIdentityProvider } from '@aws-sdk/client-cognito-identity-provider';
336
351
  import { initTRPC, TRPCError } from '@trpc/server';
337
- import { CreateAWSLambdaContextOptions } from '@trpc/server/adapters/aws-lambda';
338
- import { APIGatewayProxyEvent } from 'aws-lambda';
352
+ import type { CreateAWSLambdaContextOptions } from '@trpc/server/adapters/aws-lambda';
353
+ import type { APIGatewayProxyEvent } from 'aws-lambda';
339
354
 
340
355
  export interface IIdentityContext {
341
356
  identity?: {
@@ -345,12 +360,17 @@ export interface IIdentityContext {
345
360
  }
346
361
 
347
362
  export const createIdentityPlugin = () => {
348
- const t = initTRPC.context<IIdentityContext & CreateAWSLambdaContextOptions<APIGatewayProxyEvent>>().create();
363
+ const t = initTRPC
364
+ .context<
365
+ IIdentityContext & CreateAWSLambdaContextOptions<APIGatewayProxyEvent>
366
+ >()
367
+ .create();
349
368
 
350
369
  const cognito = new CognitoIdentityProvider();
351
370
 
352
371
  return t.procedure.use(async (opts) => {
353
- const cognitoAuthenticationProvider = opts.ctx.event.requestContext?.identity?.cognitoAuthenticationProvider;
372
+ const cognitoAuthenticationProvider =
373
+ opts.ctx.event.requestContext?.identity?.cognitoAuthenticationProvider;
354
374
 
355
375
  let sub: string | undefined = undefined;
356
376
  if (cognitoAuthenticationProvider) {
@@ -397,8 +417,8 @@ export const createIdentityPlugin = () => {
397
417
  ```ts
398
418
  import { CognitoIdentityProvider } from '@aws-sdk/client-cognito-identity-provider';
399
419
  import { initTRPC, TRPCError } from '@trpc/server';
400
- import { CreateAWSLambdaContextOptions } from '@trpc/server/adapters/aws-lambda';
401
- import { APIGatewayProxyEventV2WithIAMAuthorizer } from 'aws-lambda';
420
+ import type { CreateAWSLambdaContextOptions } from '@trpc/server/adapters/aws-lambda';
421
+ import type { APIGatewayProxyEventV2WithIAMAuthorizer } from 'aws-lambda';
402
422
 
403
423
  export interface IIdentityContext {
404
424
  identity?: {
@@ -408,7 +428,12 @@ export interface IIdentityContext {
408
428
  }
409
429
 
410
430
  export const createIdentityPlugin = () => {
411
- const t = initTRPC.context<IIdentityContext & CreateAWSLambdaContextOptions<APIGatewayProxyEventV2WithIAMAuthorizer>>().create();
431
+ const t = initTRPC
432
+ .context<
433
+ IIdentityContext &
434
+ CreateAWSLambdaContextOptions<APIGatewayProxyEventV2WithIAMAuthorizer>
435
+ >()
436
+ .create();
412
437
 
413
438
  const cognito = new CognitoIdentityProvider();
414
439
 
@@ -481,12 +506,16 @@ export interface IIdentityContext {
481
506
 
482
507
  Note that we define an additional _optional_ property on the context. tRPC manages ensuring that this is defined in procedures which have correctly configured this middleware.
483
508
 
484
- Next, the middleware itself:
509
+ Next, the middleware itself. The event type and the location of the claims differ between a REST API and an HTTP API, so the implementation depends on your selected `infra`:
510
+
511
+ <Tabs syncKey="http-rest">
512
+ <TabItem label="REST API" _filter={{ infra: 'rest-lambda' }}>
513
+ A REST API's Cognito User Pools authorizer places the claims at `event.requestContext.authorizer.claims`:
485
514
 
486
515
  ```ts
487
516
  import { initTRPC, TRPCError } from '@trpc/server';
488
- import { CreateAWSLambdaContextOptions } from '@trpc/server/adapters/aws-lambda';
489
- import { APIGatewayProxyEvent } from 'aws-lambda';
517
+ import type { CreateAWSLambdaContextOptions } from '@trpc/server/adapters/aws-lambda';
518
+ import type { APIGatewayProxyEvent } from 'aws-lambda';
490
519
 
491
520
  export interface IIdentityContext {
492
521
  identity?: {
@@ -497,7 +526,9 @@ export interface IIdentityContext {
497
526
 
498
527
  export const createIdentityPlugin = () => {
499
528
  const t = initTRPC
500
- .context<IIdentityContext & CreateAWSLambdaContextOptions<APIGatewayProxyEvent>>()
529
+ .context<
530
+ IIdentityContext & CreateAWSLambdaContextOptions<APIGatewayProxyEvent>
531
+ >()
501
532
  .create();
502
533
 
503
534
  return t.procedure.use(async (opts) => {
@@ -506,7 +537,7 @@ export const createIdentityPlugin = () => {
506
537
  | undefined;
507
538
 
508
539
  const sub = claims?.sub;
509
- const username = claims?.username;
540
+ const username = claims?.username ?? claims?.['cognito:username'];
510
541
 
511
542
  if (!sub || !username) {
512
543
  throw new TRPCError({
@@ -527,6 +558,59 @@ export const createIdentityPlugin = () => {
527
558
  });
528
559
  };
529
560
  ```
561
+ </TabItem>
562
+ <TabItem label="HTTP API" _filter={{ infra: 'http-lambda' }}>
563
+ An HTTP API's JWT authorizer delivers a payload-v2 event whose claims sit one level deeper, at `event.requestContext.authorizer.jwt.claims`. The context must be typed on `APIGatewayProxyEventV2WithJWTAuthorizer` to match the one the generated `publicProcedure` uses — otherwise `.concat()` fails with tRPC's `Context mismatch` error:
564
+
565
+ ```ts
566
+ import { initTRPC, TRPCError } from '@trpc/server';
567
+ import type { CreateAWSLambdaContextOptions } from '@trpc/server/adapters/aws-lambda';
568
+ import type { APIGatewayProxyEventV2WithJWTAuthorizer } from 'aws-lambda';
569
+
570
+ export interface IIdentityContext {
571
+ identity?: {
572
+ sub: string;
573
+ username: string;
574
+ };
575
+ }
576
+
577
+ export const createIdentityPlugin = () => {
578
+ const t = initTRPC
579
+ .context<
580
+ IIdentityContext &
581
+ CreateAWSLambdaContextOptions<APIGatewayProxyEventV2WithJWTAuthorizer>
582
+ >()
583
+ .create();
584
+
585
+ return t.procedure.use(async (opts) => {
586
+ const claims = opts.ctx.event.requestContext?.authorizer?.jwt?.claims as
587
+ | Record<string, string>
588
+ | undefined;
589
+
590
+ const sub = claims?.sub;
591
+ const username = claims?.username ?? claims?.['cognito:username'];
592
+
593
+ if (!sub || !username) {
594
+ throw new TRPCError({
595
+ code: 'FORBIDDEN',
596
+ message: 'Unable to determine calling user',
597
+ });
598
+ }
599
+
600
+ return await opts.next({
601
+ ctx: {
602
+ ...opts.ctx,
603
+ identity: {
604
+ sub,
605
+ username,
606
+ },
607
+ },
608
+ });
609
+ });
610
+ };
611
+ ```
612
+ </TabItem>
613
+ </Tabs>
530
614
 
531
615
  You can then mix the plugin into any procedure that needs the caller's identity:
532
616
 
@@ -61,6 +61,8 @@ The generator will add the following files to your existing TypeScript project.
61
61
  - router.ts tRPC router with agent procedures
62
62
  - agent.ts Main agent definition with sample tools
63
63
  - session.ts Resolves the SessionManager used to persist conversation state
64
+ - schema/
65
+ - z-async-iterable.ts Zod schema for the router's streamed responses
64
66
  - client.ts Vended client for invoking your agent
65
67
  - agent-core-trpc-client.ts Client factory for connecting to agents on AgentCore Runtime
66
68
  - Dockerfile Container image definition (only when `infra` is `agentcore-ecr`)
@@ -81,6 +83,8 @@ The entry point uses the [Strands A2A Express Server](https://strandsagents.com/
81
83
  - index.ts A2A Express server entry point
82
84
  - agent.ts Main agent definition with sample tools
83
85
  - session.ts Resolves the SessionManager used to persist conversation state
86
+ - middleware/
87
+ - session-id-middleware.ts Binds the inbound AgentCore session ID for the request
84
88
  - Dockerfile Container image definition (only when `infra` is `agentcore-ecr`)
85
89
  - package.json Updated with Strands and Express dependencies
86
90
  - project.json Updated with agent serve targets
@@ -99,6 +103,8 @@ The entry point uses [`@ag-ui/aws-strands`](https://www.npmjs.com/package/@ag-ui
99
103
  - index.ts AG-UI server entry point (Express + SSE)
100
104
  - agent.ts Main agent definition with sample tools
101
105
  - session.ts Resolves the SessionManager used to persist conversation state
106
+ - middleware/
107
+ - session-id-middleware.ts Binds the inbound AgentCore session ID for the request
102
108
  - Dockerfile Container image definition (only when `infra` is `agentcore-ecr`)
103
109
  - package.json Updated with Strands and AG-UI dependencies
104
110
  - project.json Updated with agent serve targets
@@ -280,7 +286,9 @@ If you have added multiple components to your project (agents, MCP servers, etc.
280
286
 
281
287
  <NxCommands commands={['agent-dev your-project']} />
282
288
 
283
- This uses `tsx --watch` to automatically restart the server when files change. The agent will be available at `http://localhost:8081` (or the assigned port if you have multiple agents).
289
+ This uses `tsx --watch` to automatically restart the server when files change. The agent will be available at `http://localhost:8081` (or the assigned port if you have multiple agents — read it from `metadata.ports` in the project's `project.json`).
290
+
291
+ A `<your-agent-name>-serve` target is also generated, which runs the agent against your deployed infrastructure and therefore requires `RUNTIME_CONFIG_APP_ID` to be set. See the <Link path="guides/local-development">Local Development</Link> guide for the difference between `dev` and `serve`.
284
292
 
285
293
  ### Chat with Your Agent
286
294
 
@@ -400,12 +408,16 @@ Generate the session ID rather than deriving it from user-supplied values such a
400
408
 
401
409
  ## Invoking your Agent
402
410
 
411
+ <OptionFilter when={{ protocol: 'http' }} description="tRPC-over-WebSocket client factory invocation details">
403
412
  Agent communication is transmitted via tRPC over WebSocket. As such, it's recommended to use the generated type-safe client factory in `client.ts`.
404
413
 
405
- <OptionFilter when={{ protocol: 'http' }} description="tRPC-over-WebSocket client factory invocation details">
406
414
  ### Invoke the Local Server
407
415
 
408
- You can invoke a locally running agent using the `.local` factory method from the client factory.
416
+ Start your agent with the `<your-agent-name>-dev` target:
417
+
418
+ <NxCommands commands={['agent-dev your-project']} />
419
+
420
+ Then invoke it using the `.local` factory method from the client factory.
409
421
 
410
422
  You can, for example create a file named `scripts/test.ts` in your workspace which imports the client:
411
423
 
@@ -420,6 +432,8 @@ const client = MyAgentClient.local({ url: 'http://localhost:8081/ws' });
420
432
  client.invoke.subscribe({ prompt: 'what is 1 plus 1?' }, { onData: console.log });
421
433
  ```
422
434
 
435
+ Substitute the port assigned to your agent — read it from `metadata.ports` in the project's `project.json`.
436
+
423
437
  :::tip[Quick Testing]
424
438
  Run with `tsx` as a quick way to test out your agent.
425
439
 
@@ -41,6 +41,12 @@ The generator creates a standalone <Link path="/guides/typescript-project">TypeS
41
41
  - authorize.ts Redirects to the Cognito Hosted UI
42
42
  - token.ts Injects the App Client secret and exchanges the token
43
43
  - mcp-proxy.ts Proxies `/mcp` requests to the upstream MCP server
44
+ - index.ts Project entry point
45
+ - rolldown.config.ts Bundle configuration, one entry per handler
46
+ - README.md Project README
47
+ - package.json Declares the handlers' dependencies
48
+ - project.json Adds a bundle target per handler
49
+ - tsconfig.json / tsconfig.lib.json / tsconfig.spec.json TypeScript configuration
44
50
 
45
51
  </FileTree>
46
52
 
@@ -103,7 +109,7 @@ You provide:
103
109
  Instantiate the generated construct in your stack, passing the required properties:
104
110
 
105
111
  ```typescript
106
- import { DcrProxy } from ':my-scope/common-constructs';
112
+ import { DcrProxy } from '@my-scope/common-constructs';
107
113
 
108
114
  new DcrProxy(this, 'DcrProxy', {
109
115
  userPoolId: userPool.userPoolId,
@@ -155,8 +161,9 @@ import {
155
161
  DcrProxy,
156
162
  MyProjectMcpServer,
157
163
  UserIdentity,
158
- } from ':my-scope/common-constructs';
164
+ } from '@my-scope/common-constructs';
159
165
  import { OAuthScope } from 'aws-cdk-lib/aws-cognito';
166
+ import * as kms from 'aws-cdk-lib/aws-kms';
160
167
  import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
161
168
 
162
169
  const identity = new UserIdentity(this, 'Identity');
@@ -179,6 +186,9 @@ const proxyClient = identity.userPool.addClient('DcrProxyClient', {
179
186
  // Store the App Client secret in Secrets Manager for the token handler to read
180
187
  const clientSecret = new secretsmanager.Secret(this, 'ClientSecret', {
181
188
  secretStringValue: proxyClient.userPoolClientSecret,
189
+ encryptionKey: new kms.Key(this, 'ClientSecretKey', {
190
+ enableKeyRotation: true,
191
+ }),
182
192
  });
183
193
 
184
194
  // The MCP server, authorizing JWTs issued for the same App Client
@@ -267,8 +277,9 @@ import {
267
277
  DcrProxy,
268
278
  MyGateway,
269
279
  UserIdentity,
270
- } from ':my-scope/common-constructs';
280
+ } from '@my-scope/common-constructs';
271
281
  import { OAuthScope } from 'aws-cdk-lib/aws-cognito';
282
+ import * as kms from 'aws-cdk-lib/aws-kms';
272
283
  import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
273
284
 
274
285
  const identity = new UserIdentity(this, 'Identity');
@@ -291,6 +302,9 @@ const proxyClient = identity.userPool.addClient('DcrProxyClient', {
291
302
  // Store the App Client secret in Secrets Manager for the token handler to read
292
303
  const clientSecret = new secretsmanager.Secret(this, 'ClientSecret', {
293
304
  secretStringValue: proxyClient.userPoolClientSecret,
305
+ encryptionKey: new kms.Key(this, 'ClientSecretKey', {
306
+ enableKeyRotation: true,
307
+ }),
294
308
  });
295
309
 
296
310
  // The gateway, authorizing JWTs issued for the same App Client
@@ -307,7 +321,7 @@ new DcrProxy(this, 'DcrProxy', {
307
321
  cognitoClientSecretArn: clientSecret.secretArn,
308
322
  cognitoHostedUiBase: identity.userPoolDomain.baseUrl(),
309
323
  // Use the gateway construct's URL rather than hardcoding it
310
- upstreamUrl: gateway.gateway.gatewayUrl,
324
+ upstreamUrl: gateway.gatewayUrl,
311
325
  });
312
326
  ```
313
327
  </Fragment>
@@ -341,7 +355,7 @@ resource "aws_secretsmanager_secret_version" "client_secret" {
341
355
 
342
356
  # The gateway, authorizing JWTs issued for the same App Client
343
357
  module "my_gateway" {
344
- source = "../../common/terraform/src/app/agentcore-gateway/my-gateway"
358
+ source = "../../common/terraform/src/app/gateways/my-gateway"
345
359
 
346
360
  user_pool_id = module.user_identity.user_pool_id
347
361
  user_pool_client_ids = [aws_cognito_user_pool_client.dcr_proxy.id]
@@ -466,8 +480,9 @@ import {
466
480
  DcrProxy,
467
481
  MyProjectMcpServer,
468
482
  UserIdentity,
469
- } from ':my-scope/common-constructs';
483
+ } from '@my-scope/common-constructs';
470
484
  import { OAuthScope, CfnManagedLoginBranding } from 'aws-cdk-lib/aws-cognito';
485
+ import * as kms from 'aws-cdk-lib/aws-kms';
471
486
  import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
472
487
 
473
488
  // The user pool created by ts#website#auth for your website users
@@ -492,6 +507,9 @@ new CfnManagedLoginBranding(this, 'DcrProxyClientBranding', {
492
507
 
493
508
  const clientSecret = new secretsmanager.Secret(this, 'ClientSecret', {
494
509
  secretStringValue: proxyClient.userPoolClientSecret,
510
+ encryptionKey: new kms.Key(this, 'ClientSecretKey', {
511
+ enableKeyRotation: true,
512
+ }),
495
513
  });
496
514
 
497
515
  const mcpServer = new MyProjectMcpServer(this, 'MyProjectMcpServer', {