AWS Builder Center
How I Built an AI Agent That Creates Its Own Tools and Triggers Itself on AWS

How I Built an AI Agent That Creates Its Own Tools and Triggers Itself on AWS

Building on the OpenClaw-on-AWS foundation, this post covers three patterns that turn a reactive agent into an autonomous one: dynamic tool creation via Gateway, scheduled self-invocation through SNS filter policies, and identity files that give the agent context when no human is in the loop.

In the first post , I mapped OpenClaw's identity-driven architecture onto AWS managed services. Identity files in S3, memory in AgentCore, scheduling via EventBridge, deployment on AgentCore Runtime. One cdk deploy, no servers.
That was the foundation. This post is about what I built on top of it.
Three capabilities turned the agent from a reactive assistant into something closer to autonomous: self-extending tools, proactive self-invocation, and a fan-out notification architecture that ties it all together. Each one maps a concept from agent research onto a concrete AWS pattern.

The Problem with Static Tool Sets

Most agent frameworks ship with a fixed set of tools. You define them at build time, deploy, and that's what the agent can do. Need a new capability? Change the code, redeploy, restart.
When your agent runs as a local process like OpenClaw does, this isn't a big deal. The agent can write a Python script to disk, import it, and start using it. The filesystem is right there. Creating a new tool is just creating a file.
When your agent runs on a managed serverless runtime, that option disappears. There's no persistent filesystem. There's no "just drop a script in the tools folder." The container is ephemeral. If the agent writes a file, it's gone on the next invocation. You can't pip install a new package at runtime. You can't hot-reload a module.
This is the fundamental tension: managed services give you scaling, availability, and minimal operational overhead, but they take away the filesystem-level flexibility that makes local agents easy to extend.
The agent started with 7 built-in tools: task management, scheduling, identity updates, memory operations, and a time utility. Useful, but limited. The first time a user asked "list my EKS clusters," the agent had nothing to work with. And unlike OpenClaw, it couldn't just write a script and call it.
The traditional answer is to anticipate every possible tool and bundle them all in. That doesn't scale. AWS has hundreds of services, each with dozens of operations. You can't pre-load all of them without drowning the model in context.
I needed the agent to grow its own capabilities on demand.

Self-Extending Tools via AgentCore Gateway

AgentCore Gateway exposes an MCP (Model Context Protocol) endpoint that the agent connects to at startup. Gateway targets become tools the agent can call. The key insight: the agent itself has a tool that creates new Gateway targets.
Here's what happens when a user asks for something the agent can't do yet:
1
2
User: I need to check my EKS clusters
Agent: I don't have an EKS tool yet. Let me create one.
Behind the scenes, the agent's manage_gateway_tools tool does this:
  1. Writes a Python Lambda function with the boto3 code for the requested operation
  2. Deploys it via the Lambda API (using a pre-provisioned IAM role from CDK)
  3. Registers the Lambda as a Gateway target with a tool schema
  4. Clears the internal tool cache so the next request picks up the new tool
The agent names the Lambda {agent_id}-custom-{tool_name}, which scopes it to the agent and lets the Gateway IAM policy use a wildcard pattern. CDK sets this up at deploy time:
1
2
3
4
5
6
7
# Gateway role can invoke any custom Lambda the agent creates
gateway.role.add_to_policy(
iam.PolicyStatement(
actions=["lambda:InvokeFunction"],
resources=[f"arn:aws:lambda:{region}:{account}:function:{agent_id}-custom-*"],
)
)
No redeployment. No code change. The agent asked for a capability, built it, registered it, and used it. Next conversation, the tool is still there.
It also supports adding full AWS service access via Smithy models (download from Maven, upload to S3, register as a Gateway target). For more targeted use cases, the focused Lambda approach tends to work well since you control exactly which operations are exposed to the model. Worth noting: the more tools you give an agent, the more context the model needs to reason over. A full Smithy model for a service like S3 or EC2 can add hundreds of operations to the tool list. That's not necessarily a problem, but it's a tradeoff to be aware of when designing your tool surface.

The Notification Architecture: SNS as a Fan-Out Hub

The V1 architecture had a simple path: EventBridge fires, Lambda sends a message to SQS, the UI polls SQS. One producer, one consumer.
That's limiting. What if you want email notifications? SMS? Slack? What if you want the agent to actually do something when a schedule fires, not just notify?
I replaced the direct SQS path with SNS as a fan-out hub:
1
2
3
4
5
6
EventBridge Schedule
→ Scheduler Invoker Lambda
→ SNS Topic (with message attributes)
→ SQS (UI polls this, always)
→ Agent Invoker Lambda (only when agent_action=true)
→ Email/SMS/Slack (optional subscriptions)
Every scheduled event flows through SNS. The Scheduler Invoker Lambda publishes with a agent_action message attribute that controls routing. SQS gets everything (raw message delivery). The Agent Invoker Lambda only triggers when agent_action=true, using SNS filter policies.
Adding a new notification channel is just adding an SNS subscription. No code changes, no redeployment.

Proactive Self-Invocation: The Agent Triggers Itself

This is the piece that makes the agent autonomous.
In V1, schedules only produced notifications. The user had to read the notification and then ask the agent to do something about it. That's not an assistant, that's a reminder app.
The schedule_task tool now accepts an agent_action parameter. When set to true, the schedule doesn't just notify. It triggers the agent to execute the task.
1
2
User: Check my EKS clusters every hour and let me know if anything looks wrong
Agent: I'll create a recurring schedule with agent_action enabled.
The flow:
  1. EventBridge fires on the cron schedule
  2. Scheduler Invoker Lambda publishes to SNS with agent_action: "true"
  3. SNS filter policy routes the message to the Agent Invoker Lambda
  4. Agent Invoker calls invoke_agent_runtime with the task prompt
  5. The agent runs, uses its tools (including the EKS tool it created earlier), produces a result
  6. Agent Invoker publishes the result back to SNS with agent_action: "false"
  7. The result flows to SQS for the UI, but does NOT re-trigger the Agent Invoker
Step 6 is critical. Without agent_action: "false" on the result message, you get an infinite loop: agent produces result, result triggers agent, agent produces result. The SNS filter policy breaks the cycle.
The Agent Invoker Lambda is about 40 lines of code. It doesn't contain any business logic. It's pure plumbing: receive SNS message, call the runtime, publish the result. All the intelligence lives in the agent.

How These Pieces Compose

The interesting thing is how these three capabilities combine.
Consider this conversation:
1
2
User: I need to monitor my EKS cluster health every 6 hours.
If a node is NotReady, scale up the node group.
The agent:
  1. Creates a Lambda tool for EKS cluster health checks (self-extending)
  2. Creates a Lambda tool for node group scaling (self-extending)
  3. Creates an EventBridge schedule with agent_action=true and a task prompt that says "check EKS cluster health, if any node is NotReady, scale up the node group" (proactive self-invocation)
  4. Every 6 hours, the agent wakes up, runs the health check, makes a decision, and acts on it (autonomous behavior)
  5. Results flow to SNS, then to SQS for the user to review, and optionally to email (fan-out notifications)
The user set this up through a conversation. No YAML. No runbooks. No deployment pipeline. The agent built its own tools, scheduled its own work, and executes autonomously.

The Cache Invalidation Problem

One thing worth calling out: tool caching. The AgentCore Runtime entrypoint caches components per agent to avoid rebuilding on every request. That includes the MCP client connection to the Gateway, which caches the tool list.
When the agent adds a new Gateway target, the cached tool list is stale. The new tool exists in the Gateway but the agent doesn't know about it until the cache is cleared.
I solved this with a clear_caches() function that the manage_gateway_tools tool calls after any add or remove operation:
1
2
3
4
5
6
def clear_caches():
"""Clear all caches so next request rebuilds components and picks up new tools."""
with _components_lock:
_components_cache.clear()
with _agent_lock:
_agent_cache.clear()
The next request rebuilds everything from scratch, including a fresh MCP tool list from the Gateway. It's not elegant, but it's correct. The rebuild takes a few seconds, which is acceptable for a tool-addition operation that happens infrequently.

Memory: Two Systems, Two Purposes

I ended up with two memory systems that serve different purposes.
AgentCore Memory is the agent's brain. It handles short-term conversation context and long-term memory through three automatic strategies: summarization, user preference tracking, and semantic fact extraction. It persists across sessions. The Strands SDK integrates it via AgentCoreMemorySessionManager with no extra code.
The S3 identity files (SOUL.md, AGENTS.md, IDENTITY.md, USER.md, MEMORY.md) are the agent's identity and operating context, loaded into the system prompt at every invocation. They define who the agent is, how it behaves, and what curated knowledge it carries.
The S3 files give something AgentCore Memory doesn't: direct editability. You can open USER.md, change preferences, and the agent picks it up next invocation. You can roll back IDENTITY.md if the agent's self-conception drifted. S3 versioning makes this safe. The agent decides what goes where: conversational context flows into AgentCore Memory automatically, and when something is important enough to persist as operating identity, it writes to S3 using its save_to_memory, update_user_profile, or update_identity tools.

What I Learned

Building this taught me a few things about agent architecture:
The tool-calling loop is table stakes. Every framework does it. What differentiates a useful agent is everything around the loop: how it maintains identity across sessions, how it remembers what matters, how it acts without being prompted, and how it grows its own capabilities.
Managed services remove the wrong kind of complexity. I didn't want to operate a vector database for memory, a cron daemon for scheduling, or a container orchestrator for deployment. Those are solved problems. The interesting problems are in the agent's behavior: when should it create a tool vs. ask for clarification? How should it decide what to remember? When is autonomous action appropriate vs. notification?
Fan-out architectures are underrated for agents. SNS as a message bus with filter policies gives you routing, multi-channel delivery, and loop prevention in a service that costs almost nothing. Every agent that does more than respond to prompts needs something like this.
Self-extension needs guardrails. The custom tool Lambda role uses ReadOnlyAccess by default. For agents that need to create or modify resources, you'd scope up the permissions with specific policy statements. The pattern works, but the permissions model needs thought.

Try It

1
2
3
4
git clone https://github.com/lusoal/openclaw-redesign-awsbedrock.git
cd openclaw-redesign-awsbedrock
pip install -e ".[dev,infra]"
cd infra && cdk deploy
Change SOUL.md to give it a different personality. Set enable_prompt_routing: true in cdk.json to let Bedrock pick the optimal model per request. Add SNS email subscriptions for notifications. Ask it to build tools you need.
The agent is the starting point, not the finished product.
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