@aws/nx-plugin-mcp 1.0.0-rc.44 → 1.0.0-rc.45

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.
@@ -0,0 +1,569 @@
1
+ ---
2
+ title: DCR Proxy
3
+ description: Generate an OAuth Dynamic Client Registration proxy for Cognito-authenticated MCP servers
4
+ generator: ts#dcr-proxy
5
+ ---
6
+
7
+ import { FileTree } from '@astrojs/starlight/components';
8
+ import Link from '@components/link.astro';
9
+ import RunGenerator from '@components/run-generator.astro';
10
+ import GeneratorParameters from '@components/generator-parameters.astro';
11
+ import NxCommands from '@components/nx-commands.astro';
12
+ import Infrastructure from '@components/infrastructure.astro';
13
+ import Snippet from '@components/snippet.astro';
14
+
15
+ The DCR Proxy generator creates an OAuth [Dynamic Client Registration (DCR)](https://datatracker.ietf.org/doc/html/rfc7591) proxy in front of an Amazon Cognito User Pool.
16
+
17
+ MCP clients (such as Claude Code, Kiro CLI or the MCP Inspector) expect to authenticate against an OAuth authorization server that supports Dynamic Client Registration and metadata discovery. Amazon Cognito does not support DCR natively, and its App Client secret must never be exposed to a public client. This proxy bridges that gap: it keeps the Cognito Hosted UI flow intact, implements DCR, injects the App Client secret server-side during the token exchange, and forwards MCP traffic to your upstream MCP server.
18
+
19
+ ## Usage
20
+
21
+ ### Generate a DCR proxy
22
+
23
+ <RunGenerator generator="ts#dcr-proxy" />
24
+
25
+ ### Options
26
+
27
+ <GeneratorParameters generator="ts#dcr-proxy" />
28
+
29
+ ## Generator Output
30
+
31
+ The generator creates a standalone <Link path="/guides/typescript-project">TypeScript project</Link> containing the Lambda handlers, and infrastructure to deploy them based on your selected `iacProvider`.
32
+
33
+ <FileTree>
34
+
35
+ - \<dcr-proxy-name>
36
+ - src/
37
+ - handlers/
38
+ - authorization-server-metadata.ts Serves `/.well-known/oauth-authorization-server` and `/.well-known/openid-configuration`
39
+ - protected-resource-metadata.ts Serves `/.well-known/oauth-protected-resource`
40
+ - register.ts RFC 7591 Dynamic Client Registration
41
+ - authorize.ts Redirects to the Cognito Hosted UI
42
+ - token.ts Injects the App Client secret and exchanges the token
43
+ - mcp-proxy.ts Proxies `/mcp` requests to the upstream MCP server
44
+
45
+ </FileTree>
46
+
47
+ The handlers are bundled independently with [Rolldown](https://rolldown.rs/), and both IaC providers reference the resulting bundle output.
48
+
49
+ ### Infrastructure
50
+
51
+ <Snippet name="shared-constructs" />
52
+
53
+ For deploying the proxy, the following files are generated:
54
+
55
+ <Infrastructure>
56
+ <Fragment slot="cdk">
57
+ <FileTree>
58
+ - packages/common/constructs/src
59
+ - app
60
+ - dcr-proxies
61
+ - \<dcr-proxy-name>
62
+ - \<dcr-proxy-name>.ts CDK construct which deploys the proxy
63
+ </FileTree>
64
+ </Fragment>
65
+ <Fragment slot="terraform">
66
+ <FileTree>
67
+ - packages/common/terraform/src
68
+ - app
69
+ - dcr-proxies
70
+ - \<dcr-proxy-name>
71
+ - \<dcr-proxy-name>.tf Terraform module which deploys the proxy
72
+ </FileTree>
73
+ </Fragment>
74
+ </Infrastructure>
75
+
76
+ The infrastructure provisions an API Gateway HTTP API with the following routes:
77
+
78
+ | Route | Description |
79
+ | --- | --- |
80
+ | `GET /.well-known/oauth-protected-resource` | Protected resource metadata |
81
+ | `GET /.well-known/oauth-authorization-server` | Authorization server metadata |
82
+ | `GET /.well-known/openid-configuration` | OpenID configuration (served by the authorization server metadata handler) |
83
+ | `POST /register` | Dynamic Client Registration |
84
+ | `GET /authorize` | Authorization (redirects to the Cognito Hosted UI) |
85
+ | `POST /oauth/token` | Token exchange (injects the App Client secret) |
86
+ | `ANY /mcp` | Proxy to the upstream MCP server |
87
+
88
+ Only the token handler is granted read access to the Cognito App Client secret in Secrets Manager.
89
+
90
+ ## Deploying the DCR Proxy
91
+
92
+ The proxy does not create your Cognito resources or your MCP server. Instead, you inject the identifiers of resources managed elsewhere (whether generated by this plugin or provisioned separately), keeping the proxy decoupled from how those resources are provisioned.
93
+
94
+ You provide:
95
+
96
+ - The Cognito User Pool id and App Client id
97
+ - The ARN of a Secrets Manager secret holding the App Client secret. The token handler reads this at runtime; the value is never exposed to the client.
98
+ - The base URL of the Cognito Hosted UI domain
99
+ - The full URL of your upstream MCP server
100
+
101
+ <Infrastructure>
102
+ <Fragment slot="cdk">
103
+ Instantiate the generated construct in your stack, passing the required properties:
104
+
105
+ ```typescript
106
+ import { DcrProxy } from ':my-scope/common-constructs';
107
+
108
+ new DcrProxy(this, 'DcrProxy', {
109
+ userPoolId: userPool.userPoolId,
110
+ userPoolClientId: userPoolClient.userPoolClientId,
111
+ cognitoClientSecretArn: clientSecret.secretArn,
112
+ cognitoHostedUiBase: userPoolDomain.baseUrl(),
113
+ upstreamUrl: 'https://my-agentcore-runtime-url/mcp',
114
+ });
115
+ ```
116
+
117
+ The construct exposes the proxy endpoints (`proxyUrl`, `mcpUrl`, `metadataUrl`, `tokenEndpoint`, `registrationEndpoint`) as readonly properties.
118
+ </Fragment>
119
+ <Fragment slot="terraform">
120
+ Reference the generated module from your Terraform configuration, passing the required variables:
121
+
122
+ ```hcl
123
+ module "dcr_proxy" {
124
+ source = "../../common/terraform/src/app/dcr-proxies/dcr-proxy"
125
+
126
+ user_pool_id = aws_cognito_user_pool.main.id
127
+ user_pool_client_id = aws_cognito_user_pool_client.main.id
128
+ cognito_client_secret_arn = aws_secretsmanager_secret.client_secret.arn
129
+ cognito_hosted_ui_base = "https://${aws_cognito_user_pool_domain.main.domain}.auth.${data.aws_region.current.region}.amazoncognito.com"
130
+ upstream_url = "https://my-agentcore-runtime-url/mcp"
131
+ asset_bucket_name = module.asset_bucket.bucket_name
132
+ }
133
+ ```
134
+
135
+ The module exposes the proxy endpoints (`proxy_url`, `mcp_url`, `metadata_url`, `token_endpoint`, `registration_endpoint`) as outputs.
136
+ </Fragment>
137
+ </Infrastructure>
138
+
139
+ :::note
140
+ The `/mcp` proxy uses API Gateway HTTP API integrations, which have a hard 29 second timeout. Long-running upstream requests are buffered by the proxy rather than streamed.
141
+ :::
142
+
143
+ :::tip[Reusing an existing User Pool]
144
+ If you already have a User Pool from the <Link path="guides/react-website-auth">`ts#website#auth`</Link> generator, you can front an MCP server for the same website users. See <Link path="guides/ts-dcr-proxy#reusing-a-useridentity-user-pool">Reusing a UserIdentity User Pool</Link> below.
145
+ :::
146
+
147
+ ### Fronting an MCP Server
148
+
149
+ To front an MCP server generated with the <Link path="guides/ts-mcp-server">`ts#mcp-server`</Link> (or <Link path="guides/py-mcp-server">`py#mcp-server`</Link>) generator using `--auth cognito`, pass the **same** User Pool and App Client to both the MCP server and the proxy, and use the MCP server construct's `invocationUrl` as the proxy's `upstreamUrl`.
150
+
151
+ <Infrastructure>
152
+ <Fragment slot="cdk">
153
+ ```typescript
154
+ import {
155
+ DcrProxy,
156
+ MyProjectMcpServer,
157
+ UserIdentity,
158
+ } from ':my-scope/common-constructs';
159
+ import { OAuthScope } from 'aws-cdk-lib/aws-cognito';
160
+ import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
161
+
162
+ const identity = new UserIdentity(this, 'Identity');
163
+
164
+ // A confidential App Client the proxy uses for the token exchange. Register the
165
+ // callback URLs your clients use (see below).
166
+ const proxyClient = identity.userPool.addClient('DcrProxyClient', {
167
+ generateSecret: true,
168
+ oAuth: {
169
+ flows: { authorizationCodeGrant: true },
170
+ scopes: [OAuthScope.OPENID, OAuthScope.EMAIL, OAuthScope.PROFILE],
171
+ callbackUrls: [
172
+ 'http://localhost:41100/callback',
173
+ // Callback used by Claude Desktop
174
+ 'https://claude.ai/api/mcp/auth_callback',
175
+ ],
176
+ },
177
+ });
178
+
179
+ // Store the App Client secret in Secrets Manager for the token handler to read
180
+ const clientSecret = new secretsmanager.Secret(this, 'ClientSecret', {
181
+ secretStringValue: proxyClient.userPoolClientSecret,
182
+ });
183
+
184
+ // The MCP server, authorizing JWTs issued for the same App Client
185
+ const mcpServer = new MyProjectMcpServer(this, 'MyProjectMcpServer', {
186
+ identity: {
187
+ userPool: identity.userPool,
188
+ userPoolClient: proxyClient,
189
+ },
190
+ });
191
+
192
+ new DcrProxy(this, 'DcrProxy', {
193
+ userPoolId: identity.userPool.userPoolId,
194
+ userPoolClientId: proxyClient.userPoolClientId,
195
+ cognitoClientSecretArn: clientSecret.secretArn,
196
+ cognitoHostedUiBase: identity.userPoolDomain.baseUrl(),
197
+ // Use the MCP server construct's invocation URL rather than hardcoding it
198
+ upstreamUrl: mcpServer.invocationUrl,
199
+ });
200
+ ```
201
+ </Fragment>
202
+ <Fragment slot="terraform">
203
+ ```hcl
204
+ # A confidential App Client the proxy uses for the token exchange. Register the
205
+ # callback URLs your clients use (see below).
206
+ resource "aws_cognito_user_pool_client" "dcr_proxy" {
207
+ name = "dcr-proxy-client"
208
+ user_pool_id = module.user_identity.user_pool_id
209
+ generate_secret = true
210
+ allowed_oauth_flows = ["code"]
211
+ allowed_oauth_flows_user_pool_client = true
212
+ allowed_oauth_scopes = ["openid", "email", "profile"]
213
+ callback_urls = [
214
+ "http://localhost:41100/callback",
215
+ # Callback used by Claude Desktop
216
+ "https://claude.ai/api/mcp/auth_callback",
217
+ ]
218
+ }
219
+
220
+ # Store the App Client secret in Secrets Manager for the token handler to read
221
+ resource "aws_secretsmanager_secret" "client_secret" {
222
+ name = "my-dcr-proxy-client-secret"
223
+ }
224
+
225
+ resource "aws_secretsmanager_secret_version" "client_secret" {
226
+ secret_id = aws_secretsmanager_secret.client_secret.id
227
+ secret_string = aws_cognito_user_pool_client.dcr_proxy.client_secret
228
+ }
229
+
230
+ # The MCP server, authorizing JWTs issued for the same App Client
231
+ module "my_project_mcp_server" {
232
+ source = "../../common/terraform/src/app/mcp-servers/my-project-mcp-server"
233
+
234
+ user_pool_id = module.user_identity.user_pool_id
235
+ user_pool_client_ids = [aws_cognito_user_pool_client.dcr_proxy.id]
236
+ appconfig_application_id = module.runtime_config.application_id
237
+ appconfig_application_arn = module.runtime_config.application_arn
238
+ }
239
+
240
+ module "dcr_proxy" {
241
+ source = "../../common/terraform/src/app/dcr-proxies/dcr-proxy"
242
+
243
+ user_pool_id = module.user_identity.user_pool_id
244
+ user_pool_client_id = aws_cognito_user_pool_client.dcr_proxy.id
245
+ cognito_client_secret_arn = aws_secretsmanager_secret.client_secret.arn
246
+ cognito_hosted_ui_base = "https://${module.user_identity.user_pool_domain}.auth.${data.aws_region.current.region}.amazoncognito.com"
247
+ # Use the MCP server module's invocation URL rather than hardcoding it
248
+ upstream_url = module.my_project_mcp_server.invocation_url
249
+ asset_bucket_name = module.asset_bucket.bucket_name
250
+ }
251
+ ```
252
+ </Fragment>
253
+ </Infrastructure>
254
+
255
+ ### Fronting an AgentCore Gateway
256
+
257
+ To front an [AgentCore Gateway](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway.html) generated with the <Link path="guides/agentcore-gateway">`agentcore-gateway`</Link> generator using `--auth cognito`, pass the **same** User Pool and App Client to both the gateway and the proxy, and use the gateway construct's `gatewayUrl` as the proxy's `upstreamUrl`.
258
+
259
+ :::caution
260
+ The proxy's OAuth flow requires a Cognito-authenticated gateway. Generate the gateway with `--auth cognito`; the default IAM-authenticated gateway is not compatible with the proxy.
261
+ :::
262
+
263
+ <Infrastructure>
264
+ <Fragment slot="cdk">
265
+ ```typescript
266
+ import {
267
+ DcrProxy,
268
+ MyGateway,
269
+ UserIdentity,
270
+ } from ':my-scope/common-constructs';
271
+ import { OAuthScope } from 'aws-cdk-lib/aws-cognito';
272
+ import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
273
+
274
+ const identity = new UserIdentity(this, 'Identity');
275
+
276
+ // A confidential App Client the proxy uses for the token exchange. Register the
277
+ // callback URLs your clients use (see below).
278
+ const proxyClient = identity.userPool.addClient('DcrProxyClient', {
279
+ generateSecret: true,
280
+ oAuth: {
281
+ flows: { authorizationCodeGrant: true },
282
+ scopes: [OAuthScope.OPENID, OAuthScope.EMAIL, OAuthScope.PROFILE],
283
+ callbackUrls: [
284
+ 'http://localhost:41100/callback',
285
+ // Callback used by Claude Desktop
286
+ 'https://claude.ai/api/mcp/auth_callback',
287
+ ],
288
+ },
289
+ });
290
+
291
+ // Store the App Client secret in Secrets Manager for the token handler to read
292
+ const clientSecret = new secretsmanager.Secret(this, 'ClientSecret', {
293
+ secretStringValue: proxyClient.userPoolClientSecret,
294
+ });
295
+
296
+ // The gateway, authorizing JWTs issued for the same App Client
297
+ const gateway = new MyGateway(this, 'MyGateway', {
298
+ identity: {
299
+ userPool: identity.userPool,
300
+ userPoolClient: proxyClient,
301
+ },
302
+ });
303
+
304
+ new DcrProxy(this, 'DcrProxy', {
305
+ userPoolId: identity.userPool.userPoolId,
306
+ userPoolClientId: proxyClient.userPoolClientId,
307
+ cognitoClientSecretArn: clientSecret.secretArn,
308
+ cognitoHostedUiBase: identity.userPoolDomain.baseUrl(),
309
+ // Use the gateway construct's URL rather than hardcoding it
310
+ upstreamUrl: gateway.gateway.gatewayUrl,
311
+ });
312
+ ```
313
+ </Fragment>
314
+ <Fragment slot="terraform">
315
+ ```hcl
316
+ # A confidential App Client the proxy uses for the token exchange. Register the
317
+ # callback URLs your clients use (see below).
318
+ resource "aws_cognito_user_pool_client" "dcr_proxy" {
319
+ name = "dcr-proxy-client"
320
+ user_pool_id = module.user_identity.user_pool_id
321
+ generate_secret = true
322
+ allowed_oauth_flows = ["code"]
323
+ allowed_oauth_flows_user_pool_client = true
324
+ allowed_oauth_scopes = ["openid", "email", "profile"]
325
+ callback_urls = [
326
+ "http://localhost:41100/callback",
327
+ # Callback used by Claude Desktop
328
+ "https://claude.ai/api/mcp/auth_callback",
329
+ ]
330
+ }
331
+
332
+ # Store the App Client secret in Secrets Manager for the token handler to read
333
+ resource "aws_secretsmanager_secret" "client_secret" {
334
+ name = "my-dcr-proxy-client-secret"
335
+ }
336
+
337
+ resource "aws_secretsmanager_secret_version" "client_secret" {
338
+ secret_id = aws_secretsmanager_secret.client_secret.id
339
+ secret_string = aws_cognito_user_pool_client.dcr_proxy.client_secret
340
+ }
341
+
342
+ # The gateway, authorizing JWTs issued for the same App Client
343
+ module "my_gateway" {
344
+ source = "../../common/terraform/src/app/agentcore-gateway/my-gateway"
345
+
346
+ user_pool_id = module.user_identity.user_pool_id
347
+ user_pool_client_ids = [aws_cognito_user_pool_client.dcr_proxy.id]
348
+ }
349
+
350
+ module "dcr_proxy" {
351
+ source = "../../common/terraform/src/app/dcr-proxies/dcr-proxy"
352
+
353
+ user_pool_id = module.user_identity.user_pool_id
354
+ user_pool_client_id = aws_cognito_user_pool_client.dcr_proxy.id
355
+ cognito_client_secret_arn = aws_secretsmanager_secret.client_secret.arn
356
+ cognito_hosted_ui_base = "https://${module.user_identity.user_pool_domain}.auth.${data.aws_region.current.region}.amazoncognito.com"
357
+ # Use the gateway module's URL rather than hardcoding it
358
+ upstream_url = module.my_gateway.gateway_url
359
+ asset_bucket_name = module.asset_bucket.bucket_name
360
+ }
361
+ ```
362
+ </Fragment>
363
+ </Infrastructure>
364
+
365
+ ### Allowing Client Redirect URIs
366
+
367
+ Because the proxy implements Dynamic Client Registration virtually — there is no per-client Cognito App Client — every MCP client authenticates through the **single App Client** you pass to the proxy. During the OAuth flow the proxy forwards the client's `redirect_uri` to the Cognito Hosted UI unchanged, so Cognito performs the authoritative check: the callback must be registered as a **callback URL on that App Client**, otherwise Cognito rejects the login.
368
+
369
+ This is a side effect of the virtual DCR design. A client can register any `redirect_uri` with the proxy, but the login only succeeds if that exact URL is one of the App Client's callback URLs. Cognito matches callback URLs exactly, including the port, so clients that listen on a random ephemeral port cannot be covered by a wildcard — you must pin each client to a fixed callback URL and register that exact URL on the App Client.
370
+
371
+ Add the callback URLs your clients use when you create the App Client:
372
+
373
+ <Infrastructure>
374
+ <Fragment slot="cdk">
375
+ ```typescript
376
+ const userPoolClient = userPool.addClient('DcrProxyClient', {
377
+ generateSecret: true,
378
+ oAuth: {
379
+ flows: { authorizationCodeGrant: true },
380
+ callbackUrls: [
381
+ // Local clients: pin to a fixed, uncommon port rather than a default one
382
+ 'http://localhost:41100/callback',
383
+ // Callback used by Claude Desktop
384
+ 'https://claude.ai/api/mcp/auth_callback',
385
+ ],
386
+ },
387
+ });
388
+ ```
389
+ </Fragment>
390
+ <Fragment slot="terraform">
391
+ ```hcl
392
+ resource "aws_cognito_user_pool_client" "dcr_proxy" {
393
+ # ...
394
+ generate_secret = true
395
+ allowed_oauth_flows = ["code"]
396
+ allowed_oauth_flows_user_pool_client = true
397
+ callback_urls = [
398
+ # Local clients: pin to a fixed, uncommon port rather than a default one
399
+ "http://localhost:41100/callback",
400
+ # Callback used by Claude Desktop
401
+ "https://claude.ai/api/mcp/auth_callback",
402
+ ]
403
+ }
404
+ ```
405
+ </Fragment>
406
+ </Infrastructure>
407
+
408
+ :::caution
409
+ Only register callback URLs you trust — any client that can drive a browser to one of these URLs can complete a login against your User Pool.
410
+ :::
411
+
412
+ ## Consuming a Proxied MCP Server
413
+
414
+ The proxy lets MCP clients authenticate against your Cognito User Pool without any client-specific configuration beyond the proxy URL. When a client connects to the `/mcp` endpoint, it discovers the OAuth metadata (via `/.well-known/oauth-protected-resource` and `/.well-known/oauth-authorization-server`), dynamically registers itself, and drives the user through the Cognito Hosted UI login. The proxy injects the App Client secret during the token exchange, so the client never needs it.
415
+
416
+ To connect a client, point it at the proxy's `mcpUrl` (i.e. `<proxyUrl>/mcp`). The examples below assume your proxy is deployed at `https://my-proxy.example.com`.
417
+
418
+ :::note
419
+ Each client uses a callback URL during the OAuth flow. That exact URL — including its port — must be registered as a [callback URL on your Cognito App Client](#allowing-client-redirect-uris), otherwise the login fails. Where a client would otherwise pick a random port, pin it to a fixed value and register that value.
420
+ :::
421
+
422
+ ### Claude Code
423
+
424
+ Add the proxied server with the [`claude mcp add`](https://code.claude.com/docs/en/mcp) command, using the HTTP transport. By default Claude Code listens on a random callback port; pass `--callback-port` to pin it to the port registered on your App Client (`41100` in the examples above). Claude Code always uses the `/callback` path, so the resulting redirect URI is `http://localhost:41100/callback`:
425
+
426
+ ```bash
427
+ claude mcp add --transport http --callback-port 41100 my-proxied-server https://my-proxy.example.com/mcp
428
+ ```
429
+
430
+ When you first invoke a tool from the server, Claude Code opens the Cognito Hosted UI to authenticate before the request is proxied upstream.
431
+
432
+ ### Kiro CLI
433
+
434
+ Add the server to your [Kiro CLI](https://kiro.dev/docs/cli/mcp/) MCP configuration, using the HTTP transport. Without an explicit `oauth.redirectUri` Kiro picks a random callback port; set it to the URL registered on your App Client so the port and path match exactly:
435
+
436
+ ```json
437
+ {
438
+ "mcpServers": {
439
+ "my-proxied-server": {
440
+ "type": "http",
441
+ "url": "https://my-proxy.example.com/mcp",
442
+ "oauth": {
443
+ "redirectUri": "http://localhost:41100/callback"
444
+ }
445
+ }
446
+ }
447
+ }
448
+ ```
449
+
450
+ Kiro CLI triggers the Cognito Hosted UI login on first use and manages the resulting tokens for subsequent requests.
451
+
452
+ :::tip
453
+ Any MCP client that supports the Streamable HTTP transport along with OAuth authorization and Dynamic Client Registration can connect to the proxy the same way — supply the `/mcp` URL and the client handles discovery, registration, and login for you.
454
+ :::
455
+
456
+ ## Reusing a UserIdentity User Pool
457
+
458
+ If you already have a User Pool from the <Link path="guides/react-website-auth">`ts#website#auth`</Link> generator (the `UserIdentity` construct), you can front an MCP server for the **same users** who log in to your website. Reuse its `userPool`, but add a **separate App Client** for the proxy: the website's client is a public client without a secret, whereas the DCR proxy requires a confidential client (`generateSecret: true`) whose secret the token handler injects during the token exchange.
459
+
460
+ `UserIdentity` configures the User Pool domain with [Managed Login (version 2)](https://docs.aws.amazon.com/cognito/latest/developerguide/managed-login.html). Managed Login requires a **branding style per App Client**, so you must create one for the new proxy client — otherwise its hosted login page returns `403`.
461
+
462
+ <Infrastructure>
463
+ <Fragment slot="cdk">
464
+ ```typescript
465
+ import {
466
+ DcrProxy,
467
+ MyProjectMcpServer,
468
+ UserIdentity,
469
+ } from ':my-scope/common-constructs';
470
+ import { OAuthScope, CfnManagedLoginBranding } from 'aws-cdk-lib/aws-cognito';
471
+ import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
472
+
473
+ // The user pool created by ts#website#auth for your website users
474
+ const identity = new UserIdentity(this, 'Identity');
475
+
476
+ // A confidential App Client on the SAME user pool for the DCR proxy
477
+ const proxyClient = identity.userPool.addClient('DcrProxyClient', {
478
+ generateSecret: true,
479
+ oAuth: {
480
+ flows: { authorizationCodeGrant: true },
481
+ scopes: [OAuthScope.OPENID, OAuthScope.EMAIL, OAuthScope.PROFILE],
482
+ callbackUrls: ['http://localhost:41100/callback'],
483
+ },
484
+ });
485
+
486
+ // Managed Login needs a branding style for the new client
487
+ new CfnManagedLoginBranding(this, 'DcrProxyClientBranding', {
488
+ userPoolId: identity.userPool.userPoolId,
489
+ clientId: proxyClient.userPoolClientId,
490
+ useCognitoProvidedValues: true,
491
+ });
492
+
493
+ const clientSecret = new secretsmanager.Secret(this, 'ClientSecret', {
494
+ secretStringValue: proxyClient.userPoolClientSecret,
495
+ });
496
+
497
+ const mcpServer = new MyProjectMcpServer(this, 'MyProjectMcpServer', {
498
+ identity: {
499
+ userPool: identity.userPool,
500
+ userPoolClient: proxyClient,
501
+ },
502
+ });
503
+
504
+ new DcrProxy(this, 'DcrProxy', {
505
+ userPoolId: identity.userPool.userPoolId,
506
+ userPoolClientId: proxyClient.userPoolClientId,
507
+ cognitoClientSecretArn: clientSecret.secretArn,
508
+ // The UserIdentity construct always creates a domain
509
+ cognitoHostedUiBase: identity.userPoolDomain.baseUrl(),
510
+ upstreamUrl: mcpServer.invocationUrl,
511
+ });
512
+ ```
513
+ </Fragment>
514
+ <Fragment slot="terraform">
515
+ ```hcl
516
+ # The user pool module created by ts#website#auth for your website users
517
+ module "user_identity" {
518
+ source = "../../common/terraform/src/core/user-identity"
519
+ }
520
+
521
+ # A confidential App Client on the SAME user pool for the DCR proxy
522
+ resource "aws_cognito_user_pool_client" "dcr_proxy" {
523
+ name = "dcr-proxy-client"
524
+ user_pool_id = module.user_identity.user_pool_id
525
+ generate_secret = true
526
+ allowed_oauth_flows = ["code"]
527
+ allowed_oauth_flows_user_pool_client = true
528
+ allowed_oauth_scopes = ["openid", "email", "profile"]
529
+ callback_urls = ["http://localhost:41100/callback"]
530
+ }
531
+
532
+ # Managed Login needs a branding style for the new client
533
+ resource "aws_cognito_managed_login_branding" "dcr_proxy" {
534
+ user_pool_id = module.user_identity.user_pool_id
535
+ client_id = aws_cognito_user_pool_client.dcr_proxy.id
536
+ use_cognito_provided_values = true
537
+ }
538
+
539
+ resource "aws_secretsmanager_secret" "client_secret" {
540
+ name = "my-dcr-proxy-client-secret"
541
+ }
542
+
543
+ resource "aws_secretsmanager_secret_version" "client_secret" {
544
+ secret_id = aws_secretsmanager_secret.client_secret.id
545
+ secret_string = aws_cognito_user_pool_client.dcr_proxy.client_secret
546
+ }
547
+
548
+ module "my_project_mcp_server" {
549
+ source = "../../common/terraform/src/app/mcp-servers/my-project-mcp-server"
550
+
551
+ user_pool_id = module.user_identity.user_pool_id
552
+ user_pool_client_ids = [aws_cognito_user_pool_client.dcr_proxy.id]
553
+ appconfig_application_id = module.runtime_config.application_id
554
+ appconfig_application_arn = module.runtime_config.application_arn
555
+ }
556
+
557
+ module "dcr_proxy" {
558
+ source = "../../common/terraform/src/app/dcr-proxies/dcr-proxy"
559
+
560
+ user_pool_id = module.user_identity.user_pool_id
561
+ user_pool_client_id = aws_cognito_user_pool_client.dcr_proxy.id
562
+ cognito_client_secret_arn = aws_secretsmanager_secret.client_secret.arn
563
+ cognito_hosted_ui_base = "https://${module.user_identity.user_pool_domain}.auth.${data.aws_region.current.region}.amazoncognito.com"
564
+ upstream_url = module.my_project_mcp_server.invocation_url
565
+ asset_bucket_name = module.asset_bucket.bucket_name
566
+ }
567
+ ```
568
+ </Fragment>
569
+ </Infrastructure>
@@ -56,7 +56,7 @@ If the `functionPath` option is provided, the generator will add the handler to
56
56
 
57
57
  <Snippet name="shared-constructs" />
58
58
 
59
- The generator creates infrastructure as code for deploying your function based on your selected `iacProvider`:
59
+ The generator creates infrastructure as code for deploying your function based on your selected `iac`:
60
60
 
61
61
  <Infrastructure>
62
62
  <Fragment slot="cdk">
@@ -69,7 +69,7 @@ The generator creates two related projects in the `<directory>/<api-name>` direc
69
69
 
70
70
  ### Infrastructure
71
71
 
72
- Since this generator creates infrastructure as code based on your chosen `iacProvider`, it will create a project in `packages/common` which includes the relevant CDK constructs or Terraform modules.
72
+ Since this generator creates infrastructure as code based on your chosen `iac`, it will create a project in `packages/common` which includes the relevant CDK constructs or Terraform modules.
73
73
 
74
74
  The common infrastructure as code project is structured as follows:
75
75
 
@@ -581,7 +581,7 @@ The local server will not only hot-reload when you make TypeScript changes to yo
581
581
 
582
582
  ## Deploying your Smithy API
583
583
 
584
- The generator creates CDK or Terraform infrastructure based on your selected `iacProvider`.
584
+ The generator creates CDK or Terraform infrastructure based on your selected `iac`.
585
585
 
586
586
  <Infrastructure>
587
587
  <Fragment slot="cdk">
@@ -765,7 +765,7 @@ If you are actively working on both your CDK infrastructure and Smithy API toget
765
765
  </Fragment>
766
766
  <Fragment slot="terraform">
767
767
  :::note[Terraform Limitations]
768
- We do not support type-safe integrations for Terraform, and therefore no code generation targets are configured if you selected Terraform for your `iacProvider`.
768
+ We do not support type-safe integrations for Terraform, and therefore no code generation targets are configured if you selected Terraform for your `iac`.
769
769
  :::
770
770
  </Fragment>
771
771
  </Infrastructure>
@@ -43,7 +43,7 @@ The generator will create the following project structure in the `<directory>/<n
43
43
 
44
44
  </FileTree>
45
45
 
46
- If you set the `enableStageConfig` option, the generator also creates two shared packages for centralized credential management (if they don't already exist):
46
+ If you set the `stageConfig` option, the generator also creates two shared packages for centralized credential management (if they don't already exist):
47
47
 
48
48
  <FileTree>
49
49
 
@@ -99,9 +99,9 @@ new ApplicationStage(app, 'my-app-sandbox', {
99
99
 
100
100
  The `env` property tells CDK which AWS account and region to deploy to. `CDK_DEFAULT_ACCOUNT` and `CDK_DEFAULT_REGION` are resolved automatically by the CDK CLI from your active AWS credentials. See the [CDK environments documentation](https://docs.aws.amazon.com/cdk/v2/guide/environments.html) for more details.
101
101
 
102
- If you generated with `enableStageConfig`, the `main.ts` reads account and region from a centralized config file instead, falling back to environment variables when no config is set:
102
+ If you generated with `stageConfig`, the `main.ts` reads account and region from a centralized config file instead, falling back to environment variables when no config is set:
103
103
 
104
- ```ts title="src/main.ts (with enableStageConfig)"
104
+ ```ts title="src/main.ts (with stageConfig)"
105
105
  import stagesConfig from ':my-scope/common-infra-config';
106
106
 
107
107
  const projectStages = stagesConfig.projects?.['packages/infra']?.stages ?? {};
@@ -158,12 +158,12 @@ export class ApplicationStage extends Stage {
158
158
  ### Stage Credential Configuration
159
159
 
160
160
  :::note[Staged Configuration]
161
- This section applies when you generate with `enableStageConfig`. Without it, the generator produces a simpler setup where you manage AWS credentials yourself (e.g., by exporting `AWS_PROFILE` before deploying).
161
+ This section applies when you generate with `stageConfig`. Without it, the generator produces a simpler setup where you manage AWS credentials yourself (e.g., by exporting `AWS_PROFILE` before deploying).
162
162
  :::
163
163
 
164
164
  When you have multiple stages targeting different AWS accounts, managing credentials manually can be error-prone, especially as the number of stages grows.
165
165
 
166
- The `enableStageConfig` option solves this by generating two shared packages:
166
+ The `stageConfig` option solves this by generating two shared packages:
167
167
 
168
168
  - **`packages/common/infra-config`** — A single config file where you map each stage to its AWS credentials, account, and region. This is importable from any package in your workspace, so your CDK `main.ts` can read account and region from the same source of truth.
169
169
  - **`packages/common/scripts`** — `infra-deploy` and `infra-destroy` commands that wrap CDK with automatic credential resolution. When you run `deploy`, the script reads the config, sets the right AWS environment variables for the CDK child process, and runs `cdk deploy`. Your shell environment is never modified.
@@ -375,7 +375,7 @@ After a build, you can deploy your infrastructure to AWS using the `deploy` targ
375
375
  Use the `deploy-ci` target if deploying in a CI/CD pipeline. See below for more details.
376
376
  :::
377
377
 
378
- First, make sure you have AWS credentials configured. If you generated with `enableStageConfig` and have configured stage credentials in `packages/common/infra-config/src/stages.config.ts`, the deploy command will automatically resolve and apply the correct credentials for the target stage. Otherwise, ensure your AWS credentials are set in your environment (e.g., via `AWS_PROFILE` or environment variables). See the [AWS credentials documentation](https://docs.aws.amazon.com/sdkref/latest/guide/access.html) for the available options.
378
+ First, make sure you have AWS credentials configured. If you generated with `stageConfig` and have configured stage credentials in `packages/common/infra-config/src/stages.config.ts`, the deploy command will automatically resolve and apply the correct credentials for the target stage. Otherwise, ensure your AWS credentials are set in your environment (e.g., via `AWS_PROFILE` or environment variables). See the [AWS credentials documentation](https://docs.aws.amazon.com/sdkref/latest/guide/access.html) for the available options.
379
379
 
380
380
  Then run the deploy target:
381
381
 
@@ -3,7 +3,7 @@ title: Deploying your Function
3
3
  ---
4
4
  import Infrastructure from '@components/infrastructure.astro';
5
5
 
6
- This generator creates CDK or Terraform infrastructure as code based on your selected `iacProvider`. You can use this to deploy your function.
6
+ This generator creates CDK or Terraform infrastructure as code based on your selected `iac`. You can use this to deploy your function.
7
7
 
8
8
  <Infrastructure>
9
9
  <Fragment slot="cdk">
@@ -5,7 +5,7 @@ import Infrastructure from '@components/infrastructure.astro';
5
5
  import Link from '@components/link.astro';
6
6
  import Drawer from '@components/drawer.astro';
7
7
 
8
- The relational database generator creates CDK or Terraform infrastructure based on your selected `iacProvider`.
8
+ The relational database generator creates CDK or Terraform infrastructure based on your selected `iac`.
9
9
 
10
10
  <Infrastructure>
11
11
  <Fragment slot="cdk">