@aws/nx-plugin 1.0.0-rc.72 → 1.0.0-rc.74

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 (145) hide show
  1. package/LICENSE-THIRD-PARTY +1379 -2592
  2. package/README.md +7 -89
  3. package/migrations.json +38 -23
  4. package/package.json +4 -4
  5. package/src/agentcore-gateway/__snapshots__/generator.spec.ts.snap +1 -1
  6. package/src/infra/app/__snapshots__/generator.spec.ts.snap +3 -3
  7. package/src/internal/test-matrix/generator.js +15 -0
  8. package/src/internal/test-matrix/generator.js.map +1 -1
  9. package/src/license/config.d.ts +9 -0
  10. package/src/license/config.js +28 -0
  11. package/src/license/config.js.map +1 -1
  12. package/src/license/dependency-check/collectors/collector.js.map +1 -1
  13. package/src/license/dependency-check/collectors/python-collector.js +9 -3
  14. package/src/license/dependency-check/collectors/python-collector.js.map +1 -1
  15. package/src/license/generator.js +3 -17
  16. package/src/license/generator.js.map +1 -1
  17. package/src/license/known-exceptions.d.ts +0 -1
  18. package/src/license/known-exceptions.js +1 -23
  19. package/src/license/known-exceptions.js.map +1 -1
  20. package/src/migrations/latest/apt-security-updates-in-container-images/__snapshots__/migration.spec.ts.snap +90 -0
  21. package/src/migrations/latest/apt-security-updates-in-container-images/metadata.json +3 -0
  22. package/src/migrations/latest/apt-security-updates-in-container-images/migration.js +90 -0
  23. package/src/migrations/latest/apt-security-updates-in-container-images/migration.js.map +1 -0
  24. package/src/migrations/latest/py-agent-session-id-middleware-extraction/metadata.json +3 -0
  25. package/src/migrations/latest/py-agent-session-id-middleware-extraction/migration.js +118 -0
  26. package/src/migrations/latest/py-agent-session-id-middleware-extraction/migration.js.map +1 -0
  27. package/src/migrations/latest/remove-mcp-inspector-license-exceptions/metadata.json +3 -0
  28. package/src/migrations/latest/remove-mcp-inspector-license-exceptions/migration.d.ts +6 -0
  29. package/src/migrations/latest/remove-mcp-inspector-license-exceptions/migration.js +38 -0
  30. package/src/migrations/latest/remove-mcp-inspector-license-exceptions/migration.js.map +1 -0
  31. package/src/migrations/v1.0.0-rc.67/0001-smithy-server-package-rename/migration.js.map +1 -0
  32. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/migration.js.map +1 -0
  33. package/src/migrations/{latest/py-agent-a2a-lifespan-construction → v1.0.0-rc.70/0001-py-agent-a2a-lifespan-construction}/migration.js +14 -2
  34. package/src/migrations/v1.0.0-rc.70/0001-py-agent-a2a-lifespan-construction/migration.js.map +1 -0
  35. package/src/migrations/v1.0.0-rc.71/0001-py-agent-a2a-httpx-client-per-call/migration.d.ts +6 -0
  36. package/src/migrations/v1.0.0-rc.71/0001-py-agent-a2a-httpx-client-per-call/migration.js.map +1 -0
  37. package/src/migrations/v1.0.0-rc.72/0001-py-agent-session-management-support/migration.d.ts +2 -0
  38. package/src/migrations/v1.0.0-rc.72/0001-py-agent-session-management-support/migration.js.map +1 -0
  39. package/src/preset/__snapshots__/generator.spec.ts.snap +2 -2
  40. package/src/py/agent/__snapshots__/generator.constructs.spec.ts.snap +14 -22
  41. package/src/py/agent/__snapshots__/generator.frameworks.spec.ts.snap +10 -21
  42. package/src/py/agent/__snapshots__/generator.protocols.spec.ts.snap +2 -2
  43. package/src/py/agent/a2a-connection/generator.js +5 -11
  44. package/src/py/agent/a2a-connection/generator.js.map +1 -1
  45. package/src/py/agent/files/deploy/Dockerfile.template +7 -0
  46. package/src/py/agent/files/langchain/a2a/main.py.template +4 -15
  47. package/src/py/agent/files/langchain/ag-ui/main.py.template +2 -13
  48. package/src/py/agent/files/langchain/common/middleware/__init__.py.template +0 -0
  49. package/src/py/agent/files/langchain/common/middleware/session_id_middleware.py.template +17 -0
  50. package/src/py/agent/files/langchain/http/main.py.template +2 -14
  51. package/src/py/agent/files/strands/a2a/main.py.template +4 -15
  52. package/src/py/agent/files/strands/ag-ui/main.py.template +2 -13
  53. package/src/py/agent/files/strands/common/middleware/__init__.py.template +0 -0
  54. package/src/py/agent/files/strands/common/middleware/session_id_middleware.py.template +17 -0
  55. package/src/py/agent/files/strands/http/main.py.template +2 -17
  56. package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +5 -5
  57. package/src/py/mcp-server/files/deploy/Dockerfile.template +7 -0
  58. package/src/py/mcp-server/generator.js +8 -3
  59. package/src/py/mcp-server/generator.js.map +1 -1
  60. package/src/py/rdb/__snapshots__/generator.spec.ts.snap +7 -5
  61. package/src/py/rdb/generator.js +36 -2
  62. package/src/py/rdb/generator.js.map +1 -1
  63. package/src/sdk/agentcore-harness.d.ts +6 -0
  64. package/src/sdk/agentcore-harness.js +7 -0
  65. package/src/sdk/agentcore-harness.js.map +1 -0
  66. package/src/sdk/smithy.d.ts +6 -0
  67. package/src/sdk/smithy.js +7 -0
  68. package/src/sdk/smithy.js.map +1 -0
  69. package/src/smithy/project/__snapshots__/generator.spec.ts.snap +14 -14
  70. package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +1 -1
  71. package/src/ts/agent/__snapshots__/generator.spec.ts.snap +9 -2
  72. package/src/ts/agent/a2a-connection/generator.js +4 -2
  73. package/src/ts/agent/a2a-connection/generator.js.map +1 -1
  74. package/src/ts/agent/files/deploy/Dockerfile.template +7 -0
  75. package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +2 -2
  76. package/src/ts/mcp-server/files/Dockerfile.template +7 -0
  77. package/src/ts/mcp-server/generator.js +8 -3
  78. package/src/ts/mcp-server/generator.js.map +1 -1
  79. package/src/ts/rdb/__snapshots__/generator.spec.ts.snap +8 -6
  80. package/src/ts/rdb/generator.js +29 -2
  81. package/src/ts/rdb/generator.js.map +1 -1
  82. package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +15 -15
  83. package/src/ts/react-website/app/generator.js +4 -2
  84. package/src/ts/react-website/app/generator.js.map +1 -1
  85. package/src/ts/react-website/cognito-auth/__snapshots__/generator.terraform.spec.ts.snap +1 -1
  86. package/src/utils/agent-connection/agent-connection.js +5 -9
  87. package/src/utils/agent-connection/agent-connection.js.map +1 -1
  88. package/src/utils/ast.d.ts +29 -0
  89. package/src/utils/ast.js +31 -0
  90. package/src/utils/ast.js.map +1 -1
  91. package/src/utils/metrics.js +2 -2
  92. package/src/utils/metrics.js.map +1 -1
  93. package/src/utils/versions.d.ts +73 -73
  94. package/src/utils/versions.js +70 -70
  95. package/src/utils/versions.js.map +1 -1
  96. package/src/migrations/latest/py-agent-a2a-httpx-client-per-call/migration.js.map +0 -1
  97. package/src/migrations/latest/py-agent-a2a-lifespan-construction/migration.js.map +0 -1
  98. package/src/migrations/latest/py-agent-session-management-support/migration.js.map +0 -1
  99. package/src/migrations/latest/smithy-server-package-rename/migration.js.map +0 -1
  100. package/src/migrations/latest/translate-script-whole-file-writes/migration.js.map +0 -1
  101. package/src/py/rdb/files/__name__/__init__.py.template +0 -3
  102. package/src/py/rdb/files/config.json.template +0 -14
  103. package/src/ts/rdb/files/config.json.template +0 -14
  104. package/src/ts/rdb/files/src/index.ts.template +0 -1
  105. /package/src/migrations/latest/{py-agent-a2a-httpx-client-per-call → apt-security-updates-in-container-images}/migration.d.ts +0 -0
  106. /package/src/migrations/latest/{py-agent-session-management-support → py-agent-session-id-middleware-extraction}/migration.d.ts +0 -0
  107. /package/src/migrations/{latest/smithy-server-package-rename → v1.0.0-rc.67/0001-smithy-server-package-rename}/metadata.json +0 -0
  108. /package/src/migrations/{latest/py-agent-a2a-lifespan-construction → v1.0.0-rc.67/0001-smithy-server-package-rename}/migration.d.ts +0 -0
  109. /package/src/migrations/{latest/smithy-server-package-rename → v1.0.0-rc.67/0001-smithy-server-package-rename}/migration.js +0 -0
  110. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/build-system-prompt.ts.fixture +0 -0
  111. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/build-user-prompt.ts.fixture +0 -0
  112. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/config-path.ts.fixture +0 -0
  113. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/file-to-translate.ts.fixture +0 -0
  114. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/get-files-to-translate.ts.fixture +0 -0
  115. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/header.ts.fixture +0 -0
  116. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/log.ts.fixture +0 -0
  117. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/main-call.ts.fixture +0 -0
  118. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/main.ts.fixture +0 -0
  119. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/project-root.ts.fixture +0 -0
  120. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/run-with-concurrency.ts.fixture +0 -0
  121. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/scripts-dir.ts.fixture +0 -0
  122. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/files/translate-file-for-language.ts.fixture +0 -0
  123. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/already-migrated.grit +0 -0
  124. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/build-system-prompt.grit +0 -0
  125. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/build-user-prompt.grit +0 -0
  126. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/config-path.grit +0 -0
  127. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/file-to-translate.grit +0 -0
  128. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/get-files-to-translate.grit +0 -0
  129. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/log.grit +0 -0
  130. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/main-call.grit +0 -0
  131. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/main.grit +0 -0
  132. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/project-root.grit +0 -0
  133. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/run-with-concurrency.grit +0 -0
  134. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/grit/translate-file-for-language.grit +0 -0
  135. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/metadata.json +0 -0
  136. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/migration.d.ts +0 -0
  137. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/migration.js +0 -0
  138. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/test-fixtures/released-script-first.ts.fixture +0 -0
  139. /package/src/migrations/{latest/translate-script-whole-file-writes → v1.0.0-rc.68/0001-translate-script-whole-file-writes}/test-fixtures/released-script.ts.fixture +0 -0
  140. /package/src/migrations/{latest/py-agent-a2a-lifespan-construction → v1.0.0-rc.70/0001-py-agent-a2a-lifespan-construction}/metadata.json +0 -0
  141. /package/src/migrations/{latest/smithy-server-package-rename → v1.0.0-rc.70/0001-py-agent-a2a-lifespan-construction}/migration.d.ts +0 -0
  142. /package/src/migrations/{latest/py-agent-a2a-httpx-client-per-call → v1.0.0-rc.71/0001-py-agent-a2a-httpx-client-per-call}/metadata.json +0 -0
  143. /package/src/migrations/{latest/py-agent-a2a-httpx-client-per-call → v1.0.0-rc.71/0001-py-agent-a2a-httpx-client-per-call}/migration.js +0 -0
  144. /package/src/migrations/{latest/py-agent-session-management-support → v1.0.0-rc.72/0001-py-agent-session-management-support}/metadata.json +0 -0
  145. /package/src/migrations/{latest/py-agent-session-management-support → v1.0.0-rc.72/0001-py-agent-session-management-support}/migration.js +0 -0
@@ -618,14 +618,12 @@ from fastapi import FastAPI, Request
618
618
  from fastapi.middleware.cors import CORSMiddleware
619
619
  from fastapi.responses import JSONResponse, StreamingResponse
620
620
  from proj_agent_connection import get_current_session_id, session_id_context
621
- from starlette.middleware.base import BaseHTTPMiddleware
622
621
 
623
622
  from .agent import get_agent
623
+ from .middleware.session_id_middleware import SESSION_ID_HEADER, SessionIdMiddleware
624
624
 
625
625
  logging.basicConfig(level=logging.INFO)
626
626
 
627
- SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
628
-
629
627
 
630
628
  @asynccontextmanager
631
629
  async def lifespan(app: FastAPI):
@@ -638,15 +636,6 @@ async def lifespan(app: FastAPI):
638
636
  yield
639
637
 
640
638
 
641
- class _SessionIdMiddleware(BaseHTTPMiddleware):
642
- """Bind the session ID for this request so downstream MCP / A2A clients forward it on outbound calls."""
643
-
644
- async def dispatch(self, request: Request, call_next):
645
- session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
646
- with session_id_context(session_id):
647
- return await call_next(request)
648
-
649
-
650
639
  app = FastAPI(title="SnapshotAgent", lifespan=lifespan)
651
640
  # Allow browser-based AG-UI clients (e.g. a connected React website) to call
652
641
  # this agent cross-origin, including the CORS preflight.
@@ -657,7 +646,7 @@ app.add_middleware(
657
646
  allow_methods=["*"],
658
647
  allow_headers=["*"],
659
648
  )
660
- app.add_middleware(_SessionIdMiddleware)
649
+ app.add_middleware(SessionIdMiddleware)
661
650
 
662
651
 
663
652
  @app.post("/invocations")
@@ -712,20 +701,20 @@ name = "proj.test_project"
712
701
  version = "0.1.0"
713
702
  dependencies = [
714
703
  "proj-agent-connection",
715
- "aws-lambda-powertools==3.31.1",
704
+ "aws-lambda-powertools==3.34.0",
716
705
  "aws-opentelemetry-distro==0.19.0",
717
706
  "bedrock-agentcore==1.21.0",
718
- "boto3==1.43.67",
707
+ "boto3==1.43.72",
719
708
  "fastapi==0.141.1",
720
709
  "mcp==1.28.1",
721
- "langchain==1.3.14",
722
- "langchain-aws==1.7.0",
723
- "langgraph==1.2.10",
710
+ "langchain==1.3.15",
711
+ "langchain-aws==1.7.1",
712
+ "langgraph==1.2.11",
724
713
  "langgraph-checkpoint-sqlite==3.1.1",
725
714
  "aiosqlite==0.22.1",
726
- "ag-ui-protocol==0.1.19",
727
- "ag-ui-langgraph==0.0.42",
728
- "uvicorn==0.52.1"
715
+ "ag-ui-protocol==0.1.20",
716
+ "ag-ui-langgraph==0.0.43",
717
+ "uvicorn==0.52.3"
729
718
  ]
730
719
 
731
720
  [dependency-groups]
@@ -676,7 +676,7 @@ exports[`py#agent generator > should match snapshot for Terraform generated file
676
676
  }
677
677
  null = {
678
678
  source = "hashicorp/null"
679
- version = "3.3.0"
679
+ version = "3.3.1"
680
680
  }
681
681
  random = {
682
682
  source = "hashicorp/random"
@@ -1088,7 +1088,7 @@ resource "null_resource" "runtime_ready" {
1088
1088
 
1089
1089
  provisioner "local-exec" {
1090
1090
  command = <<-EOT
1091
- uv run --with boto3==1.43.67 python -c "
1091
+ uv run --with boto3==1.43.72 python -c "
1092
1092
  import boto3
1093
1093
  import json
1094
1094
  import os
@@ -4,7 +4,7 @@
4
4
  */ import { generateFiles, joinPathFragments, OverwriteStrategy, updateProjectConfiguration } from "@nx/devkit";
5
5
  import { addPyDependencies } from "../../../utils/add-dependencies.js";
6
6
  import { AGENT_CONNECTION_PY_DEPENDENCIES, addPythonCoreClient, addPythonReExport, ensurePythonAgentConnectionProject, getPythonAgentConnectionModuleName, getPythonAgentConnectionProject, getPythonAgentConnectionProjectDir, PY_CLIENT_NAMING, resolveAgentFramework } from "../../../utils/agent-connection/agent-connection.js";
7
- import { addPythonDestructuredImport, applyGritQL, matchGritQL } from "../../../utils/ast.js";
7
+ import { addPythonDestructuredImport, appendToArrayViaGritQL, applyGritQL, matchGritQL } from "../../../utils/ast.js";
8
8
  import { declareDependencies, ownedElsewhere } from "../../../utils/declared-dependencies.js";
9
9
  import { formatFilesInSubtree } from "../../../utils/format.js";
10
10
  import { installDependencies } from "../../../utils/install.js";
@@ -169,16 +169,10 @@ export const pyAgentA2aConnectionGenerator = async (tree, options)=>{
169
169
  * Scoped to the framework's agent constructor so unrelated `tools=` keyword
170
170
  * arguments elsewhere in the file are not touched.
171
171
  */ const addToolToAgent = async (tree, filePath, toolName, framework)=>{
172
- const scope = AGENT_CONSTRUCTOR[framework];
173
- await applyGritQL(tree, filePath, py(`\`tools=$old\` where {
174
- $old <: within \`${scope}\`,
175
- $old <: not contains \`${toolName}\`,
176
- if ($old <: \`[]\`) {
177
- $old => \`[${toolName}]\`
178
- } else {
179
- $old <: \`[$items]\` where { $items += \`, ${toolName}\` }
180
- }
181
- }`));
172
+ await appendToArrayViaGritQL(tree, filePath, 'tools=', toolName, {
173
+ scope: AGENT_CONSTRUCTOR[framework],
174
+ language: 'py'
175
+ });
182
176
  };
183
177
  /**
184
178
  * Wrap the get_agent body so the remote A2A client is created and exposed
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../packages/nx-plugin/src/py/agent/a2a-connection/generator.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n type GeneratorCallback,\n generateFiles,\n joinPathFragments,\n OverwriteStrategy,\n type Tree,\n updateProjectConfiguration,\n} from '@nx/devkit';\nimport { addPyDependencies } from '../../../utils/add-dependencies';\nimport {\n AGENT_CONNECTION_PY_DEPENDENCIES,\n type AgentFramework,\n addPythonCoreClient,\n addPythonReExport,\n ensurePythonAgentConnectionProject,\n getPythonAgentConnectionModuleName,\n getPythonAgentConnectionProject,\n getPythonAgentConnectionProjectDir,\n PY_CLIENT_NAMING,\n resolveAgentFramework,\n} from '../../../utils/agent-connection/agent-connection';\nimport {\n addPythonDestructuredImport,\n applyGritQL,\n matchGritQL,\n} from '../../../utils/ast';\nimport {\n declareDependencies,\n ownedElsewhere,\n} from '../../../utils/declared-dependencies';\nimport { formatFilesInSubtree } from '../../../utils/format';\nimport { installDependencies } from '../../../utils/install';\nimport { addGeneratorMetricsIfApplicable } from '../../../utils/metrics';\nimport { snakeCase } from '../../../utils/names';\nimport {\n addComponentGeneratorMetadata,\n addDependencyToTargetIfNotPresent,\n getGeneratorInfo,\n type NxGeneratorInfo,\n readProjectConfigurationUnqualified,\n} from '../../../utils/nx';\nimport { toProjectRelativePath } from '../../../utils/paths';\nimport { addWorkspaceDependencyToPyProject } from '../../../utils/py';\nimport type { PyAgentA2aConnectionGeneratorSchema } from './schema';\n\n/** Prefix a GritQL pattern with `language python` */\nconst py = (pattern: string) => `language python\\n${pattern}`;\n\n/** The module each framework imports the `tool` decorator from. */\nconst TOOL_IMPORT_MODULE: Record<AgentFramework, string> = {\n strands: 'strands',\n langchain: 'langchain_core.tools',\n};\n\n/** The GritQL pattern matching each framework's agent constructor. */\nconst AGENT_CONSTRUCTOR: Record<AgentFramework, string> = {\n strands: 'yield Agent($_)',\n langchain: 'create_agent($_)',\n};\n\n/** The metadata this generator records, which its predicates read. */\nexport interface PyAgentA2aConnectionMetadata {\n readonly framework: AgentFramework;\n}\n\n// Each framework's Layer-2 client needs its own A2A transport: Strands wraps\n// strands' `A2AAgent` (strands-agents[a2a]); LangChain drives the a2a SDK\n// directly (a2a-sdk) and must not pull Strands in.\nexport const DEPENDENCIES = declareDependencies<PyAgentA2aConnectionMetadata>()(\n {\n py: [\n { name: 'boto3' },\n { name: 'httpx' },\n { name: 'strands-agents[a2a]', when: (m) => m.framework === 'strands' },\n { name: 'a2a-sdk', when: (m) => m.framework === 'langchain' },\n ...ownedElsewhere(AGENT_CONNECTION_PY_DEPENDENCIES),\n ],\n },\n);\n\nexport const PY_AGENT_A2A_CONNECTION_GENERATOR_INFO: NxGeneratorInfo =\n getGeneratorInfo(import.meta.filename);\n\nexport const pyAgentA2aConnectionGenerator = async (\n tree: Tree,\n options: PyAgentA2aConnectionGeneratorSchema,\n): Promise<GeneratorCallback> => {\n const sourceProject = readProjectConfigurationUnqualified(\n tree,\n options.sourceProject,\n );\n const targetProject = readProjectConfigurationUnqualified(\n tree,\n options.targetProject,\n );\n\n const agentComponent = options.sourceComponent;\n const targetAgentComponent = options.targetComponent;\n\n if (!agentComponent || !targetAgentComponent) {\n throw new Error(\n 'Both sourceComponent and targetComponent must be provided for py#agent -> a2a connections',\n );\n }\n\n if ((targetAgentComponent.protocol ?? '').toLowerCase() !== 'a2a') {\n throw new Error(\n `Target agent '${targetAgentComponent.name}' uses the ${targetAgentComponent.protocol ?? 'http'} protocol — only A2A agents can be connected as tools.`,\n );\n }\n\n if (\n targetAgentComponent.auth &&\n targetAgentComponent.auth.toLowerCase() !== 'iam'\n ) {\n throw new Error(\n `A2A agent connection currently only supports IAM authentication, but '${targetAgentComponent.name}' uses '${targetAgentComponent.auth}' authentication.`,\n );\n }\n\n const targetAgentComponentName = targetAgentComponent.name ?? 'agent';\n const targetAgentClassName = targetAgentComponent.rc as string;\n const targetAgentSnakeCase = snakeCase(targetAgentClassName);\n const targetAgentPort = targetAgentComponent.port ?? 9000;\n\n // The source agent's framework drives everything that varies: the client\n // naming + app template (shared with the mcp/gateway generators via\n // PY_CLIENT_NAMING), the A2A transport dep (see DEPENDENCIES), and the\n // agent.py transform below. Each framework wraps its own A2A Layer-2 client\n // (Strands' A2AAgent vs an a2a-sdk-only client) over the shared,\n // framework-agnostic A2A client config. Keyed on the framework, not a\n // boolean, so a third framework stays additive.\n const framework = resolveAgentFramework(agentComponent.framework);\n const naming = PY_CLIENT_NAMING[framework];\n\n // Recorded below and read by the declaration's predicates, so the packages\n // added here are exactly the ones the version sync will own.\n const metadata: PyAgentA2aConnectionMetadata = { framework };\n\n // 1. Ensure the shared Python agent-connection project exists + has the\n // A2A core client (framework Layer-2) and its shared SigV4 auth helper.\n await ensurePythonAgentConnectionProject(tree, DEPENDENCIES);\n await addPythonCoreClient(tree, 'a2a', DEPENDENCIES, framework);\n\n const agentConnectionProjectDir = getPythonAgentConnectionProjectDir(tree);\n const agentConnectionModuleName = getPythonAgentConnectionModuleName(tree);\n\n // A2A client config + shared auth helper deps, plus the framework's A2A\n // transport dep. These belong to the shared agent-connection project, which\n // holds the client.\n addPyDependencies(tree, DEPENDENCIES, {\n metadata,\n projectRoot: agentConnectionProjectDir,\n });\n\n // 2. Generate the per-connection client into the shared agent-connection project\n const appDir = joinPathFragments(\n agentConnectionProjectDir,\n agentConnectionModuleName,\n 'app',\n );\n generateFiles(\n tree,\n joinPathFragments(import.meta.dirname, 'files', naming.appTemplateSubdir),\n appDir,\n {\n targetAgentSnakeCase,\n targetAgentClassName,\n targetAgentPort,\n agentConnectionModuleName,\n },\n { overwriteStrategy: OverwriteStrategy.KeepExisting },\n );\n\n // Ensure app/__init__.py exists\n const appInitPath = joinPathFragments(appDir, '__init__.py');\n if (!tree.exists(appInitPath)) {\n tree.write(appInitPath, '');\n }\n\n // Add re-export to module __init__.py\n const moduleInitPath = joinPathFragments(\n agentConnectionProjectDir,\n agentConnectionModuleName,\n '__init__.py',\n );\n await addPythonReExport(\n tree,\n moduleInitPath,\n `.app.${targetAgentSnakeCase}_client_${naming.clientModuleSuffix}`,\n `${targetAgentClassName}Client${naming.clientClassSuffix}`,\n );\n\n // 3. Transform agent.py to add the A2A client import + wrap it as a tool\n const agentSourceDir = joinPathFragments(\n sourceProject.root,\n agentComponent.path ?? 'src',\n );\n const agentFilePath = joinPathFragments(agentSourceDir, 'agent.py');\n\n if (tree.exists(agentFilePath)) {\n const clientClassName = `${targetAgentClassName}Client${naming.clientClassSuffix}`;\n const clientVarName = targetAgentSnakeCase;\n const toolName = `ask_${targetAgentSnakeCase}`;\n\n await addImportToAgentFile(\n tree,\n agentFilePath,\n agentConnectionModuleName,\n clientClassName,\n framework,\n );\n await addToolToAgent(tree, agentFilePath, toolName, framework);\n await addClientToolToGetAgent(\n tree,\n agentFilePath,\n clientClassName,\n clientVarName,\n toolName,\n targetAgentClassName,\n framework,\n );\n }\n\n // 4. Add workspace dependency from agent project to agent-connection project\n addWorkspaceDependencyToPyProject(\n tree,\n sourceProject,\n getPythonAgentConnectionProject(tree),\n );\n\n // 5. Set up dev target dependencies — chain onto the target agent\n const agentName = agentComponent.name ?? 'agent';\n const devTargetName = `${agentName}-dev`;\n const targetDevTargetName = `${targetAgentComponentName}-dev`;\n\n if (sourceProject.targets?.[devTargetName]) {\n addDependencyToTargetIfNotPresent(sourceProject, devTargetName, {\n projects: [targetProject.name],\n target: targetDevTargetName,\n });\n updateProjectConfiguration(tree, sourceProject.name, sourceProject);\n }\n\n // Recorded so the version sync knows this connection's dependencies are ours.\n addComponentGeneratorMetadata(\n tree,\n sourceProject.name,\n PY_AGENT_A2A_CONNECTION_GENERATOR_INFO,\n toProjectRelativePath(sourceProject, agentFilePath),\n targetAgentClassName,\n // `sourcePath` names the source component this connection is made from, so\n // the pair is identifiable rather than just the two projects.\n { ...metadata, sourcePath: options.sourceComponent?.path },\n );\n\n await addGeneratorMetricsIfApplicable(tree, [\n PY_AGENT_A2A_CONNECTION_GENERATOR_INFO,\n ]);\n\n await formatFilesInSubtree(tree);\n return () =>\n installDependencies(tree, options.preferInstallDependencies, {\n languages: ['typescript'],\n });\n};\n\n/**\n * Add imports to agent.py:\n * - `tool` (from `strands`, or `langchain_core.tools` for LangChain agents)\n * - the per-connection client class from the shared agent-connection module\n */\nconst addImportToAgentFile = async (\n tree: Tree,\n filePath: string,\n agentConnectionModuleName: string,\n clientClassName: string,\n framework: AgentFramework,\n): Promise<void> => {\n await addPythonDestructuredImport(\n tree,\n filePath,\n ['tool'],\n TOOL_IMPORT_MODULE[framework],\n );\n await addPythonDestructuredImport(\n tree,\n filePath,\n [clientClassName],\n agentConnectionModuleName,\n );\n};\n\n/**\n * Add the A2A tool to the Agent's tools array using GritQL.\n *\n * Scoped to the framework's agent constructor so unrelated `tools=` keyword\n * arguments elsewhere in the file are not touched.\n */\nconst addToolToAgent = async (\n tree: Tree,\n filePath: string,\n toolName: string,\n framework: AgentFramework,\n): Promise<void> => {\n const scope = AGENT_CONSTRUCTOR[framework];\n await applyGritQL(\n tree,\n filePath,\n py(`\\`tools=$old\\` where {\n $old <: within \\`${scope}\\`,\n $old <: not contains \\`${toolName}\\`,\n if ($old <: \\`[]\\`) {\n $old => \\`[${toolName}]\\`\n } else {\n $old <: \\`[$items]\\` where { $items += \\`, ${toolName}\\` }\n }\n}`),\n );\n};\n\n/**\n * Wrap the get_agent body so the remote A2A client is created and exposed\n * as a `@tool`-decorated closure before constructing the Agent.\n *\n * Unlike MCP clients, A2A clients aren't context managers — we don't need\n * a `with` block around them. Creation + tool definition happen at the\n * top of the function body, anchored on the framework's agent constructor.\n */\nconst addClientToolToGetAgent = async (\n tree: Tree,\n filePath: string,\n clientClassName: string,\n clientVarName: string,\n toolName: string,\n targetAgentClassName: string,\n framework: AgentFramework,\n): Promise<void> => {\n if (await matchGritQL(tree, filePath, py(`\\`${clientClassName}.create\\``))) {\n return;\n }\n\n // Both frameworks' tools are @tool-decorated callables (the decorator's\n // import differs, handled in addImportToAgentFile), defined inline in\n // get_agent. The client is directly callable, returning the remote's reply.\n const toolBlock = `${clientVarName} = ${clientClassName}.create()\n\n @tool\n def ${toolName}(prompt: str) -> str:\n \"\"\"Delegate a question to the remote ${targetAgentClassName} A2A agent and return its reply.\"\"\"\n return str(${clientVarName}(prompt))\n`;\n\n // Prepend client creation + tool definition to the function body, anchored on\n // the framework's agent constructor. This works whether or not a prior MCP\n // block is present since we insert before any existing statements.\n const anchor = AGENT_CONSTRUCTOR[framework];\n await applyGritQL(\n tree,\n filePath,\n py(`\\`def get_agent($params):\n $body\\` where {\n $body <: contains \\`${anchor}\\`,\n $body <: not contains \\`${clientClassName}.create\\`\n} => \\`def get_agent($params):\n ${toolBlock}\n $body\\``),\n );\n};\n\nexport default pyAgentA2aConnectionGenerator;\n"],"names":["generateFiles","joinPathFragments","OverwriteStrategy","updateProjectConfiguration","addPyDependencies","AGENT_CONNECTION_PY_DEPENDENCIES","addPythonCoreClient","addPythonReExport","ensurePythonAgentConnectionProject","getPythonAgentConnectionModuleName","getPythonAgentConnectionProject","getPythonAgentConnectionProjectDir","PY_CLIENT_NAMING","resolveAgentFramework","addPythonDestructuredImport","applyGritQL","matchGritQL","declareDependencies","ownedElsewhere","formatFilesInSubtree","installDependencies","addGeneratorMetricsIfApplicable","snakeCase","addComponentGeneratorMetadata","addDependencyToTargetIfNotPresent","getGeneratorInfo","readProjectConfigurationUnqualified","toProjectRelativePath","addWorkspaceDependencyToPyProject","py","pattern","TOOL_IMPORT_MODULE","strands","langchain","AGENT_CONSTRUCTOR","DEPENDENCIES","name","when","m","framework","PY_AGENT_A2A_CONNECTION_GENERATOR_INFO","filename","pyAgentA2aConnectionGenerator","tree","options","sourceProject","targetProject","agentComponent","sourceComponent","targetAgentComponent","targetComponent","Error","protocol","toLowerCase","auth","targetAgentComponentName","targetAgentClassName","rc","targetAgentSnakeCase","targetAgentPort","port","naming","metadata","agentConnectionProjectDir","agentConnectionModuleName","projectRoot","appDir","dirname","appTemplateSubdir","overwriteStrategy","KeepExisting","appInitPath","exists","write","moduleInitPath","clientModuleSuffix","clientClassSuffix","agentSourceDir","root","path","agentFilePath","clientClassName","clientVarName","toolName","addImportToAgentFile","addToolToAgent","addClientToolToGetAgent","agentName","devTargetName","targetDevTargetName","targets","projects","target","sourcePath","preferInstallDependencies","languages","filePath","scope","toolBlock","anchor"],"mappings":"AAAA;;;CAGC,GACD,SAEEA,aAAa,EACbC,iBAAiB,EACjBC,iBAAiB,EAEjBC,0BAA0B,QACrB,aAAa;AACpB,SAASC,iBAAiB,QAAQ,qCAAkC;AACpE,SACEC,gCAAgC,EAEhCC,mBAAmB,EACnBC,iBAAiB,EACjBC,kCAAkC,EAClCC,kCAAkC,EAClCC,+BAA+B,EAC/BC,kCAAkC,EAClCC,gBAAgB,EAChBC,qBAAqB,QAChB,sDAAmD;AAC1D,SACEC,2BAA2B,EAC3BC,WAAW,EACXC,WAAW,QACN,wBAAqB;AAC5B,SACEC,mBAAmB,EACnBC,cAAc,QACT,0CAAuC;AAC9C,SAASC,oBAAoB,QAAQ,2BAAwB;AAC7D,SAASC,mBAAmB,QAAQ,4BAAyB;AAC7D,SAASC,+BAA+B,QAAQ,4BAAyB;AACzE,SAASC,SAAS,QAAQ,0BAAuB;AACjD,SACEC,6BAA6B,EAC7BC,iCAAiC,EACjCC,gBAAgB,EAEhBC,mCAAmC,QAC9B,uBAAoB;AAC3B,SAASC,qBAAqB,QAAQ,0BAAuB;AAC7D,SAASC,iCAAiC,QAAQ,uBAAoB;AAGtE,mDAAmD,GACnD,MAAMC,KAAK,CAACC,UAAoB,CAAC,iBAAiB,EAAEA,SAAS;AAE7D,iEAAiE,GACjE,MAAMC,qBAAqD;IACzDC,SAAS;IACTC,WAAW;AACb;AAEA,oEAAoE,GACpE,MAAMC,oBAAoD;IACxDF,SAAS;IACTC,WAAW;AACb;AAOA,6EAA6E;AAC7E,0EAA0E;AAC1E,mDAAmD;AACnD,OAAO,MAAME,eAAelB,sBAC1B;IACEY,IAAI;QACF;YAAEO,MAAM;QAAQ;QAChB;YAAEA,MAAM;QAAQ;QAChB;YAAEA,MAAM;YAAuBC,MAAM,CAACC,IAAMA,EAAEC,SAAS,KAAK;QAAU;QACtE;YAAEH,MAAM;YAAWC,MAAM,CAACC,IAAMA,EAAEC,SAAS,KAAK;QAAY;WACzDrB,eAAeb;KACnB;AACH,GACA;AAEF,OAAO,MAAMmC,yCACXf,iBAAiB,YAAYgB,QAAQ,EAAE;AAEzC,OAAO,MAAMC,gCAAgC,OAC3CC,MACAC;IAEA,MAAMC,gBAAgBnB,oCACpBiB,MACAC,QAAQC,aAAa;IAEvB,MAAMC,gBAAgBpB,oCACpBiB,MACAC,QAAQE,aAAa;IAGvB,MAAMC,iBAAiBH,QAAQI,eAAe;IAC9C,MAAMC,uBAAuBL,QAAQM,eAAe;IAEpD,IAAI,CAACH,kBAAkB,CAACE,sBAAsB;QAC5C,MAAM,IAAIE,MACR;IAEJ;IAEA,IAAI,AAACF,CAAAA,qBAAqBG,QAAQ,IAAI,EAAC,EAAGC,WAAW,OAAO,OAAO;QACjE,MAAM,IAAIF,MACR,CAAC,cAAc,EAAEF,qBAAqBb,IAAI,CAAC,WAAW,EAAEa,qBAAqBG,QAAQ,IAAI,OAAO,sDAAsD,CAAC;IAE3J;IAEA,IACEH,qBAAqBK,IAAI,IACzBL,qBAAqBK,IAAI,CAACD,WAAW,OAAO,OAC5C;QACA,MAAM,IAAIF,MACR,CAAC,sEAAsE,EAAEF,qBAAqBb,IAAI,CAAC,QAAQ,EAAEa,qBAAqBK,IAAI,CAAC,iBAAiB,CAAC;IAE7J;IAEA,MAAMC,2BAA2BN,qBAAqBb,IAAI,IAAI;IAC9D,MAAMoB,uBAAuBP,qBAAqBQ,EAAE;IACpD,MAAMC,uBAAuBpC,UAAUkC;IACvC,MAAMG,kBAAkBV,qBAAqBW,IAAI,IAAI;IAErD,yEAAyE;IACzE,oEAAoE;IACpE,uEAAuE;IACvE,4EAA4E;IAC5E,iEAAiE;IACjE,sEAAsE;IACtE,gDAAgD;IAChD,MAAMrB,YAAY1B,sBAAsBkC,eAAeR,SAAS;IAChE,MAAMsB,SAASjD,gBAAgB,CAAC2B,UAAU;IAE1C,2EAA2E;IAC3E,6DAA6D;IAC7D,MAAMuB,WAAyC;QAAEvB;IAAU;IAE3D,wEAAwE;IACxE,2EAA2E;IAC3E,MAAM/B,mCAAmCmC,MAAMR;IAC/C,MAAM7B,oBAAoBqC,MAAM,OAAOR,cAAcI;IAErD,MAAMwB,4BAA4BpD,mCAAmCgC;IACrE,MAAMqB,4BAA4BvD,mCAAmCkC;IAErE,wEAAwE;IACxE,4EAA4E;IAC5E,oBAAoB;IACpBvC,kBAAkBuC,MAAMR,cAAc;QACpC2B;QACAG,aAAaF;IACf;IAEA,iFAAiF;IACjF,MAAMG,SAASjE,kBACb8D,2BACAC,2BACA;IAEFhE,cACE2C,MACA1C,kBAAkB,YAAYkE,OAAO,EAAE,SAASN,OAAOO,iBAAiB,GACxEF,QACA;QACER;QACAF;QACAG;QACAK;IACF,GACA;QAAEK,mBAAmBnE,kBAAkBoE,YAAY;IAAC;IAGtD,gCAAgC;IAChC,MAAMC,cAActE,kBAAkBiE,QAAQ;IAC9C,IAAI,CAACvB,KAAK6B,MAAM,CAACD,cAAc;QAC7B5B,KAAK8B,KAAK,CAACF,aAAa;IAC1B;IAEA,sCAAsC;IACtC,MAAMG,iBAAiBzE,kBACrB8D,2BACAC,2BACA;IAEF,MAAMzD,kBACJoC,MACA+B,gBACA,CAAC,KAAK,EAAEhB,qBAAqB,QAAQ,EAAEG,OAAOc,kBAAkB,EAAE,EAClE,GAAGnB,qBAAqB,MAAM,EAAEK,OAAOe,iBAAiB,EAAE;IAG5D,yEAAyE;IACzE,MAAMC,iBAAiB5E,kBACrB4C,cAAciC,IAAI,EAClB/B,eAAegC,IAAI,IAAI;IAEzB,MAAMC,gBAAgB/E,kBAAkB4E,gBAAgB;IAExD,IAAIlC,KAAK6B,MAAM,CAACQ,gBAAgB;QAC9B,MAAMC,kBAAkB,GAAGzB,qBAAqB,MAAM,EAAEK,OAAOe,iBAAiB,EAAE;QAClF,MAAMM,gBAAgBxB;QACtB,MAAMyB,WAAW,CAAC,IAAI,EAAEzB,sBAAsB;QAE9C,MAAM0B,qBACJzC,MACAqC,eACAhB,2BACAiB,iBACA1C;QAEF,MAAM8C,eAAe1C,MAAMqC,eAAeG,UAAU5C;QACpD,MAAM+C,wBACJ3C,MACAqC,eACAC,iBACAC,eACAC,UACA3B,sBACAjB;IAEJ;IAEA,6EAA6E;IAC7EX,kCACEe,MACAE,eACAnC,gCAAgCiC;IAGlC,kEAAkE;IAClE,MAAM4C,YAAYxC,eAAeX,IAAI,IAAI;IACzC,MAAMoD,gBAAgB,GAAGD,UAAU,IAAI,CAAC;IACxC,MAAME,sBAAsB,GAAGlC,yBAAyB,IAAI,CAAC;IAE7D,IAAIV,cAAc6C,OAAO,EAAE,CAACF,cAAc,EAAE;QAC1ChE,kCAAkCqB,eAAe2C,eAAe;YAC9DG,UAAU;gBAAC7C,cAAcV,IAAI;aAAC;YAC9BwD,QAAQH;QACV;QACAtF,2BAA2BwC,MAAME,cAAcT,IAAI,EAAES;IACvD;IAEA,8EAA8E;IAC9EtB,8BACEoB,MACAE,cAAcT,IAAI,EAClBI,wCACAb,sBAAsBkB,eAAemC,gBACrCxB,sBACA,2EAA2E;IAC3E,8DAA8D;IAC9D;QAAE,GAAGM,QAAQ;QAAE+B,YAAYjD,QAAQI,eAAe,EAAE+B;IAAK;IAG3D,MAAM1D,gCAAgCsB,MAAM;QAC1CH;KACD;IAED,MAAMrB,qBAAqBwB;IAC3B,OAAO,IACLvB,oBAAoBuB,MAAMC,QAAQkD,yBAAyB,EAAE;YAC3DC,WAAW;gBAAC;aAAa;QAC3B;AACJ,EAAE;AAEF;;;;CAIC,GACD,MAAMX,uBAAuB,OAC3BzC,MACAqD,UACAhC,2BACAiB,iBACA1C;IAEA,MAAMzB,4BACJ6B,MACAqD,UACA;QAAC;KAAO,EACRjE,kBAAkB,CAACQ,UAAU;IAE/B,MAAMzB,4BACJ6B,MACAqD,UACA;QAACf;KAAgB,EACjBjB;AAEJ;AAEA;;;;;CAKC,GACD,MAAMqB,iBAAiB,OACrB1C,MACAqD,UACAb,UACA5C;IAEA,MAAM0D,QAAQ/D,iBAAiB,CAACK,UAAU;IAC1C,MAAMxB,YACJ4B,MACAqD,UACAnE,GAAG,CAAC;mBACW,EAAEoE,MAAM;yBACF,EAAEd,SAAS;;eAErB,EAAEA,SAAS;;+CAEqB,EAAEA,SAAS;;CAEzD,CAAC;AAEF;AAEA;;;;;;;CAOC,GACD,MAAMG,0BAA0B,OAC9B3C,MACAqD,UACAf,iBACAC,eACAC,UACA3B,sBACAjB;IAEA,IAAI,MAAMvB,YAAY2B,MAAMqD,UAAUnE,GAAG,CAAC,EAAE,EAAEoD,gBAAgB,SAAS,CAAC,IAAI;QAC1E;IACF;IAEA,wEAAwE;IACxE,sEAAsE;IACtE,4EAA4E;IAC5E,MAAMiB,YAAY,GAAGhB,cAAc,GAAG,EAAED,gBAAgB;;;QAGlD,EAAEE,SAAS;6CAC0B,EAAE3B,qBAAqB;mBACjD,EAAE0B,cAAc;AACnC,CAAC;IAEC,8EAA8E;IAC9E,2EAA2E;IAC3E,mEAAmE;IACnE,MAAMiB,SAASjE,iBAAiB,CAACK,UAAU;IAC3C,MAAMxB,YACJ4B,MACAqD,UACAnE,GAAG,CAAC;;sBAEc,EAAEsE,OAAO;0BACL,EAAElB,gBAAgB;;IAExC,EAAEiB,UAAU;WACL,CAAC;AAEZ;AAEA,eAAexD,8BAA8B"}
1
+ {"version":3,"sources":["../../../../../../../packages/nx-plugin/src/py/agent/a2a-connection/generator.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n type GeneratorCallback,\n generateFiles,\n joinPathFragments,\n OverwriteStrategy,\n type Tree,\n updateProjectConfiguration,\n} from '@nx/devkit';\nimport { addPyDependencies } from '../../../utils/add-dependencies';\nimport {\n AGENT_CONNECTION_PY_DEPENDENCIES,\n type AgentFramework,\n addPythonCoreClient,\n addPythonReExport,\n ensurePythonAgentConnectionProject,\n getPythonAgentConnectionModuleName,\n getPythonAgentConnectionProject,\n getPythonAgentConnectionProjectDir,\n PY_CLIENT_NAMING,\n resolveAgentFramework,\n} from '../../../utils/agent-connection/agent-connection';\nimport {\n addPythonDestructuredImport,\n appendToArrayViaGritQL,\n applyGritQL,\n matchGritQL,\n} from '../../../utils/ast';\nimport {\n declareDependencies,\n ownedElsewhere,\n} from '../../../utils/declared-dependencies';\nimport { formatFilesInSubtree } from '../../../utils/format';\nimport { installDependencies } from '../../../utils/install';\nimport { addGeneratorMetricsIfApplicable } from '../../../utils/metrics';\nimport { snakeCase } from '../../../utils/names';\nimport {\n addComponentGeneratorMetadata,\n addDependencyToTargetIfNotPresent,\n getGeneratorInfo,\n type NxGeneratorInfo,\n readProjectConfigurationUnqualified,\n} from '../../../utils/nx';\nimport { toProjectRelativePath } from '../../../utils/paths';\nimport { addWorkspaceDependencyToPyProject } from '../../../utils/py';\nimport type { PyAgentA2aConnectionGeneratorSchema } from './schema';\n\n/** Prefix a GritQL pattern with `language python` */\nconst py = (pattern: string) => `language python\\n${pattern}`;\n\n/** The module each framework imports the `tool` decorator from. */\nconst TOOL_IMPORT_MODULE: Record<AgentFramework, string> = {\n strands: 'strands',\n langchain: 'langchain_core.tools',\n};\n\n/** The GritQL pattern matching each framework's agent constructor. */\nconst AGENT_CONSTRUCTOR: Record<AgentFramework, string> = {\n strands: 'yield Agent($_)',\n langchain: 'create_agent($_)',\n};\n\n/** The metadata this generator records, which its predicates read. */\nexport interface PyAgentA2aConnectionMetadata {\n readonly framework: AgentFramework;\n}\n\n// Each framework's Layer-2 client needs its own A2A transport: Strands wraps\n// strands' `A2AAgent` (strands-agents[a2a]); LangChain drives the a2a SDK\n// directly (a2a-sdk) and must not pull Strands in.\nexport const DEPENDENCIES = declareDependencies<PyAgentA2aConnectionMetadata>()(\n {\n py: [\n { name: 'boto3' },\n { name: 'httpx' },\n { name: 'strands-agents[a2a]', when: (m) => m.framework === 'strands' },\n { name: 'a2a-sdk', when: (m) => m.framework === 'langchain' },\n ...ownedElsewhere(AGENT_CONNECTION_PY_DEPENDENCIES),\n ],\n },\n);\n\nexport const PY_AGENT_A2A_CONNECTION_GENERATOR_INFO: NxGeneratorInfo =\n getGeneratorInfo(import.meta.filename);\n\nexport const pyAgentA2aConnectionGenerator = async (\n tree: Tree,\n options: PyAgentA2aConnectionGeneratorSchema,\n): Promise<GeneratorCallback> => {\n const sourceProject = readProjectConfigurationUnqualified(\n tree,\n options.sourceProject,\n );\n const targetProject = readProjectConfigurationUnqualified(\n tree,\n options.targetProject,\n );\n\n const agentComponent = options.sourceComponent;\n const targetAgentComponent = options.targetComponent;\n\n if (!agentComponent || !targetAgentComponent) {\n throw new Error(\n 'Both sourceComponent and targetComponent must be provided for py#agent -> a2a connections',\n );\n }\n\n if ((targetAgentComponent.protocol ?? '').toLowerCase() !== 'a2a') {\n throw new Error(\n `Target agent '${targetAgentComponent.name}' uses the ${targetAgentComponent.protocol ?? 'http'} protocol — only A2A agents can be connected as tools.`,\n );\n }\n\n if (\n targetAgentComponent.auth &&\n targetAgentComponent.auth.toLowerCase() !== 'iam'\n ) {\n throw new Error(\n `A2A agent connection currently only supports IAM authentication, but '${targetAgentComponent.name}' uses '${targetAgentComponent.auth}' authentication.`,\n );\n }\n\n const targetAgentComponentName = targetAgentComponent.name ?? 'agent';\n const targetAgentClassName = targetAgentComponent.rc as string;\n const targetAgentSnakeCase = snakeCase(targetAgentClassName);\n const targetAgentPort = targetAgentComponent.port ?? 9000;\n\n // The source agent's framework drives everything that varies: the client\n // naming + app template (shared with the mcp/gateway generators via\n // PY_CLIENT_NAMING), the A2A transport dep (see DEPENDENCIES), and the\n // agent.py transform below. Each framework wraps its own A2A Layer-2 client\n // (Strands' A2AAgent vs an a2a-sdk-only client) over the shared,\n // framework-agnostic A2A client config. Keyed on the framework, not a\n // boolean, so a third framework stays additive.\n const framework = resolveAgentFramework(agentComponent.framework);\n const naming = PY_CLIENT_NAMING[framework];\n\n // Recorded below and read by the declaration's predicates, so the packages\n // added here are exactly the ones the version sync will own.\n const metadata: PyAgentA2aConnectionMetadata = { framework };\n\n // 1. Ensure the shared Python agent-connection project exists + has the\n // A2A core client (framework Layer-2) and its shared SigV4 auth helper.\n await ensurePythonAgentConnectionProject(tree, DEPENDENCIES);\n await addPythonCoreClient(tree, 'a2a', DEPENDENCIES, framework);\n\n const agentConnectionProjectDir = getPythonAgentConnectionProjectDir(tree);\n const agentConnectionModuleName = getPythonAgentConnectionModuleName(tree);\n\n // A2A client config + shared auth helper deps, plus the framework's A2A\n // transport dep. These belong to the shared agent-connection project, which\n // holds the client.\n addPyDependencies(tree, DEPENDENCIES, {\n metadata,\n projectRoot: agentConnectionProjectDir,\n });\n\n // 2. Generate the per-connection client into the shared agent-connection project\n const appDir = joinPathFragments(\n agentConnectionProjectDir,\n agentConnectionModuleName,\n 'app',\n );\n generateFiles(\n tree,\n joinPathFragments(import.meta.dirname, 'files', naming.appTemplateSubdir),\n appDir,\n {\n targetAgentSnakeCase,\n targetAgentClassName,\n targetAgentPort,\n agentConnectionModuleName,\n },\n { overwriteStrategy: OverwriteStrategy.KeepExisting },\n );\n\n // Ensure app/__init__.py exists\n const appInitPath = joinPathFragments(appDir, '__init__.py');\n if (!tree.exists(appInitPath)) {\n tree.write(appInitPath, '');\n }\n\n // Add re-export to module __init__.py\n const moduleInitPath = joinPathFragments(\n agentConnectionProjectDir,\n agentConnectionModuleName,\n '__init__.py',\n );\n await addPythonReExport(\n tree,\n moduleInitPath,\n `.app.${targetAgentSnakeCase}_client_${naming.clientModuleSuffix}`,\n `${targetAgentClassName}Client${naming.clientClassSuffix}`,\n );\n\n // 3. Transform agent.py to add the A2A client import + wrap it as a tool\n const agentSourceDir = joinPathFragments(\n sourceProject.root,\n agentComponent.path ?? 'src',\n );\n const agentFilePath = joinPathFragments(agentSourceDir, 'agent.py');\n\n if (tree.exists(agentFilePath)) {\n const clientClassName = `${targetAgentClassName}Client${naming.clientClassSuffix}`;\n const clientVarName = targetAgentSnakeCase;\n const toolName = `ask_${targetAgentSnakeCase}`;\n\n await addImportToAgentFile(\n tree,\n agentFilePath,\n agentConnectionModuleName,\n clientClassName,\n framework,\n );\n await addToolToAgent(tree, agentFilePath, toolName, framework);\n await addClientToolToGetAgent(\n tree,\n agentFilePath,\n clientClassName,\n clientVarName,\n toolName,\n targetAgentClassName,\n framework,\n );\n }\n\n // 4. Add workspace dependency from agent project to agent-connection project\n addWorkspaceDependencyToPyProject(\n tree,\n sourceProject,\n getPythonAgentConnectionProject(tree),\n );\n\n // 5. Set up dev target dependencies — chain onto the target agent\n const agentName = agentComponent.name ?? 'agent';\n const devTargetName = `${agentName}-dev`;\n const targetDevTargetName = `${targetAgentComponentName}-dev`;\n\n if (sourceProject.targets?.[devTargetName]) {\n addDependencyToTargetIfNotPresent(sourceProject, devTargetName, {\n projects: [targetProject.name],\n target: targetDevTargetName,\n });\n updateProjectConfiguration(tree, sourceProject.name, sourceProject);\n }\n\n // Recorded so the version sync knows this connection's dependencies are ours.\n addComponentGeneratorMetadata(\n tree,\n sourceProject.name,\n PY_AGENT_A2A_CONNECTION_GENERATOR_INFO,\n toProjectRelativePath(sourceProject, agentFilePath),\n targetAgentClassName,\n // `sourcePath` names the source component this connection is made from, so\n // the pair is identifiable rather than just the two projects.\n { ...metadata, sourcePath: options.sourceComponent?.path },\n );\n\n await addGeneratorMetricsIfApplicable(tree, [\n PY_AGENT_A2A_CONNECTION_GENERATOR_INFO,\n ]);\n\n await formatFilesInSubtree(tree);\n return () =>\n installDependencies(tree, options.preferInstallDependencies, {\n languages: ['typescript'],\n });\n};\n\n/**\n * Add imports to agent.py:\n * - `tool` (from `strands`, or `langchain_core.tools` for LangChain agents)\n * - the per-connection client class from the shared agent-connection module\n */\nconst addImportToAgentFile = async (\n tree: Tree,\n filePath: string,\n agentConnectionModuleName: string,\n clientClassName: string,\n framework: AgentFramework,\n): Promise<void> => {\n await addPythonDestructuredImport(\n tree,\n filePath,\n ['tool'],\n TOOL_IMPORT_MODULE[framework],\n );\n await addPythonDestructuredImport(\n tree,\n filePath,\n [clientClassName],\n agentConnectionModuleName,\n );\n};\n\n/**\n * Add the A2A tool to the Agent's tools array using GritQL.\n *\n * Scoped to the framework's agent constructor so unrelated `tools=` keyword\n * arguments elsewhere in the file are not touched.\n */\nconst addToolToAgent = async (\n tree: Tree,\n filePath: string,\n toolName: string,\n framework: AgentFramework,\n): Promise<void> => {\n await appendToArrayViaGritQL(tree, filePath, 'tools=', toolName, {\n scope: AGENT_CONSTRUCTOR[framework],\n language: 'py',\n });\n};\n\n/**\n * Wrap the get_agent body so the remote A2A client is created and exposed\n * as a `@tool`-decorated closure before constructing the Agent.\n *\n * Unlike MCP clients, A2A clients aren't context managers — we don't need\n * a `with` block around them. Creation + tool definition happen at the\n * top of the function body, anchored on the framework's agent constructor.\n */\nconst addClientToolToGetAgent = async (\n tree: Tree,\n filePath: string,\n clientClassName: string,\n clientVarName: string,\n toolName: string,\n targetAgentClassName: string,\n framework: AgentFramework,\n): Promise<void> => {\n if (await matchGritQL(tree, filePath, py(`\\`${clientClassName}.create\\``))) {\n return;\n }\n\n // Both frameworks' tools are @tool-decorated callables (the decorator's\n // import differs, handled in addImportToAgentFile), defined inline in\n // get_agent. The client is directly callable, returning the remote's reply.\n const toolBlock = `${clientVarName} = ${clientClassName}.create()\n\n @tool\n def ${toolName}(prompt: str) -> str:\n \"\"\"Delegate a question to the remote ${targetAgentClassName} A2A agent and return its reply.\"\"\"\n return str(${clientVarName}(prompt))\n`;\n\n // Prepend client creation + tool definition to the function body, anchored on\n // the framework's agent constructor. This works whether or not a prior MCP\n // block is present since we insert before any existing statements.\n const anchor = AGENT_CONSTRUCTOR[framework];\n await applyGritQL(\n tree,\n filePath,\n py(`\\`def get_agent($params):\n $body\\` where {\n $body <: contains \\`${anchor}\\`,\n $body <: not contains \\`${clientClassName}.create\\`\n} => \\`def get_agent($params):\n ${toolBlock}\n $body\\``),\n );\n};\n\nexport default pyAgentA2aConnectionGenerator;\n"],"names":["generateFiles","joinPathFragments","OverwriteStrategy","updateProjectConfiguration","addPyDependencies","AGENT_CONNECTION_PY_DEPENDENCIES","addPythonCoreClient","addPythonReExport","ensurePythonAgentConnectionProject","getPythonAgentConnectionModuleName","getPythonAgentConnectionProject","getPythonAgentConnectionProjectDir","PY_CLIENT_NAMING","resolveAgentFramework","addPythonDestructuredImport","appendToArrayViaGritQL","applyGritQL","matchGritQL","declareDependencies","ownedElsewhere","formatFilesInSubtree","installDependencies","addGeneratorMetricsIfApplicable","snakeCase","addComponentGeneratorMetadata","addDependencyToTargetIfNotPresent","getGeneratorInfo","readProjectConfigurationUnqualified","toProjectRelativePath","addWorkspaceDependencyToPyProject","py","pattern","TOOL_IMPORT_MODULE","strands","langchain","AGENT_CONSTRUCTOR","DEPENDENCIES","name","when","m","framework","PY_AGENT_A2A_CONNECTION_GENERATOR_INFO","filename","pyAgentA2aConnectionGenerator","tree","options","sourceProject","targetProject","agentComponent","sourceComponent","targetAgentComponent","targetComponent","Error","protocol","toLowerCase","auth","targetAgentComponentName","targetAgentClassName","rc","targetAgentSnakeCase","targetAgentPort","port","naming","metadata","agentConnectionProjectDir","agentConnectionModuleName","projectRoot","appDir","dirname","appTemplateSubdir","overwriteStrategy","KeepExisting","appInitPath","exists","write","moduleInitPath","clientModuleSuffix","clientClassSuffix","agentSourceDir","root","path","agentFilePath","clientClassName","clientVarName","toolName","addImportToAgentFile","addToolToAgent","addClientToolToGetAgent","agentName","devTargetName","targetDevTargetName","targets","projects","target","sourcePath","preferInstallDependencies","languages","filePath","scope","language","toolBlock","anchor"],"mappings":"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"}
@@ -1,5 +1,12 @@
1
1
  FROM <%- pythonBaseImage %>
2
2
 
3
+ # Apply the distribution's security updates: base image tags lag behind the
4
+ # security suite, so its OS packages carry fixed HIGH/CRITICAL vulnerabilities
5
+ # flagged by the image scan.
6
+ RUN apt-get update && \
7
+ apt-get upgrade -y --no-install-recommends && \
8
+ rm -rf /var/lib/apt/lists/*
9
+
3
10
  # Remove the base image's pip: the agent runs from the bundled environment and
4
11
  # never needs it at runtime, and pip's vendored packages carry known
5
12
  # HIGH/CRITICAL vulnerabilities flagged by the image scan.
@@ -1,6 +1,5 @@
1
1
  import logging
2
2
  import os
3
- import uuid
4
3
  from contextlib import asynccontextmanager
5
4
 
6
5
  from a2a.server.agent_execution import AgentExecutor, RequestContext
@@ -10,18 +9,17 @@ from a2a.server.request_handlers import DefaultRequestHandler
10
9
  from a2a.server.tasks import InMemoryTaskStore, TaskUpdater
11
10
  from a2a.types import AgentCapabilities, AgentCard, Part, TextPart
12
11
  from a2a.utils import new_task
13
- from fastapi import FastAPI, Request
14
- from starlette.middleware.base import BaseHTTPMiddleware
12
+ from fastapi import FastAPI
15
13
 
16
- from <%- agentConnectionModuleName %> import get_current_session_id, session_id_context
14
+ from <%- agentConnectionModuleName %> import get_current_session_id
17
15
 
18
16
  from .agent import get_agent
17
+ from .middleware.session_id_middleware import SessionIdMiddleware
19
18
 
20
19
  logging.basicConfig(level=logging.INFO)
21
20
 
22
21
  PORT = int(os.environ.get("PORT", "9000"))
23
22
  RUNTIME_URL = os.environ.get("AGENTCORE_RUNTIME_URL", f"http://localhost:{PORT}/")
24
- SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
25
23
  DEFAULT_MODES = ["text/plain"]
26
24
 
27
25
 
@@ -73,17 +71,8 @@ _agent_card = AgentCard(
73
71
  a2a_app = A2AStarletteApplication(agent_card=_agent_card, http_handler=_handler).build()
74
72
 
75
73
 
76
- class _SessionIdMiddleware(BaseHTTPMiddleware):
77
- """Bind the inbound session (or a fresh UUID) to async context."""
78
-
79
- async def dispatch(self, request: Request, call_next):
80
- session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
81
- with session_id_context(session_id):
82
- return await call_next(request)
83
-
84
-
85
74
  app = FastAPI(title="<%= agentNameClassName %>", lifespan=lifespan)
86
- app.add_middleware(_SessionIdMiddleware)
75
+ app.add_middleware(SessionIdMiddleware)
87
76
 
88
77
 
89
78
  @app.get("/ping")
@@ -10,14 +10,12 @@ from fastapi import FastAPI, Request
10
10
  from fastapi.middleware.cors import CORSMiddleware
11
11
  from fastapi.responses import JSONResponse, StreamingResponse
12
12
  from <%- agentConnectionModuleName %> import get_current_session_id, session_id_context
13
- from starlette.middleware.base import BaseHTTPMiddleware
14
13
 
15
14
  from .agent import get_agent
15
+ from .middleware.session_id_middleware import SESSION_ID_HEADER, SessionIdMiddleware
16
16
 
17
17
  logging.basicConfig(level=logging.INFO)
18
18
 
19
- SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
20
-
21
19
 
22
20
  @asynccontextmanager
23
21
  async def lifespan(app: FastAPI):
@@ -30,15 +28,6 @@ async def lifespan(app: FastAPI):
30
28
  yield
31
29
 
32
30
 
33
- class _SessionIdMiddleware(BaseHTTPMiddleware):
34
- """Bind the session ID for this request so downstream MCP / A2A clients forward it on outbound calls."""
35
-
36
- async def dispatch(self, request: Request, call_next):
37
- session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
38
- with session_id_context(session_id):
39
- return await call_next(request)
40
-
41
-
42
31
  app = FastAPI(title="<%= agentNameClassName %>", lifespan=lifespan)
43
32
  # Allow browser-based AG-UI clients (e.g. a connected React website) to call
44
33
  # this agent cross-origin, including the CORS preflight.
@@ -49,7 +38,7 @@ app.add_middleware(
49
38
  allow_methods=["*"],
50
39
  allow_headers=["*"],
51
40
  )
52
- app.add_middleware(_SessionIdMiddleware)
41
+ app.add_middleware(SessionIdMiddleware)
53
42
 
54
43
 
55
44
  @app.post("/invocations")
@@ -0,0 +1,17 @@
1
+ import uuid
2
+
3
+ from fastapi import Request
4
+ from starlette.middleware.base import BaseHTTPMiddleware
5
+
6
+ from <%- agentConnectionModuleName %> import session_id_context
7
+
8
+ SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
9
+
10
+
11
+ class SessionIdMiddleware(BaseHTTPMiddleware):
12
+ """Bind the session ID for this request so downstream MCP / A2A clients forward it on outbound calls."""
13
+
14
+ async def dispatch(self, request: Request, call_next):
15
+ session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
16
+ with session_id_context(session_id):
17
+ return await call_next(request)
@@ -2,14 +2,11 @@ import uuid
2
2
 
3
3
  import uvicorn
4
4
  from bedrock_agentcore.runtime.models import PingStatus
5
- from fastapi import Request
6
5
  from pydantic import BaseModel, Field
7
- from starlette.middleware.base import BaseHTTPMiddleware
8
6
  from <%- agentConnectionModuleName %> import get_current_session_id, session_id_context
9
7
 
10
8
  from .init import JsonStreamingResponse, app
11
-
12
- SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
9
+ from .middleware.session_id_middleware import SessionIdMiddleware
13
10
 
14
11
 
15
12
  class InvokeInput(BaseModel):
@@ -59,16 +56,7 @@ async def invoke(input: InvokeInput) -> JsonStreamingResponse:
59
56
  return JsonStreamingResponse(handle_invoke(input, session_id))
60
57
 
61
58
 
62
- class _SessionIdMiddleware(BaseHTTPMiddleware):
63
- """Bind the inbound session (or a fresh UUID) to async context."""
64
-
65
- async def dispatch(self, request: Request, call_next):
66
- session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
67
- with session_id_context(session_id):
68
- return await call_next(request)
69
-
70
-
71
- app.add_middleware(_SessionIdMiddleware)
59
+ app.add_middleware(SessionIdMiddleware)
72
60
 
73
61
 
74
62
  @app.get("/ping")
@@ -1,23 +1,21 @@
1
1
  import logging
2
2
  import os
3
- import uuid
4
3
  from contextlib import asynccontextmanager
5
4
  from types import SimpleNamespace
6
5
  from typing import cast
7
6
 
8
- from fastapi import FastAPI, Request
9
- from starlette.middleware.base import BaseHTTPMiddleware
7
+ from fastapi import FastAPI
10
8
  from strands import Agent
11
9
  from strands.multiagent.a2a import A2AServer
12
- from <%- agentConnectionModuleName %> import session_id_context, with_session_id
10
+ from <%- agentConnectionModuleName %> import with_session_id
13
11
 
14
12
  from .agent import get_agent
13
+ from .middleware.session_id_middleware import SessionIdMiddleware
15
14
 
16
15
  logging.basicConfig(level=logging.INFO)
17
16
 
18
17
  PORT = int(os.environ.get("PORT", "9000"))
19
18
  RUNTIME_URL = os.environ.get("AGENTCORE_RUNTIME_URL", f"http://localhost:{PORT}/")
20
- SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
21
19
  AGENT_NAME = "<%= agentNameClassName %>"
22
20
  AGENT_DESCRIPTION = "A Strands Agent exposed via the Agent-to-Agent (A2A) protocol."
23
21
 
@@ -41,17 +39,8 @@ async def lifespan(app: FastAPI):
41
39
  yield
42
40
 
43
41
 
44
- class _SessionIdMiddleware(BaseHTTPMiddleware):
45
- """Bind the inbound session (or a fresh UUID) to async context."""
46
-
47
- async def dispatch(self, request: Request, call_next):
48
- session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
49
- with session_id_context(session_id):
50
- return await call_next(request)
51
-
52
-
53
42
  app = FastAPI(lifespan=lifespan)
54
- app.add_middleware(_SessionIdMiddleware)
43
+ app.add_middleware(SessionIdMiddleware)
55
44
 
56
45
 
57
46
  @app.get("/ping")
@@ -9,15 +9,13 @@ from fastapi import FastAPI, Request
9
9
  from fastapi.middleware.cors import CORSMiddleware
10
10
  from fastapi.responses import StreamingResponse
11
11
  from <%- agentConnectionModuleName %> import get_current_session_id, session_id_context
12
- from starlette.middleware.base import BaseHTTPMiddleware
13
12
 
14
13
  from .agent import get_agent
14
+ from .middleware.session_id_middleware import SESSION_ID_HEADER, SessionIdMiddleware
15
15
  from .session import get_session_manager
16
16
 
17
17
  logging.basicConfig(level=logging.INFO)
18
18
 
19
- SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
20
-
21
19
 
22
20
  @asynccontextmanager
23
21
  async def lifespan(app: FastAPI):
@@ -33,15 +31,6 @@ async def lifespan(app: FastAPI):
33
31
  yield
34
32
 
35
33
 
36
- class _SessionIdMiddleware(BaseHTTPMiddleware):
37
- """Bind the session ID for this request so downstream MCP / A2A clients forward it on outbound calls."""
38
-
39
- async def dispatch(self, request: Request, call_next):
40
- session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
41
- with session_id_context(session_id):
42
- return await call_next(request)
43
-
44
-
45
34
  app = FastAPI(title="AWS Strands - <%= agentNameClassName %>", lifespan=lifespan)
46
35
  app.add_middleware(
47
36
  CORSMiddleware,
@@ -50,7 +39,7 @@ app.add_middleware(
50
39
  allow_methods=["*"],
51
40
  allow_headers=["*"],
52
41
  )
53
- app.add_middleware(_SessionIdMiddleware)
42
+ app.add_middleware(SessionIdMiddleware)
54
43
 
55
44
 
56
45
  @app.post("/invocations")
@@ -0,0 +1,17 @@
1
+ import uuid
2
+
3
+ from fastapi import Request
4
+ from starlette.middleware.base import BaseHTTPMiddleware
5
+
6
+ from <%- agentConnectionModuleName %> import session_id_context
7
+
8
+ SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
9
+
10
+
11
+ class SessionIdMiddleware(BaseHTTPMiddleware):
12
+ """Bind the session ID for this request so downstream MCP / A2A clients forward it on outbound calls."""
13
+
14
+ async def dispatch(self, request: Request, call_next):
15
+ session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
16
+ with session_id_context(session_id):
17
+ return await call_next(request)
@@ -1,15 +1,9 @@
1
- import uuid
2
-
3
1
  import uvicorn
4
2
  from bedrock_agentcore.runtime.models import PingStatus
5
- from fastapi import Request
6
3
  from pydantic import BaseModel, Field
7
- from starlette.middleware.base import BaseHTTPMiddleware
8
- from <%- agentConnectionModuleName %> import session_id_context
9
4
 
10
5
  from .init import JsonStreamingResponse, app
11
-
12
- SESSION_ID_HEADER = "x-amzn-bedrock-agentcore-runtime-session-id"
6
+ from .middleware.session_id_middleware import SessionIdMiddleware
13
7
 
14
8
 
15
9
  class InvokeInput(BaseModel):
@@ -41,16 +35,7 @@ async def invoke(input: InvokeInput) -> JsonStreamingResponse:
41
35
  return JsonStreamingResponse(handle_invoke(input))
42
36
 
43
37
 
44
- class _SessionIdMiddleware(BaseHTTPMiddleware):
45
- """Bind the inbound session (or a fresh UUID) to async context."""
46
-
47
- async def dispatch(self, request: Request, call_next):
48
- session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())
49
- with session_id_context(session_id):
50
- return await call_next(request)
51
-
52
-
53
- app.add_middleware(_SessionIdMiddleware)
38
+ app.add_middleware(SessionIdMiddleware)
54
39
 
55
40
 
56
41
  @app.get("/ping")
@@ -159,7 +159,7 @@ exports[`py#mcp-server generator > should match snapshot for Terraform generated
159
159
  }
160
160
  null = {
161
161
  source = "hashicorp/null"
162
- version = "3.3.0"
162
+ version = "3.3.1"
163
163
  }
164
164
  random = {
165
165
  source = "hashicorp/random"
@@ -571,7 +571,7 @@ resource "null_resource" "runtime_ready" {
571
571
 
572
572
  provisioner "local-exec" {
573
573
  command = <<-EOT
574
- uv run --with boto3==1.43.67 python -c "
574
+ uv run --with boto3==1.43.72 python -c "
575
575
  import boto3
576
576
  import json
577
577
  import os
@@ -846,10 +846,10 @@ exports[`py#mcp-server generator > should match snapshot for generated files > u
846
846
  name = "proj.test_project"
847
847
  version = "0.1.0"
848
848
  dependencies = [
849
- "aws-lambda-powertools==3.31.1",
849
+ "aws-lambda-powertools==3.34.0",
850
850
  "mcp==1.28.1",
851
- "uvicorn==0.52.1",
852
- "boto3==1.43.67",
851
+ "uvicorn==0.52.3",
852
+ "boto3==1.43.72",
853
853
  "aws-opentelemetry-distro==0.19.0"
854
854
  ]
855
855
 
@@ -1,5 +1,12 @@
1
1
  FROM <%- pythonBaseImage %>
2
2
 
3
+ # Apply the distribution's security updates: base image tags lag behind the
4
+ # security suite, so its OS packages carry fixed HIGH/CRITICAL vulnerabilities
5
+ # flagged by the image scan.
6
+ RUN apt-get update && \
7
+ apt-get upgrade -y --no-install-recommends && \
8
+ rm -rf /var/lib/apt/lists/*
9
+
3
10
  # Remove the base image's pip: the server runs from the bundled environment and
4
11
  # never needs it at runtime, and pip's vendored packages carry known
5
12
  # HIGH/CRITICAL vulnerabilities flagged by the image scan.
@@ -2,8 +2,6 @@
2
2
  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */ import { generateFiles, joinPathFragments, OverwriteStrategy, updateProjectConfiguration } from "@nx/devkit";
5
- import { ensureLicenseExceptions } from "../../license/config.js";
6
- import { MCP_INSPECTOR_EXCEPTIONS } from "../../license/known-exceptions.js";
7
5
  import { addPyDependencies, addTsDependencies } from "../../utils/add-dependencies.js";
8
6
  import { addMcpServerInfra } from "../../utils/agent-core-constructs/agent-core-constructs.js";
9
7
  import { addPythonBundleTarget } from "../../utils/bundle/bundle.js";
@@ -19,6 +17,7 @@ import { kebabCase, toClassName, toSnakeCase } from "../../utils/names.js";
19
17
  import { getNpmScope } from "../../utils/npm-scope.js";
20
18
  import { addComponentDevTarget, addComponentGeneratorMetadata, addDependencyToTargetIfNotPresent, getGeneratorInfo, readProjectConfigurationUnqualified } from "../../utils/nx.js";
21
19
  import { toProjectRelativePath } from "../../utils/paths.js";
20
+ import { registerPnpmBuiltDependencies } from "../../utils/pnpm-workspace.js";
22
21
  import { assignPort } from "../../utils/port.js";
23
22
  import { SHARED_CONSTRUCTS_DEPENDENCIES, sharedConstructsGenerator } from "../../utils/shared-constructs.js";
24
23
  import { BASE_IMAGES } from "../../utils/versions.js";
@@ -169,6 +168,13 @@ export const pyMcpServerGenerator = async (tree, options)=>{
169
168
  iac
170
169
  } : {}
171
170
  };
171
+ // The MCP inspector has a postinstall script that cascades installs into its
172
+ // client packages, which no-ops when the package is installed as a dependency.
173
+ // Register it as an explicitly-rejected build so pnpm 11's default
174
+ // `strictDepBuilds=true` skips it instead of failing the install.
175
+ registerPnpmBuiltDependencies(tree, {
176
+ '@modelcontextprotocol/inspector': false
177
+ });
172
178
  addPyDependencies(tree, DEPENDENCIES, {
173
179
  metadata,
174
180
  projectRoot: project.root
@@ -253,7 +259,6 @@ export const pyMcpServerGenerator = async (tree, options)=>{
253
259
  await addGeneratorMetricsIfApplicable(tree, [
254
260
  PY_MCP_SERVER_GENERATOR_INFO
255
261
  ]);
256
- await ensureLicenseExceptions(tree, MCP_INSPECTOR_EXCEPTIONS);
257
262
  await formatFilesInSubtree(tree);
258
263
  return ()=>installDependencies(tree, options.preferInstallDependencies, {
259
264
  languages: [