diff --git a/docs/en/DEPLOY_OPTION.md b/docs/en/DEPLOY_OPTION.md index e099a7910..f6dae5d66 100644 --- a/docs/en/DEPLOY_OPTION.md +++ b/docs/en/DEPLOY_OPTION.md @@ -681,41 +681,44 @@ const envs: Record> = { } ``` -### Enabling AgentCore Use Cases +### Enabling AgentCore Use Case This is a use case for integrating with agents created in AgentCore. (Experimental: Breaking changes may be made without notice) Enabling `createGenericAgentCoreRuntime` will deploy the default AgentCore Runtime. -By default, it is deployed to the `modelRegion`, but you can override this by specifying `agentCoreRegion`. +By default, it is deployed to `modelRegion`, but you can override it by specifying `agentCoreRegion`. -The default agents available in AgentCore can utilize MCP servers defined in [mcp.json](https://github.com/aws-samples/generative-ai-use-cases/blob/main/packages/cdk/lambda-python/generic-agent-core-runtime/mcp.json). -This default agent is available in Agent Builder, and users can create any agent from MCPs that administrators have permitted. +The default agents available in AgentCore can use MCP servers defined in [generic/mcp.json](packages/cdk/lambda-python/generic-agent-core-runtime/mcp-configs/generic/mcp.json). The MCP servers defined by default are AWS-related MCP servers and MCP servers related to current time. -For details, please refer to the documentation [here](https://awslabs.github.io/mcp/). -When adding MCP servers, please add them to the aforementioned `mcp.json`. -However, MCP servers that start with methods other than `uvx` require development work such as rewriting the Dockerfile. +For more details, please refer to [this documentation](https://awslabs.github.io/mcp/). +To add MCP servers, add them to the aforementioned `generic/mcp.json`. -With `agentCoreExternalRuntimes`, you can use externally created AgentCore Runtimes. +You can use externally created AgentCore Runtimes with `agentCoreExternalRuntimes`. -To enable AgentCore use cases, the `docker` command must be executable. +When accessing services outside AWS from AgentCore Runtime, use AgentCore Gateway. +By specifying the Gateway ARN in `agentCoreGatewayArns`, an IAM policy following the principle of least privilege will be configured. +After configuration, use `mcp-proxy-for-aws` in the MCP settings to specify the endpoint. +For details, refer to the [mcp-proxy-for-aws documentation](https://github.com/aws/mcp-proxy-for-aws). + +To enable the AgentCore use case, the `docker` command must be executable. > [!WARNING] -> On Linux machines using x86_64 CPUs (Intel, AMD, etc.), run the following command before cdk deployment: +> On Linux machines using x86_64 CPUs (Intel, AMD, etc.), execute the following command before deploying: > > ``` > docker run --privileged --rm tonistiigi/binfmt --install arm64 > ``` > -> If you do not run the above command, the following error will occur: -> During the deployment process, ARM-based container images used by AgentCore Runtime are built. When building ARM container images on x86_64 CPUs, errors occur due to CPU architecture differences. +> If you do not execute the above command, the following error will occur. +> During the deployment process, ARM-based container images used by AgentCore Runtime are built. When building ARM container images on x86_64 CPUs, errors occur due to differences in CPU architecture. > > ``` > ERROR: failed to solve: process "/bin/sh -c apt-get update -y && apt-get install curl nodejs npm graphviz -y" did not complete successfully: exit code: 255 > AgentCoreStack: fail: docker build --tag cdkasset-64ba68f71e3d29f5b84d8e8d062e841cb600c436bb68a540d6fce32fded36c08 --platform linux/arm64 . exited with error code 1: #0 building with "default" instance using docker driver > ``` > -> Running this command makes temporary configuration changes to the host Linux Kernel. It registers QEMU emulator custom handlers in Binary Format Miscellaneous (binfmt_misc), enabling ARM container image builds. The configuration returns to its original state after reboot, so the command must be re-executed before re-deployments. +> Executing this command makes temporary configuration changes to the host's Linux Kernel. By registering QEMU custom handlers in Binary Format Miscellaneous (binfmt_misc), ARM container images can be built. The configuration reverts after a reboot, so re-execution is required when deploying again. **Edit [parameter.ts](/packages/cdk/parameter.ts)** @@ -725,6 +728,9 @@ const envs: Record> = { dev: { createGenericAgentCoreRuntime: true, agentCoreRegion: 'us-west-2', + agentCoreGatewayArns: [ + 'arn:aws:bedrock-agentcore:us-west-2::gateway/', + ], agentCoreExternalRuntimes: [ { name: 'AgentCore1', @@ -744,6 +750,9 @@ const envs: Record> = { "context": { "createGenericAgentCoreRuntime": true, "agentCoreRegion": "us-west-2", + "agentCoreGatewayArns": [ + "arn:aws:bedrock-agentcore:us-west-2::gateway/" + ], "agentCoreExternalRuntimes": [ { "name": "AgentCore1", @@ -754,6 +763,51 @@ const envs: Record> = { } ``` +### Enabling AgentBuilder Use Case + +This is a use case where users can freely create Agents for each use case by configuring system prompts and arbitrary MCPs. (Experimental: Breaking changes may be made without notice) + +Similar to the AgentCore use case, administrators pre-register MCPs in [agent-builder/mcp.json](packages/cdk/lambda-python/generic-agent-core-runtime/mcp-configs/agent-builder/mcp.json). Users can selectively use their preferred MCPs from those registered by administrators. + +Enabling `agentBuilderEnabled` will deploy the AgentCore Runtime for Agent Builder. +By default, it is deployed to `modelRegion`, but you can override it by specifying `agentCoreRegion`. + +When accessing services outside AWS, use AgentCore Gateway. +By specifying the Gateway ARN in `agentCoreGatewayArns`, an IAM policy following the principle of least privilege will be configured. +After configuration, use `mcp-proxy-for-aws` in the MCP settings to specify the endpoint. +For details, refer to the [mcp-proxy-for-aws documentation](https://github.com/aws/mcp-proxy-for-aws). + +**Edit [parameter.ts](/packages/cdk/parameter.ts)** + +```typescript +// parameter.ts +const envs: Record> = { + dev: { + agentBuilderEnabled: true, + agentCoreRegion: 'us-west-2', + agentCoreGatewayArns: [ + 'arn:aws:bedrock-agentcore:us-west-2::gateway/', + ], + }, +}; +``` + +**Edit [packages/cdk/cdk.json](/packages/cdk/cdk.json)** + +```json +// cdk.json + +{ + "context": { + "agentBuilderEnabled": true, + "agentCoreRegion": "us-west-2", + "agentCoreGatewayArns": [ + "arn:aws:bedrock-agentcore:us-west-2::gateway/" + ] + } +} +``` + ### Enabling Voice Chat Use Case > [!NOTE] diff --git a/docs/ja/DEPLOY_OPTION.md b/docs/ja/DEPLOY_OPTION.md index 05871c5c9..bb370e6f5 100644 --- a/docs/ja/DEPLOY_OPTION.md +++ b/docs/ja/DEPLOY_OPTION.md @@ -703,16 +703,19 @@ AgentCore で作成したエージェントと連携するユースケースで `createGenericAgentCoreRuntime` を有効化するとデフォルトの AgentCore Runtime がデプロイされます。 デフォルトでは `modelRegion` にデプロイされますが、`agentCoreRegion` を指定し上書きすることが可能です。 -AgentCore で使用できるデフォルトのエージェントは、[mcp.json](https://github.com/aws-samples/generative-ai-use-cases/blob/main/packages/cdk/lambda-python/generic-agent-core-runtime/mcp.json) で定義する MCP サーバーを利用することができます。 -このデフォルトのエージェントは Agent Builder で利用でき、ユーザーは管理者が許可した MCP から任意のエージェントを作成することができます。 +AgentCore で使用できるデフォルトのエージェントは、[generic/mcp.json](packages/cdk/lambda-python/generic-agent-core-runtime/mcp-configs/generic/mcp.json) で定義する MCP サーバーを利用することができます。 デフォルトで定義されている MCP サーバーは、AWS に関連する MCP サーバー及び、現在時刻に関連する MCP サーバーです。 詳細は[こちら](https://awslabs.github.io/mcp/)のドキュメントをご参照ください。 -MCP サーバーを追加する場合は上述の `mcp.json` に追記してください。 -ただし、`uvx` 以外で起動する MCP サーバーは Dockefile の書き換え等開発が必要です。 +MCP サーバーを追加する場合は上述の `generic/mcp.json` に追記してください。 `agentCoreExternalRuntimes` で外部で作成した AgentCore Runtime を利用することが可能です。 +AgentCore Runtime から AWS 外部のサービスにアクセスする場合、AgentCore Gateway を使用します。 +`agentCoreGatewayArns` に Gateway の ARN を指定することで、最小権限の原則に従った IAM ポリシーが設定されます。 +設定後、MCP 設定で `mcp-proxy-for-aws` を使用してエンドポイントを指定します。 +詳細は [mcp-proxy-for-aws のドキュメント](https://github.com/aws/mcp-proxy-for-aws)を参照してください。 + AgentCore ユースケースを有効化するためには、`docker` コマンドが実行可能である必要があります。 > [!WARNING] @@ -740,6 +743,9 @@ const envs: Record> = { dev: { createGenericAgentCoreRuntime: true, agentCoreRegion: 'us-west-2', + agentCoreGatewayArns: [ + 'arn:aws:bedrock-agentcore:us-west-2::gateway/', + ], agentCoreExternalRuntimes: [ { name: 'AgentCore1', @@ -759,6 +765,9 @@ const envs: Record> = { "context": { "createGenericAgentCoreRuntime": true, "agentCoreRegion": "us-west-2", + "agentCoreGatewayArns": [ + "arn:aws:bedrock-agentcore:us-west-2::gateway/" + ], "agentCoreExternalRuntimes": [ { "name": "AgentCore1", @@ -769,6 +778,51 @@ const envs: Record> = { } ``` +### AgentBuilder ユースケースの有効化 + +ユーザーがシステムプロンプトと任意の MCP を設定することでユースケースごとの Agent を自由に作成できるユースケースです。(Experimental: 予告なく破壊的変更を行うことがあります) + +AgentCore ユースケースと同様に [agent-builder/mcp.json](packages/cdk/lambda-python/generic-agent-core-runtime/mcp-configs/agent-builder/mcp.json) にて管理者側で MCP を事前に登録します。管理者が登録したものからユーザーが好きな MCP を選択式で利用できます。 + +`agentBuilderEnabled` を有効化すると Agent Builder 向けの AgentCore Runtime がデプロイされます。 +デフォルトでは `modelRegion` にデプロイされますが、`agentCoreRegion` を指定し上書きすることが可能です。 + +AWS 外部のサービスにアクセスする場合、AgentCore Gateway を使用します。 +`agentCoreGatewayArns` に Gateway の ARN を指定することで、最小権限の原則に従った IAM ポリシーが設定されます。 +設定後、MCP 設定で `mcp-proxy-for-aws` を使用してエンドポイントを指定します。 +詳細は [mcp-proxy-for-aws のドキュメント](https://github.com/aws/mcp-proxy-for-aws)を参照してください。 + +**[parameter.ts](/packages/cdk/parameter.ts) を編集** + +```typescript +// parameter.ts +const envs: Record> = { + dev: { + agentBuilderEnabled: true, + agentCoreRegion: 'us-west-2', + agentCoreGatewayArns: [ + 'arn:aws:bedrock-agentcore:us-west-2::gateway/', + ], + }, +}; +``` + +**[packages/cdk/cdk.json](/packages/cdk/cdk.json) を編集** + +```json +// cdk.json + +{ + "context": { + "agentBuilderEnabled": true, + "agentCoreRegion": "us-west-2", + "agentCoreGatewayArns": [ + "arn:aws:bedrock-agentcore:us-west-2::gateway/" + ] + } +} +``` + ### 音声チャットユースケースの有効化 > [!NOTE] diff --git a/docs/overrides/hooks/anchors.py b/docs/overrides/hooks/anchors.py index 35c5ddd5b..e27cb4692 100644 --- a/docs/overrides/hooks/anchors.py +++ b/docs/overrides/hooks/anchors.py @@ -1,28 +1,29 @@ import re -from mkdocs import utils as mkdocs_utils + # Override absolute path to start with edit_uri def override_absolute_path(html, page, config, files): - absolute_path_replace_uri = config.get('extra', {}).get('absolute_path_replace_uri') + absolute_path_replace_uri = config.get("extra", {}).get("absolute_path_replace_uri") if not absolute_path_replace_uri: return html - if absolute_path_replace_uri.endswith('/'): + if absolute_path_replace_uri.endswith("/"): absolute_path_replace_uri = absolute_path_replace_uri[:-1] link_pattern = r'= 18.0.0" }, "peerDependencies": { - "aws-cdk-lib": "^2.221.0", + "@aws-cdk/aws-bedrock-alpha": "2.227.0-alpha.0", + "aws-cdk-lib": "^2.227.0", + "constructs": "^10.0.0" + } + }, + "node_modules/@aws-cdk/aws-bedrock-alpha": { + "version": "2.227.0-alpha.0", + "resolved": "https://registry.npmjs.org/@aws-cdk/aws-bedrock-alpha/-/aws-bedrock-alpha-2.227.0-alpha.0.tgz", + "integrity": "sha512-SZp1r3c2e9G048e6bo84yoxd3/l5tL2cGli0oXkcTKg6tV94ovvStDrFg8CpZnoT6Dq853ksfsnBtHzqXGJKCw==", + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">= 18.0.0" + }, + "peerDependencies": { + "aws-cdk-lib": "^2.227.0", "constructs": "^10.0.0" } }, @@ -17580,9 +17595,9 @@ } }, "node_modules/aws-cdk-lib": { - "version": "2.221.0", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.221.0.tgz", - "integrity": "sha512-lOS0EnE4NQBW2+/0/qxwDBtGYGt8Fa/lCYsvC2/XTU3vBZ454mKkdI9zWKspZJ+Fo5b2iS1Q7iiSIbQVat2xJA==", + "version": "2.229.0", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.229.0.tgz", + "integrity": "sha512-Wsopzi0rf5oWxv+GDhO8+OYEsu2CAj+uuYxSqTO4LYqRsHoWCepWHK7q4tUFBRzo/j2YcVh2ezuSlaSA+41kQw==", "bundleDependencies": [ "@balena/dockerignore", "case", @@ -35880,7 +35895,7 @@ "packages/cdk": { "hasInstallScript": true, "dependencies": { - "@aws-cdk/aws-bedrock-agentcore-alpha": "2.221.0-alpha.0", + "@aws-cdk/aws-bedrock-agentcore-alpha": "2.227.0-alpha.0", "@aws-cdk/aws-lambda-python-alpha": "^2.220.0-alpha.0", "@aws-sdk/client-bedrock-agent": "^3.755.0", "@aws-sdk/client-bedrock-agent-runtime": "^3.755.0", diff --git a/packages/cdk/cdk.json b/packages/cdk/cdk.json index a73e7dcf1..dcf331edd 100644 --- a/packages/cdk/cdk.json +++ b/packages/cdk/cdk.json @@ -68,6 +68,7 @@ "createGenericAgentCoreRuntime": false, "agentCoreRegion": null, "agentCoreExternalRuntimes": [], + "agentCoreGatewayArns": [], "allowedIpV4AddressRanges": null, "allowedIpV6AddressRanges": null, "allowedCountryCodes": null, diff --git a/packages/cdk/lambda-python/generic-agent-core-runtime/Dockerfile b/packages/cdk/lambda-python/generic-agent-core-runtime/Dockerfile index ed219d636..2d6ccfa88 100644 --- a/packages/cdk/lambda-python/generic-agent-core-runtime/Dockerfile +++ b/packages/cdk/lambda-python/generic-agent-core-runtime/Dockerfile @@ -14,15 +14,19 @@ ENV UV_PROJECT_ENVIRONMENT=/tmp/.venv RUN apt-get update -y && apt-get install curl nodejs npm graphviz -y RUN curl -LsSf https://astral.sh/uv/install.sh | sh +# Pre-install mcp-remote for faster startup +RUN npm install -g mcp-remote + # Copy dependency files COPY pyproject.toml .python-version uv.lock ./ - # Install Python dependencies RUN uv sync # Copy application files COPY app.py ./ COPY src/ ./src/ +COPY mcp-configs ./mcp-configs + # Expose port 8080 as required by AgentCore EXPOSE 8080 diff --git a/packages/cdk/assets/mcp-configs/generic.json b/packages/cdk/lambda-python/generic-agent-core-runtime/mcp-configs/agent-builder/mcp.json similarity index 86% rename from packages/cdk/assets/mcp-configs/generic.json rename to packages/cdk/lambda-python/generic-agent-core-runtime/mcp-configs/agent-builder/mcp.json index fb1c02c8d..60b9cb34a 100644 --- a/packages/cdk/assets/mcp-configs/generic.json +++ b/packages/cdk/lambda-python/generic-agent-core-runtime/mcp-configs/agent-builder/mcp.json @@ -74,6 +74,17 @@ "category": "Search", "description": "Web search and research capabilities powered by Tavily" } + }, + "tavily-gateway": { + "command": "uvx", + "args": [ + "mcp-proxy-for-aws", + "https://.gateway.bedrock-agentcore..amazonaws.com/mcp" + ], + "metadata": { + "category": "Search", + "description": "Web search and research capabilities powered by Tavily" + } } } } diff --git a/packages/cdk/assets/mcp-configs/agent-builder.json b/packages/cdk/lambda-python/generic-agent-core-runtime/mcp-configs/generic/mcp.json similarity index 100% rename from packages/cdk/assets/mcp-configs/agent-builder.json rename to packages/cdk/lambda-python/generic-agent-core-runtime/mcp-configs/generic/mcp.json diff --git a/packages/cdk/lambda-python/generic-agent-core-runtime/src/tools.py b/packages/cdk/lambda-python/generic-agent-core-runtime/src/tools.py index cfa3e00c3..e74c9c5ea 100644 --- a/packages/cdk/lambda-python/generic-agent-core-runtime/src/tools.py +++ b/packages/cdk/lambda-python/generic-agent-core-runtime/src/tools.py @@ -3,6 +3,7 @@ import json import logging import os +from concurrent.futures import ThreadPoolExecutor from typing import Any import boto3 @@ -26,6 +27,25 @@ logger = logging.getLogger(__name__) +def _create_mcp_client(server_name: str, server_config: dict, uv_env: dict) -> tuple[str, MCPClient | None]: + """Create and start an MCP client (for parallel execution)""" + try: + client = MCPClient( + lambda: stdio_client( + StdioServerParameters( + command=server_config["command"], + args=server_config.get("args", []), + env={**uv_env, **server_config.get("env", {})}, + ) + ) + ) + client.start() + return server_name, client + except Exception as e: + logger.error(f"Error creating MCP client for {server_name}: {e}") + return server_name, None + + class ToolManager: """Manages tools including MCP tools and built-in tools.""" @@ -45,42 +65,27 @@ def load_mcp_tools(self) -> list[Any]: return self.mcp_tools try: - # First try to load from environment variable - mcp_servers_env = os.environ.get("MCP_SERVERS") - if mcp_servers_env: - logger.info("Loading MCP configuration from environment variable") - mcp_servers = json.loads(mcp_servers_env) - else: - # Fallback to mcp.json file - logger.info("Loading MCP configuration from mcp.json file") - mcp_config_path = "mcp.json" - if not os.path.exists(mcp_config_path): - logger.warning(f"MCP configuration file not found at {mcp_config_path}") - self.mcp_tools = [] - return self.mcp_tools + # Log UV environment configuration + uv_env = get_uv_environment() + # Load from MCP_CONFIG_PATH + mcp_config_path = os.environ.get("MCP_CONFIG_PATH") + if mcp_config_path and os.path.exists(mcp_config_path): + logger.info(f"Loading MCP configuration from {mcp_config_path}") with open(mcp_config_path) as f: mcp_config = json.load(f) mcp_servers = mcp_config.get("mcpServers", {}) + else: + return [] mcp_clients = [] - uv_env = get_uv_environment() - for server_name, server in mcp_servers.items(): - try: - client = MCPClient( - lambda server=server: stdio_client( - StdioServerParameters( - command=server["command"], - args=server.get("args", []), - env={**uv_env, **server.get("env", {})}, - ) - ) - ) - client.start() - mcp_clients.append(client) - except Exception as e: - logger.error(f"Error creating MCP client for {server_name}: {e}") + with ThreadPoolExecutor() as executor: + futures = [executor.submit(_create_mcp_client, name, config, uv_env) for name, config in mcp_servers.items()] + for future in futures: + name, client = future.result() + if client: + mcp_clients.append(client) # Flatten the tools self.mcp_tools = sum([c.list_tools_sync() for c in mcp_clients], []) @@ -98,45 +103,31 @@ def load_mcp_tools_by_names(self, server_names: list[str]) -> list[Any]: return [] try: - # First try to load from environment variable - mcp_servers_env = os.environ.get("MCP_SERVERS") - if mcp_servers_env: - logger.info("Loading MCP configuration from environment variable") - available_servers = json.loads(mcp_servers_env) - else: - # Fallback to mcp.json file - logger.info("Loading MCP configuration from mcp.json file") - mcp_config_path = "mcp.json" - logger.info(f"Loading MCP configuration from: {mcp_config_path}") + # Log UV environment configuration + uv_env = get_uv_environment() + + # Load from MCP_CONFIG_PATH + mcp_config_path = os.environ.get("MCP_CONFIG_PATH") + if mcp_config_path and os.path.exists(mcp_config_path): + logger.info(f"Loading MCP configuration from {mcp_config_path}") with open(mcp_config_path) as f: mcp_config = json.load(f) available_servers = mcp_config.get("mcpServers", {}) + else: + return [] logger.info(f"Found {len(available_servers)} available MCP servers") mcp_clients = [] - uv_env = get_uv_environment() - for server_name in server_names: - if server_name not in available_servers: - logger.warning(f"MCP server '{server_name}' not found in configuration") - continue - - server_config = available_servers[server_name] - try: - client = MCPClient( - lambda server=server_config: stdio_client( - StdioServerParameters( - command=server["command"], - args=server.get("args", []), - env={**uv_env, **server.get("env", {})}, - ) - ) - ) - client.start() - mcp_clients.append(client) - logger.info(f"Successfully loaded MCP server: {server_name}") - except Exception as e: - logger.error(f"Error creating MCP client for {server_name}: {e}") + servers_to_load = {name: available_servers[name] for name in server_names if name in available_servers} + + with ThreadPoolExecutor() as executor: + futures = [executor.submit(_create_mcp_client, name, config, uv_env) for name, config in servers_to_load.items()] + for future in futures: + name, client = future.result() + if client: + mcp_clients.append(client) + logger.info(f"Successfully loaded MCP server: {name}") # Flatten the tools dynamic_tools = sum([c.list_tools_sync() for c in mcp_clients], []) diff --git a/packages/cdk/lib/agent-core-stack.ts b/packages/cdk/lib/agent-core-stack.ts index e319c50c5..a95996811 100644 --- a/packages/cdk/lib/agent-core-stack.ts +++ b/packages/cdk/lib/agent-core-stack.ts @@ -22,6 +22,7 @@ export class AgentCoreStack extends Stack { env: params.env, createGenericRuntime: params.createGenericAgentCoreRuntime, createAgentBuilderRuntime: params.agentBuilderEnabled, + gatewayArns: params.agentCoreGatewayArns ?? undefined, }); // Export runtime info for cross-region access via cdk-remote-stack (only if values exist) diff --git a/packages/cdk/lib/construct/generic-agent-core.ts b/packages/cdk/lib/construct/generic-agent-core.ts index 8077d5b61..a7f14ead1 100644 --- a/packages/cdk/lib/construct/generic-agent-core.ts +++ b/packages/cdk/lib/construct/generic-agent-core.ts @@ -19,7 +19,6 @@ import { } from '@aws-cdk/aws-bedrock-agentcore-alpha'; import { BucketInfo } from 'generative-ai-use-cases'; import * as path from 'path'; -import { loadMCPConfig } from '../utils/mcp-config-loader'; import { SUPPORTED_CACHE_FIELDS } from '@generative-ai-use-cases/common'; export interface AgentCoreRuntimeConfig { @@ -37,6 +36,7 @@ export interface GenericAgentCoreProps { env: string; createGenericRuntime?: boolean; createAgentBuilderRuntime?: boolean; + gatewayArns?: string[]; } interface RuntimeResources { @@ -50,6 +50,7 @@ export class GenericAgentCore extends Construct { private readonly genericRuntimeConfig: AgentCoreRuntimeConfig; private readonly agentBuilderRuntimeConfig: AgentCoreRuntimeConfig; private readonly resources: RuntimeResources; + private readonly gatewayArns?: string[]; constructor(scope: Construct, id: string, props: GenericAgentCoreProps) { super(scope, id); @@ -58,8 +59,11 @@ export class GenericAgentCore extends Construct { env, createGenericRuntime = false, createAgentBuilderRuntime = false, + gatewayArns, } = props; + this.gatewayArns = gatewayArns; + // Create bucket first this._fileBucket = this.createFileBucket(); @@ -85,13 +89,6 @@ export class GenericAgentCore extends Construct { } private loadConfigurations(env: string, bucketName: string) { - const genericMcpServers = loadMCPConfig( - path.join(__dirname, '../../assets/mcp-configs/generic.json') - ); - const agentBuilderMcpServers = loadMCPConfig( - path.join(__dirname, '../../assets/mcp-configs/agent-builder.json') - ); - return { generic: { name: `GenUGenericRuntime${env}`, @@ -102,7 +99,7 @@ export class GenericAgentCore extends Construct { serverProtocol: 'HTTP', environmentVariables: { FILE_BUCKET: bucketName, - MCP_SERVERS: JSON.stringify(genericMcpServers), + MCP_CONFIG_PATH: '/var/task/mcp-configs/generic/mcp.json', SUPPORTED_CACHE_FIELDS: JSON.stringify(SUPPORTED_CACHE_FIELDS), }, }, @@ -116,7 +113,7 @@ export class GenericAgentCore extends Construct { serverProtocol: 'HTTP', environmentVariables: { FILE_BUCKET: bucketName, - MCP_SERVERS: JSON.stringify(agentBuilderMcpServers), + MCP_CONFIG_PATH: '/var/task/mcp-configs/agent-builder/mcp.json', SUPPORTED_CACHE_FIELDS: JSON.stringify(SUPPORTED_CACHE_FIELDS), }, }, @@ -150,7 +147,7 @@ export class GenericAgentCore extends Construct { ); } - this.configureRolePermissions(role); + this.configureRolePermissions(role, this.gatewayArns); return resources; } @@ -187,7 +184,7 @@ export class GenericAgentCore extends Construct { }); } - private configureRolePermissions(role: Role): void { + private configureRolePermissions(role: Role, gatewayArns?: string[]): void { // Bedrock permissions role.addToPolicy( new PolicyStatement({ @@ -239,6 +236,16 @@ export class GenericAgentCore extends Construct { }) ); + // Gateway tools + role.addToPolicy( + new PolicyStatement({ + sid: 'AllowGatewayInvocation', + effect: Effect.ALLOW, + actions: ['bedrock-agentcore:InvokeGateway'], + resources: gatewayArns && gatewayArns.length > 0 ? gatewayArns : ['*'], + }) + ); + this._fileBucket.grantWrite(role); } diff --git a/packages/cdk/lib/generative-ai-use-cases-stack.ts b/packages/cdk/lib/generative-ai-use-cases-stack.ts index e17973846..2041be55c 100644 --- a/packages/cdk/lib/generative-ai-use-cases-stack.ts +++ b/packages/cdk/lib/generative-ai-use-cases-stack.ts @@ -168,7 +168,10 @@ export class GenerativeAiUseCasesStack extends Stack { // Load MCP configuration for Web frontend const mcpServers = loadMCPConfig( - path.join(__dirname, '../assets/mcp-configs/agent-builder.json') + path.join( + __dirname, + '../lambda-python/generic-agent-core-runtime/mcp-configs/agent-builder/mcp.json' + ) ); const safeMCPConfig = extractSafeMCPConfig(mcpServers); diff --git a/packages/cdk/lib/stack-input.ts b/packages/cdk/lib/stack-input.ts index 3c066d1eb..25ebad8f3 100644 --- a/packages/cdk/lib/stack-input.ts +++ b/packages/cdk/lib/stack-input.ts @@ -151,6 +151,7 @@ const baseStackInputSchema = z.object({ agentBuilderEnabled: z.boolean().default(false), createGenericAgentCoreRuntime: z.boolean().default(false), agentCoreRegion: z.string().nullish(), + agentCoreGatewayArns: z.array(z.string()).nullish(), agentCoreExternalRuntimes: z .array( z.object({ diff --git a/packages/cdk/mcp-api/app.py b/packages/cdk/mcp-api/app.py index 8def7a868..d4348a4a5 100644 --- a/packages/cdk/mcp-api/app.py +++ b/packages/cdk/mcp-api/app.py @@ -16,19 +16,19 @@ from uuid import uuid4 UV_ENV = { - 'UV_NO_CACHE': '1', - 'UV_PYTHON': '/usr/local/bin/python', - 'UV_TOOL_DIR': '/tmp/.uv/tool', - 'UV_TOOL_BIN_DIR': '/tmp/.uv/tool/bin', - 'UV_PROJECT_ENVIRONMENT': '/tmp/.venv', - 'npm_config_cache': '/tmp/.npm', - 'AWS_REGION': os.environ['AWS_REGION'], - 'AWS_ACCESS_KEY_ID': os.environ['AWS_ACCESS_KEY_ID'], - 'AWS_SECRET_ACCESS_KEY': os.environ['AWS_SECRET_ACCESS_KEY'], - 'AWS_SESSION_TOKEN': os.environ['AWS_SESSION_TOKEN'], + "UV_NO_CACHE": "1", + "UV_PYTHON": "/usr/local/bin/python", + "UV_TOOL_DIR": "/tmp/.uv/tool", + "UV_TOOL_BIN_DIR": "/tmp/.uv/tool/bin", + "UV_PROJECT_ENVIRONMENT": "/tmp/.venv", + "npm_config_cache": "/tmp/.npm", + "AWS_REGION": os.environ["AWS_REGION"], + "AWS_ACCESS_KEY_ID": os.environ["AWS_ACCESS_KEY_ID"], + "AWS_SECRET_ACCESS_KEY": os.environ["AWS_SECRET_ACCESS_KEY"], + "AWS_SESSION_TOKEN": os.environ["AWS_SESSION_TOKEN"], } -WORKSPACE_DIR = '/tmp/ws' +WORKSPACE_DIR = "/tmp/ws" FIXED_SYSTEM_PROMPT = f"""## About File Output - You are running on AWS Lambda. Therefore, when writing files, always write them under `{WORKSPACE_DIR}`. @@ -37,60 +37,67 @@ - If the output file is an image file, the S3 URL output must be in Markdown format. """ + def stream_chunk(text, trace): - return json.dumps({ 'text': text, 'trace': trace}, ensure_ascii=False) + '\n' + return json.dumps({"text": text, "trace": trace}, ensure_ascii=False) + "\n" + def is_message(event): - return 'message' in event + return "message" in event + def is_assistant(event): - return event['message']['role'] == 'assistant' + return event["message"]["role"] == "assistant" + def extract_text(event): - contents = event['message']['content'] + contents = event["message"]["content"] for c in contents: - if 'text' in c: - return c['text'] + if "text" in c: + return c["text"] return None + def extract_tool_use(event): - contents = event['message']['content'] + contents = event["message"]["content"] for c in contents: - if 'toolUse' in c: - tool_use = c['toolUse'] - return { - 'name': tool_use['name'], - 'input': tool_use['input'] - } + if "toolUse" in c: + tool_use = c["toolUse"] + return {"name": tool_use["name"], "input": tool_use["input"]} return None + def extract_tool_result(event): - res = '' + res = "" - contents = event['message']['content'] + contents = event["message"]["content"] for c in contents: - if 'toolResult' in c: - res_contents = c['toolResult']['content'] + if "toolResult" in c: + res_contents = c["toolResult"]["content"] for t in res_contents: - if 'text' in t: - res += t['text'] + if "text" in t: + res += t["text"] return res + def create_session_id(): return str(uuid4()) + def create_ws_directory(): - logging.info('Create ws directory') + logging.info("Create ws directory") pathlib.Path(WORKSPACE_DIR).mkdir(exist_ok=True) + def clean_ws_directory(): - logging.info('Clean ws directory...') + logging.info("Clean ws directory...") shutil.rmtree(WORKSPACE_DIR) + @tool def upload_file_to_s3_and_retrieve_s3_url(filepath: str) -> str: """Upload the file at /tmp/ws/* and retrieve the s3 path @@ -100,82 +107,98 @@ def upload_file_to_s3_and_retrieve_s3_url(filepath: str) -> str: """ global session_id - bucket = os.environ['FILE_BUCKET'] - region = os.environ['AWS_REGION'] + bucket = os.environ["FILE_BUCKET"] + region = os.environ["AWS_REGION"] if not filepath.startswith(WORKSPACE_DIR): - raise ValueError(f'{filepath} does not appear to be a file under the {WORKSPACE_DIR} directory. Files to be uploaded must exist under {WORKSPACE_DIR}.') + raise ValueError( + f"{filepath} does not appear to be a file under the {WORKSPACE_DIR} directory. Files to be uploaded must exist under {WORKSPACE_DIR}." + ) filename = os.path.basename(filepath) - key = f'mcp/{session_id}/{filename}' + key = f"mcp/{session_id}/{filename}" - s3 = boto3.client('s3') + s3 = boto3.client("s3") s3.upload_file(filepath, bucket, key) - return f'https://{bucket}.s3.{region}.amazonaws.com/{key}' + return f"https://{bucket}.s3.{region}.amazonaws.com/{key}" + app = FastAPI() # Shared MCP clients app.mcp_tools = None -@app.get('/') + +@app.get("/") async def healthcheck(): return Response(status_code=status.HTTP_200_OK) + class UnrecordedMessage(BaseModel): role: str content: str + class Model(BaseModel): modelId: str region: str + class StreamingRequest(BaseModel): systemPrompt: str userPrompt: str messages: List[UnrecordedMessage] model: Model + def convert_unrecorded_message_to_strands_messages(messages: List[UnrecordedMessage]): - return list(map(lambda m: { 'role': m.role, 'content': [{ 'text': m.content }] }, messages)) + return list( + map(lambda m: {"role": m.role, "content": [{"text": m.content}]}, messages) + ) + def safe_parse_mcp_json(): res = [] - with open('mcp.json', 'r') as f: + with open("mcp.json", "r") as f: mcp_json = json.loads(f.read()) - if 'mcpServers' not in mcp_json: - raise Exception('mcpServers not defined in mcp.json') + if "mcpServers" not in mcp_json: + raise Exception("mcpServers not defined in mcp.json") - mcp_servers = mcp_json['mcpServers'] + mcp_servers = mcp_json["mcpServers"] mcp_server_names = mcp_servers.keys() for server_name in mcp_server_names: server = mcp_servers[server_name] - res.append({ - 'command': server['command'], - 'args': server['args'] if 'args' in server else [], - 'env': server['env'] if 'env' in server else {}, - }) + res.append( + { + "command": server["command"], + "args": server["args"] if "args" in server else [], + "env": server["env"] if "env" in server else {}, + } + ) return res + def make_mcp_client(server): def spawn(): return stdio_client( StdioServerParameters( - command=server['command'], - args=server['args'], + command=server["command"], + args=server["args"], env={ **UV_ENV, - **server['env'], + **server["env"], }, ) ) + return MCPClient(spawn) + def load_mcp_tools(): mcp_servers = safe_parse_mcp_json() mcp_clients = [make_mcp_client(s) for s in mcp_servers] @@ -188,7 +211,8 @@ def load_mcp_tools(): app.mcp_tools = mcp_tools -@app.post('/streaming') + +@app.post("/streaming") async def streaming(request: StreamingRequest): if app.mcp_tools is None: load_mcp_tools() @@ -198,7 +222,7 @@ async def generate(): session_id = create_session_id() - logging.info(f'New session {session_id}') + logging.info(f"New session {session_id}") create_ws_directory() @@ -207,12 +231,11 @@ async def generate(): ) bedrock_model = BedrockModel( - model_id=request.model.modelId, - boto_session=session + model_id=request.model.modelId, boto_session=session ) agent = Agent( - system_prompt=f'{request.systemPrompt}\n{FIXED_SYSTEM_PROMPT}', + system_prompt=f"{request.systemPrompt}\n{FIXED_SYSTEM_PROMPT}", messages=convert_unrecorded_message_to_strands_messages(request.messages), model=bedrock_model, tools=app.mcp_tools + [upload_file_to_s3_and_retrieve_s3_url], @@ -226,30 +249,35 @@ async def generate(): tool_use = extract_tool_use(event) if text is not None and tool_use is not None: - yield stream_chunk('', f'{text}\n') - yield stream_chunk('', f'```\n{tool_use["name"]}: {tool_use["input"]}\n```\n') + yield stream_chunk("", f"{text}\n") + yield stream_chunk( + "", f"```\n{tool_use['name']}: {tool_use['input']}\n```\n" + ) elif text is not None: yield stream_chunk(text, None) else: - yield stream_chunk('', f'```\n{tool_use["name"]}: {tool_use["input"]}\n```\n') + yield stream_chunk( + "", f"```\n{tool_use['name']}: {tool_use['input']}\n```\n" + ) else: tool_result = extract_tool_result(event) if len(tool_result) > 200: - tool_result = tool_result[:200] + '...' - yield stream_chunk('', f'```\n{tool_result}\n```\n') + tool_result = tool_result[:200] + "..." + yield stream_chunk("", f"```\n{tool_result}\n```\n") clean_ws_directory() return StreamingResponse( generate(), - media_type='text/event-stream', + media_type="text/event-stream", ) -if __name__ == '__main__': + +if __name__ == "__main__": logging.basicConfig( level=logging.INFO, - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', - datefmt='%Y-%m-%d %H:%M:%S' + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", ) - uvicorn.run(app, host='0.0.0.0', port=8080) + uvicorn.run(app, host="0.0.0.0", port=8080) diff --git a/packages/cdk/package.json b/packages/cdk/package.json index 66ae1de0e..80dbea19d 100644 --- a/packages/cdk/package.json +++ b/packages/cdk/package.json @@ -26,7 +26,7 @@ "typescript": "~5.4.5" }, "dependencies": { - "@aws-cdk/aws-bedrock-agentcore-alpha": "2.221.0-alpha.0", + "@aws-cdk/aws-bedrock-agentcore-alpha": "2.227.0-alpha.0", "@aws-cdk/aws-lambda-python-alpha": "^2.220.0-alpha.0", "@aws-sdk/client-bedrock-agent": "^3.755.0", "@aws-sdk/client-bedrock-agent-runtime": "^3.755.0", diff --git a/packages/cdk/test/__snapshots__/generative-ai-use-cases.test.ts.snap b/packages/cdk/test/__snapshots__/generative-ai-use-cases.test.ts.snap index 48da5257f..702e38062 100644 --- a/packages/cdk/test/__snapshots__/generative-ai-use-cases.test.ts.snap +++ b/packages/cdk/test/__snapshots__/generative-ai-use-cases.test.ts.snap @@ -4526,7 +4526,7 @@ exports[`GenerativeAiUseCases matches the snapshot (closed network mode) 4`] = ` "AgentRuntimeArtifact": { "ContainerConfiguration": { "ContainerUri": { - "Fn::Sub": "123456890123.dkr.ecr.us-east-1.\${AWS::URLSuffix}/cdk-hnb659fds-container-assets-123456890123-us-east-1:69eb434ee242daa7224dd9fb0f5cf9142a71fe81dac805352a11509e5ecff870", + "Fn::Sub": "123456890123.dkr.ecr.us-east-1.\${AWS::URLSuffix}/cdk-hnb659fds-container-assets-123456890123-us-east-1:269f4f4a951791e332d743bff1e6d6850b8c4702c14f0c17086d0fb9a616a782", }, }, }, @@ -4535,7 +4535,7 @@ exports[`GenerativeAiUseCases matches the snapshot (closed network mode) 4`] = ` "FILE_BUCKET": { "Ref": "GenericAgentCoreAgentCoreFileBucket0430DA42", }, - "MCP_SERVERS": "{"time":{"command":"uvx","args":["mcp-server-time"],"metadata":{"category":"Utility","description":"Provides current time and date functionality"}},"aws-knowledge-mcp-server":{"command":"npx","args":["mcp-remote","https://knowledge-mcp.global.api.aws"],"metadata":{"category":"AWS","description":"AWS Knowledge Base MCP server for enterprise knowledge access"}},"awslabs.aws-documentation-mcp-server":{"command":"uvx","args":["awslabs.aws-documentation-mcp-server@latest"],"metadata":{"category":"AWS","description":"Access AWS documentation and guides"}},"awslabs.cdk-mcp-server":{"command":"uvx","args":["awslabs.cdk-mcp-server@latest"],"metadata":{"category":"AWS","description":"AWS CDK code generation and assistance"}},"awslabs.aws-diagram-mcp-server":{"command":"uvx","args":["awslabs.aws-diagram-mcp-server@latest"],"metadata":{"category":"AWS","description":"Generate AWS architecture diagrams"}},"awslabs.nova-canvas-mcp-server":{"command":"uvx","args":["awslabs.nova-canvas-mcp-server@latest"],"env":{"AWS_REGION":"us-east-1"},"metadata":{"category":"AI/ML","description":"Amazon Nova Canvas image generation"}},"tavily-search":{"command":"npx","args":["-y","mcp-remote","https://mcp.tavily.com/mcp/?tavilyApiKey="],"metadata":{"category":"Search","description":"Web search and research capabilities powered by Tavily"}}}", + "MCP_CONFIG_PATH": "/var/task/mcp-configs/agent-builder/mcp.json", "SUPPORTED_CACHE_FIELDS": "{"anthropic.claude-opus-4-5-20251101-v1:0":["messages","system","tools"],"anthropic.claude-sonnet-4-5-20250929-v1:0":["messages","system","tools"],"anthropic.claude-haiku-4-5-20251001-v1:0":["messages","system","tools"],"anthropic.claude-opus-4-1-20250805-v1:0":["messages","system","tools"],"anthropic.claude-opus-4-20250514-v1:0":["messages","system","tools"],"anthropic.claude-sonnet-4-20250514-v1:0":["messages","system","tools"],"anthropic.claude-3-7-sonnet-20250219-v1:0":["messages","system","tools"],"anthropic.claude-3-5-haiku-20241022-v1:0":["messages","system","tools"],"amazon.nova-premier-v1:0":["messages","system"],"amazon.nova-pro-v1:0":["messages","system"],"amazon.nova-lite-v1:0":["messages","system"],"amazon.nova-micro-v1:0":["messages","system"],"amazon.nova-2-lite-v1:0":["messages","system"]}", }, "NetworkConfiguration": { @@ -4654,6 +4654,120 @@ exports[`GenerativeAiUseCases matches the snapshot (closed network mode) 4`] = ` "Properties": { "PolicyDocument": { "Statement": [ + { + "Action": [ + "logs:DescribeLogStreams", + "logs:CreateLogGroup", + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":logs:us-east-1:123456890123:log-group:/aws/bedrock-agentcore/runtimes/*", + ], + ], + }, + "Sid": "LogGroupAccess", + }, + { + "Action": "logs:DescribeLogGroups", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":logs:us-east-1:123456890123:log-group:*", + ], + ], + }, + "Sid": "DescribeLogGroups", + }, + { + "Action": [ + "logs:CreateLogStream", + "logs:PutLogEvents", + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":logs:us-east-1:123456890123:log-group:/aws/bedrock-agentcore/runtimes/*:log-stream:*", + ], + ], + }, + "Sid": "LogStreamAccess", + }, + { + "Action": [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords", + "xray:GetSamplingRules", + "xray:GetSamplingTargets", + ], + "Effect": "Allow", + "Resource": "*", + "Sid": "XRayAccess", + }, + { + "Action": "cloudwatch:PutMetricData", + "Condition": { + "StringEquals": { + "cloudwatch:namespace": "bedrock-agentcore", + }, + }, + "Effect": "Allow", + "Resource": "*", + "Sid": "CloudWatchMetrics", + }, + { + "Action": [ + "bedrock-agentcore:GetWorkloadAccessToken", + "bedrock-agentcore:GetWorkloadAccessTokenForJWT", + "bedrock-agentcore:GetWorkloadAccessTokenForUserId", + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":bedrock-agentcore:us-east-1:123456890123:workload-identity-directory/default", + ], + ], + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":bedrock-agentcore:us-east-1:123456890123:workload-identity-directory/default/workload-identity/*", + ], + ], + }, + ], + "Sid": "GetAgentAccessToken", + }, { "Action": [ "bedrock:InvokeModel", @@ -4690,6 +4804,12 @@ exports[`GenerativeAiUseCases matches the snapshot (closed network mode) 4`] = ` "Resource": "*", "Sid": "Tools", }, + { + "Action": "bedrock-agentcore:InvokeGateway", + "Effect": "Allow", + "Resource": "*", + "Sid": "AllowGatewayInvocation", + }, { "Action": [ "s3:DeleteObject*", @@ -4795,7 +4915,7 @@ exports[`GenerativeAiUseCases matches the snapshot (closed network mode) 4`] = ` "AgentRuntimeArtifact": { "ContainerConfiguration": { "ContainerUri": { - "Fn::Sub": "123456890123.dkr.ecr.us-east-1.\${AWS::URLSuffix}/cdk-hnb659fds-container-assets-123456890123-us-east-1:69eb434ee242daa7224dd9fb0f5cf9142a71fe81dac805352a11509e5ecff870", + "Fn::Sub": "123456890123.dkr.ecr.us-east-1.\${AWS::URLSuffix}/cdk-hnb659fds-container-assets-123456890123-us-east-1:269f4f4a951791e332d743bff1e6d6850b8c4702c14f0c17086d0fb9a616a782", }, }, }, @@ -4804,7 +4924,7 @@ exports[`GenerativeAiUseCases matches the snapshot (closed network mode) 4`] = ` "FILE_BUCKET": { "Ref": "GenericAgentCoreAgentCoreFileBucket0430DA42", }, - "MCP_SERVERS": "{"time":{"command":"uvx","args":["mcp-server-time"],"metadata":{"category":"Utility","description":"Provides current time and date functionality"}},"aws-knowledge-mcp-server":{"command":"npx","args":["mcp-remote","https://knowledge-mcp.global.api.aws"],"metadata":{"category":"AWS","description":"AWS Knowledge Base MCP server for enterprise knowledge access"}},"awslabs.aws-documentation-mcp-server":{"command":"uvx","args":["awslabs.aws-documentation-mcp-server@latest"],"metadata":{"category":"AWS","description":"Access AWS documentation and guides"}},"awslabs.cdk-mcp-server":{"command":"uvx","args":["awslabs.cdk-mcp-server@latest"],"metadata":{"category":"AWS","description":"AWS CDK code generation and assistance"}},"awslabs.aws-diagram-mcp-server":{"command":"uvx","args":["awslabs.aws-diagram-mcp-server@latest"],"metadata":{"category":"AWS","description":"Generate AWS architecture diagrams"}},"awslabs.nova-canvas-mcp-server":{"command":"uvx","args":["awslabs.nova-canvas-mcp-server@latest"],"env":{"AWS_REGION":"us-east-1"},"metadata":{"category":"AI/ML","description":"Amazon Nova Canvas image generation"}},"tavily-search":{"command":"npx","args":["-y","mcp-remote","https://mcp.tavily.com/mcp/?tavilyApiKey="],"metadata":{"category":"Search","description":"Web search and research capabilities powered by Tavily"}}}", + "MCP_CONFIG_PATH": "/var/task/mcp-configs/generic/mcp.json", "SUPPORTED_CACHE_FIELDS": "{"anthropic.claude-opus-4-5-20251101-v1:0":["messages","system","tools"],"anthropic.claude-sonnet-4-5-20250929-v1:0":["messages","system","tools"],"anthropic.claude-haiku-4-5-20251001-v1:0":["messages","system","tools"],"anthropic.claude-opus-4-1-20250805-v1:0":["messages","system","tools"],"anthropic.claude-opus-4-20250514-v1:0":["messages","system","tools"],"anthropic.claude-sonnet-4-20250514-v1:0":["messages","system","tools"],"anthropic.claude-3-7-sonnet-20250219-v1:0":["messages","system","tools"],"anthropic.claude-3-5-haiku-20241022-v1:0":["messages","system","tools"],"amazon.nova-premier-v1:0":["messages","system"],"amazon.nova-pro-v1:0":["messages","system"],"amazon.nova-lite-v1:0":["messages","system"],"amazon.nova-micro-v1:0":["messages","system"],"amazon.nova-2-lite-v1:0":["messages","system"]}", }, "NetworkConfiguration": { @@ -5158,7 +5278,7 @@ exports[`GenerativeAiUseCases matches the snapshot (closed network mode) 6`] = ` "Value": "", }, "McpServersConfig": { - "Value": "{"time":{"metadata":{"category":"Utility","description":"Provides current time and date functionality"}},"aws-knowledge-mcp-server":{"metadata":{"category":"AWS","description":"AWS Knowledge Base MCP server for enterprise knowledge access"}},"awslabs.aws-documentation-mcp-server":{"metadata":{"category":"AWS","description":"Access AWS documentation and guides"}},"awslabs.cdk-mcp-server":{"metadata":{"category":"AWS","description":"AWS CDK code generation and assistance"}},"awslabs.aws-diagram-mcp-server":{"metadata":{"category":"AWS","description":"Generate AWS architecture diagrams"}},"awslabs.nova-canvas-mcp-server":{"metadata":{"category":"AI/ML","description":"Amazon Nova Canvas image generation"}},"tavily-search":{"metadata":{"category":"Search","description":"Web search and research capabilities powered by Tavily"}}}", + "Value": "{"time":{"metadata":{"category":"Utility","description":"Provides current time and date functionality"}},"aws-knowledge-mcp-server":{"metadata":{"category":"AWS","description":"AWS Knowledge Base MCP server for enterprise knowledge access"}},"awslabs.aws-documentation-mcp-server":{"metadata":{"category":"AWS","description":"Access AWS documentation and guides"}},"awslabs.cdk-mcp-server":{"metadata":{"category":"AWS","description":"AWS CDK code generation and assistance"}},"awslabs.aws-diagram-mcp-server":{"metadata":{"category":"AWS","description":"Generate AWS architecture diagrams"}},"awslabs.nova-canvas-mcp-server":{"metadata":{"category":"AI/ML","description":"Amazon Nova Canvas image generation"}},"tavily-search":{"metadata":{"category":"Search","description":"Web search and research capabilities powered by Tavily"}},"tavily-gateway":{"metadata":{"category":"Search","description":"Web search and research capabilities powered by Tavily"}}}", }, "ModelIds": { "Value": { @@ -13696,17 +13816,12 @@ exports[`GenerativeAiUseCases matches the snapshot (closed network mode) 6`] = ` "dynamodb:DescribeTable", ], "Effect": "Allow", - "Resource": [ - { - "Fn::GetAtt": [ - "DatabaseStatsTable9C709761", - "Arn", - ], - }, - { - "Ref": "AWS::NoValue", - }, - ], + "Resource": { + "Fn::GetAtt": [ + "DatabaseStatsTable9C709761", + "Arn", + ], + }, }, ], "Version": "2012-10-17", @@ -16306,17 +16421,12 @@ exports[`GenerativeAiUseCases matches the snapshot (closed network mode) 6`] = ` "dynamodb:DescribeTable", ], "Effect": "Allow", - "Resource": [ - { - "Fn::GetAtt": [ - "DatabaseStatsTable9C709761", - "Arn", - ], - }, - { - "Ref": "AWS::NoValue", - }, - ], + "Resource": { + "Fn::GetAtt": [ + "DatabaseStatsTable9C709761", + "Arn", + ], + }, }, ], "Version": "2012-10-17", @@ -19088,7 +19198,7 @@ exports[`GenerativeAiUseCases matches the snapshot (closed network mode) 6`] = ` "VITE_APP_INLINE_AGENTS": "false", "VITE_APP_MCP_ENABLED": "false", "VITE_APP_MCP_ENDPOINT": "", - "VITE_APP_MCP_SERVERS_CONFIG": "{"time":{"metadata":{"category":"Utility","description":"Provides current time and date functionality"}},"aws-knowledge-mcp-server":{"metadata":{"category":"AWS","description":"AWS Knowledge Base MCP server for enterprise knowledge access"}},"awslabs.aws-documentation-mcp-server":{"metadata":{"category":"AWS","description":"Access AWS documentation and guides"}},"awslabs.cdk-mcp-server":{"metadata":{"category":"AWS","description":"AWS CDK code generation and assistance"}},"awslabs.aws-diagram-mcp-server":{"metadata":{"category":"AWS","description":"Generate AWS architecture diagrams"}},"awslabs.nova-canvas-mcp-server":{"metadata":{"category":"AI/ML","description":"Amazon Nova Canvas image generation"}},"tavily-search":{"metadata":{"category":"Search","description":"Web search and research capabilities powered by Tavily"}}}", + "VITE_APP_MCP_SERVERS_CONFIG": "{"time":{"metadata":{"category":"Utility","description":"Provides current time and date functionality"}},"aws-knowledge-mcp-server":{"metadata":{"category":"AWS","description":"AWS Knowledge Base MCP server for enterprise knowledge access"}},"awslabs.aws-documentation-mcp-server":{"metadata":{"category":"AWS","description":"Access AWS documentation and guides"}},"awslabs.cdk-mcp-server":{"metadata":{"category":"AWS","description":"AWS CDK code generation and assistance"}},"awslabs.aws-diagram-mcp-server":{"metadata":{"category":"AWS","description":"Generate AWS architecture diagrams"}},"awslabs.nova-canvas-mcp-server":{"metadata":{"category":"AI/ML","description":"Amazon Nova Canvas image generation"}},"tavily-search":{"metadata":{"category":"Search","description":"Web search and research capabilities powered by Tavily"}},"tavily-gateway":{"metadata":{"category":"Search","description":"Web search and research capabilities powered by Tavily"}}}", "VITE_APP_MODEL_IDS": { "Fn::Join": [ "", @@ -26486,7 +26596,7 @@ exports[`GenerativeAiUseCases matches the snapshot 4`] = ` "AgentRuntimeArtifact": { "ContainerConfiguration": { "ContainerUri": { - "Fn::Sub": "123456890123.dkr.ecr.us-east-1.\${AWS::URLSuffix}/cdk-hnb659fds-container-assets-123456890123-us-east-1:69eb434ee242daa7224dd9fb0f5cf9142a71fe81dac805352a11509e5ecff870", + "Fn::Sub": "123456890123.dkr.ecr.us-east-1.\${AWS::URLSuffix}/cdk-hnb659fds-container-assets-123456890123-us-east-1:269f4f4a951791e332d743bff1e6d6850b8c4702c14f0c17086d0fb9a616a782", }, }, }, @@ -26495,7 +26605,7 @@ exports[`GenerativeAiUseCases matches the snapshot 4`] = ` "FILE_BUCKET": { "Ref": "GenericAgentCoreAgentCoreFileBucket0430DA42", }, - "MCP_SERVERS": "{"time":{"command":"uvx","args":["mcp-server-time"],"metadata":{"category":"Utility","description":"Provides current time and date functionality"}},"aws-knowledge-mcp-server":{"command":"npx","args":["mcp-remote","https://knowledge-mcp.global.api.aws"],"metadata":{"category":"AWS","description":"AWS Knowledge Base MCP server for enterprise knowledge access"}},"awslabs.aws-documentation-mcp-server":{"command":"uvx","args":["awslabs.aws-documentation-mcp-server@latest"],"metadata":{"category":"AWS","description":"Access AWS documentation and guides"}},"awslabs.cdk-mcp-server":{"command":"uvx","args":["awslabs.cdk-mcp-server@latest"],"metadata":{"category":"AWS","description":"AWS CDK code generation and assistance"}},"awslabs.aws-diagram-mcp-server":{"command":"uvx","args":["awslabs.aws-diagram-mcp-server@latest"],"metadata":{"category":"AWS","description":"Generate AWS architecture diagrams"}},"awslabs.nova-canvas-mcp-server":{"command":"uvx","args":["awslabs.nova-canvas-mcp-server@latest"],"env":{"AWS_REGION":"us-east-1"},"metadata":{"category":"AI/ML","description":"Amazon Nova Canvas image generation"}},"tavily-search":{"command":"npx","args":["-y","mcp-remote","https://mcp.tavily.com/mcp/?tavilyApiKey="],"metadata":{"category":"Search","description":"Web search and research capabilities powered by Tavily"}}}", + "MCP_CONFIG_PATH": "/var/task/mcp-configs/agent-builder/mcp.json", "SUPPORTED_CACHE_FIELDS": "{"anthropic.claude-opus-4-5-20251101-v1:0":["messages","system","tools"],"anthropic.claude-sonnet-4-5-20250929-v1:0":["messages","system","tools"],"anthropic.claude-haiku-4-5-20251001-v1:0":["messages","system","tools"],"anthropic.claude-opus-4-1-20250805-v1:0":["messages","system","tools"],"anthropic.claude-opus-4-20250514-v1:0":["messages","system","tools"],"anthropic.claude-sonnet-4-20250514-v1:0":["messages","system","tools"],"anthropic.claude-3-7-sonnet-20250219-v1:0":["messages","system","tools"],"anthropic.claude-3-5-haiku-20241022-v1:0":["messages","system","tools"],"amazon.nova-premier-v1:0":["messages","system"],"amazon.nova-pro-v1:0":["messages","system"],"amazon.nova-lite-v1:0":["messages","system"],"amazon.nova-micro-v1:0":["messages","system"],"amazon.nova-2-lite-v1:0":["messages","system"]}", }, "NetworkConfiguration": { @@ -26614,6 +26724,120 @@ exports[`GenerativeAiUseCases matches the snapshot 4`] = ` "Properties": { "PolicyDocument": { "Statement": [ + { + "Action": [ + "logs:DescribeLogStreams", + "logs:CreateLogGroup", + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":logs:us-east-1:123456890123:log-group:/aws/bedrock-agentcore/runtimes/*", + ], + ], + }, + "Sid": "LogGroupAccess", + }, + { + "Action": "logs:DescribeLogGroups", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":logs:us-east-1:123456890123:log-group:*", + ], + ], + }, + "Sid": "DescribeLogGroups", + }, + { + "Action": [ + "logs:CreateLogStream", + "logs:PutLogEvents", + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":logs:us-east-1:123456890123:log-group:/aws/bedrock-agentcore/runtimes/*:log-stream:*", + ], + ], + }, + "Sid": "LogStreamAccess", + }, + { + "Action": [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords", + "xray:GetSamplingRules", + "xray:GetSamplingTargets", + ], + "Effect": "Allow", + "Resource": "*", + "Sid": "XRayAccess", + }, + { + "Action": "cloudwatch:PutMetricData", + "Condition": { + "StringEquals": { + "cloudwatch:namespace": "bedrock-agentcore", + }, + }, + "Effect": "Allow", + "Resource": "*", + "Sid": "CloudWatchMetrics", + }, + { + "Action": [ + "bedrock-agentcore:GetWorkloadAccessToken", + "bedrock-agentcore:GetWorkloadAccessTokenForJWT", + "bedrock-agentcore:GetWorkloadAccessTokenForUserId", + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":bedrock-agentcore:us-east-1:123456890123:workload-identity-directory/default", + ], + ], + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":bedrock-agentcore:us-east-1:123456890123:workload-identity-directory/default/workload-identity/*", + ], + ], + }, + ], + "Sid": "GetAgentAccessToken", + }, { "Action": [ "bedrock:InvokeModel", @@ -26650,6 +26874,12 @@ exports[`GenerativeAiUseCases matches the snapshot 4`] = ` "Resource": "*", "Sid": "Tools", }, + { + "Action": "bedrock-agentcore:InvokeGateway", + "Effect": "Allow", + "Resource": "*", + "Sid": "AllowGatewayInvocation", + }, { "Action": [ "s3:DeleteObject*", @@ -26755,7 +26985,7 @@ exports[`GenerativeAiUseCases matches the snapshot 4`] = ` "AgentRuntimeArtifact": { "ContainerConfiguration": { "ContainerUri": { - "Fn::Sub": "123456890123.dkr.ecr.us-east-1.\${AWS::URLSuffix}/cdk-hnb659fds-container-assets-123456890123-us-east-1:69eb434ee242daa7224dd9fb0f5cf9142a71fe81dac805352a11509e5ecff870", + "Fn::Sub": "123456890123.dkr.ecr.us-east-1.\${AWS::URLSuffix}/cdk-hnb659fds-container-assets-123456890123-us-east-1:269f4f4a951791e332d743bff1e6d6850b8c4702c14f0c17086d0fb9a616a782", }, }, }, @@ -26764,7 +26994,7 @@ exports[`GenerativeAiUseCases matches the snapshot 4`] = ` "FILE_BUCKET": { "Ref": "GenericAgentCoreAgentCoreFileBucket0430DA42", }, - "MCP_SERVERS": "{"time":{"command":"uvx","args":["mcp-server-time"],"metadata":{"category":"Utility","description":"Provides current time and date functionality"}},"aws-knowledge-mcp-server":{"command":"npx","args":["mcp-remote","https://knowledge-mcp.global.api.aws"],"metadata":{"category":"AWS","description":"AWS Knowledge Base MCP server for enterprise knowledge access"}},"awslabs.aws-documentation-mcp-server":{"command":"uvx","args":["awslabs.aws-documentation-mcp-server@latest"],"metadata":{"category":"AWS","description":"Access AWS documentation and guides"}},"awslabs.cdk-mcp-server":{"command":"uvx","args":["awslabs.cdk-mcp-server@latest"],"metadata":{"category":"AWS","description":"AWS CDK code generation and assistance"}},"awslabs.aws-diagram-mcp-server":{"command":"uvx","args":["awslabs.aws-diagram-mcp-server@latest"],"metadata":{"category":"AWS","description":"Generate AWS architecture diagrams"}},"awslabs.nova-canvas-mcp-server":{"command":"uvx","args":["awslabs.nova-canvas-mcp-server@latest"],"env":{"AWS_REGION":"us-east-1"},"metadata":{"category":"AI/ML","description":"Amazon Nova Canvas image generation"}},"tavily-search":{"command":"npx","args":["-y","mcp-remote","https://mcp.tavily.com/mcp/?tavilyApiKey="],"metadata":{"category":"Search","description":"Web search and research capabilities powered by Tavily"}}}", + "MCP_CONFIG_PATH": "/var/task/mcp-configs/generic/mcp.json", "SUPPORTED_CACHE_FIELDS": "{"anthropic.claude-opus-4-5-20251101-v1:0":["messages","system","tools"],"anthropic.claude-sonnet-4-5-20250929-v1:0":["messages","system","tools"],"anthropic.claude-haiku-4-5-20251001-v1:0":["messages","system","tools"],"anthropic.claude-opus-4-1-20250805-v1:0":["messages","system","tools"],"anthropic.claude-opus-4-20250514-v1:0":["messages","system","tools"],"anthropic.claude-sonnet-4-20250514-v1:0":["messages","system","tools"],"anthropic.claude-3-7-sonnet-20250219-v1:0":["messages","system","tools"],"anthropic.claude-3-5-haiku-20241022-v1:0":["messages","system","tools"],"amazon.nova-premier-v1:0":["messages","system"],"amazon.nova-pro-v1:0":["messages","system"],"amazon.nova-lite-v1:0":["messages","system"],"amazon.nova-micro-v1:0":["messages","system"],"amazon.nova-2-lite-v1:0":["messages","system"]}", }, "NetworkConfiguration": { @@ -27118,7 +27348,7 @@ exports[`GenerativeAiUseCases matches the snapshot 6`] = ` "Value": "", }, "McpServersConfig": { - "Value": "{"time":{"metadata":{"category":"Utility","description":"Provides current time and date functionality"}},"aws-knowledge-mcp-server":{"metadata":{"category":"AWS","description":"AWS Knowledge Base MCP server for enterprise knowledge access"}},"awslabs.aws-documentation-mcp-server":{"metadata":{"category":"AWS","description":"Access AWS documentation and guides"}},"awslabs.cdk-mcp-server":{"metadata":{"category":"AWS","description":"AWS CDK code generation and assistance"}},"awslabs.aws-diagram-mcp-server":{"metadata":{"category":"AWS","description":"Generate AWS architecture diagrams"}},"awslabs.nova-canvas-mcp-server":{"metadata":{"category":"AI/ML","description":"Amazon Nova Canvas image generation"}},"tavily-search":{"metadata":{"category":"Search","description":"Web search and research capabilities powered by Tavily"}}}", + "Value": "{"time":{"metadata":{"category":"Utility","description":"Provides current time and date functionality"}},"aws-knowledge-mcp-server":{"metadata":{"category":"AWS","description":"AWS Knowledge Base MCP server for enterprise knowledge access"}},"awslabs.aws-documentation-mcp-server":{"metadata":{"category":"AWS","description":"Access AWS documentation and guides"}},"awslabs.cdk-mcp-server":{"metadata":{"category":"AWS","description":"AWS CDK code generation and assistance"}},"awslabs.aws-diagram-mcp-server":{"metadata":{"category":"AWS","description":"Generate AWS architecture diagrams"}},"awslabs.nova-canvas-mcp-server":{"metadata":{"category":"AI/ML","description":"Amazon Nova Canvas image generation"}},"tavily-search":{"metadata":{"category":"Search","description":"Web search and research capabilities powered by Tavily"}},"tavily-gateway":{"metadata":{"category":"Search","description":"Web search and research capabilities powered by Tavily"}}}", }, "ModelIds": { "Value": { @@ -35549,17 +35779,12 @@ exports[`GenerativeAiUseCases matches the snapshot 6`] = ` "dynamodb:DescribeTable", ], "Effect": "Allow", - "Resource": [ - { - "Fn::GetAtt": [ - "DatabaseStatsTable9C709761", - "Arn", - ], - }, - { - "Ref": "AWS::NoValue", - }, - ], + "Resource": { + "Fn::GetAtt": [ + "DatabaseStatsTable9C709761", + "Arn", + ], + }, }, ], "Version": "2012-10-17", @@ -37709,17 +37934,12 @@ exports[`GenerativeAiUseCases matches the snapshot 6`] = ` "dynamodb:DescribeTable", ], "Effect": "Allow", - "Resource": [ - { - "Fn::GetAtt": [ - "DatabaseStatsTable9C709761", - "Arn", - ], - }, - { - "Ref": "AWS::NoValue", - }, - ], + "Resource": { + "Fn::GetAtt": [ + "DatabaseStatsTable9C709761", + "Arn", + ], + }, }, ], "Version": "2012-10-17", @@ -39994,7 +40214,7 @@ exports[`GenerativeAiUseCases matches the snapshot 6`] = ` "VITE_APP_INLINE_AGENTS": "false", "VITE_APP_MCP_ENABLED": "false", "VITE_APP_MCP_ENDPOINT": "", - "VITE_APP_MCP_SERVERS_CONFIG": "{"time":{"metadata":{"category":"Utility","description":"Provides current time and date functionality"}},"aws-knowledge-mcp-server":{"metadata":{"category":"AWS","description":"AWS Knowledge Base MCP server for enterprise knowledge access"}},"awslabs.aws-documentation-mcp-server":{"metadata":{"category":"AWS","description":"Access AWS documentation and guides"}},"awslabs.cdk-mcp-server":{"metadata":{"category":"AWS","description":"AWS CDK code generation and assistance"}},"awslabs.aws-diagram-mcp-server":{"metadata":{"category":"AWS","description":"Generate AWS architecture diagrams"}},"awslabs.nova-canvas-mcp-server":{"metadata":{"category":"AI/ML","description":"Amazon Nova Canvas image generation"}},"tavily-search":{"metadata":{"category":"Search","description":"Web search and research capabilities powered by Tavily"}}}", + "VITE_APP_MCP_SERVERS_CONFIG": "{"time":{"metadata":{"category":"Utility","description":"Provides current time and date functionality"}},"aws-knowledge-mcp-server":{"metadata":{"category":"AWS","description":"AWS Knowledge Base MCP server for enterprise knowledge access"}},"awslabs.aws-documentation-mcp-server":{"metadata":{"category":"AWS","description":"Access AWS documentation and guides"}},"awslabs.cdk-mcp-server":{"metadata":{"category":"AWS","description":"AWS CDK code generation and assistance"}},"awslabs.aws-diagram-mcp-server":{"metadata":{"category":"AWS","description":"Generate AWS architecture diagrams"}},"awslabs.nova-canvas-mcp-server":{"metadata":{"category":"AI/ML","description":"Amazon Nova Canvas image generation"}},"tavily-search":{"metadata":{"category":"Search","description":"Web search and research capabilities powered by Tavily"}},"tavily-gateway":{"metadata":{"category":"Search","description":"Web search and research capabilities powered by Tavily"}}}", "VITE_APP_MODEL_IDS": { "Fn::Join": [ "",