# AgentCore Harness — Managed Agent Loop (config-based) ## Table of Contents - What It Is - Harness vs. Runtime - Deployment Workflow - Configuration Surface - Per-Invocation Overrides - Versions and Endpoints - Streaming Response Format - Security Considerations - Additional Resources ## What It Is AgentCore Harness is a **managed agent loop**: you declare what the agent is (model, system prompt, tools, skills, memory, limits) as configuration, and AgentCore runs the reasoning → tool-call → result → response loop for you. There is no orchestration code to write and no container to build. The loop is powered by Strands Agents. Each session runs in an **isolated, stateful microVM** with its own filesystem and shell. Use Harness when you want the fastest path from config to a running agent. Key capabilities: - **Models:** Bedrock (Converse), OpenAI, Google Gemini, and any LiteLLM-compatible provider (including self-hosted endpoints). Select or switch the model per invocation without redeploying. - **Tools:** built-in `shell` and `file_operations`; opt-in AgentCore Gateway, remote MCP servers, AgentCore Browser, AgentCore Code Interpreter, and inline (client-side) functions. - **Skills:** attach Agent Skills (the open AgentSkills.io standard — `SKILL.md` + optional scripts/references) from four sources: pre-built **AWS Skills**, any **Git** repo (e.g. the Anthropic skills repo), **Amazon S3**, or the session **filesystem**. Set as a harness default or override per invocation. - **Memory:** short-term (within a session) and long-term (across sessions), scoped per user via `actorId`. - **Operations:** versioning with named endpoints, observability via CloudWatch, and one inbound auth method per harness — SigV4 (IAM) or OAuth JWT. Refer to the latest [AgentCore Harness documentation](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness.html) for the authoritative capability list, and the [Harness tools documentation](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-tools.html) for the full set of built-in and opt-in tools. ## Harness vs. Runtime Both run inside AgentCore Runtime infrastructure, but they solve different problems: | | Harness | Runtime | |---|---------|---------| | **You provide** | Configuration (no code) | Agent code + ARM64 container | | **Agent loop** | Managed (Strands) | You write it | | **Change model / add a tool** | Config change, no redeploy | Code change + redeploy | | **Framework choice** | Strands only | Any (LangGraph, CrewAI, custom) | | **Best for** | Fast setup, dynamic config | Custom loop control, non-loop patterns (graph/workflow), bidirectional streaming, hooks | **Decision guide:** | Question | Answer → Choose | |----------|-----------------| | Want an agent loop without writing orchestration code? | Harness | | Need a specific framework or full control of the loop? | Runtime — see [runtime procedure](agentcore-runtime.md) | | Need graph/workflow (non-agent-loop) execution or bidirectional streaming? | Runtime | Start with Harness; drop down to Runtime only when configuration is not enough. ## Deployment Workflow This is the authoritative create-and-invoke procedure — follow it over any external documentation, which may not reflect the latest API shape. When answering "how do I create and invoke a Harness" (or create/deploy/get-started), you MUST cover every step below; do not summarize them away, do not stop at `create-harness`, and do not collapse the three API stages into a single call. You can create and invoke a harness with the AgentCore CLI (fastest) or directly with the AWS SDK / CLI. The AWS MCP server is recommended for executing these AWS operations (sandboxed execution, audit logging) but is not required — the AWS CLI and SDK commands below work standalone. ``` Deployment Progress (all three stages are required — creation alone does not yield a callable agent): - [ ] Step 1: Create the harness (control plane) — minimum input is a name and an execution role - [ ] Step 2: Wait for status READY (poll get-harness) — you cannot invoke before READY - [ ] Step 3: Invoke (data plane) with a runtimeSessionId (>=33 chars) and a messages list, then read the streamed response events ``` **Common mistakes to avoid (the API does NOT work this way):** - Skipping Step 2 — a harness is not invokable until `get-harness` reports `READY`. - Invoking with `--input-text` or `--harness-id` — there is no such parameter. Invocation is on the **data plane** (`bedrock-agentcore`), takes a `runtimeSessionId` (≥33 chars) and a `messages` list, and returns a **stream** of events you must iterate (see Streaming Response Format). Treating the response as a single string drops the agent's output. **AgentCore CLI:** ```bash npm install -g @aws/agentcore@preview # Set the execution-limit guardrails (max iterations, max tokens, timeout) explicitly # rather than relying on defaults — see Security Considerations. Use `agentcore create --help` # for the current flag names, or set them on the underlying create-harness call below. agentcore create --name myresearchagent --model-provider bedrock agentcore deploy agentcore invoke --harness myresearchagent --session-id "$(uuidgen)" "Hello, what can you do?" ``` Useful CLI commands: `agentcore dev` (local dev server + inspector), `agentcore status`, `agentcore add harness`. **AWS CLI / SDK:** ```bash # Step 1: create. Only --harness-name and --execution-role-arn are required, but # set the execution-limit guardrails explicitly rather than relying on defaults, # and add --authorizer-configuration for any harness exposed beyond a trusted caller # (see Security Considerations). aws bedrock-agentcore-control create-harness \ --harness-name "MyHarness" \ --execution-role-arn "arn:aws:iam:::role/" \ --max-iterations 25 \ --max-tokens 4096 \ --timeout-seconds 300 # Step 2: poll until "status": "READY"; note the harness ARN. # Status progresses CREATING -> READY; CREATE_FAILED / UPDATE_FAILED / DELETE_FAILED are terminal failures to inspect. aws bedrock-agentcore-control get-harness --harness-id "" ``` ```python # Step 3: invoke (boto3). If no model is configured, the harness applies a # default Bedrock model — check the CreateHarness API reference for the current one. import boto3 client = boto3.client("bedrock-agentcore", region_name="") response = client.invoke_harness( harnessArn="", runtimeSessionId="", messages=[{"role": "user", "content": [{"text": "Hello"}]}], ) for event in response["stream"]: if "contentBlockDelta" in event: delta = event["contentBlockDelta"].get("delta", {}) if "text" in delta: print(delta["text"], end="", flush=True) ``` **Constraints:** - `harnessName` must start with a letter and contain only letters, digits, and underscores, max 40 characters. - `runtimeSessionId` MUST be at least 33 characters — a standard UUID (36 chars, with hyphens) satisfies this. If your `uuidgen` strips hyphens (32 chars), it will be too short; append a suffix or concatenate two. Over the wire it maps to the `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id` header. Reuse the same session id across invocations to continue the conversation in the same environment. - When no model is configured the harness applies a default Bedrock model; check the [CreateHarness API reference](https://docs.aws.amazon.com/bedrock-agentcore-control/latest/APIReference/API_CreateHarness.html) for the current default, and `aws bedrock list-foundation-models` for available model IDs. - Install the AgentCore CLI from the `@aws/agentcore@preview` npm channel (`npm install -g @aws/agentcore@preview`). - Refer to the latest AWS documentation for authoritative API parameters. ## Configuration Surface `create-harness` requires only `harnessName` and `executionRoleArn`. Everything below is optional and declarative: | Field | Purpose | |-------|---------| | `model` | Model provider config (Bedrock / OpenAI / Gemini / LiteLLM) | | `systemPrompt` | System instructions (list of text content blocks) | | `tools` / `allowedTools` | Tool definitions and an allowlist filter | | `skills` | Agent Skills from four sources: AWS Skills, Git, Amazon S3, or filesystem path | | `memory` | Short-term and/or long-term AgentCore Memory | | `maxIterations`, `maxTokens`, `timeoutSeconds` | Execution limits — set explicitly rather than relying on service defaults (see the [CreateHarness API reference](https://docs.aws.amazon.com/bedrock-agentcore-control/latest/APIReference/API_CreateHarness.html) for current defaults) | | `environment` | `agentCoreRuntimeEnvironment` holds `networkConfiguration` (VPC), `filesystemConfigurations` (session storage, EFS, or S3 Files), and `lifecycleConfiguration` | | `environmentArtifact` | Custom container image (bring-your-own environment) | | `environmentVariables` | Non-sensitive configuration only | | `authorizerConfiguration` | Inbound OAuth JWT (see Security Considerations) | | `truncation`, `tags` | Context-window truncation strategy; resource tags | Refer to the latest [CreateHarness API reference](https://docs.aws.amazon.com/bedrock-agentcore-control/latest/APIReference/API_CreateHarness.html) for the authoritative field list. ### Field shapes The optional fields are typed shapes, not loose key/values — use the exact member names below. `model`, `memory`, and each `skills` entry are **unions** (set exactly one variant); `tools` and `systemPrompt` are **lists**. ```jsonc // model: HarnessModelConfiguration union — variant key is bedrockModelConfig // (NOT a bare "bedrock"), and tuning params are flat (NO "inferenceConfig" wrapper). // Other variants: openAiModelConfig, geminiModelConfig, liteLlmModelConfig // (each requires modelId; openAi/gemini also require apiKeyArn — the ARN of an // AgentCore Identity API-key credential provider holding the provider key, never // the raw key inline). "model": { "bedrockModelConfig": { "modelId": "", // required; look up with `aws bedrock list-foundation-models` "maxTokens": 4096, "temperature": 0.7, "topP": 0.9, "apiFormat": "converse_stream", // converse_stream | responses | chat_completions "additionalParams": {} // optional: provider-specific params passed through unchanged }} // systemPrompt: list of content blocks (NOT a bare string) "systemPrompt": [ { "text": "You are a helpful assistant." } ] // tools: list of { type, name?, config }; type is a wire enum, config holds the matching variant "tools": [ { "type": "agentcore_code_interpreter", "config": { "agentCoreCodeInterpreter": {} } }, { "type": "agentcore_gateway", "config": { "agentCoreGateway": { "gatewayArn": "" } } }, { "type": "remote_mcp", "name": "my_mcp", "config": { "remoteMcp": { "url": "https://mcp.example.com/mcp" } } } ] // skills: list of HarnessSkill unions — variant keys are path | s3 | git | awsSkills "skills": [ { "path": "./skills/my-local-skill" }, { "s3": { "uri": "s3://my-bucket/skills/my-skill/" } }, { "git": { "url": "https://github.com/example/skills-repo", "path": "subdir/my-skill" } }, { "awsSkills": {} } ] // memory: HarnessMemoryConfiguration union — managedMemoryConfiguration | agentCoreMemoryConfiguration | disabled "memory": { "disabled": {} } // stateless "memory": { "agentCoreMemoryConfiguration": { "arn": "" } } // bring-your-own (arn required) // authorizerConfiguration: union — only member is customJWTAuthorizer (see Security Considerations) "authorizerConfiguration": { "customJWTAuthorizer": { "discoveryUrl": "https:///.well-known/openid-configuration", // required "allowedClients": [""], "allowedAudience": [""] // recommended: validates the JWT aud claim }} ``` Member names verified against the `Bedrock-AgentCore-Control` API model; confirm field names and provider-variant differences in the [CreateHarness API reference](https://docs.aws.amazon.com/bedrock-agentcore-control/latest/APIReference/API_CreateHarness.html). ## Per-Invocation Overrides `invoke-harness` can override the harness configuration for a single call **without redeploying** — this is what makes prototyping, A/B testing, and multi-tenancy simple. Overridable fields include `model`, `systemPrompt`, `tools`, `allowedTools`, `skills`, `maxIterations`, `maxTokens`, `timeoutSeconds`, and `actorId`. Refer to the [InvokeHarness API reference](https://docs.aws.amazon.com/bedrock-agentcore/latest/APIReference/API_InvokeHarness.html) for the authoritative list. Because these are caller-supplied, treat them as a trust boundary — see Security Considerations. ## Versions and Endpoints - Each update produces an **immutable version** (`list-harness-versions`). - **Named endpoints** point at a version (`create-harness-endpoint`, `get/update/delete/list-harness-endpoint(s)`). The endpoint name `DEFAULT` is reserved. - **Roll back instantly** by repointing an endpoint at an earlier version — no rebuild. ## Streaming Response Format `InvokeHarness` returns a stream of typed events: `messageStart`, `contentBlockStart`, `contentBlockDelta`, `contentBlockStop`, `messageStop`, and `metadata` (token usage and latency). Error conditions surface as `validationException`, `internalServerException`, or `runtimeClientError` events in the stream. `contentBlockDelta` carries a `delta` of `text`, `toolUse`, `toolResult`, or `reasoningContent`. `messageStop` carries a `stopReason` — common values include `end_turn`, `tool_use`, `max_tokens`, `max_iterations_exceeded`, `max_output_tokens_exceeded`, and `timeout_exceeded`, among others. The imperative shell operation `InvokeAgentRuntimeCommand` (`POST /runtimes//commands`) runs a single shell command in a session and streams `contentStart` / `contentDelta` (stdout, stderr) / `contentStop` (exit code, status). ## Security Considerations **Execution role and caller permissions:** - The execution role's trust policy MUST allow the AgentCore service principal `bedrock-agentcore.amazonaws.com` to assume it (`sts:AssumeRole`). Keep the role least-privilege: over-permissive execution roles are a common customer mistake — restrict Bedrock model ARNs to specific inference profiles rather than `*`, and grant only the AgentCore actions the harness actually uses. Use the [sample execution role policy](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-security.html#harness-execution-role-policy) as the starting point and scope it down. - You MUST scope the trust policy with confused-deputy conditions so only your own harnesses can assume the role — without them, any harness in any account could assume the role via the `bedrock-agentcore.amazonaws.com` principal: ```json { "Effect": "Allow", "Principal": { "Service": "bedrock-agentcore.amazonaws.com" }, "Action": "sts:AssumeRole", "Condition": { "StringEquals": { "aws:SourceAccount": "" }, "ArnLike": { "aws:SourceArn": "arn:aws:bedrock-agentcore:::harness/*" } } } ``` - Harness caller APIs require permissions on both the harness and the underlying runtime and memory resources. For example, `InvokeHarness` requires both `bedrock-agentcore:InvokeHarness` and `bedrock-agentcore:InvokeAgentRuntime`; `CreateHarness` requires `bedrock-agentcore:CreateHarness` plus `iam:PassRole` (for the execution role), `bedrock-agentcore:GetAgentRuntime`, `bedrock-agentcore:CreateAgentRuntime`, `bedrock-agentcore:GetMemory`, and `bedrock-agentcore:CreateMemory`. (Omitting `iam:PassRole` is the most common cause of a CreateHarness `AccessDenied`.) Refer to the [execution role policy](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-security.html) for the full per-API table. **Trust boundary — all `InvokeHarness` input is trusted:** - Any caller that passes the inbound auth gate (SigV4 or OAuth JWT) has access to the full microVM session and all configured tools. The harness does not sanitize input or filter content blocks. - If you expose the harness to users you do not fully trust, validate and sanitize messages — and strip caller-supplied override fields — in your application layer before calling `InvokeHarness`. - The `model` field (including `additionalParams`) is passed to the provider unchanged: a caller could redirect requests to another endpoint (LiteLLM `apiBase`), inject headers, or attempt role assumption. Strip or allowlist the `model` field for untrusted callers, and deny `sts:AssumeRole` on the execution role when role switching is not required. - `skills` are fetched per session (from AWS Skills, Git, S3, or the filesystem) and injected into the agent context as trusted input — including any scripts they carry. Review skill content and allowlist permitted sources. There is no IAM condition key to restrict the `skills` field per invocation, so if you forward caller-supplied input to `InvokeHarness` you MUST strip or allowlist the `skills` field in your application layer before the call — an invoke-time skill with the same name overrides the harness default. - Each invocation spins up a microVM session with tool access (including `shell`), so an unconstrained caller can drive significant cost or resource exhaustion. Put rate limiting in front of the harness (Amazon API Gateway or application-layer throttling), and set `maxIterations`, `maxTokens`, and `timeoutSeconds` explicitly as cost/abuse guardrails rather than relying on defaults. For a harness exposed to external or untrusted callers (especially on the OAuth JWT path), add AWS WAF in front of API Gateway as a defense-in-depth layer for request filtering, bot control, and IP-based rules. **Inbound authentication — SigV4 or OAuth JWT (one per harness):** - A harness accepts exactly one inbound auth method, decided by whether it has an `authorizerConfiguration`: **SigV4** (AWS IAM) when absent, **OAuth JWT** when present. The harness rejects a Bearer token on a SigV4 harness, and rejects SigV4 on an OAuth JWT harness — there is no mixed mode. - **Per-user identity for downstream tools requires OAuth JWT.** SigV4 does NOT propagate per-user identity into downstream tool calls, so AgentCore Identity Token Vault features (user-scoped tokens, on-behalf-of exchange) are only available on the OAuth JWT inbound path. - OAuth JWT config is `authorizerConfiguration.customJWTAuthorizer` with `discoveryUrl` (required), `allowedAudience`, and `allowedClients`. With the CLI use `--authorizer-type CUSTOM_JWT --discovery-url --allowed-clients `. (Do not use `oidcAuthorizerConfiguration` — that name appears in some examples but is not the API field.) - Set `allowedAudience` and/or `allowedClients` to constrain which tokens are accepted: `allowedAudience` validates the JWT `aud` claim and `allowedClients` validates the client ID, so a token issued for a different service or client cannot be replayed against the harness. A JWT authorizer with neither constraint accepts any valid token from the issuer. **Network and container:** - Use VPC mode (`environment.agentCoreRuntimeEnvironment.networkConfiguration`) to reach private resources. The harness pulls its container from Amazon ECR Public (`public.ecr.aws`) at the start of each session — ECR Public has no VPC endpoint, so a VPC-mode harness MUST have a NAT gateway with a route to an internet gateway, or sessions fail to start with image-pull timeouts. - Scope the VPC security groups to only the destinations the harness needs (model endpoints, tool hosts) using specific CIDR ranges or security-group references. Do NOT use `0.0.0.0/0` for inbound rules — when adding the NAT/internet-gateway route above, take care not to widen inbound access in the process. - Keep secrets out of `environmentVariables`; use AWS Secrets Manager or AgentCore Identity credential providers. **Encryption in transit and at rest:** - All remote connections MUST use TLS (HTTPS only) to prevent unencrypted traffic — remote MCP server URLs, any LiteLLM `apiBase` endpoint, and Git/S3 skill sources MUST be HTTPS. - Enable encryption at rest for filesystem and skill storage: use KMS-encrypted EFS access points, and encrypted S3 buckets for S3 Files mounts and for any skills fetched from S3. See the [Amazon S3 security best practices](https://docs.aws.amazon.com/AmazonS3/latest/userguide/security-best-practices.html) and [Amazon EFS security considerations](https://docs.aws.amazon.com/efs/latest/ug/security-considerations.html). **Logging and monitoring:** - Enable CloudTrail for all harness API calls (`CreateHarness`, `UpdateHarness`, `DeleteHarness`, `InvokeHarness`) to audit configuration changes and invocations. - Configure CloudWatch alarms for security-relevant signals — invocation-rate spikes and authorization failures. - Harness observability traces capture agent steps and tool inputs/outputs (especially `shell` and `file_operations`), which may contain PII or other sensitive data. Encrypt the CloudWatch Logs log groups with a customer-managed KMS key, set appropriate retention periods, and review what flows through tools before enabling verbose tracing. - Refer to the latest [AgentCore Harness security documentation](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-security.html) for current guidance. ## Additional Resources - [AgentCore Harness overview](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness.html) - [Get started with Harness](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-get-started.html) - [Harness vs. Runtime](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-vs-runtime.html) - [Harness skills (sources and configuration)](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-skills.html) - [Harness security and access controls](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-security.html) - [CreateHarness API reference](https://docs.aws.amazon.com/bedrock-agentcore-control/latest/APIReference/API_CreateHarness.html) - [InvokeHarness API reference](https://docs.aws.amazon.com/bedrock-agentcore/latest/APIReference/API_InvokeHarness.html)