If you have ever hand-written an agent loop — call the model, parse the tool call, run the tool, feed the result back, decide whether to stop — you know it is 200 lines of plumbing before the agent does anything interesting. AWS's AgentCore Harness, generally available since June 18, 2026, deletes that plumbing. You declare what the agent is and let the managed runtime run the loop.
Here is the entire mental model, and it fits in one sentence: CreateHarness defines the agent once; InvokeHarness runs a turn — and everything between the model and the tool call is AWS's problem, not yours. This walkthrough goes from an empty boto3 session to a running, tool-using agent, and flags the two places the defaults will surprise you.
0. Upgrade boto3 first#
invoke_harness was added recently. If your client is stale, the method simply won't exist and you'll get a confusing AttributeError. Do this before anything else:
pip install --upgrade boto3
1. Define the agent — create_harness#
A harness is a declaration: a default model, a system prompt, a tool list, and execution limits, stored once and reused. You are not writing behavior here — you are writing configuration.
import boto3
cp = boto3.client("bedrock-agentcore-control")
harness = cp.create_harness(
name="research-assistant",
instructions=(
"You are a research assistant. Use the code interpreter for any "
"calculation or data parsing. Cite every factual claim."
),
modelConfiguration={
"modelId": "anthropic.claude-opus-4-8",
"apiFormat": "messages",
},
tools=[
{"type": "agentcore_code_interpreter"},
{"type": "agentcore_browser"},
],
)
harness_id = harness["harnessId"]
Two things to notice. First, tools is a list of names, not implementations — agentcore_code_interpreter is a real isolated shell-plus-filesystem, and agentcore_browser is a real headless browser, both provisioned for you. Second, instructions is the harness default; you can override it per turn, which matters in the next step.
2. Run a turn — invoke_harness#
Now the payoff. One call runs the full loop — inference, tool selection, tool execution, observation, and the decision to continue or stop — and hands you the final answer.
dp = boto3.client("bedrock-agentcore")
resp = dp.invoke_harness(
harnessId=harness_id,
sessionId="user-42-session-1", # same id later = same memory
input={"text": "What's the 30-day moving average of these values? [file attached]"},
)
print(resp["output"]["text"])
That's it. The agent decided to open the code interpreter, ran the math in its own microVM, read the result back, and answered — and you wrote none of that control flow.
The sessionId is load-bearing: reuse it and AgentCore Memory carries context across turns and even across sessions, with no database of your own. Use a fresh id and you get a clean slate.
3. The two overrides worth knowing#
The harness default is a starting point, not a cage. InvokeHarness accepts a per-call model configuration and a per-call system prompt, so you can route a single harness across models by cost or task:
resp = dp.invoke_harness(
harnessId=harness_id,
sessionId="user-42-session-1",
modelConfiguration={"modelId": "openai.gpt-5", "apiFormat": "responses"},
systemPrompt="Answer in one paragraph. No tools this turn.",
input={"text": "Summarize the finding for a non-technical founder."},
)
This is the feature people miss: the harness is model-agnostic and switches providers mid-session without losing context — Bedrock, OpenAI, Gemini, or any LiteLLM-compatible provider. Run the expensive model for the hard reasoning turn, drop to a cheap one for the summary, same conversation.
4. Add a real tool without writing a tool#
To give the agent your own capabilities, you don't ship code into the loop — you point the harness at an MCP server or an AgentCore Gateway target:
tools=[
{"type": "agentcore_code_interpreter"},
{"type": "mcp", "url": "https://tools.yourco.com/mcp"},
]
The runtime speaks MCP as a transport, so the same server you already run for Claude Desktop or your own stack drops straight in. This is the connective tissue between the harness and everything else you've built — and it's why the harness is worth reaching for even if you're already invested in AgentCore's primitives.
What you gave up, and what you kept#
Be honest about the trade. You gave up the loop — you no longer see, or control, the exact sequence of model and tool calls. For most agents that's a gift; for a few (tight latency budgets, exotic control flow, a loop you need to unit-test line by line) it's a dealbreaker. That's the real decision, and it's worth thinking through before you commit.
What you kept is everything that makes an agent production-grade and is tedious to build: per-session microVM isolation, memory, identity, observability, and a tool surface that speaks MCP. The pricing reflects the split — there is no separate harness charge; you pay only for the primitives you actually use. For a team that wants an agent in production this week instead of a runtime project this quarter, two API calls is a very good trade.



