Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 13 additions & 19 deletions plugins/aws-agents/skills/agents-build/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ description: >
"A2A", "A2A auth", "orchestrator not delegating", "specialist not
called", "migrate Bedrock Agent", "migration issue", "change model",
"browser tool", "code
interpreter", "delete agent", "tear down", "agentcore remove",
interpreter", "delete agent", "agentcore remove",
"cross-account memory",
"add payments capability to my agent", "wire payments plugin",
"integrate x402 payments with the agent I'm building",
Expand All @@ -24,7 +24,7 @@ metadata:
type: skill
version: "1.0.0"
author: aws-agentcore
requires-cli: ">=0.9.0"
requires-cli: ">=1.0.0"
---

# build
Expand All @@ -41,13 +41,13 @@ Add capabilities to your AgentCore agent project.
- Adding the Browser tool so the agent can navigate websites
- Adding the Code Interpreter so the agent can execute code in a sandbox
- Adding AgentCore Payments so the agent can pay for x402- or MPP-protected APIs, tools, or content
- Removing resources from your project or tearing down a deployment
- Removing individual resources from your project

Do NOT use for:

- Connecting to external tools/APIs via Gateway (OpenAPI specs, Lambda, MCP servers, credentials, policies) → use `agents-connect`
- Scaffolding a new project → use `agents-get-started`
- Deploying → use `agents-deploy`
- Deploying or tearing down a deployment → use `agents-deploy`

## Input

Expand All @@ -61,13 +61,13 @@ Do NOT use for:

### Step 0: Verify CLI version

Run `agentcore --version`. This skill requires v0.9.0 or later.
Run `agentcore --version`. This skill requires v1.0.0 or later.

If older: "Run `agentcore update` to get the latest version."
If older, offer `npm install -g @aws/agentcore@latest` (or `agentcore update`), then verify the new version. For a v0 project (`"version": 1`), commit current work, run `agentcore migrate` to review its plan, and apply the reviewed plan with `agentcore migrate --yes` before following v1 instructions.

### Step 1: Read project context

Read `agentcore/agentcore.json` to understand the current project — framework, existing resources, agent configuration.
Read `agentcore/agentcore.json` to understand the current project — its `runtimes`, `harnesses`, `memories`, and other resources. For a Harness, read `app/<harness>/harness.yaml` too.

If `agentcore/agentcore.json` is not found:

Expand Down Expand Up @@ -99,7 +99,8 @@ Based on the developer's prompt and `$ARGUMENTS`, load the appropriate reference
| Browser tool, web navigation, form filling, scraping, Nova Act, Playwright, live view | [`references/browser.md`](references/browser.md) |
| Code Interpreter, execute code, sandbox, run Python/JS/TS, data analysis in agent, pandas | [`references/code-interpreter.md`](references/code-interpreter.md) |
| Payments, pay for x402 or MPP content, 402 Payment Required, Machine Payments Protocol, WWW-Authenticate: Payment, microtransactions, paid API/tool, payment manager/connector | [`references/payments.md`](references/payments.md) |
| Delete agent, remove resource, tear down, clean up, destroy, start fresh | [`references/teardown.md`](references/teardown.md) |
| Remove one resource from the project | [`references/teardown.md`](references/teardown.md) |
| Tear down a deployed target, clean up, destroy, start fresh | `agents-deploy` teardown workflow |
| Change model, switch model, use Haiku/Sonnet/Nova, different model | Inline — see "Changing the model" below |

If the developer asks about the difference between local dev and deployed (e.g., "why does my memory work after deploy but not locally?"), load [`references/local-vs-deployed.md`](references/local-vs-deployed.md) alongside the specific workflow reference.
Expand All @@ -110,9 +111,9 @@ If the intent is ambiguous, ask the developer which capability they want to add.

### Changing the model

The model is configured in `app/<AgentName>/model/load.py` (scaffolded by `agentcore create`). To change it:
For a Strands Runtime, the model is configured in `app/<RuntimeName>/model/load.py` (the `agent-python-strands` template uses `app/agent/model/load.py`). To change it:

1. Open `app/<AgentName>/model/load.py`
1. Open `app/<RuntimeName>/model/load.py`
2. Change the `model_id` parameter in the `BedrockModel()` constructor

```python
Expand All @@ -135,18 +136,11 @@ After changing the model:
- If using `agents-harden`, update the IAM policy to scope to the new model ARN
- Run `agentcore dev` to test locally, then `agentcore deploy` to update the deployed agent

No `agentcore.json` change is needed — the model is configured in code, not in the project config.
For a Bedrock Harness, edit `app/<HarnessName>/harness.yaml` instead, under `model.bedrockModelConfig.modelId`, then run `agentcore build` and `agentcore deploy`. Other Harness model providers use their corresponding model configuration block. A Runtime model is configured in code.

### Pre-flight: validate any `--name` before generating the CLI command

Whichever reference you load, most end up producing an `agentcore add <resource> --name <something>` command. The CLI fails **late** on invalid names — you'll see the error after walking through prompts, not before running the command. Validate up front:

| Resource | Max chars | Allowed | Starts with |
|---|---|---|---|
| Agent (`add agent`) | 48 | alphanumeric + `_` | letter |
| Memory, gateway, gateway-target, credential, evaluator, online-eval, policy, policy-engine, payment-manager, payment-connector | 48 | alphanumeric + `_` | letter |

Count the characters before constructing the command. If the name is over the limit or contains hyphens, dots, or spaces, push back: "`<name>` is N characters / uses `-`, which the CLI rejects. How about `<suggestion>`?" Never run the command with an invalid name hoping the CLI message will be clear.
Check `agentcore add <resource> --help` and the v1 schema for the selected resource's name rules. Resource names differ from project names; do not apply one rule to every resource.

Note: `agentcore create --name` (the project name) has a **stricter 23-char limit** and does not allow underscores. That's covered in `agents-get-started`; if you see the developer re-running create, flag the 23-char limit specifically.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ Install: `pip install bedrock-agentcore nova-act boto3`

The `browser_session` context manager handles start/stop. Do not leak sessions — always use the context manager or wrap raw `BrowserClient` calls in try/finally.

**Credential handling:** the Nova Act API key is a secret. If this is running inside an AgentCore Runtime agent, register it as a credential (`agentcore add credential --name NovaAct --api-key ...`) and retrieve it with `@requires_api_key(provider_name="NovaAct")`. Do not put it in runtime env vars. See `agents-connect` Path D.
**Credential handling:** the Nova Act API key is a secret. If this is running inside an AgentCore Runtime agent, register it as a credential (`agentcore add credentials api-key --name NovaAct --api-key file://<secret-file>`) and retrieve it with `@requires_api_key(provider_name="NovaAct")`. Do not put it in runtime env vars. See `agents-connect` Path D.

## Path C — Playwright for scripted automation

Expand Down Expand Up @@ -230,5 +230,5 @@ If your agent runs in VPC mode, the Browser tool can also run in VPC. See [`vpc.

- Browser sessions are always wrapped in a context manager or try/finally — never leaked
- IAM is scoped to `browser/*` in the account, not `Resource: "*"`
- Nova Act API keys and other secrets use `agentcore add credential` + `@requires_api_key`, not env vars
- Nova Act API keys and other secrets use `agentcore add credentials api-key` + `@requires_api_key`, not env vars
- The code handles the case where the agent runs outside AgentCore Runtime (no `.env.local`, no credential provider) — typically by reading a local secret for development and the credential provider for production
57 changes: 25 additions & 32 deletions plugins/aws-agents/skills/agents-build/references/integrate.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,43 +30,27 @@ Do NOT use for:

### Step 1: Check deployment status

Read `agentcore/agentcore.json` to get the agent name. Then check if it's deployed:
Read `agentcore/agentcore.json` for the Runtime name and `agentcore/aws-targets.json` for the intended deployment target. Keep that target and its region for the SDK example below. Then check whether that Runtime is deployed there:

```bash
agentcore status --type agent
agentcore status --target <target> --json
```

**If not deployed:** "Your agent needs to be deployed before you can call it from an app. Run `agentcore deploy` first, or use the `agents-deploy` skill for guidance."
**If not deployed:** "Your agent needs to be deployed to `<target>` before you can call it from an app. Run `agentcore deploy --target <target>` first, or use the `agents-deploy` skill for guidance."

Do not proceed until the agent is deployed.

### Step 2: Get the agent endpoint
### Step 2: Get the Runtime ARN and target

```bash
agentcore fetch access --name <AgentName> --type agent
agentcore status --target <target> --json
```

This returns:

- **Agent Runtime ARN** — needed for SDK invocation
- **Endpoint URL** — for direct HTTPS calls
- **Auth configuration** — what auth method is configured

Note the auth type from the output. It determines how the client app authenticates.
Select the deployed `runtime` entry by name in the `resources` array. Its `arn` is the identifier for SDK invocation. Use the selected target's region, including for `agentcore runtime get --id <runtime-id> --region <target-region> --json` when further service details are needed. The old `agentcore fetch access` command is absent in v1.

### Step 3: Determine auth method

Read the agent's `authorizerType` field from `agentcore/agentcore.json` (it's a top-level field on the runtime entry; JWT details live in the separate `authorizerConfiguration` object on the same runtime).

| Auth type | How the client authenticates | Best for |
|---|---|---|
| **None** (default) | IAM SigV4 signing on the request | Backend services with AWS credentials |
| **AWS_IAM** | IAM SigV4 signing on the request | Backend services, Lambda-to-agent calls |
| **CUSTOM_JWT** | Bearer token in Authorization header | Web/mobile apps with an identity provider |

**If no authorizer is configured:** The agent uses IAM auth by default. The calling identity needs `bedrock-agentcore:InvokeAgentRuntime` permission.

**If CUSTOM_JWT:** The client sends a JWT from the configured identity provider. The agent validates it against the discovery URL, allowed audience, and allowed clients configured during setup.
Read the Runtime entry's `authorizerType` and `authorizerConfiguration` in `agentcore/agentcore.json`. An unset authorizer uses IAM. AWS_IAM callers sign requests with SigV4 and need `bedrock-agentcore:InvokeAgentRuntime`; CUSTOM_JWT callers present a JWT from the configured issuer. Keep bearer tokens out of source code and logs. For a quick CLI verification, use `agentcore invoke --runtime <name> --payload '{"prompt":"Hello"}' --target <target>` and add `--bearer-token` only for a JWT Runtime.

### Step 4: Generate client code

Expand All @@ -88,7 +72,7 @@ try:
payload=json.dumps({
"prompt": "Hello, what can you do?"
}).encode(),
runtimeSessionId="session-123", # reuse for multi-turn conversations
runtimeSessionId="550e8400-e29b-41d4-a716-446655440000", # reuse for multi-turn conversations
)

# Handle streaming response — response["response"] is a StreamingBody
Expand Down Expand Up @@ -121,15 +105,22 @@ except ClientError as e:

```python
import requests
from urllib.parse import quote

AGENT_URL = "<ENDPOINT_URL>"
AGENT_URL = (
"https://bedrock-agentcore.<REGION>.amazonaws.com/runtimes/"
+ quote("<AGENT_RUNTIME_ARN>", safe="")
+ "/invocations"
)
JWT_TOKEN = "<TOKEN_FROM_YOUR_IDP>"
session_id = "550e8400-e29b-41d4-a716-446655440000" # reuse for later turns

response = requests.post(
AGENT_URL,
headers={
"Authorization": f"Bearer {JWT_TOKEN}",
"Content-Type": "application/json",
"X-Amzn-Bedrock-AgentCore-Runtime-Session-Id": session_id,
},
json={"prompt": "Hello, what can you do?"},
stream=True,
Expand All @@ -156,7 +147,7 @@ const response = await client.send(
payload: new TextEncoder().encode(
JSON.stringify({ prompt: "Hello, what can you do?" })
),
runtimeSessionId: "session-123",
runtimeSessionId: "550e8400-e29b-41d4-a716-446655440000",
})
);

Expand All @@ -170,14 +161,16 @@ for await (const chunk of response.response) {
#### JavaScript/TypeScript (fetch) — JWT auth

```typescript
const AGENT_URL = "<ENDPOINT_URL>";
const AGENT_URL = `https://bedrock-agentcore.<REGION>.amazonaws.com/runtimes/${encodeURIComponent("<AGENT_RUNTIME_ARN>")}/invocations`;
const JWT_TOKEN = "<TOKEN_FROM_YOUR_IDP>";
const sessionId = "550e8400-e29b-41d4-a716-446655440000"; // reuse for later turns

const response = await fetch(AGENT_URL, {
method: "POST",
headers: {
Authorization: `Bearer ${JWT_TOKEN}`,
"Content-Type": "application/json",
"X-Amzn-Bedrock-AgentCore-Runtime-Session-Id": sessionId,
},
body: JSON.stringify({ prompt: "Hello, what can you do?" }),
});
Expand All @@ -195,10 +188,10 @@ while (true) {

Explain how sessions work:

- **`runtimeSessionId`** — pass the same value across multiple calls to maintain conversation context
- **`runtimeSessionId`** — pass the same value across calls so the Runtime can route them to the same session
- Generate a unique session ID per user conversation (e.g., UUID)
- Sessions are server-side — the agent remembers the conversation history for that session ID
- If you omit the session ID, each call is stateless (no conversation memory)
- The session ID alone does not save conversation history. Check that the deployed agent keeps history in its process or uses a session or memory manager. The CLI's Strands template caches agents by session ID and wires a memory manager when configured; custom Runtime code must implement its own history handling.
- If you omit the session ID, the service assigns one for that invocation, so a later call cannot deliberately resume it.

```python
import uuid
Expand Down Expand Up @@ -305,7 +298,7 @@ response = agentcore.invoke_agent_runtime(
agentRuntimeArn="arn:aws:bedrock-agentcore:<REGION>:<AGENT_ACCOUNT_ID>:runtime/<RUNTIME_NAME>",
qualifier="DEFAULT",
payload=json.dumps({"prompt": "hello"}).encode(),
runtimeSessionId="session-123",
runtimeSessionId="550e8400-e29b-41d4-a716-446655440000",
)
```

Expand Down Expand Up @@ -342,7 +335,7 @@ client = boto3.client("bedrock-agentcore", region_name="<REGION>")
response = client.invoke_agent_runtime_command(
agentRuntimeArn="<AGENT_RUNTIME_ARN>",
qualifier="DEFAULT",
runtimeSessionId="session-123", # must be an existing session
runtimeSessionId="550e8400-e29b-41d4-a716-446655440000", # must be an existing session
command="ls -la /tmp && cat /tmp/status.json",
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ AgentCore has a local dev server (`agentcore dev`) and a deployed runtime. They
| Framework model calls | ✅ if Bedrock creds are available | ✅ |
| Python/JS function tools (framework-native) | ✅ | ✅ |
| Credentials (`@requires_api_key`, `@requires_access_token`) | ✅ from `agentcore/.env.local` | ✅ from Secrets Manager |
| Memory | ❌ env var not set locally | ✅ `MEMORY_<NAME>_ID` injected |
| Gateway | ❌ env var not set locally | ✅ `AGENTCORE_GATEWAY_<NAME>_URL` injected |
| Memory | Not injected locally; can use a deployed Memory if its ID and AWS access are supplied | ✅ `MEMORY_<NAME>_ID` injected |
| Gateway | Not injected locally; can call a deployed Gateway if its URL and auth are supplied | ✅ `AGENTCORE_GATEWAY_<NAME>_URL` injected |
| Cedar policy evaluation | ❌ policies only enforced at gateway | ✅ |
| Traces (X-Ray) | ✅ `agentcore dev` emits OTEL to CloudWatch by default; disable with `--no-traces` | ✅ auto-enabled |
| CloudWatch logs | ✅ via ADOT / OTEL wiring (same path as traces) | ✅ if using `logging` module + OTEL |
| **Evaluator *definition*** (`agentcore add evaluator`, writing the instructions/code) | ✅ — writes to `agentcore.json`; custom code is unit-testable locally | ✅ |
| **`agentcore run eval`** (on-demand eval over traces) | ✅ — operates on CloudWatch spans; local-dev spans land there if OTEL is on (default) | ✅ |
| **`agentcore eval ondemand`** (on-demand eval over traces) | ✅ — operates on CloudWatch spans; local-dev spans land there if OTEL is on (default) | ✅ |
| **`Evaluate` API with hand-constructed spans** (boto3) | ✅ — no runtime needed at all; submit `SessionSpans` directly | ✅ |
| **Dataset runner** (`OnDemandEvaluationDatasetRunner`) | ❌ invokes an AgentCore Runtime agent in its pipeline | ✅ |
| **Online eval monitoring** (`agentcore add online-eval`) | ❌ ingests traces continuously from deployed runtime | ✅ |
Expand Down Expand Up @@ -54,13 +54,13 @@ else:

## Testing workflow

Because memory, gateway, and policies don't work locally, the realistic test loop is:
To test memory, Gateway, and policies with the deployed agent's identity, use this loop:

1. **Local:** `agentcore dev` to verify the agent's code structure, framework wiring, system prompt, and any in-code logic
2. **Deploy to a staging target:** `agentcore deploy --target staging` to test with real memory, gateway, and policies
2. **Deploy to a staging target:** define `staging` with its account and region in `agentcore/aws-targets.json` first, then run `agentcore deploy --target staging` to test with real memory, gateway, and policies. Named targets are not created automatically.
3. **Production:** only after staging validation

Don't expect `agentcore dev` to reproduce a production failure involving memory recall, gateway tool calls, or policy denials — those require a deployed environment.
`agentcore dev` can call deployed Memory or Gateway resources when you configure them explicitly, but it runs under local credentials and does not reproduce the deployed Runtime's IAM identity.

## Common "works locally, fails deployed" causes

Expand Down
Loading
Loading