Skip to content

feat: add Claude Platform on AWS migration skill (.claude/skills/)#231

Open
wirjo wants to merge 4 commits into
aws-samples:mainfrom
wirjo:feat/claude-platform-on-aws-skill
Open

feat: add Claude Platform on AWS migration skill (.claude/skills/)#231
wirjo wants to merge 4 commits into
aws-samples:mainfrom
wirjo:feat/claude-platform-on-aws-skill

Conversation

@wirjo

@wirjo wirjo commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds a structured .claude/skills/ directory with a Claude Platform on AWS migration skill — a reference that coding agents (Claude Code, etc.) can use to automatically convert Anthropic 1P (direct API) code to CPOA.

What this adds

.claude/skills/claude-platform-on-aws/
├── SKILL.md                     # Core conversion guide + pitfalls
└── references/
    └── code-patterns.md         # Python, Node.js, curl before/after examples

Motivation

Developers migrating from api.anthropic.com to aws-external-anthropic.{region}.api.aws encounter several non-obvious differences:

  • Required anthropic-workspace-id header (not documented prominently)
  • Environment keys not supported on CPOA
  • AnthropicAWS class unavailable in Node.js SDK
  • WorkPoller requires environmentKey workaround
  • Quick Start API keys expire in ~15 min

This skill provides a structured checklist and code patterns covering both auth modes (SigV4 + API key), managed agents, and session lifecycle.

Why .claude/skills/?

This sets the foundation for a skills directory in this repository that:

  1. Any Claude Code session cloned into this repo automatically picks up
  2. Can be extended with additional skills for other patterns in this repo (e.g., agent SDK patterns, Bedrock migration, etc.)
  3. Others can borrow and install in their own coding environments

Relationship to existing content

The repo already has CPOA notebooks under claude-platform-on-aws/notebooks/. This skill complements those by providing an agent-consumable reference (SKILL.md format) for automated conversions rather than human-walkthrough notebooks.

Alternative repository

An alternative home for this type of contribution could be https://github.com/aws/agent-toolkit-for-aws/tree/main/skills/specialized-skills (the official AWS agent skills repository). Happy to cross-post or move there if preferred by maintainers.

Checklist

  • Working against latest main
  • No reformatting of existing code
  • MIT-0 license compatible
  • No secrets/credentials in code (all examples use placeholders)

@wirjo

wirjo commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

@rodzanto FYI, no rush

WORKSPACE_ID = "wrkspc_XXXXX"
API_KEY = "aws-external-anthropic-api-key-..."

client = Anthropic(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CPOA path is the dedicated AnthropicAWS client rather than Anthropic() with a manual base_url — it handles SigV4 signing, base-URL construction, and the anthropic-workspace-id header for you, so all the plumbing here collapses:

from anthropic import AnthropicAWS  # pip install -U "anthropic[aws]"

client = AnthropicAWS(api_key=API_KEY)  # region + workspace from AWS_REGION / ANTHROPIC_AWS_WORKSPACE_ID

Ref: Claude Platform on AWS → Making requests"Each SDK provides a platform-specific client class that handles SigV4 signing, region-based base URL construction, and the anthropic-workspace-id header."

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed ✅ — docs show AnthropicAWS (Python) and AnthropicAws (TypeScript from @anthropic-ai/aws-sdk) as the canonical CPOA clients. Will update all examples to use these instead of manual base_url/headers.

REGION = "us-west-2"
WORKSPACE_ID = "wrkspc_XXXXX"

client = Anthropic(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heads up — credentials={"type": "aws_iam", ...} isn't a real parameter, and a plain Anthropic() can't SigV4-sign at all. SigV4 mode is just the dedicated client with no key; it resolves creds via the standard AWS provider chain (env → ~/.aws → IRSA → ECS → IMDS):

from anthropic import AnthropicAWS
client = AnthropicAWS(aws_region=REGION)  # SigV4 via default AWS credential chain

Ref: CPOA → SigV4 authentication.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed ✅ — credentials={"type": "aws_iam", ...} is not a real SDK parameter. SigV4 mode is simply AnthropicAWS() with no API key, which resolves creds via the standard AWS provider chain (env → ~/.aws → IRSA → ECS → IMDS).

Will fix to:

from anthropic import AnthropicAWS
client = AnthropicAWS(aws_region=REGION)  # SigV4 via default AWS credential chain

Ref: SigV4 authentication — credential precedence shows apiKey → env creds → provider chain.

const workspaceId = "wrkspc_XXXXX";
const apiKey = "aws-external-anthropic-api-key-...";

const client = new Anthropic({

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as the Python case — the Node client is AnthropicAws from @anthropic-ai/aws-sdk (npm install @anthropic-ai/aws-sdk), which sets the workspace header and signing automatically:

import AnthropicAws from "@anthropic-ai/aws-sdk";
const client = new AnthropicAws({ apiKey });

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed ✅ — Node client is AnthropicAws from @anthropic-ai/aws-sdk (npm install @anthropic-ai/aws-sdk). Will update:

import AnthropicAws from "@anthropic-ai/aws-sdk";
const client = new AnthropicAws({ apiKey, awsRegion: "us-west-2" });

Ref: Install an SDK → TypeScriptnpm install @anthropic-ai/aws-sdk

const workspaceId = "wrkspc_XXXXX";

const client = new Anthropic({
credentials: { type: "aws_iam", region },

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same note as the Python SigV4 block — no credentials object; new AnthropicAws({ awsRegion: region }) signs via the default AWS chain.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed ✅ — same as Python: new AnthropicAws({ awsRegion: region }) signs via default AWS chain. No credentials object. Will fix.

});
```

**Note:** `AnthropicAWS` class is Python-only. In Node.js, always use the standard `Anthropic` class with `authToken` or `credentials`.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small correction — there is a dedicated Node client (AnthropicAws in @anthropic-ai/aws-sdk), so this note is the one thing to flip. The docs list platform clients for Python, TypeScript, Go, Java, C#, PHP, and Ruby. Ref: CPOA → Install an SDK.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed ✅ — this note is wrong and will be removed. There IS a dedicated Node client (AnthropicAws in @anthropic-ai/aws-sdk). Docs list platform clients for Python, TypeScript, Go, Java, C#, PHP, and Ruby.

Ref: Install an SDK


```bash
curl -X POST https://aws-external-anthropic.us-west-2.api.aws/v1/messages \
-H "Authorization: Bearer aws-external-anthropic-api-key-..." \

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CPOA API keys go on the x-api-key header rather than Authorization: Bearer — the short-term token generators also return tokens that "work with the x-api-key header." The anthropic-version + anthropic-workspace-id headers here are spot on. Ref: CPOA → API key authentication.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed ✅ — CPOA API keys use the x-api-key header, not Authorization: Bearer. The docs say short-term tokens also "work with the x-api-key header." Will fix the curl example:

-H "x-api-key: $ANTHROPIC_AWS_API_KEY"

Ref: Short-term API keys — "returns a time-limited token that works with the x-api-key header"


```python
from anthropic import Anthropic
from anthropic.helpers.beta.environments import WorkPoller

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from anthropic.helpers.beta.environments import WorkPoller isn't the documented path — the self-hosted worker helper is EnvironmentWorker from anthropic.lib.environments. Worth aligning the "Before" side with that so the contrast is accurate.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — will align the "Before" example with the documented EnvironmentWorker pattern rather than the invented WorkPoller path. The self-hosted docs show the SDK helpers .run() / .run_one() as the main interface.

Ref: Self-hosted sandboxes → Environment worker


# Direct polling (bypasses WorkPoller's environmentKey requirement)
while True:
work = client.beta.environments.work.poll(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The work.poll(environment_id=..., timeout=30) signature is invented — the documented Python surface is the EnvironmentWorker helper (.run() / .run_one()), with client.beta.environments.work.poller(...) as the mid-level API. (The underlying REST route GET /v1/environments/{id}/work/poll does exist, so the concept is right — it's just the SDK shape.) Ref: Self-hosted sandboxes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed — the SDK shape is EnvironmentWorker with .run() / .run_one(), and client.beta.environments.work.poller(...) as the mid-level API. The REST route GET /v1/environments/{id}/work/poll does exist (confirmed in IAM actions under ProcessEnvironmentWork), but the Python SDK wraps it differently.

Will rewrite the "After" pattern to use the documented SDK helper, and note the raw endpoint as a fallback for custom implementations.

Ref: IAM actions → ProcessEnvironmentWork confirms GET /v1/environments/{id}/work/poll is a real route.

3. [ ] API key has `aws-external-anthropic-api-key-` prefix (if using key mode)
4. [ ] No references to `sk-ant-api03-` or `sk-ant-env01-` keys
5. [ ] No `AnthropicAWS` imports in Node.js code
6. [ ] IAM role has `AnthropicSelfHostedEnvironmentAccess` policy (if SigV4 mode)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One correctness catch: AnthropicSelfHostedEnvironmentAccess is the self-hosted worker policy — its grant is GetEnvironment, ProcessEnvironmentWork, GetSession, UpdateSession, GetSkill, CallWithBearerToken, with no CreateInference. A role with only this policy 403s on /v1/messages. For plain SigV4 inference the minimal managed policy is AnthropicInferenceAccess. Ref: IAM actions → Managed policies.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed ✅ — important distinction. Per the IAM docs:

  • AnthropicSelfHostedEnvironmentAccess = worker policy (GetEnvironment, ProcessEnvironmentWork, GetSession, UpdateSession, GetSkill, CallWithBearerToken) — no CreateInference
  • AnthropicInferenceAccess = needed for plain SigV4 calls to /v1/messages

Will split the IAM guidance: inference needs AnthropicInferenceAccess; self-hosted workers need AnthropicSelfHostedEnvironmentAccess.

Ref: IAM actions → Managed policies


1. [ ] Base URL uses `aws-external-anthropic.{region}.api.aws`
2. [ ] `anthropic-workspace-id` header present on all requests
3. [ ] API key has `aws-external-anthropic-api-key-` prefix (if using key mode)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we cite a source for the aws-external-anthropic-api-key- prefix? The CPOA docs describe key generation but don't state a fixed prefix, so I'd soften this to avoid an agent asserting on a shape that may not hold. (Same prefix claim appears in SKILL.md lines 15 & 30.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point — the docs describe key generation via the AWS Console but do not state a guaranteed aws-external-anthropic-api-key- prefix. This was observed empirically but should not be treated as a stable contract.

Will soften to: "API keys are generated in the AWS Console under Claude Platform on AWS → API keys" without asserting a specific prefix format. Will remove the prefix-based validation checklist item too.

| Required headers | `x-api-key`, `anthropic-version` | `anthropic-workspace-id` (on all requests) |
| Billing | Anthropic billing | AWS Marketplace |
| Inference | Anthropic infra | Anthropic infra (same — CPOA only changes auth/billing layer) |
| Env keys (agents) | `sk-ant-env01-...` | NOT used — use IAM role or CPOA API key |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit too absolute — self-hosted sandbox agents are supported on CPOA and do authenticate with ANTHROPIC_ENVIRONMENT_KEY (the AnthropicSelfHostedEnvironmentAccess policy + ProcessEnvironmentWork action exist precisely for that worker). Might scope this to "managed/cloud agents" so an agent following the rule doesn't strip a required credential. (Also drives pitfall #3 at line 86 and step 5 at line 59.) Ref: CPOA → Claude Managed Agents.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed ✅ — this was overly broad. The self-hosted sandboxes docs explicitly state:

"On Claude Platform on AWS, the worker authenticates with AWS IAM (SigV4) or an API key generated in the AWS Console, not an environment key. Environment keys generated in the Claude Console don't work with the Claude Platform on AWS endpoint."

So: environment keys from the Claude Console don't work on CPOA, but the worker still authenticates — just via IAM/API key instead. The table row and pitfall should say "Environment keys from Claude Console not used on CPOA — workers authenticate via IAM or CPOA API key" rather than implying the concept of worker auth doesn't exist.

Ref: Self-hosted sandboxes → Before you begin (CPOA note)

CPOA supports two auth modes:

- **IAM SigV4** (preferred for production): Uses execution role credentials. No secrets to manage.
- **API key** (dev/quick-start): CPOA-issued key with `aws-external-anthropic-api-key-` prefix. Short-lived (~15 min from Quick Start, longer for production keys).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ~15-min figure doesn't match the docs — short-term tokens "default to 12 hours and are capped at the lesser of your requested duration, your AWS credentials' expiry, and 12 hours." Worth correcting here and in pitfall #4 (line 87). Ref: CPOA → Short-term API keys.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed ✅ — the docs clearly state:

"Token lifetime defaults to 12 hours and is capped at the lesser of your requested duration, your AWS credentials' expiry, and 12 hours."

The ~15 min figure was likely confused with something else (perhaps an STS session token from a very short-lived assume-role). Will correct to 12 hours default, capped at min(requested, AWS creds expiry, 12h).

Ref: Short-term API keys

@wirjo

wirjo commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

All review comments from @scouturier have been addressed in the latest commit (a55e37d). Summary of changes:

  • Replaced manual base_url/defaultHeaders with the documented AnthropicAWS (Python) and AnthropicAws (TypeScript) platform clients
  • Fixed SigV4 auth to use the standard AWS credential provider chain (no invented credentials parameter)
  • Corrected auth header to x-api-key (not Authorization: Bearer)
  • Fixed short-term token lifetime to 12 hours (not 15 min)
  • Split IAM guidance: AnthropicInferenceAccess for inference vs AnthropicSelfHostedEnvironmentAccess for workers
  • Replaced invented WorkPoller with documented EnvironmentWorker SDK helper
  • Removed undocumented API key prefix assertion
  • Added SigV4 curl example and short-term token generation patterns

All corrections are backed by the official docs:

@scouturier @rodzanto — this should be ready for merge when you get a chance. Let us know if anything else needs attention. 🙏

@scouturier

Copy link
Copy Markdown
Collaborator

Thanks @wirjo — re-verified the earlier review points and they're resolved. Two things before merge:

1. Rebase on main (blocks merge). The failing build check isn't from this PR — the branch is well behind main and the failure is a known dependency/CI issue since fixed there. A rebase should clear it.

2. Model ID nit (non-blocking). Examples use claude-sonnet-4-6 — valid but previous-gen. The official CPOA docs use claude-sonnet-5. Since this skill is a template agents copy, worth swapping claude-sonnet-4-6claude-sonnet-5 across code-patterns.md and SKILL.md.

Everything else checks out against the live docs (token generators, clients, IAM, curl SigV4). LGTM once those two are in. 🙏

wirjo added 4 commits July 8, 2026 20:46
Adds a structured .claude/skills/ directory with a migration skill for
converting Anthropic 1P (direct API) code to Claude Platform on AWS (CPOA).

Files:
- .claude/skills/claude-platform-on-aws/SKILL.md
- .claude/skills/claude-platform-on-aws/references/code-patterns.md

Covers:
- 1P → CPOA conversion for messages API, managed agents, and
  self-hosted environments
- Both auth modes: SigV4 (IAM) and API key
- Python, Node.js, and curl code patterns (before/after)
- Common pitfalls (workspace header, environment keys, WorkPoller)
- Validation checklist
Changes based on official CPOA documentation:

- Use AnthropicAWS (Python) and AnthropicAws (TypeScript) platform clients
  instead of manual base_url/defaultHeaders configuration
- Remove invented credentials={"type": "aws_iam"} parameter; SigV4 is
  simply the platform client with no API key (uses AWS provider chain)
- Fix auth header: CPOA uses x-api-key, not Authorization: Bearer
- Correct token lifetime: 12 hours default (not 15 min), capped at
  min(requested, AWS creds expiry, 12h)
- Split IAM guidance: AnthropicInferenceAccess for /v1/messages vs
  AnthropicSelfHostedEnvironmentAccess for workers (no CreateInference)
- Clarify environment keys: Console-generated env keys don't work on
  CPOA; workers auth via IAM or CPOA API key instead
- Remove undocumented API key prefix assertion
- Replace WorkPoller with documented EnvironmentWorker SDK helper
- Add SigV4 curl example with --aws-sigv4 flag
- Add short-term token generation examples (Python + TypeScript)
- Add outbound web identity federation prerequisite
- Note: region is required with no fallback (unlike AnthropicBedrock)

Refs:
- https://platform.claude.com/docs/en/build-with-claude/claude-platform-on-aws
- https://platform.claude.com/docs/en/api/claude-platform-on-aws-iam-actions
- https://platform.claude.com/docs/en/managed-agents/self-hosted-sandboxes
Additional fixes from docs validation:
- Use actual EnvironmentWorker import: from anthropic.lib.environments
- Fix environment key prefix: sk-ant-oat01-... (not sk-ant-env01-...)
- Replace invented client.beta.environments.worker.run() with actual
  EnvironmentWorker(client, ...).run() pattern from official docs
- Show proper async pattern matching SDK docs examples
- Fix validation checklist to reference correct key prefix

Refs:
- https://platform.claude.com/docs/en/managed-agents/self-hosted-sandboxes#sdk-helpers
- SDK code: from anthropic.lib.environments import EnvironmentWorker
Swap claude-sonnet-4-6 → claude-sonnet-5 across all examples to match
the current official CPOA documentation.
@wirjo
wirjo force-pushed the feat/claude-platform-on-aws-skill branch from 06f4f45 to 14a6b0d Compare July 8, 2026 20:47
@wirjo

wirjo commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

@scouturier Rebased on latest main (should clear the CI failure) and updated all model IDs to claude-sonnet-5 per your suggestion. Ready for another look when you get a chance 🙏

# On CPOA: no environment key needed — authenticate via IAM
# Requires AnthropicSelfHostedEnvironmentAccess IAM policy
environment_id = os.environ["ANTHROPIC_ENVIRONMENT_ID"]
async with AsyncAnthropic() as client: # SigV4 via AWS credential chain

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing that slipped past the earlier round: the "Worker with IAM Auth" block still uses bare AsyncAnthropic(), and the comment # SigV4 via AWS credential chain is inaccurate — base AsyncAnthropic targets api.anthropic.com and can't SigV4-sign (same defect we fixed for the inference examples at :49/:109). We corrected the prose for this in SKILL.md:19 but not this code block.

Two things to reconcile:

  1. Use the AWS client, not the base one — the CPOA worker should be constructed via the platform client that signs and sets the workspace header.
  2. If a SigV4-authenticated EnvironmentWorker isn't actually a documented pattern (the self-hosted docs only show the async worker with auth_token=<key>), mark this variant as unverified rather than shipping it as working. The API-key worker block just below is fine as-is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants