@aws/nx-plugin-mcp 1.0.0-rc.6 → 1.0.0-rc.60

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 (191) hide show
  1. package/bin/aws-nx-mcp.js +11731 -10471
  2. package/docs/get_started/building-with-ai.mdx +116 -0
  3. package/docs/get_started/concepts.mdx +67 -0
  4. package/docs/get_started/existing-project.mdx +180 -0
  5. package/docs/get_started/graph-builder.mdx +39 -0
  6. package/docs/get_started/quick-start.mdx +266 -0
  7. package/docs/get_started/tutorials/contribute-generator.mdx +408 -0
  8. package/docs/get_started/tutorials/dungeon-game/1.mdx +1210 -0
  9. package/docs/get_started/tutorials/dungeon-game/2.mdx +237 -0
  10. package/docs/get_started/tutorials/dungeon-game/3.mdx +76 -0
  11. package/docs/get_started/tutorials/dungeon-game/4.mdx +162 -0
  12. package/docs/get_started/tutorials/dungeon-game/overview.mdx +145 -0
  13. package/docs/get_started/tutorials/dungeon-game/wrap-up.mdx +41 -0
  14. package/docs/get_started/tutorials/existing-project.mdx +4 -0
  15. package/docs/get_started/upgrading.mdx +147 -0
  16. package/docs/guides/agentcore-gateway.mdx +490 -0
  17. package/docs/guides/connection/agentcore-gateway-agent.mdx +222 -0
  18. package/docs/guides/connection/agentcore-gateway-gateway.mdx +154 -0
  19. package/docs/guides/connection/agentcore-gateway-mcp.mdx +134 -0
  20. package/docs/guides/connection/py-agent-a2a.mdx +48 -16
  21. package/docs/guides/connection/py-agent-dynamodb.mdx +116 -0
  22. package/docs/guides/connection/py-agent-gateway.mdx +178 -0
  23. package/docs/guides/connection/py-agent-mcp.mdx +43 -14
  24. package/docs/guides/connection/py-agent-rdb.mdx +178 -0
  25. package/docs/guides/connection/py-fast-api-dynamodb.mdx +56 -0
  26. package/docs/guides/connection/py-fast-api-rdb.mdx +184 -0
  27. package/docs/guides/connection/py-mcp-server-dynamodb.mdx +116 -0
  28. package/docs/guides/connection/py-mcp-server-rdb.mdx +187 -0
  29. package/docs/guides/connection/react-agentcore-gateway.mdx +112 -0
  30. package/docs/guides/connection/react-agui.mdx +13 -13
  31. package/docs/guides/connection/react-fastapi.mdx +38 -2
  32. package/docs/guides/connection/react-py-agent.mdx +9 -15
  33. package/docs/guides/connection/react-smithy.mdx +3 -3
  34. package/docs/guides/connection/react-trpc.mdx +1 -1
  35. package/docs/guides/connection/react-ts-agent.mdx +8 -8
  36. package/docs/guides/connection/smithy-dynamodb.mdx +5 -5
  37. package/docs/guides/connection/smithy-rdb.mdx +9 -9
  38. package/docs/guides/connection/trpc-dynamodb.mdx +5 -5
  39. package/docs/guides/connection/trpc-rdb.mdx +6 -6
  40. package/docs/guides/connection/ts-agent-a2a.mdx +14 -11
  41. package/docs/guides/connection/ts-agent-dynamodb.mdx +5 -5
  42. package/docs/guides/connection/ts-agent-gateway.mdx +143 -0
  43. package/docs/guides/connection/ts-agent-mcp.mdx +12 -9
  44. package/docs/guides/connection/ts-agent-rdb.mdx +70 -25
  45. package/docs/guides/connection/ts-mcp-server-dynamodb.mdx +5 -5
  46. package/docs/guides/connection/ts-mcp-server-rdb.mdx +68 -18
  47. package/docs/guides/connection.mdx +122 -5
  48. package/docs/guides/docker-bundling.mdx +68 -11
  49. package/docs/guides/fastapi.mdx +249 -9
  50. package/docs/guides/local-development.mdx +87 -0
  51. package/docs/guides/nx-generator.mdx +7 -2
  52. package/docs/guides/nx-migration.mdx +165 -0
  53. package/docs/guides/py-agent.mdx +264 -49
  54. package/docs/guides/py-dynamodb.mdx +476 -0
  55. package/docs/guides/py-mcp-server.mdx +61 -2
  56. package/docs/guides/py-rdb.mdx +265 -0
  57. package/docs/guides/python-lambda-function.mdx +1 -1
  58. package/docs/guides/react-website-auth.mdx +65 -4
  59. package/docs/guides/react-website.mdx +144 -25
  60. package/docs/guides/runtime-config.mdx +1 -1
  61. package/docs/guides/security.mdx +75 -0
  62. package/docs/guides/smithy-project.mdx +175 -0
  63. package/docs/guides/terraform-project.mdx +2 -2
  64. package/docs/guides/trpc.mdx +53 -16
  65. package/docs/guides/ts-agent.mdx +183 -10
  66. package/docs/guides/ts-dcr-proxy.mdx +569 -0
  67. package/docs/guides/ts-dynamodb.mdx +66 -242
  68. package/docs/guides/ts-lambda-function.mdx +1 -1
  69. package/docs/guides/ts-mcp-server.mdx +109 -29
  70. package/docs/guides/ts-nx-plugin.mdx +3 -3
  71. package/docs/guides/ts-rdb.mdx +113 -467
  72. package/docs/guides/ts-smithy-api.mdx +221 -16
  73. package/docs/guides/typescript-infrastructure.mdx +25 -15
  74. package/docs/guides/typescript-project.mdx +133 -26
  75. package/docs/guides/workspace.mdx +10 -3
  76. package/docs/snippets/agent/architecture.mdx +1 -1
  77. package/docs/snippets/agent/bedrock-deployment.mdx +9 -5
  78. package/docs/snippets/agent/runtime-arn.mdx +23 -2
  79. package/docs/snippets/agent/securing-your-agent.mdx +39 -0
  80. package/docs/snippets/api/access-logging.mdx +33 -0
  81. package/docs/snippets/api/cors-configuration-cdk-note.mdx +1 -1
  82. package/docs/snippets/api/cors-configuration-terraform-note.mdx +1 -1
  83. package/docs/snippets/api/type-safe-api-integrations.mdx +33 -2
  84. package/docs/snippets/api/waf-configuration.mdx +3 -3
  85. package/docs/snippets/connection/a2a-infrastructure.mdx +1 -1
  86. package/docs/snippets/connection/dynamodb-local-development.mdx +2 -2
  87. package/docs/snippets/connection/lambda-dynamodb-access.mdx +1 -1
  88. package/docs/snippets/connection/py-dynamodb-local-development.mdx +7 -0
  89. package/docs/snippets/connection/py-lambda-rdb-ssl-requirements.mdx +7 -0
  90. package/docs/snippets/connection/rdb-api-infrastructure.mdx +51 -19
  91. package/docs/snippets/dynamodb/deploying-table.mdx +166 -0
  92. package/docs/snippets/dynamodb/gsi-config.mdx +38 -0
  93. package/docs/snippets/dynamodb/infrastructure.mdx +33 -0
  94. package/docs/snippets/dynamodb/local-dev-start.mdx +13 -0
  95. package/docs/snippets/dynamodb/local-dev-windows.mdx +15 -0
  96. package/docs/snippets/lambda-function/deploying-your-function.mdx +3 -3
  97. package/docs/snippets/mcp/architecture.mdx +1 -1
  98. package/docs/snippets/mcp/bedrock-deployment.mdx +9 -5
  99. package/docs/snippets/mcp/config.mdx +3 -2
  100. package/docs/snippets/pdk-migration/example/01-migrate-api.mdx +7 -5
  101. package/docs/snippets/pdk-migration/example/02-migrate-website.mdx +29 -10
  102. package/docs/snippets/pdk-migration/example/03-migrate-infra.mdx +4 -4
  103. package/docs/snippets/pdk-migration/faq/type-safe-api.mdx +5 -111
  104. package/docs/snippets/prerequisites.mdx +2 -1
  105. package/docs/snippets/rdb/architecture.mdx +38 -0
  106. package/docs/snippets/rdb/cluster-instances.mdx +31 -0
  107. package/docs/snippets/rdb/deletion-protection.mdx +34 -0
  108. package/docs/snippets/rdb/deploying.mdx +187 -0
  109. package/docs/snippets/rdb/encryption-key-rotation.mdx +30 -0
  110. package/docs/snippets/rdb/engine-version.mdx +63 -0
  111. package/docs/snippets/rdb/infrastructure.mdx +35 -0
  112. package/docs/snippets/rdb/logging-mysql.mdx +5 -0
  113. package/docs/snippets/rdb/logging-postgres.mdx +5 -0
  114. package/docs/snippets/rdb/performance-insights.mdx +34 -0
  115. package/docs/snippets/rdb/rds-proxy.mdx +50 -0
  116. package/docs/snippets/rdb/removal-policy.mdx +57 -0
  117. package/docs/snippets/rdb/serverless-capacity.mdx +32 -0
  118. package/docs/snippets/required-prerequisites.mdx +1 -2
  119. package/docs/snippets/runtime-config-app-id-note.mdx +8 -0
  120. package/docs/snippets/shared-constructs.mdx +1 -1
  121. package/docs/snippets/trivy-image-scan.mdx +37 -0
  122. package/generators.json +145 -10
  123. package/package.json +1 -1
  124. package/src/agentcore-gateway/agent-connection/schema.json +31 -0
  125. package/src/agentcore-gateway/gateway-connection/schema.json +31 -0
  126. package/src/agentcore-gateway/mcp-connection/schema.json +31 -0
  127. package/src/agentcore-gateway/react-connection/schema.json +31 -0
  128. package/src/agentcore-gateway/schema.json +72 -0
  129. package/src/connection/schema.json +5 -0
  130. package/src/infra/app/schema.json +5 -0
  131. package/src/init/schema.json +35 -0
  132. package/src/internal/test-matrix/schema.json +21 -0
  133. package/src/license/schema.json +5 -0
  134. package/src/preset/schema.json +16 -5
  135. package/src/py/agent/a2a-connection/schema.json +5 -0
  136. package/src/py/agent/gateway-connection/schema.json +31 -0
  137. package/src/py/agent/mcp-connection/schema.json +5 -0
  138. package/src/py/agent/react-connection/schema.json +5 -0
  139. package/src/py/agent/schema.json +15 -1
  140. package/src/py/api/schema.json +5 -0
  141. package/src/py/dynamodb/agent-connection/schema.json +27 -0
  142. package/src/py/dynamodb/fast-api-connection/schema.json +23 -0
  143. package/src/py/dynamodb/mcp-server-connection/schema.json +27 -0
  144. package/src/py/dynamodb/schema.json +76 -0
  145. package/src/py/fast-api/react/schema.json +5 -0
  146. package/src/py/fast-api/schema.json +6 -0
  147. package/src/py/lambda-function/schema.json +5 -0
  148. package/src/py/mcp-server/schema.json +6 -0
  149. package/src/py/project/schema.json +5 -0
  150. package/src/py/rdb/agent-connection/schema.json +27 -0
  151. package/src/py/rdb/fast-api-connection/schema.json +23 -0
  152. package/src/py/rdb/mcp-server-connection/schema.json +27 -0
  153. package/src/py/rdb/schema.json +78 -0
  154. package/src/smithy/project/schema.json +28 -1
  155. package/src/smithy/react-connection/schema.json +5 -0
  156. package/src/smithy/ts/api/schema.json +6 -0
  157. package/src/terraform/project/schema.json +5 -0
  158. package/src/trpc/backend/schema.json +6 -0
  159. package/src/trpc/react/schema.json +5 -0
  160. package/src/ts/agent/a2a-connection/schema.json +5 -0
  161. package/src/ts/agent/gateway-connection/schema.json +31 -0
  162. package/src/ts/agent/mcp-connection/schema.json +5 -0
  163. package/src/ts/agent/react-connection/schema.json +5 -0
  164. package/src/ts/agent/schema.json +14 -0
  165. package/src/ts/api/schema.json +5 -0
  166. package/src/ts/astro-docs/schema.json +3 -3
  167. package/src/ts/dcr-proxy/schema.json +44 -0
  168. package/src/ts/docs/schema.json +3 -3
  169. package/src/ts/dynamodb/agent-connection/schema.json +5 -0
  170. package/src/ts/dynamodb/mcp-server-connection/schema.json +5 -0
  171. package/src/ts/dynamodb/schema.json +26 -2
  172. package/src/ts/dynamodb/smithy-connection/schema.json +5 -0
  173. package/src/ts/dynamodb/trpc-connection/schema.json +5 -0
  174. package/src/ts/lambda-function/schema.json +5 -0
  175. package/src/ts/lib/schema.json +5 -0
  176. package/src/ts/mcp-server/schema.json +6 -0
  177. package/src/ts/nx-generator/schema.json +5 -0
  178. package/src/ts/nx-migration/schema.json +63 -0
  179. package/src/ts/nx-plugin/schema.json +5 -0
  180. package/src/ts/rdb/agent-connection/schema.json +5 -0
  181. package/src/ts/rdb/mcp-server-connection/schema.json +5 -0
  182. package/src/ts/rdb/schema.json +7 -1
  183. package/src/ts/rdb/smithy-connection/schema.json +5 -0
  184. package/src/ts/rdb/trpc-connection/schema.json +5 -0
  185. package/src/ts/react-website/app/schema.json +12 -6
  186. package/src/ts/react-website/cognito-auth/schema.json +5 -0
  187. package/src/ts/react-website/runtime-config/schema.json +5 -0
  188. package/src/ts/website/app/schema.json +11 -6
  189. package/src/ts/website/auth/schema.json +5 -0
  190. /package/docs/snippets/connection/{lambda-rdb-ssl-requirements.mdx → ts-lambda-rdb-ssl-requirements.mdx} +0 -0
  191. /package/docs/snippets/connection/{mcp-server-rdb-ssl-requirements.mdx → ts-mcp-server-rdb-ssl-requirements.mdx} +0 -0
@@ -0,0 +1,1210 @@
1
+ ---
2
+ title: Set up a monorepo
3
+ description: A walkthrough of how to build an agentic AI-powered dungeon adventure game using the @aws/nx-plugin.
4
+ ---
5
+
6
+ import { Aside, Code, FileTree, Steps, Tabs, TabItem } from '@astrojs/starlight/components';
7
+ import { Image } from 'astro:assets';
8
+ import Link from '@components/link.astro';
9
+ import Drawer from '@components/drawer.astro';
10
+ import RunGenerator from '@components/run-generator.astro';
11
+ import NxCommands from '@components/nx-commands.astro';
12
+ import InstallCommand from '@components/install-command.astro';
13
+ import CreateNxWorkspaceCommand from '@components/create-nx-workspace-command.astro';
14
+ import PackageManagerShortCommand from '@components/package-manager-short-command.astro';
15
+ import E2EDiff from '@components/e2e-diff.astro';
16
+
17
+ import dungeonAdventureArchitecturePng from '@assets/dungeon-game-architecture.png'
18
+ import baselineWebsitePng from '@assets/baseline-website.png'
19
+ import baselineGamePng from '@assets/baseline-game.png'
20
+ import nxGraphPng from '@assets/nx-graph.png'
21
+ import gameSelectPng from '@assets/game-select.png'
22
+ import gameConversationPng from '@assets/game-conversation.png'
23
+
24
+ ## Task 1: Create a monorepo
25
+
26
+ To create a new monorepo, from within your desired directory, run the following command:
27
+
28
+ <CreateNxWorkspaceCommand workspace="dungeon-adventure" iac="cdk" />
29
+
30
+ :::note[CDK as IaC Provider]
31
+ We use `--iac=cdk` as we will use CDK for infrastructure as code in this tutorial. The Nx Plugin for AWS also supports `terraform`.
32
+ :::
33
+
34
+ This will set up a NX monorepo within the `dungeon-adventure` directory. When you open the directory in VSCode, you will see this file structure:
35
+
36
+ <FileTree>
37
+ - .nx/
38
+ - .vscode/
39
+ - node_modules/
40
+ - packages/ this is where your sub-projects will reside
41
+ - .gitignore
42
+ - biome.json configures Biome for linting and formatting
43
+ - nx.json configures the Nx CLI and monorepo defaults
44
+ - package.json all node dependencies are defined here
45
+ - pnpm-lock.yaml or bun.lock, yarn.lock, package-lock.json depending on package manager
46
+ - pnpm-workspace.yaml if using pnpm
47
+ - README.md
48
+ - tsconfig.base.json all node based sub-projects extend this
49
+ - tsconfig.json
50
+ - aws-nx-plugin.config.mts configuraton for the Nx Plugin for AWS
51
+ </FileTree>
52
+
53
+ We can now start creating our different sub-projects using the `@aws/nx-plugin`.
54
+
55
+ <Aside type="tip" title="Commit Often">It is best practice to ensure all your unstaged files are committed in Git before running any generators. This allows you to see what has changed after running your generator via `git diff`.</Aside>
56
+
57
+ ## Task 2: Create a Game API
58
+
59
+ First, let's create our Game API. To do this, create a tRPC API called `GameApi` using these steps:
60
+
61
+ <RunGenerator generator="ts#api" requiredParameters={{ name: "GameApi", framework: "trpc" }} noInteractive />
62
+
63
+ <br />
64
+
65
+ You will see some new files appear in your file tree.
66
+
67
+ <Aside title="Root Dependencies">
68
+ The root `package.json` is now configured with a `type` of `module`, which means ESM is the default module type for all node based sub-projects vended by the `@aws/nx-plugin`.
69
+ For more details on working with TypeScript projects, refer to the <Link path="guides/typescript-project">ts#project generator guide</Link>.
70
+ </Aside>
71
+
72
+ <Drawer title="ts#api updated files" trigger="Click here to examine these files in more detail.">
73
+ Below is a list of all files which have been generated by the `ts#api` generator. We are going to examine some of the key files highlighted in the file tree:
74
+ <FileTree>
75
+ - packages/
76
+ - common/
77
+ - constructs/
78
+ - src/
79
+ - app/ app specific cdk constructs
80
+ - apis/
81
+ - **game-api.ts** cdk construct to create your tRPC API
82
+ - index.ts
83
+ - ...
84
+ - index.ts
85
+ - core/ generic cdk constructs
86
+ - api/
87
+ - rest-api.ts base cdk construct for an API Gateway Rest API
88
+ - trpc-utils.ts utilities for trpc API CDK constructs
89
+ - utils.ts utilities for API constructs
90
+ - index.ts
91
+ - runtime-config.ts
92
+ - index.ts
93
+ - project.json
94
+ - ...
95
+ - game-api/ tRPC API
96
+ - src/
97
+ - client/ vanilla client typically used for ts machine to machine calls
98
+ - index.ts
99
+ - middleware/ powertools instrumentation
100
+ - error.ts
101
+ - index.ts
102
+ - logger.ts
103
+ - metrics.ts
104
+ - tracer.ts
105
+ - schema/ definitions of inputs and outputs for your API
106
+ - index.ts
107
+ - **echo.ts** sample input and output schema
108
+ - z-async-iterable.ts wrapper Zod schema for tRPC subscription output
109
+ - procedures/ specific implementations for your API procedures/routes
110
+ - **echo.ts** sample procedure implementation
111
+ - index.ts
112
+ - init.ts sets up context and middleware
113
+ - handler.ts Lambda handler entrypoint (uses response streaming for REST APIs)
114
+ - local-server.ts used when running the tRPC server locally
115
+ - **router.ts** defines the tRPC router and all procedures
116
+ - project.json
117
+ - ...
118
+ - vitest.workspace.ts
119
+ </FileTree>
120
+
121
+ Let us look at these key files:
122
+
123
+ ```ts {7}
124
+ // packages/game-api/src/router.ts
125
+ import { echo } from './procedures/echo.js';
126
+ import { t } from './init.js';
127
+
128
+ export const router = t.router;
129
+
130
+ export const appRouter = router({
131
+ echo,
132
+ });
133
+
134
+ export type AppRouter = typeof appRouter;
135
+ ```
136
+ The router defines the tRPC router for your API and is the place where you will declare all of your API methods. As you can see above, we have a method called `echo` with it's implementation in the `./procedures/echo.ts` file. The Lambda handler entrypoint is in `handler.ts`, which is configured automatically by the generator.
137
+
138
+ ```ts {7-10}
139
+ // packages/game-api/src/procedures/echo.ts
140
+ import { publicProcedure } from '../init.js';
141
+ import {
142
+ EchoInputSchema,
143
+ EchoOutputSchema,
144
+ } from '../schema/echo.js';
145
+
146
+ export const echo = publicProcedure
147
+ .input(EchoInputSchema)
148
+ .output(EchoOutputSchema)
149
+ .query((opts) => ({ message: opts.input.message }));
150
+ ```
151
+
152
+ This file is the implementation of the `echo` method and as you can see is strongly typed by declaring its input and output data structures.
153
+
154
+ ```ts
155
+ // packages/game-api/src/schema/echo.ts
156
+ import { z } from 'zod';
157
+
158
+ export const EchoInputSchema = z.object({
159
+ message: z.string().max(1024),
160
+ });
161
+
162
+ export type IEchoInput = z.TypeOf<typeof EchoInputSchema>;
163
+
164
+ export const EchoOutputSchema = z.object({
165
+ message: z.string().max(1024),
166
+ });
167
+
168
+ export type IEchoOutput = z.TypeOf<typeof EchoOutputSchema>;
169
+ ```
170
+
171
+ All tRPC schema definitions are defined using [Zod](https://zod.dev/) and are exported as typescript types via the `z.TypeOf` syntax.
172
+
173
+ ```ts
174
+ // packages/common/constructs/src/app/apis/game-api.ts
175
+ import { Construct } from 'constructs';
176
+ import * as url from 'url';
177
+ import { Distribution } from 'aws-cdk-lib/aws-cloudfront';
178
+ import {
179
+ Code,
180
+ Runtime,
181
+ Function,
182
+ FunctionProps,
183
+ Tracing,
184
+ } from 'aws-cdk-lib/aws-lambda';
185
+ import {
186
+ AuthorizationType,
187
+ LambdaIntegration,
188
+ ResponseTransferMode,
189
+ } from 'aws-cdk-lib/aws-apigateway';
190
+ import { Aspects, Duration } from 'aws-cdk-lib';
191
+ import {
192
+ PolicyDocument,
193
+ PolicyStatement,
194
+ Effect,
195
+ AnyPrincipal,
196
+ IGrantable,
197
+ Grant,
198
+ } from 'aws-cdk-lib/aws-iam';
199
+ import {
200
+ IntegrationBuilder,
201
+ RestApiIntegration,
202
+ } from '../../core/api/utils.js';
203
+ import { findCloudFrontDomainNames } from '../../core/cloudfront.js';
204
+ import { AddCorsPreflightAspect, RestApi } from '../../core/api/rest-api.js';
205
+ import { Procedures, routerToOperations } from '../../core/api/trpc-utils.js';
206
+ import { AppRouter, appRouter } from '@dungeon-adventure/game-api';
207
+
208
+ // String union type for all API operation names
209
+ type Operations = Procedures<AppRouter>;
210
+
211
+ /**
212
+ * Properties for creating a GameApi construct
213
+ *
214
+ * @template TIntegrations - Map of operation names to their integrations
215
+ */
216
+ export interface GameApiProps<
217
+ TIntegrations extends Record<Operations, RestApiIntegration>,
218
+ > {
219
+ /**
220
+ * Map of operation names to their API Gateway integrations
221
+ */
222
+ integrations: TIntegrations;
223
+ }
224
+
225
+ /**
226
+ * A CDK construct that creates and configures an AWS API Gateway REST API
227
+ * specifically for GameApi.
228
+ * @template TIntegrations - Map of operation names to their integrations
229
+ */
230
+ export class GameApi<
231
+ TIntegrations extends Record<Operations, RestApiIntegration>,
232
+ > extends RestApi<Operations, TIntegrations> {
233
+ private allowedOrigins: readonly string[] = ['*'];
234
+
235
+ /**
236
+ * Creates default integrations for all operations, which implement each operation as
237
+ * its own individual lambda function.
238
+ *
239
+ * @param scope - The CDK construct scope
240
+ * @returns An IntegrationBuilder with default lambda integrations
241
+ */
242
+ public static defaultIntegrations = (scope: Construct) => {
243
+ return IntegrationBuilder.rest({
244
+ operations: routerToOperations(appRouter),
245
+ defaultIntegrationOptions: {
246
+ runtime: Runtime.NODEJS_LATEST,
247
+ handler: 'index.handler',
248
+ code: Code.fromAsset(
249
+ url.fileURLToPath(
250
+ new URL(
251
+ '../../../../../../dist/packages/game-api/bundle',
252
+ import.meta.url,
253
+ ),
254
+ ),
255
+ ),
256
+ timeout: Duration.seconds(30),
257
+ tracing: Tracing.ACTIVE,
258
+ } as FunctionProps,
259
+ buildDefaultIntegration: (op, props: FunctionProps) => {
260
+ const handler = new Function(scope, `GameApi${op}Handler`, props);
261
+ return {
262
+ handler,
263
+ integration: new LambdaIntegration(handler, {
264
+ responseTransferMode: ResponseTransferMode.STREAM,
265
+ }),
266
+ };
267
+ },
268
+ });
269
+ };
270
+
271
+ constructor(
272
+ scope: Construct,
273
+ id: string,
274
+ props: GameApiProps<TIntegrations>,
275
+ ) {
276
+ super(scope, id, {
277
+ apiName: 'GameApi',
278
+ defaultMethodOptions: {
279
+ authorizationType: AuthorizationType.IAM,
280
+ },
281
+ deployOptions: {
282
+ tracingEnabled: true,
283
+ },
284
+ policy: new PolicyDocument({
285
+ statements: [
286
+ // Open up OPTIONS to allow browsers to make unauthenticated preflight requests
287
+ new PolicyStatement({
288
+ effect: Effect.ALLOW,
289
+ principals: [new AnyPrincipal()],
290
+ actions: ['execute-api:Invoke'],
291
+ resources: ['execute-api:/*/OPTIONS/*'],
292
+ }),
293
+ ],
294
+ }),
295
+ operations: routerToOperations(appRouter),
296
+ ...props,
297
+ });
298
+ Aspects.of(this).add(new AddCorsPreflightAspect(() => this.allowedOrigins));
299
+ }
300
+
301
+ /**
302
+ * Restricts CORS to the provided origins
303
+ *
304
+ * Configures the CloudFront distribution domains or origin strings
305
+ * as the only permitted CORS origins in API Gateway preflight responses and the AWS
306
+ * Lambda integrations. Any custom domain names (aliases) configured on a CloudFront
307
+ * distribution are included automatically alongside its default `*.cloudfront.net`
308
+ * domain.
309
+ *
310
+ * @param origins - The origin strings, CloudFront distributions, or objects containing a CloudFront distribution to grant CORS from
311
+ */
312
+ public restrictCorsTo(
313
+ ...origins: (string | Distribution | { cloudFrontDistribution: Distribution })[]
314
+ ) {
315
+ const allowedOrigins = origins.flatMap((origin) =>
316
+ typeof origin === 'string'
317
+ ? [origin]
318
+ : findCloudFrontDomainNames(
319
+ 'cloudFrontDistribution' in origin
320
+ ? origin.cloudFrontDistribution
321
+ : origin,
322
+ ).map((domain) => `https://${domain}`),
323
+ );
324
+
325
+ this.allowedOrigins = allowedOrigins;
326
+
327
+ // Set ALLOWED_ORIGINS environment variable for all Lambda integrations
328
+ Object.values(this.integrations).forEach((integration) => {
329
+ if ('handler' in integration && integration.handler instanceof Function) {
330
+ integration.handler.addEnvironment(
331
+ 'ALLOWED_ORIGINS',
332
+ allowedOrigins.join(','),
333
+ );
334
+ }
335
+ });
336
+ }
337
+
338
+ /**
339
+ * Grants IAM permissions to invoke any method on this API.
340
+ *
341
+ * @param grantee - The IAM principal to grant permissions to
342
+ */
343
+ public grantInvokeAccess(grantee: IGrantable) {
344
+ // Here we grant grantee permission to call the api.
345
+ // Machine to machine fine-grained access can be defined here using more specific principals (eg roles or
346
+ // users) and resources (eg which api paths may be invoked by which principal) if required.
347
+ this.api.addToResourcePolicy(
348
+ new PolicyStatement({
349
+ effect: Effect.ALLOW,
350
+ principals: [grantee.grantPrincipal],
351
+ actions: ['execute-api:Invoke'],
352
+ resources: ['execute-api:/*'],
353
+ }),
354
+ );
355
+
356
+ Grant.addToPrincipal({
357
+ grantee,
358
+ actions: ['execute-api:Invoke'],
359
+ resourceArns: [this.api.arnForExecuteApi('*', '/*', '*')],
360
+ });
361
+ }
362
+ }
363
+ ```
364
+
365
+ This is the CDK construct that defines our `GameApi`. It provides a `defaultIntegrations` method which automatically creates a Lambda function for each procedure in our tRPC API, pointing to the bundled API implementation. This means that at `cdk synth` time, bundling does not occur (opposed to using [NodeJsFunction](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda_nodejs.NodejsFunction.html)) as we have already bundled it as part of the backend project's build target.
366
+
367
+ </Drawer>
368
+
369
+ ## Task 3: Create Story agents
370
+
371
+ Now let's create our Story Agents.
372
+
373
+ ### Story agent: Python project
374
+
375
+ To create a Python project:
376
+
377
+ <RunGenerator generator="py#project" requiredParameters={{name:"story"}} noInteractive />
378
+
379
+ You will see some new files appear in your file tree.
380
+ <Drawer title="py#project updated files" trigger="Click here to examine these files in more detail.">
381
+ The `py#project` generates these files:
382
+
383
+ <FileTree>
384
+ - .venv/ single virtual env for monorepo
385
+ - packages/
386
+ - story/
387
+ - dungeon_adventure_story/ python module
388
+ - tests/
389
+ - .python-version
390
+ - pyproject.toml
391
+ - project.json
392
+ - .python-version pinned uv python version
393
+ - pyproject.toml
394
+ - uv.lock
395
+ </FileTree>
396
+
397
+ This has configured a Python project and [UV Workspace](https://docs.astral.sh/uv/concepts/projects/workspaces/) with shared virtual environment.
398
+
399
+ </Drawer>
400
+
401
+ ### Story agent
402
+
403
+ To add a Strands agent to the project with the `py#agent` generator:
404
+
405
+ <RunGenerator generator="py#agent" requiredParameters={{project:"story", auth:"cognito", protocol:"ag-ui"}} noInteractive />
406
+
407
+ :::note[AG-UI protocol]
408
+ We choose `--protocol=ag-ui` so the agent speaks the [Agent-User Interaction protocol](https://docs.copilotkit.ai/aws-strands/protocol) — this lets our React website talk to it directly via [CopilotKit](https://docs.copilotkit.ai/), with streaming, tool calls, and conversation history handled by the protocol instead of a hand-rolled HTTP client.
409
+ :::
410
+
411
+ You will see some new files appear in your file tree.
412
+ <Drawer title="py#agent updated files" trigger="Click here to examine these files in more detail.">
413
+ The `py#agent` generates these files:
414
+
415
+ <FileTree>
416
+ - packages/
417
+ - story/
418
+ - dungeon_adventure_story/ python module
419
+ - agent/
420
+ - init.py sets up the FastAPI app and middleware
421
+ - main.py entrypoint for your agent in Bedrock AgentCore Runtime
422
+ - agent.py defines an example agent and tools
423
+ - Dockerfile defines the docker image for deployment to AgentCore Runtime
424
+ - common/constructs/
425
+ - src
426
+ - app/agents/story-agent/
427
+ - story-agent.ts construct for deploying your Story agent to AgentCore Runtime
428
+ </FileTree>
429
+
430
+ Let's take a look at some of the files in detail:
431
+
432
+ ```python
433
+ # agent/agent.py
434
+ from contextlib import contextmanager
435
+
436
+ from strands import Agent, tool
437
+ from strands_tools import current_time
438
+
439
+
440
+ @tool
441
+ def subtract(a: int, b: int) -> int:
442
+ return a - b
443
+
444
+
445
+ @contextmanager
446
+ def get_agent():
447
+ yield Agent(
448
+ name="StoryAgent",
449
+ description="StoryAgent Agent",
450
+ system_prompt="""
451
+ You are a mathematical wizard.
452
+ Use your tools for mathematical tasks.
453
+ Refer to tools as your 'spellbook'.
454
+ """,
455
+ tools=[subtract, current_time],
456
+ )
457
+ ```
458
+
459
+ This creates an example Strands agent and defines a subtraction tool.
460
+
461
+ ```python
462
+ # agent/main.py
463
+ import logging
464
+ import uuid
465
+
466
+ from ag_ui_strands import StrandsAgent, create_strands_app
467
+ from dungeon_adventure_agent_connection import session_id_context
468
+ from fastapi import Request
469
+ from starlette.middleware.base import BaseHTTPMiddleware
470
+
471
+ from .agent import get_agent
472
+
473
+ logging.basicConfig(level=logging.INFO)
474
+
475
+ SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
476
+
477
+ # Create AG-UI agent wrapper
478
+ _agent_ctx = get_agent()
479
+ _agent = _agent_ctx.__enter__()
480
+
481
+ agui_agent = StrandsAgent(
482
+ agent=_agent,
483
+ name="StoryAgent",
484
+ description="An Agent exposed via the AG-UI protocol.",
485
+ )
486
+
487
+
488
+ class _SessionIdMiddleware(BaseHTTPMiddleware):
489
+ """Bind the session ID for this request so downstream MCP / A2A clients forward it on outbound calls."""
490
+
491
+ async def dispatch(self, request: Request, call_next):
492
+ session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
493
+ with session_id_context(session_id):
494
+ return await call_next(request)
495
+
496
+
497
+ # Create FastAPI app with AG-UI endpoint and health check
498
+ app = create_strands_app(agui_agent, path="/invocations")
499
+ app.add_middleware(_SessionIdMiddleware)
500
+ ```
501
+
502
+ This is the entrypoint for the agent. Because we selected `--protocol=ag-ui`, the generator wraps our Strands `Agent` with `StrandsAgent` from [`ag_ui_strands`](https://docs.copilotkit.ai/aws-strands/integration) and mounts it on a FastAPI app that speaks the [AG-UI protocol](https://docs.copilotkit.ai/aws-strands/protocol) — this is what CopilotKit will talk to from the React website. The `_SessionIdMiddleware` binds the inbound AgentCore runtime session ID onto a `ContextVar` so any downstream MCP/A2A client we wire up later (e.g. the Inventory MCP server in <Link path="get_started/tutorials/dungeon-game/2">Module 2</Link>) automatically forwards it on its outbound calls. In <Link path="get_started/tutorials/dungeon-game/3">Module 3</Link> we'll also add a `session_manager_provider` so each thread id gets its own `S3SessionManager` and conversation history persists across turns.
503
+
504
+ ```ts
505
+ // common/constructs/src/app/agents/story-agent.ts
506
+ import { Lazy, Names } from 'aws-cdk-lib';
507
+ import { Platform } from 'aws-cdk-lib/aws-ecr-assets';
508
+ import { Construct } from 'constructs';
509
+ import { execSync } from 'child_process';
510
+ import * as path from 'path';
511
+ import * as url from 'url';
512
+ import {
513
+ AgentRuntimeArtifact,
514
+ ProtocolType,
515
+ Runtime,
516
+ RuntimeProps,
517
+ } from 'aws-cdk-lib/aws-bedrockagentcore';
518
+ import { IGrantable, IPrincipal } from 'aws-cdk-lib/aws-iam';
519
+
520
+ export type StoryAgentProps = Omit<
521
+ RuntimeProps,
522
+ 'runtimeName' | 'protocolConfiguration' | 'agentRuntimeArtifact'
523
+ >;
524
+
525
+ export class StoryAgent extends Construct implements IGrantable {
526
+ public readonly dockerImage: AgentRuntimeArtifact;
527
+ public readonly agentCoreRuntime: Runtime;
528
+
529
+ constructor(scope: Construct, id: string, props?: StoryAgentProps) {
530
+ super(scope, id);
531
+
532
+ this.dockerImage = AgentRuntimeArtifact.fromAsset(
533
+ path.dirname(url.fileURLToPath(new URL(import.meta.url))),
534
+ {
535
+ platform: Platform.LINUX_ARM64,
536
+ extraHash: execSync(
537
+ `docker inspect dungeon-adventure-story-agent:latest --format '{{.Id}}'`,
538
+ { encoding: 'utf-8' },
539
+ ).trim(),
540
+ },
541
+ );
542
+
543
+ this.agentCoreRuntime = new Runtime(this, 'StoryAgent', {
544
+ runtimeName: Lazy.string({
545
+ produce: () =>
546
+ Names.uniqueResourceName(this.agentCoreRuntime, { maxLength: 40 }),
547
+ }),
548
+ protocolConfiguration: ProtocolType.HTTP,
549
+ agentRuntimeArtifact: this.dockerImage,
550
+ ...props,
551
+ });
552
+ }
553
+
554
+ public get grantPrincipal(): IPrincipal {
555
+ return this.agentCoreRuntime.grantPrincipal;
556
+ }
557
+ }
558
+ ```
559
+
560
+ This configures a CDK `AgentRuntimeArtifact` which uploads your agent Docker image to ECR, and hosts it using AgentCore Runtime.
561
+
562
+ You may notice an extra `Dockerfile`, that references the Docker image from the `story` project, allowing us to co-locate the Dockerfile and agent source code.
563
+
564
+ </Drawer>
565
+
566
+ ## Task 4: Set up inventory tools
567
+
568
+ ### Inventory: TypeScript project
569
+
570
+ Let us create an MCP server to provide tools for our Story Agent to manage a player's inventory.
571
+
572
+ First, we create a TypeScript project:
573
+
574
+ <RunGenerator generator="ts#project" requiredParameters={{name:"inventory"}} noInteractive />
575
+
576
+ This will create an empty TypeScript project.
577
+
578
+ <Drawer title="ts#project updated files" trigger="Click here to examine these files in more detail.">
579
+ The `ts#project` generator generates these files.
580
+
581
+ <FileTree>
582
+ - packages/
583
+ - inventory/
584
+ - src/
585
+ - index.ts entry point with example function
586
+ - project.json project configuration
587
+ - vitest.config.mts test configuration
588
+ - tsconfig.json base typescript configuration for the project
589
+ - tsconfig.lib.json typescript configuration for the project targeted for compilation and bundling
590
+ - tsconfig.spec.json typescript configuration for tests
591
+ - tsconfig.base.json updated to configure an alias for other projects to reference this
592
+ </FileTree>
593
+
594
+ </Drawer>
595
+
596
+ ### Inventory: MCP server
597
+
598
+ Next, we'll add an MCP server to our TypeScript project:
599
+
600
+ <RunGenerator generator="ts#mcp-server" requiredParameters={{project:"inventory"}} noInteractive />
601
+
602
+ This will add an MCP server.
603
+ <Drawer title="ts#mcp-server updated files" trigger="Click here to examine these files in more detail.">
604
+ The `ts#mcp-server` generator generates these files.
605
+
606
+ <FileTree>
607
+ - packages/
608
+ - inventory/
609
+ - src/mcp-server/
610
+ - index.ts barrel export
611
+ - server.ts creates the MCP server
612
+ - tools/
613
+ - divide.ts example tool
614
+ - resources/
615
+ - sample-guidance.ts example resource
616
+ - stdio.ts entry point for MCP with STDIO transport
617
+ - http.ts entry point for MCP with Streamable HTTP transport
618
+ - Dockerfile builds the image for AgentCore Runtime
619
+ - rolldown.config.ts configuration for bundling the MCP server for deployment to AgentCore
620
+ - common/constructs/
621
+ - src
622
+ - app/mcp-servers/inventory-mcp-server/
623
+ - inventory-mcp-server.ts construct for deploying your inventory MCP server to AgentCore Runtime
624
+ </FileTree>
625
+
626
+ </Drawer>
627
+
628
+ ## Task 5: Create the game database
629
+
630
+ Our game state — saved games and each player's inventory — lives in [Amazon DynamoDB](https://aws.amazon.com/dynamodb/). Create a DynamoDB project called `DungeonDb` with the `ts#dynamodb` generator:
631
+
632
+ <RunGenerator generator="ts#dynamodb" requiredParameters={{name:"DungeonDb"}} noInteractive />
633
+
634
+ :::tip[Local-first development]
635
+ The `ts#dynamodb` generator vends a `dev` target that runs [DynamoDB Local](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.html) in a container. Combined with the `connection` generator (which we run below), this means **the entire game runs on your machine without a deployment** until the end of the tutorial.
636
+ :::
637
+
638
+ You will see some new files appear in your file tree.
639
+ <Drawer title="ts#dynamodb updated files" trigger="Click here to examine these files in more detail.">
640
+ The `ts#dynamodb` generator generates these files.
641
+
642
+ <FileTree>
643
+ - packages/
644
+ - dungeon-db/
645
+ - config.json DynamoDB configuration including port, table name, container settings and Global Secondary Indexes
646
+ - src/
647
+ - index.ts entry point and exports
648
+ - client.ts DynamoDB client singleton and table name resolution
649
+ - entities/
650
+ - example.ts example ElectroDB entity (we will replace this)
651
+ - index.ts entity exports
652
+ - project.json adds the `dev` and `pull-image` targets
653
+ - common/
654
+ - scripts/
655
+ - src/
656
+ - dynamodb/
657
+ - create-local-table.ts creates the table in DynamoDB Local
658
+ - pull-image.ts pulls the DynamoDB Local image
659
+ - start-container.ts starts the DynamoDB Local container
660
+ - constructs/
661
+ - src/
662
+ - app/dynamodb/
663
+ - dungeon-db.ts construct for provisioning your table
664
+ - core/
665
+ - dynamodb.ts generic DynamoDB table construct
666
+ </FileTree>
667
+
668
+ The generated `src/client.ts` exports `getDynamoDBClient()` and `resolveTableName()`. When `LOCAL_DEV=true` (set automatically by the `dev` targets) these connect to DynamoDB Local; otherwise they connect to AWS and resolve the deployed table name from <Link path="guides/runtime-config">Runtime Configuration</Link>. We'll model our `Game` and `Inventory` entities in this project in <Link path="get_started/tutorials/dungeon-game/2">Module 2</Link>.
669
+
670
+ For more details, refer to the <Link path="guides/ts-dynamodb">ts#dynamodb generator guide</Link>.
671
+
672
+ </Drawer>
673
+
674
+ ## Task 6: Create the User Interface (UI)
675
+
676
+ In this task, we will create the UI which will allow you to interact with the game.
677
+
678
+ ### Game UI: Website
679
+
680
+ To create the UI, create a website called `GameUI` using these steps:
681
+
682
+ <RunGenerator generator="ts#website" requiredParameters={{name:"GameUI", ux:"shadcn"}} noInteractive />
683
+
684
+ :::note[Shadcn UI]
685
+ We select `--ux=shadcn` so the generated website uses [shadcn/ui](https://ui.shadcn.com/) components styled with Tailwind — all our route code uses shadcn primitives like `Card`, `Button`, and `Input`, and the `connection` generator later wires a matching shadcn-themed [CopilotKit](https://docs.copilotkit.ai/) chat surface.
686
+ :::
687
+
688
+ You will see some new files appear in your file tree.
689
+
690
+ <Drawer title="ts#website updated files" trigger="Click here to examine these files in more detail.">
691
+ The `ts#website` generates these files. Let us examine some of the key files highlighted in the file tree:
692
+
693
+ <FileTree>
694
+ - packages/
695
+ - common/
696
+ - constructs/
697
+ - src/
698
+ - app/ app specific cdk constructs
699
+ - static-websites/
700
+ - **game-ui.ts** cdk construct to create your Game UI
701
+ - core/
702
+ - static-website.ts generic static website construct
703
+ - game-ui/
704
+ - public/
705
+ - src/
706
+ - components/
707
+ - AppLayout/
708
+ - index.tsx overall page layout using shadcn `SidebarProvider` + header
709
+ - app-sidebar.tsx default shadcn sidebar with nav items
710
+ - alert.tsx, spinner.tsx shadcn-wrapped feedback primitives
711
+ - routes/ @tanstack/react-router file based routes
712
+ - **index.tsx** root '/' page
713
+ - __root.tsx all pages use this component as a base
714
+ - config.ts
715
+ - **main.tsx** React entrypoint
716
+ - routeTree.gen.ts this is automatically updated by @tanstack/react-router
717
+ - styles.css imports shared shadcn globals (Tailwind v4)
718
+ - index.html
719
+ - project.json
720
+ - vite.config.mts
721
+ - ...
722
+ - common/
723
+ - shadcn/ shared shadcn/ui library (theme tokens, `Button`, `Card`, `Input`, `Sidebar`, …) imported by every `ux=shadcn` website
724
+ - src/components/ui/*
725
+ - src/styles/globals.css Tailwind + shadcn design tokens
726
+ - ...
727
+ </FileTree>
728
+
729
+ ```ts
730
+ // packages/common/constructs/src/app/static-websites/game-ui.ts
731
+ import * as url from 'url';
732
+ import { Construct } from 'constructs';
733
+ import { StaticWebsite } from '../../core/index.js';
734
+
735
+ export class GameUI extends StaticWebsite {
736
+ constructor(scope: Construct, id: string) {
737
+ super(scope, id, {
738
+ websiteName: 'GameUI',
739
+ websiteFilePath: url.fileURLToPath(
740
+ new URL(
741
+ '../../../../../../dist/packages/game-ui/bundle',
742
+ import.meta.url,
743
+ ),
744
+ ),
745
+ });
746
+ }
747
+ }
748
+ ```
749
+
750
+ This is the CDK construct that defines our GameUI. It has already configured the file path to the generated bundle for our Vite based UI. This means that at `build` time, bundling occurs within the game-ui project's build target and the output is used here.
751
+
752
+ ```tsx
753
+ // packages/game-ui/src/main.tsx
754
+ import React from 'react';
755
+ import { createRoot } from 'react-dom/client';
756
+ import { RouterProvider, createRouter } from '@tanstack/react-router';
757
+ import { routeTree } from './routeTree.gen';
758
+ import './styles.css';
759
+
760
+ export type RouterProviderContext = {};
761
+
762
+ const router = createRouter({ routeTree, context: {} });
763
+
764
+ declare module '@tanstack/react-router' {
765
+ interface Register {
766
+ router: typeof router;
767
+ }
768
+ }
769
+
770
+ const App = () => <RouterProvider router={router} context={{}} />;
771
+
772
+ const root = document.getElementById('root');
773
+ root &&
774
+ createRoot(root).render(
775
+ <React.StrictMode>
776
+ <App />
777
+ </React.StrictMode>,
778
+ );
779
+ ```
780
+
781
+ This is the entry point where React is mounted. Styling comes from Tailwind v4 tokens imported via `styles.css`. `@tanstack/react-router` is configured in [file-based routing](https://tanstack.com/router/v1/docs/framework/react/routing/file-based-routing) mode: as long as the dev server is running, any file you create under `routes/` is picked up automatically and the route tree is regenerated. Later generators (auth, connection) will AST-patch this file to wrap `<App />` in additional providers.
782
+
783
+ ```tsx
784
+ // packages/game-ui/src/routes/index.tsx
785
+ import { createFileRoute } from '@tanstack/react-router';
786
+
787
+ export const Route = createFileRoute('/')({
788
+ component: RouteComponent,
789
+ });
790
+
791
+ function RouteComponent() {
792
+ return (
793
+ <div className="text-center">
794
+ <header>
795
+ <h1>Welcome</h1>
796
+ <p>Welcome to your new React website!</p>
797
+ </header>
798
+ </div>
799
+ );
800
+ }
801
+ ```
802
+
803
+ A component will be rendered when navigating to the `/` route. `@tanstack/react-router` will manage the `Route` for you whenever you create/move this file (as long as the dev server is running).
804
+
805
+ </Drawer>
806
+
807
+ ### Game UI: Auth
808
+
809
+ Let us configure our Game UI to require authenticated access via Amazon Cognito using these steps:
810
+
811
+ <RunGenerator generator="ts#website#auth" requiredParameters={{cognitoDomain:"game-ui", project:"@dungeon-adventure/game-ui", allowSignup:true}} noInteractive />
812
+
813
+ You will see some new files appear/change in your file tree.
814
+
815
+ <Drawer title="ts#website#auth updated files" trigger="Click here to examine these files in more detail.">
816
+ The `ts#website#auth` generator updates/generates these files. Let us examine some of the key files highlighted in the file tree:
817
+
818
+ <FileTree>
819
+ - packages/
820
+ - common/
821
+ - constructs/
822
+ - src/
823
+ - core/
824
+ - user-identity.ts cdk construct for creating user/identity pools
825
+ - game-ui/
826
+ - src/
827
+ - components/
828
+ - AppLayout/
829
+ - index.tsx adds the logged in user/logout to the header
830
+ - CognitoAuth/
831
+ - index.tsx manages logging into Cognito
832
+ - RuntimeConfig/
833
+ - index.tsx fetches the `runtime-config.json` and provides it to children via context
834
+ - hooks/
835
+ - useRuntimeConfig.tsx
836
+ - **main.tsx** Updated to add Cognito
837
+ </FileTree>
838
+
839
+ ```diff lang="tsx"
840
+ // packages/game-ui/src/main.tsx
841
+ +import { useAuth } from 'react-oidc-context';
842
+ +import CognitoAuth from './components/CognitoAuth';
843
+ +import { useRuntimeConfig } from './hooks/useRuntimeConfig';
844
+ +import RuntimeConfigProvider from './components/RuntimeConfig';
845
+ import React from 'react';
846
+ import { createRoot } from 'react-dom/client';
847
+ import { RouterProvider, createRouter } from '@tanstack/react-router';
848
+ import { routeTree } from './routeTree.gen';
849
+ import './styles.css';
850
+ -export type RouterProviderContext = {};
851
+ +export type RouterProviderContext = {
852
+ + runtimeConfig?: ReturnType<typeof useRuntimeConfig>;
853
+ + auth?: ReturnType<typeof useAuth>;
854
+ +};
855
+ -const router = createRouter({ routeTree, context: {} });
856
+ +const router = createRouter({
857
+ + routeTree,
858
+ + context: { runtimeConfig: undefined, auth: undefined },
859
+ +});
860
+ // Register the router instance for type safety
861
+ declare module '@tanstack/react-router' {
862
+ interface Register {
863
+ router: typeof router;
864
+ }
865
+ }
866
+ -const App = () => <RouterProvider router={router} context={{}} />;
867
+ +const App = () => {
868
+ + const auth = useAuth();
869
+ + const runtimeConfig = useRuntimeConfig();
870
+ + return <RouterProvider router={router} context={{ runtimeConfig, auth }} />;
871
+ +};
872
+ const root = document.getElementById('root');
873
+ root &&
874
+ createRoot(root).render(
875
+ <React.StrictMode>
876
+ + <RuntimeConfigProvider>
877
+ + <CognitoAuth>
878
+ <App />
879
+ + </CognitoAuth>
880
+ + </RuntimeConfigProvider>
881
+ </React.StrictMode>,
882
+ );
883
+ ```
884
+
885
+ The `RuntimeConfigProvider` and `CognitoAuth` components have been added to the `main.tsx` file via an AST transform. This allows the `CognitoAuth` component to authenticate with Amazon Cognito by fetching the `runtime-config.json` which contains the required cognito connection configuration in order to make the backend calls to the correct destination.
886
+
887
+ </Drawer>
888
+
889
+ ### Game UI: Connect to Game API
890
+
891
+ Let us configure our Game UI to connect to our previously created Game API.
892
+
893
+ <RunGenerator generator="connection" requiredParameters={{sourceProject:"@dungeon-adventure/game-ui", targetProject:"@dungeon-adventure/game-api"}} noInteractive />
894
+
895
+ You will see some new files have appear/change in your file tree.
896
+
897
+ <Drawer title="UI -> tRPC connection updated files" trigger="Click here to examine these files in more detail.">
898
+ The `connection` generator generates/updates these files. Let us examine some of the key files highlighted in the file tree:
899
+
900
+ <FileTree>
901
+ - packages/
902
+ - game-ui/
903
+ - src/
904
+ - components/
905
+ - GameApiClientProvider.tsx sets up the GameAPI client
906
+ - hooks/
907
+ - **useGameApi.tsx** hooks to call the GameApi
908
+ - **main.tsx** injects the trpc client providers
909
+ - package.json
910
+
911
+ </FileTree>
912
+
913
+ ```tsx
914
+ // packages/game-ui/src/hooks/useGameApi.tsx
915
+ import { useContext } from 'react';
916
+ import { GameApiTRPCContext } from '../components/GameApiClientProvider';
917
+
918
+ export const useGameApi = () => {
919
+ const container = useContext(GameApiTRPCContext);
920
+ if (!container) {
921
+ throw new Error('useGameApi must be used within GameApiClientProvider');
922
+ }
923
+ return container.optionsProxy;
924
+ };
925
+
926
+ export const useGameApiClient = () => {
927
+ const container = useContext(GameApiTRPCContext);
928
+ if (!container) {
929
+ throw new Error(
930
+ 'useGameApiClient must be used within GameApiClientProvider',
931
+ );
932
+ }
933
+ return container.client;
934
+ };
935
+ ```
936
+
937
+ This hook provides access to the tRPC client for calling the GameApi. For examples on how to call tRPC APIs, refer to the <Link path="guides/connection/react-trpc#using-the-generated-code">using the tRPC hook guide</Link>.
938
+
939
+ <Aside title="Type-Safe Hooks">
940
+ Thanks to tRPC's [Typescript inference](https://trpc.io/docs/concepts), `useGameApi` doesn't need a build step for backend changes to reach the frontend — edits to the router or any procedure are picked up by the type checker instantly.
941
+ </Aside>
942
+
943
+ ```diff lang="tsx"
944
+ // packages/game-ui/src/main.tsx
945
+ +import GameApiClientProvider from './components/GameApiClientProvider';
946
+ +import QueryClientProvider from './components/QueryClientProvider';
947
+ import { useAuth } from 'react-oidc-context';
948
+ import CognitoAuth from './components/CognitoAuth';
949
+ import { useRuntimeConfig } from './hooks/useRuntimeConfig';
950
+ import RuntimeConfigProvider from './components/RuntimeConfig';
951
+ import React from 'react';
952
+ import { createRoot } from 'react-dom/client';
953
+ import { RouterProvider, createRouter } from '@tanstack/react-router';
954
+ import { routeTree } from './routeTree.gen';
955
+ import './styles.css';
956
+ ...
957
+ const root = document.getElementById('root');
958
+ root &&
959
+ createRoot(root).render(
960
+ <React.StrictMode>
961
+ <RuntimeConfigProvider>
962
+ <CognitoAuth>
963
+ + <QueryClientProvider>
964
+ + <GameApiClientProvider>
965
+ <App />
966
+ + </GameApiClientProvider>
967
+ + </QueryClientProvider>
968
+ </CognitoAuth>
969
+ </RuntimeConfigProvider>
970
+ </React.StrictMode>,
971
+ );
972
+ ```
973
+
974
+ The `main.tsx` file has been updated via an AST transform to inject the tRPC providers.
975
+
976
+ </Drawer>
977
+
978
+ ### Story Agent: Connect to Inventory MCP Server
979
+
980
+ Let us connect our Story Agent to the Inventory MCP server so the agent can discover and invoke the MCP server's tools.
981
+
982
+ <RunGenerator generator="connection" requiredParameters={{sourceProject:"story", targetProject:"inventory"}} noInteractive />
983
+
984
+ <Drawer title="Story Agent -> Inventory MCP connection updated files" trigger="Click here to examine these files in more detail.">
985
+ The `connection` generator generates/updates these files:
986
+
987
+ <FileTree>
988
+ - packages/
989
+ - common/
990
+ - agent\_connection/
991
+ - dungeon\_adventure\_agent\_connection/
992
+ - core/
993
+ - **agentcore\_endpoints.py** Framework-agnostic ARN/URL resolution
994
+ - **agentcore\_mcp\_transport.py** Framework-agnostic MCP transport
995
+ - **agentcore\_mcp\_client\_strands.py** Strands MCP client wrapping the transport
996
+ - auth/ Framework-agnostic SigV4 / session-forwarding `httpx.Auth`
997
+ - app/
998
+ - **inventory\_mcp\_server\_client\_strands.py** Strands client for connecting to the Inventory MCP server
999
+ - **\_\_init\_\_.py** Re-exports per-connection clients
1000
+ - story/
1001
+ - dungeon\_adventure\_story/agent/
1002
+ - **agent.py** Modified to import and use the MCP client
1003
+
1004
+ </FileTree>
1005
+
1006
+ The generator:
1007
+ - Creates a shared `agent_connection` Python project (if it doesn't already exist) with the core `AgentCoreMCPClientStrands`
1008
+ - Generates an `InventoryMcpServerClientStrands` class that handles connecting to the MCP server both locally (direct HTTP) and when deployed (via AgentCore with IAM auth)
1009
+ - Transforms `agent.py` to import the client, create an instance, and wire the MCP server's tools into the agent
1010
+ - Adds the `agent_connection` project as a workspace dependency of the story project
1011
+ - Updates the `dev` target to automatically start the MCP server when running locally
1012
+
1013
+ For more details, refer to the <Link path="guides/connection/py-agent-mcp">Python Agent to MCP connection guide</Link>.
1014
+ </Drawer>
1015
+
1016
+ ### Game UI: Connect to Story Agent
1017
+
1018
+ Let us connect our Game UI to the Story Agent. Since the agent speaks AG-UI, the `connection` generator wires up [CopilotKit](https://docs.copilotkit.ai/): a themed chat component and an `@ag-ui/client` `HttpAgent` ready to render.
1019
+
1020
+ <RunGenerator generator="connection" requiredParameters={{sourceProject:"@dungeon-adventure/game-ui", targetProject:"story"}} noInteractive />
1021
+
1022
+ <Drawer title="UI -> Story Agent connection updated files" trigger="Click here to examine these files in more detail.">
1023
+ The `connection` generator generates/updates these files:
1024
+
1025
+ <FileTree>
1026
+ - packages/
1027
+ - game-ui/
1028
+ - src/
1029
+ - components/
1030
+ - **AguiProvider.tsx** `CopilotKitProvider` with every connected AG-UI agent registered
1031
+ - copilot/
1032
+ - **index.tsx** Shadcn-themed `CopilotChat` / `CopilotSidebar` / `CopilotPopup`
1033
+ - ShadcnAssistantMessage.tsx, ShadcnUserMessage.tsx, ShadcnChatInput.tsx, ShadcnCursor.tsx, copilot.css
1034
+ - hooks/
1035
+ - **useAguiStoryAgent.tsx** Builds an `HttpAgent`, injects the Cognito bearer token, and pads `threadId` to AgentCore's 33-char session id
1036
+ - **main.tsx** Wraps `<App />` in `<AguiProvider>`
1037
+
1038
+ </FileTree>
1039
+
1040
+ The generator:
1041
+ - Detects the React website's `ux` (Shadcn here) and vends matching chat components.
1042
+ - Registers every connected agent on a single `CopilotKitProvider` — re-running for another agent just adds another hook.
1043
+ - Reads the agent's runtime ARN from Runtime Configuration, builds the AgentCore invocation URL, and attaches the Cognito bearer token plus the AgentCore session id header.
1044
+
1045
+ For more details, refer to the <Link path="guides/connection/react-agui">React to AG-UI connection guide</Link>.
1046
+ </Drawer>
1047
+
1048
+ ### Connect the Game API and Inventory MCP server to the database
1049
+
1050
+ Both the Game API and the Inventory MCP server read and write our DynamoDB table, so let us connect them to the `DungeonDb` project. The `connection` generator detects that the target is a `ts#dynamodb` project and wires each source project's `dev` target to start DynamoDB Local automatically.
1051
+
1052
+ <RunGenerator generator="connection" requiredParameters={{sourceProject:"@dungeon-adventure/game-api", targetProject:"@dungeon-adventure/dungeon-db"}} noInteractive />
1053
+
1054
+ <RunGenerator generator="connection" requiredParameters={{sourceProject:"@dungeon-adventure/inventory", targetProject:"@dungeon-adventure/dungeon-db"}} noInteractive />
1055
+
1056
+ <Aside type="tip" title="One command boots the whole stack locally">
1057
+ Because the Story Agent's `agent-dev` already depends on the Inventory MCP server's `mcp-server-dev` (via the `story → inventory` connection above), and both the Game API and MCP server now depend on `dungeon-db:dev`, running any one project's `dev` target starts every dependency it needs in the right order.
1058
+ </Aside>
1059
+
1060
+ ### Game UI: Infrastructure
1061
+
1062
+ Let us create the final sub-project for the CDK infrastructure.
1063
+
1064
+ <RunGenerator generator="ts#infra" requiredParameters={{name:"infra"}} noInteractive />
1065
+
1066
+ You will see some new files have appear/change in your file tree.
1067
+
1068
+ <Drawer title="ts#infra updated files" trigger="Click here to examine these files in more detail.">
1069
+ The `ts#infra` generator generates/updates these. Let us examine some of the key files highlighted in the file tree:
1070
+
1071
+ <FileTree>
1072
+ - packages/
1073
+ - common/
1074
+ - constructs/
1075
+ - src/
1076
+ - core/
1077
+ - checkov.ts
1078
+ - index.ts
1079
+ - infra
1080
+ - src/
1081
+ - stages/
1082
+ - **application-stage.ts** cdk stacks defined here
1083
+ - stacks/
1084
+ - **application-stack.ts** cdk resources defined here
1085
+ - **main.ts** entrypoint which defines all stages
1086
+ - cdk.json
1087
+ - checkov.yml
1088
+ - project.json
1089
+ - ...
1090
+ - package.json
1091
+ - tsconfig.json add references
1092
+ - tsconfig.base.json add alias
1093
+
1094
+ </FileTree>
1095
+
1096
+ ```ts
1097
+ // packages/infra/src/main.ts
1098
+ import { ApplicationStage } from './stages/application-stage.js';
1099
+ import { App } from '@dungeon-adventure/common-constructs';
1100
+
1101
+ const app = new App();
1102
+
1103
+ // Use this to deploy your own sandbox environment (assumes your CLI credentials)
1104
+ new ApplicationStage(app, 'dungeon-adventure-infra-sandbox', {
1105
+ env: {
1106
+ account: process.env.CDK_DEFAULT_ACCOUNT,
1107
+ region: process.env.CDK_DEFAULT_REGION,
1108
+ },
1109
+ });
1110
+
1111
+ app.synth();
1112
+ ```
1113
+
1114
+ <Aside type="tip" title="Fixing Import Errors">If you see an import error within your IDE, this is because our infrastructure project does not have a typescript reference set up yet in the `tsconfig.json`. Nx has been [configured](https://nx.dev/nx-api/js/generators/typescript-sync) to create these references *dynamically* whenever a build/compile is run or if you run the `nx sync` command manually. For more information refer to the <Link path="guides/typescript-project#importing-your-library-code-in-other-projects">Typescript guide</Link>.</Aside>
1115
+
1116
+ This is the entry point for your CDK application.
1117
+
1118
+ ```ts
1119
+ // packages/infra/src/stacks/application-stack.ts
1120
+ import { Stack, StackProps } from 'aws-cdk-lib';
1121
+ import { Construct } from 'constructs';
1122
+
1123
+ export class ApplicationStack extends Stack {
1124
+ constructor(scope: Construct, id: string, props?: StackProps) {
1125
+ super(scope, id, props);
1126
+
1127
+ // The code that defines your stack goes here
1128
+ }
1129
+ }
1130
+ ```
1131
+
1132
+ Let us instantiate our CDK constructs to build our dungeon adventure game.
1133
+
1134
+ </Drawer>
1135
+
1136
+ ## Task 7: Update our infrastructure
1137
+
1138
+ Let's update `packages/infra/src/stacks/application-stack.ts` to instantiate some of our generated constructs:
1139
+
1140
+ <E2EDiff before="dungeon-adventure/1/application-stack.ts.original.template" after="dungeon-adventure/1/application-stack.ts.template" lang="ts" />
1141
+
1142
+ :::note[Default Integrations]
1143
+ We supply default integrations for our Game API. By default, each operation in our API is mapped to an individual Lambda function to handle that operation.
1144
+ :::
1145
+
1146
+ ## Task 8: Build the code
1147
+
1148
+ <Drawer title="Nx commands" trigger="Now it's time for us to build our code for the first time">
1149
+
1150
+ ###### Single vs Multiple targets
1151
+
1152
+ The `run-many` command will run a target on multiple listed subprojects (`--all` will target them all). This ensures dependencies are executed in the correct order.
1153
+
1154
+ You can also trigger a build (or any other task) for a single project target by running the target on the project directly. For example, to build the `@dungeon-adventure/infra` project, run the following command:
1155
+
1156
+ <NxCommands commands={['build infra']} />
1157
+
1158
+ You can also omit the scope, and use the Nx shorthand syntax if you prefer:
1159
+
1160
+ <NxCommands commands={['build infra']} />
1161
+
1162
+ ###### Visualizing your dependencies
1163
+
1164
+ To visualize your dependencies, run:
1165
+
1166
+ <NxCommands commands={['graph']} />
1167
+ <br/>
1168
+
1169
+ <Image src={nxGraphPng} alt="nx-graph.png" width="800" height="600" />
1170
+
1171
+ ###### Caching
1172
+
1173
+ Nx relies on [caching](https://nx.dev/concepts/how-caching-works) so that you can re-use artifacts from previous builds in order to speed up development. There is some configuration required to get this to work correctly and there may be cases where you want to perform a build **without using the cache**. To do that, simply append the `--skip-nx-cache` argument to your command. For example:
1174
+
1175
+ <NxCommands commands={['build infra --skip-nx-cache']} />
1176
+ If for whatever reason you ever wanted to clear your cache (stored in the `.nx` folder), you can run the following command:
1177
+
1178
+ <NxCommands commands={['reset']} />
1179
+
1180
+ </Drawer>
1181
+
1182
+ Using the command line, run the following command to fix any lint issues first:
1183
+
1184
+ <PackageManagerShortCommand commands={["lint"]} />
1185
+
1186
+ Then, run the following command for a full build:
1187
+
1188
+ <PackageManagerShortCommand commands={["build"]} />
1189
+
1190
+ You will be prompted with the following:
1191
+
1192
+ ```bash
1193
+ NX The workspace is out of sync
1194
+
1195
+ [@nx/js:typescript-sync]: Some TypeScript configuration files are missing project references to the projects they depend on or contain outdated project references.
1196
+
1197
+ This will result in an error in CI.
1198
+
1199
+ ? Would you like to sync the identified changes to get your workspace up to date? …
1200
+ Yes, sync the changes and run the tasks
1201
+ No, run the tasks without syncing the changes
1202
+ ```
1203
+
1204
+ This message indicates that NX has detected some files which can be updated automatically for you. In this case, it is referring to the `tsconfig.json` files which do not have Typescript references set up on references projects.
1205
+
1206
+ Select the **Yes, sync the changes and run the tasks** option to proceed. You should notice all of you IDE related import errors get automatically resolved as the sync generator will add the missing typescript references automatically!
1207
+
1208
+ All built artifacts are now available within the `dist/ folder` located at the root of the monorepo. This is a standard pactice when using projects generated by the `@aws/nx-plugin` as it does not pollute your file-tree with generated files. In the event you want to clean your files, delete the `dist/` folder without worrying about build artifacts being littered throughout the file tree.
1209
+
1210
+ Congratulations! You've created all of the required sub-projects required to start implementing the core of our AI Dungeon Adventure game. 🎉🎉🎉