Development and container

Prerequisites

Python 3.12, uv, Docker (for image builds), and AWS credentials with access to the Secrets Manager secret and Bedrock.

make install          # uv sync --directory agentcore

Dependencies (agentcore/pyproject.toml): bedrock-agentcore, strands-agents, strands-agents-tools, boto3, pydantic>=2, and morpheus-llm[browser] — resolved from ../libs/morpheus-llm as an editable path dependency, so a change to the library is picked up without a reinstall.

Running locally

export SECRET_ARN=arn:aws:secretsmanager:us-east-1:625663093496:secret:agentcore-secrets-XXXXXX
make dev              # uv run --directory agentcore python -m agentcore.main

Always python -m agentcore.main. Running python agentcore/main.py puts agentcore/ at sys.path[0], so agentcore/types/ shadows the standard-library types module and imports inside the SDKs break.

This starts the BedrockAgentCoreApp HTTP server on port 9000. It needs real AWS credentials: the secret is fetched from Secrets Manager and inference goes to Bedrock.

curl -X POST http://localhost:9000/invocations \
  -H 'Content-Type: application/json' \
  -d '{"workflow":"test","payload":{"prompt":"What is 2 + 2?"}}'

Because MEMORY_ID is unset locally, agents run without memory — see AgentCore Memory.

Tests

make test             # uv run pytest agentcore/test/ -v

agentcore/test/test_main.py covers add_numbers, AgentModelIds values, registry register/list/run plus duplicate and unknown-name errors, PromptWorkflow success and missing-prompt paths, BaseWorkflow._generate_prompt() and its unknown-dependency guard, and that load_config() raises without SECRET_ARN. AWS is mocked throughout with unittest.mock.

test_agent_model_ids asserts AgentModelIds.CLAUDE_SONNET_3_5, which no longer exists in morpheus_llm.utils.enums.AgentModelIds — the enum has CLAUDE_HAIKU_3_5 but no Sonnet 3.5 member. That test fails until it is updated.

Run a single test:

uv run pytest agentcore/test/test_main.py::test_registry_run -v

pytest is configured with asyncio_mode = "auto" and pythonpath = ["."] in agentcore/pyproject.toml.

The container image

agentcore/Dockerfile:

FROM python:3.12-slim
WORKDIR /app

COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

COPY pyproject.toml uv.lock* ./
RUN uv sync --frozen --no-dev
ENV PATH="/app/.venv/bin:$PATH"

COPY . ./
ENV PYTHONPATH=/app
EXPOSE 9000 8000 8080
CMD ["python", "-m", "main"]
  • Single stage — Python needs no build step.

  • uv sync --frozen --no-dev installs production dependencies from the lockfile before the source is copied, so a source-only change reuses the dependency layer.

  • PATH puts the virtual environment’s Python first.

  • PYTHONPATH=/app supports absolute imports.

Two inconsistencies to be aware of before relying on a fresh image:

  • CMD is python -m main, but main.py imports from agentcore.config.loader import load_config. The CDK build context is agentcore/ with COPY . ./, so the package’s contents land flat in /app and there is no agentcore/ directory inside the image for those imports to resolve against.

  • uv sync --frozen --no-dev runs before COPY . ./, so the project’s own source is absent when its editable install would be performed.

Neither has been reconciled in this repository. If a container deploy fails at startup, this is the first place to look.

Building and running by hand

cd agentcore
docker build --platform linux/arm64 -t agentcore:local .

docker run --rm \
  -e SECRET_ARN="$SECRET_ARN" \
  -e AWS_REGION=us-east-1 \
  -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -e AWS_SESSION_TOKEN \
  -p 9000:9000 \
  agentcore:local

linux/arm64 matches the platform CDK targets (Platform.LINUX_ARM64). In normal operation you never build by hand: cdk deploy builds and pushes the image through DockerImageAsset.

Excluded from the image

agentcore/.dockerignore excludes .env, .venv/, test/, and .bedrock_agentcore.yaml. Secrets are always fetched at runtime; tests and CLI configuration have no place in a production image.

.bedrock_agentcore.yaml

The AgentCore CLI’s local configuration. It references the CDK-managed runtime and role rather than creating its own:

default_agent: agentcore
agents:
  agentcore:
    entrypoint: ./agentcore/main.py
    deployment_type: container
    platform: linux/amd64
    source_path: ./agentcore
    aws:
      execution_role: arn:aws:iam::625663093496:role/AgentCoreStack-agentcoreRuntimeRole…
      execution_role_auto_create: false
      account: '625663093496'
      region: us-east-1
      network_configuration: { network_mode: PUBLIC }
      protocol_configuration: { server_protocol: HTTP }
      observability: { enabled: true }
    bedrock_agentcore:
      agent_id: agentcore-Xt6rGY2nKW
      agent_arn: arn:aws:bedrock-agentcore:us-east-1:625663093496:runtime/agentcore-Xt6rGY2nKW
    memory:
      mode: NO_MEMORY

Always update execution_role from the CDK stack’s RuntimeRoleArn output after a deploy. Do not let the CLI create its own role — CDK owns all IAM.

Two fields diverge from the deployed stack: platform is linux/amd64 here while CDK builds linux/arm64, and memory.mode is NO_MEMORY while the stack provisions a memory resource and injects MEMORY_ID. The CLI path and the CDK path are not currently equivalent.

Adding a dependency

cd agentcore && uv add <package>

Never pip install. uv add updates both pyproject.toml and uv.lock, and the lockfile is what uv sync --frozen in the Dockerfile installs from — an unlocked dependency will not be in the image.