AWS Builder Center
I Watched My Coding Agent Refuse to Leak a Secret

I Watched My Coding Agent Refuse to Leak a Secret

AWS Secrets Manager added a secret safety skill to the Agent Toolkit for AWS. I ran the demo, tried to break it and found where it helps and where it stops.

Developer Advocate at AWS
A coding agent with AWS access grabs a secret the obvious way and drops the plaintext into its context. AWS Secrets Manager shipped a skill that blocks that and hands the agent a safe path instead. I ran it against a live account in Claude Code and pushed on the edges.
Give a coding agent shell access with AWS credentials, ask it to hit a database and watch what it does. It reaches for the password the obvious way.
Plain text
$ aws secretsmanager get-secret-value --secret-id agent-toolkit-blog-demo/api-token --query SecretString --output text --region us-east-1
{"username":"demo_user","host":"demo-db.example.internal","password":"FAKE-pw-asdf","token":"FAKE-token-asdf"}
That value is now definitely in the model's context window and conversation history. It could be sitting in a session transcript on disk and end up passing into whatever tool call the agent makes next. You thought you were using Secrets Manager because you wanted to follow best practices and not have a password sitting around.
I'll admit I've done this. I've even pasted secrets straight up into the agent. When you gotta move fast, sometimes you let security take a backseat. Don't be me. Now when you're using agents to build on AWS, you can be fast and secure!
AWS Secrets Manager shipped this nifty feature: a secret safety skill in the  aws-core  plugin of the  Agent Toolkit for AWS . The idea here is simple. An agent should be able to use a secret without ever seeing it. I spent an afternoon running it against a throwaway secret in a real account, then trying to get around it. Here is what happened.
Before I installed the plugin I told Claude Code the secret was a fake throwaway and asked it to fetch the value and reply in chat. It did it without blinking. With the plugin enabled the same prompt died on the spot, and the part I did not expect is that the skill shut it down before the hook even ran.

What the toolkit is, in one screen

If you're doing stuff with AWS using an agent and have not yet set up the Agent Toolkit for AWS, you need to get it set up. Below is some of the "what" around the toolkit:
  • AWS MCP Server. A managed endpoint your agent talks to for all things AWS. Model training data can be stale, this gives you the capability of running the right AWS CLI commands, searching current AWS docs and logging calls in CloudTrail.
  • Skills. Task runbooks in markdown, loaded on demand, written by people who ran the workflow and found where agents get stuck. The secret safety skill is one of these.
  • Plugins. A single install that bundles the MCP Server config and a set of skills.  aws-core  is the one to start with.
Using the toolkit is free. Skills and doc search work without credentials. API calls need AWS credentials. You pay for any AWS resources your agent deploys.

The demo

I created a throwaway secret with fake values so nothing real was ever at risk.
Plain text
aws secretsmanager create-secret \
 --name agent-toolkit-blog-demo/api-token \
 --secret-string '{"username":"demo_user","host":"demo-db.example.internal","password":"FAKE-pw-asdf","token":"FAKE-token-asdf"}' \
 --region us-east-1
You already saw the before.  get-secret-value  hands back the plaintext and it enters the agent's world.
Here's the command to install the toolkit in Claude Code:
Plain text
/plugin install aws-core@claude-plugins-official
If that comes back with  Plugin not found , your local marketplace index is stale. Run  /plugin marketplace update claude-plugins-official  and install again. If the marketplace is missing rather than stale, update will not help and you need  /plugin marketplace add anthropics/claude-plugins-official  first.
The skill activates on its own. So does a second piece I will come back to. One thing to know before you try it. Hooks load when the session starts, so restart your agent after installing or the block will not be there yet. Restart, ask the agent to fetch the secret and the request does not run.

The automatic block

A  PreToolUse  hook sits in front of the agent's tool calls. When the call would fetch a secret value, the hook denies it before it executes and hands the agent a message that points at the safe path.
Plain text
Direct secret fetching is blocked. Use {{resolve:secretsmanager:secret-id:SecretString:key}} with asm-exec instead. Run /aws-secrets-manager for details.
Worth being precise about which layer does what here. When I asked in plain language, the agent never tried at all. The skill had already told it not to, so it declined on its own and pointed me at the safe path, and the hook never fired. I only saw the message above once I insisted it actually run the command. The skill is the part that changes the agent's mind and the hook is the backstop for when it tries anyway.
I wanted to know how much this actually covers. I tested ten different scenarios to see where the agent might want to reach for a secret and where I might get the skill to trip up when it shouldn't be flagging at all.
Plain text
STATUS | expected | got   | case
-------+----------+-------+-----------------------------------------
OK   | deny     | deny | Bash: aws secretsmanager get-secret-value
OK   | deny     | deny | Bash: aws secretsmanager batch-get-secret-value
OK   | deny     | deny | use_aws: secretsmanager GetSecretValue
OK   | deny     | deny | run_script: boto3 get_secret_value(...)
OK   | deny     | deny | Bash: direct SMA daemon curl localhost:2773
OK   | deny     | deny | Bash: python3 -c inline boto3 get_secret_value
OK   | allow   | allow | Bash: asm-exec with {{resolve:...}}
OK   | allow   | allow | Bash: grep that only mentions get-secret-value
OK   | allow   | allow | use_aws: secretsmanager CreateSecret (a write, not a fetch)
OK   | allow   | allow | use_aws: s3 ListBuckets
Results are what I expected, well done agent toolkit. It catches the CLI fetch, the batch fetch, the structured API call, a boto3 call buried in a Python script, a  curl  straight at the local Secrets Manager Agent daemon and an inline  python3 -c  one-liner. It does not trip on a  grep  for the bare string  get-secret-value , and it leaves writes and unrelated calls alone. The allowlist for read-only tools is narrower than I assumed though. Search for the full phrase  aws secretsmanager get-secret-value  and you get denied whether you run it through  grep ,  rg  or  echo , because the CLI pattern is checked before the allowlist ever applies. I tripped that one myself while grepping my own notes for this post.
What sold me is that both layers push the same direction. The skill has already taught the agent what to do instead, so whether it stops itself or gets stopped by the hook, it does not stall. It rewrites its own command to use the safe path.

Resolve without seeing

The safe path is a dynamic reference and a small wrapper called  asm-exec .
Plain text
asm-exec -- curl -sS \
 -H "Authorization: Bearer {{resolve:secretsmanager:agent-toolkit-blog-demo/api-token:SecretString:token}}" \
https://api.example.com/data
The command the agent constructs holds a placeholder, not a value.  asm-exec  scans the arguments, resolves each  {{resolve:...}}  reference inside its own process, then hands the resolved arguments to the real command. The plaintext never lands in the model's context window or the session transcript.
I pointed it at a local listener that records what it receives, so I could see both sides at once.
Plain text
Agent-visible command:
asm-exec -- curl -sS -H 'Authorization: Bearer {{resolve:secretsmanager:agent-toolkit-blog-demo/api-token:SecretString:token}}' http://127.0.0.1:8799/data
​
Response the agent sees:
{"authenticated": true, "msg": "token accepted"}
​
Header the listener actually received:
Bearer FAKE-token-asdf
The token reached the target. The agent saw a placeholder going in and an API response coming back. It never saw the token.
To be sure I was not fooling myself, I resolved the reference and piped it straight into  shasum  instead of a command, then compared against the hash of the known fake token.
Plain text
$ printf %s "FAKE-token-asdf" | shasum -a 256
4953fa5206a3d4a298e6aab9f9c2f74ae50ec2d6f07f374a2df478386674593e -
​
$ asm-exec -- sh -c 'printf %s "{{resolve:secretsmanager:agent-toolkit-blog-demo/api-token:SecretString:token}}" | shasum -a 256'
4953fa5206a3d4a298e6aab9f9c2f74ae50ec2d6f07f374a2df478386674593e -
Same hash. The real value was resolved, used and never printed.
Then the part that closes the loop. I grepped the agent-visible transcript from both runs for the fake values.
Plain text
-- unguarded path (aws secretsmanager get-secret-value) --
  FAKE-pw-asdf     found
  FAKE-token-asdf found
-- safe path (asm-exec + {{resolve}}) --
  FAKE-pw-asdf     not found
  FAKE-token-asdf not found
One path writes your secret into a file on disk. The other runs the exact same task without ever writing it down.

How the pieces fit

Flow diagram. An agent's request to use a secret hits the PreToolUse hook. A direct fetch is denied and the agent rewrites it to use asm-exec with a resolve reference. asm-exec calls Secrets Manager, resolves the real value inside its own process where the plaintext stays, runs the target command, and returns only the output to the model context so the secret is never exposed

One thing I like about the design.  asm-exec  never shells out to  aws secretsmanager get-secret-value  itself, so the plaintext is never written to a local process's stdout where something could scrape it.

Where this bites

I've talked about how cool this is but I also promised to go over some rough edges. I want to be clear the skill is useful but it is also a best-effort defense. Even AWS says so in the docs. It stops the common leak but it doesn't mean you should stop thinking about security as high priority in your day-to-day actions.
It reduces exposure, it does not prove a negative. The block covers the fetch shapes I threw at it and more. A determined agent or a creative prompt can still find a path the hook does not model. Treat this as one layer. For real production stuff, you still need to keep IAM least-privilege underneath it, keep CloudTrail on and scope who can read which secret. The skill lowers the odds of a dumb leak. It does not turn the agent into a trusted process.
The safe path still calls GetSecretValue. It is not read-free.  asm-exec  resolves by calling Secrets Manager, it just does it in its own process instead of handing the value back to the agent. Your identity still needs  secretsmanager:GetSecretValue  on the secret, and the read still shows up in CloudTrail. When it goes through the MCP endpoint the event lists  invokedBy ,  sourceIPAddress  and  userAgent  all as  aws-mcp.amazonaws.com , so you can tell an agent read from one you ran yourself. The difference is where the plaintext lands, not whether the API gets called.
 asm-exec  times out on slow connections. I got  Failed to resolve  on a fresh network even with valid creds. The read timeout is 10 seconds. A retry cleared it.

Try it on something you were going to build anyway

To make sure you get the automatic block you saw in the demo above, install the plugin in Claude Code with this command:  /plugin install aws-core@claude-plugins-official , then restart your session so the hook loads.
From the  Agent Toolkit for AWS  site, you'll see this one shot command that works for most agents:
Plain text
aws configure agent-toolkit
This is the easy way to install the toolkit but it does require  AWS CLI 2.35+ . This method will auto-detect your agents, install skills, and configure the MCP Server. One catch I hit: it does not install the secret-safety skill you saw here. That one ships with the aws-core plugin (it lives under  plugins/aws-core/skills ).
Repo and docs below:
Start read-only. Use the  aws:CalledViaAWSMCP  condition key to keep the agent to reads through the MCP Server so people can get used to it, then open up write access once you trust the pattern. Loosening later is easy. Walking back a broad grant is not.


If you have any feedback on this or general thoughts on the toolkit, drop a comment below. Feel free to connect/reach out on  LinkedIn .
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article