Skip to content
Open
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Registry-First Development Workflow with Kiro

Discover agentic capabilities from your organization's AWS Agent Registry, identify gaps, build missing agents, and publish them back — __all from the Kiro IDE__ using MCP-based search and Kiro Powers for publisher workflows.

## What This Tutorial Demonstrates

This tutorial walks through a **registry-first development workflow** powered by Kiro:

1. **Search** — Use the AWS Agent Registry MCP server in Kiro to semantically search for existing agents and tools across your organization, directly from the IDE.

2. **Identify gaps** — Map discovered capabilities against your workflow requirements and pinpoint what's missing.

3. **Build & Publish** — Use Kiro Powers (which define the AWS Agent Registry publisher APIs) to build the missing agents and publish them back to the registry, all via the Kiro chat interface.

4. **Invoke** — Resolve runtime ARNs from registry records and invoke agents via AgentCore Runtime.

The key idea: a developer sitting in Kiro can go from "what agents exist?" to "I built and published the missing ones" without leaving the IDE.

![AWS Agent Registry flow on Kiro](images/KiroDiagram.png)

## What is AWS Agent Registry?

AWS Agent Registry is a centralized, governed catalog for discovering, publishing, and managing AI agents and tools across an organization. It provides semantic search for capability-based discovery, IAM and OAuth access control, rich metadata management (protocol, version, connection info, tool schemas), and a built-in governance workflow (DRAFT → PENDING_APPROVAL → APPROVED) so teams can trust what they find and reuse what others have built. AWS Agent Registry has three pain personas, Publishers - who publish capabilities to the Registry , Admins - who approve the published capabilities and Consumers - who search and access the approved registry records downstream.

![AWS Agent Registry Publisher flow ](images/Publisher-workflow.png)


![AWS Agent Registry Consumer flow](images/2-consumerflow.png)
## What is Dynamic Client Registration?

Dynamic Client Registration is an OAuth and OpenID Connect protocol that lets client applications automatically register with an authorization server instead of requiring manual pre-registration. The DCR protocol is formally defined in RFC 7591, with optional registration management extensions in RFC 7592. It was designed to work within the Open Authorization (OAuth) and OpenID Connect (OIDC) ecosystems. It enables automatic creation of client IDs, secrets, and metadata (redirect URIs, scopes), often used for automation, AI agents, and dynamic scaling.

### Why DCR Matters for Kiro

For Kiro to search the registry as an MCP tool, the registry needs to be configured with a **CUSTOM_JWT authorizer** backed by an OAuth provider (Auth0 in this example). Kiro's MCP client uses the **authorization_code + PKCE** flow — it dynamically registers itself via Dynamic Client Registration (DCR), opens a browser for login, catches the callback on localhost, and exchanges the code for a token. This means zero manual credential management for the developer.

## How Kiro Powers Fit In this Development Workflow

A **Kiro Power** is a curated, pre-packaged bundle of capabilities designed for the Kiro AI-powered IDE that gives the Kiro agent instant, specialized expertise in a specific technology or workflow.

Each power typically bundles three components :

- **POWER.md** — A steering file that acts as an onboarding manual, telling the agent what MCP tools are available and when to use them
- **MCP server configuration** — The tools and connection details for the Model Context Protocol server
- **Additional steering or hooks** — Extra guidance files or automated validation hooks [ We use this for publisher of records]

The key innovation is **dynamic context loading**: rather than loading every tool upfront (which can overwhelm the agent), powers activate only when relevant. For example, mention "database" and the Neon power loads; switch to deployment and the Netlify power activates while Neon deactivates.

In this sample Kiro Powers package the AWS Agent Registry publisher APIs (`CreateRegistryRecord`, `SubmitRegistryRecordForApproval`, etc.) as steering files that guide Kiro through the publish workflow. When you ask Kiro to "publish this agent to the registry," the Power provides the step-by-step instructions and API calls — so you get a governed publish-and-approve cycle without writing boto3 code yourself.

## Use Case:

To ground this tutorial in a real world usecase , let imagine a scenrio of AnyCompany financial services.

AnyCompany financial services firm has a multi team structure. Core teams include investment management, wealth advisory, trading operations, and compliance. Over the past year, multiple teams have independently built AI agents, MCP servers, and automation tools — but with no shared catalog, no common standard, and no way for one team to discover what another has already built.

The Wealth Advisory team has been asked to build a Quarterly Intelligence Briefing workflow.
Here's the requirement:

> *"When a publicly traded company reports quarterly results, automatically generate a comprehensive client-ready investment brief — including what happened, why it matters, how it affects each client's portfolio, and what (if any) action to consider — all within 30 minutes of the earnings release."*

In order to build this,the key capabilities needed include :
* **First**, gathering data — pull raw earnings data, market context, competitive intel.
* **Second**, analyze and synthesize — run financial analysis compliance tests and generate an investment thesis based on the data.
* **Third** is generate a perosnlaized investment brief for each client.


The Wealth Advisory team has **zero** existing capabilties in house for earnings data ingestion, financial analysis, or compliance screening. Building from scratch would take **couple of months and $1M+**.

But they don't need to build from scratch. **Other teams already have the pieces.** The Wealth Advisory team just needs a way to find them, verify they're approved for use, and wire them together into a flow.

The result: what would have been a 6-month greenfield project becomes a composition exercise — 7 agents discovered from 5 teams, 2 new agents built to fill gaps, and a single orchestrator tying them all together.




![Wealth Advisory Teams Quarterly Briefing Use Case](images/5-UsecaseviaAWSRegistry.png)

## Steps Involved:

Set Up:

- Create an Auth0 DCR-enabled registry (CUSTOM_JWT authorizer) [DCR set up instructions here](https://github.com/awslabs/agentcore-samples/blob/main/01-tutorials/10-Agent-Registry/01-advanced/kiro-registry-dcr-auth0/DCR_registry_search_mcp_in_kiro.ipynb)
- Deploy sample agents to AgentCore Runtime and register them as records

Search

- In Kiro Use the registry MCP server to search for existing agents from chat interface.
- Resolve runtime ARNs from search results and invoke agents from their URIs


Build

- Set up and Use Kiro Powers to create and publish new agents to registry from Kiro Chat interface. [Sample Kiro powers available here ](https://github.com/sanaiqbalw/amazon-bedrock-agentcore-samples/tree/br_dcr-registry_for_kiro-mcp-search/01-tutorials/10-Agent-Registry/01-advanced/kiro/kiro-power-publisher-workflow)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
AWS_REGION=us-west-2
# Auth0 OAuth (DCR) — only the domain is needed, client creds are obtained via DCR
AUTH0_DOMAIN=your-tenant.us.auth0.us.com
AUTH0_AUDIENCE=https://bedrock-agentcore.us-west-2.amazonaws.com
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#!/usr/bin/env python3
"""
Step 2: Deploy agents to AgentCore Runtime.

All agents are HTTP protocol with IAM auth (no OAuth on agents).

Run: python3 2_deploy_artifacts_on_agentcore_runtime.py market_research_agent [financial_analysis_agent ...]
"""

import argparse
import logging
import os
import shutil
import tempfile
from pathlib import Path

from bedrock_agentcore_starter_toolkit import Runtime
from setup import REGION

logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger("deploy")


def load_metadata(agent_name: str) -> dict:
mod = __import__(f"seed_agents.{agent_name}", fromlist=["METADATA"])
return mod.METADATA


def deploy_one(agent_name: str) -> dict:
"""Package and deploy a single agent to AgentCore Runtime. Returns name and ARN."""
meta = load_metadata(agent_name)
name = meta["name"]

logger.info("Deploying: %s (HTTP) — Team: %s", name, meta.get("team", "unknown"))

agent_file = Path("seed_agents") / meta["entrypoint"]
req_file = Path("seed_agents") / "requirements.txt"
project_dir = Path(tempfile.mkdtemp(prefix=f"ac-{name}-"))
shutil.copy(agent_file, project_dir / meta["entrypoint"])
if req_file.exists():
shutil.copy(req_file, project_dir / "requirements.txt")

original_dir = os.getcwd()
os.chdir(project_dir)

try:
runtime = Runtime()
runtime.configure(
entrypoint=meta["entrypoint"],
agent_name=name,
protocol="HTTP",
region=REGION,
auto_create_execution_role=True,
auto_create_ecr=True,
requirements_file="requirements.txt" if req_file.exists() else None,
)
result = runtime.launch(auto_update_on_conflict=True)
logger.info("Deployed: %s — ARN: %s", name, result.agent_arn)
return {"name": name, "agent_arn": result.agent_arn}
finally:
os.chdir(original_dir)
shutil.rmtree(project_dir, ignore_errors=True)


def main():
parser = argparse.ArgumentParser(description="Deploy agent(s) to AgentCore Runtime")
parser.add_argument("agents", nargs="+", help="Agent module names (e.g. market_research_agent)")
args = parser.parse_args()

results = []
for name in args.agents:
try:
results.append(deploy_one(name))
except Exception as e:
logger.error("Failed to deploy %s: %s", name, e)

logger.info("Deployed %d/%d agents", len(results), len(args.agents))
for r in results:
logger.info(" %s | %s", r["name"], r["agent_arn"])


if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#!/usr/bin/env python3
"""
Step 3: Register agents in the AgentCore Registry.

All agents are registered as CUSTOM descriptorType with inline metadata.
Runtime ARNs are auto-discovered from AgentCore Runtime if not passed via --arn.

Run: python3 3_add_records_to_registry.py market_research_agent [--arn <runtime-arn>]
"""

import argparse
import json
import logging
import time

import boto3
import requests as http_requests
from botocore.auth import SigV4Auth
from botocore.awsrequest import AWSRequest

from setup import get_cp_client, REGION

logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger("register")


def load_metadata(agent_name: str) -> dict:
mod = __import__(f"seed_agents.{agent_name}", fromlist=["METADATA"])
return mod.METADATA


def discover_runtime_arn(agent_name: str) -> str:
"""Auto-discover runtime ARN by listing agents on AgentCore Runtime control plane (paginated)."""
ac_cp = boto3.client("bedrock-agentcore-control", region_name=REGION)
try:
resp = ac_cp.list_agent_runtimes()
while True:
for agent in resp.get("agentRuntimes", []):
if agent["agentRuntimeName"] == agent_name:
logger.info("Auto-discovered ARN for %s: %s", agent_name, agent["agentRuntimeArn"])
return agent["agentRuntimeArn"]
nt = resp.get("nextToken")
if not nt:
break
resp = ac_cp.list_agent_runtimes(nextToken=nt)
except Exception as e:
logger.warning("Could not auto-discover ARN for %s: %s", agent_name, e)
return None


def _create_record_raw(registry_id, body: dict) -> str:
"""Create a registry record via signed HTTP."""
cp_client = get_cp_client()
url = f"{cp_client.meta.endpoint_url}/registries/{registry_id}/records"
data = json.dumps(body)
req = AWSRequest(method="POST", url=url, data=data, headers={"Content-Type": "application/json"})
creds = boto3.Session(region_name=REGION).get_credentials().get_frozen_credentials()
SigV4Auth(creds, "bedrock-agentcore", REGION).add_auth(req)
resp = http_requests.post(url, headers=dict(req.headers), data=data)
resp.raise_for_status()
return resp.json()["recordArn"].split("/")[-1]


def wait_for_draft(cp, registry_id, record_id, max_wait=30):
for _ in range(max_wait // 3):
time.sleep(3)
rec = cp.get_registry_record(registryId=registry_id, recordId=record_id)
if rec["status"] not in ("CREATING", "UPDATING"):
return rec["status"]
return "CREATING"


def approve(cp, registry_id, record_id):
cp.submit_registry_record_for_approval(registryId=registry_id, recordId=record_id)
creds = boto3.Session(region_name=REGION).get_credentials().get_frozen_credentials()
url = f"{cp.meta.endpoint_url}/registries/{registry_id}/records/{record_id}/status"
body = json.dumps({"status": "APPROVED", "statusReason": "Approved"})
req = AWSRequest(method="PATCH", url=url, data=body, headers={"Content-Type": "application/json"})
SigV4Auth(creds, "bedrock-agentcore", REGION).add_auth(req)
http_requests.request(method="PATCH", url=url, headers=dict(req.headers), data=body).raise_for_status()


def register_agent(cp, registry_id, meta: dict, runtime_arn: str = None) -> str:
"""Create a CUSTOM registry record for an agent and approve it."""
inline = {
"name": meta["name"],
"description": meta["description"],
"version": meta.get("version", "1.0.0"),
"protocol": "HTTP",
"team": meta.get("team", ""),
"capabilities": meta.get("capabilities", []),
"tools": meta["tools"],
}
if runtime_arn:
inline["runtimeArn"] = runtime_arn

record_id = _create_record_raw(registry_id, {
"name": meta["name"],
"description": meta["description"],
"descriptorType": "CUSTOM",
"recordVersion": meta.get("version", "1.0"),
"descriptors": {"custom": {"inlineContent": json.dumps(inline)}},
})
logger.info("Created: %s -> %s", meta["name"], record_id)

status = wait_for_draft(cp, registry_id, record_id)
logger.info("Status: %s", status)
approve(cp, registry_id, record_id)
logger.info("Approved: %s", meta["name"])
return record_id


def main():
parser = argparse.ArgumentParser(description="Register agent(s) in AgentCore Registry")
parser.add_argument("agent", help="Agent module name")
parser.add_argument("--arn", help="Runtime ARN (auto-discovered if omitted)")
parser.add_argument("--registry-id", help="Override REGISTRY_ID from .env")
args = parser.parse_args()

registry_id = args.registry_id
if not registry_id:
logger.error("No REGISTRY_ID set. Run 1_create_registry.py first.")
return

meta = load_metadata(args.agent)
cp = get_cp_client()

runtime_arn = args.arn or discover_runtime_arn(meta["name"])

logger.info("Registering: %s — Team: %s — ARN: %s", meta["name"], meta.get("team", "unknown"), runtime_arn or "NONE")
record_id = register_agent(cp, registry_id, meta, runtime_arn=runtime_arn)
logger.info("Done: %s | Record: %s", meta["name"], record_id)


if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""Approval Workflow Agent — compliance gate for POC (auto-approves)."""

METADATA = {
"name": "approval_workflow_agent",
"description": "Agent that orchestrates multi-step approval workflows with escalation and notification",
"protocol": "HTTP",
"entrypoint": "approval_workflow_agent.py",
"version": "1.0.0",
"team": "Compliance",
"capabilities": ["approval-routing", "compliance-check", "escalation"],
"tools": [
{"name": "submit_for_compliance_review", "description": "Submit document for compliance review"},
{"name": "check_approval_status", "description": "Check status of a compliance review"},
],
}

from strands import Agent, tool # noqa: E402
from strands.models import BedrockModel # noqa: E402
from bedrock_agentcore.runtime import BedrockAgentCoreApp # noqa: E402

app = BedrockAgentCoreApp()


@tool
def submit_for_compliance_review(document_type: str, content_summary: str) -> str:
"""Submit a document for compliance review."""
return f"""{{"status": "APPROVED",
"review_id": "CR-2025-00847",
"document_type": "{document_type}",
"reviewer": "Compliance Bot v2",
"checks_passed": [
"No forward-looking statements without disclaimers",
"Risk disclosures present",
"No material non-public information detected",
"Suitability disclaimers included"
],
"flags": [],
"approved_at": "2025-01-30T16:30:00Z",
"notes": "Auto-approved — all compliance checks passed"}}"""


@tool
def check_approval_status(review_id: str) -> str:
"""Check the status of a compliance review."""
return f"""{{"review_id": "{review_id}", "status": "APPROVED",
"approved_at": "2025-01-30T16:30:00Z"}}"""


agent = Agent(
model=BedrockModel(model_id="us.anthropic.claude-sonnet-4-20250514-v1:0"),
tools=[submit_for_compliance_review, check_approval_status],
system_prompt="You are a compliance workflow agent. Submit documents for review and report approval status.",
)


@app.entrypoint
def invoke(payload, context=None):
result = agent(payload.get("prompt", "Submit investment thesis for compliance review"))
return {"result": str(result)}


if __name__ == "__main__":
app.run()
Loading
Loading