Welcome back, aspiring AI developer! In previous chapters, we’ve explored the foundational aspects of AWS Kiro, learned how to set up our environment, and started leveraging its out-of-the-box AI capabilities for coding. Kiro is already a powerful assistant, but what if your development workflow has unique needs that Kiro doesn’t address by default?

This chapter is where Kiro truly transforms from an intelligent assistant into a bespoke development partner. We’re going to unlock Kiro’s full potential by learning how to build custom Kiro agents. You’ll discover how to extend Kiro’s functionalities, automate specific tasks, and integrate your own logic directly into the AI-powered development environment. By the end of this chapter, you’ll be able to design, implement, and test your own Kiro agents, tailoring Kiro to your exact project requirements.

To get the most out of this chapter, ensure you have a working Kiro setup from Chapter 2 and a basic understanding of Kiro’s core interaction model from Chapter 3. Let’s dive in and make Kiro truly yours!

Core Concepts of Kiro Agent Development

Before we jump into coding, let’s understand the fundamental building blocks that make custom Kiro agents possible. Kiro’s architecture is designed for extensibility, allowing you to inject your own intelligence and logic at various stages of its operation.

The Kiro Agent Architecture: Intent, Knowledge, Execution, Oversight

Kiro agents operate within a sophisticated four-layer architecture that governs how they understand, plan, act, and review tasks. Understanding these layers is key to knowing where and how your custom agent fits in.

  1. Intent: This is the initial layer where Kiro (or your custom agent) interprets the user’s request. It’s about understanding what the user wants to achieve, identifying the core problem, and breaking it down into actionable steps.
  2. Knowledge: Once the intent is clear, the agent accesses relevant information. This includes project context, official documentation, internal best practices, and even external APIs. Your custom agent can contribute to or leverage this knowledge base.
  3. Execution: This is where the agent performs the actual work. It generates code, modifies files, runs tests, or interacts with external tools. Your custom logic primarily lives here, or influences actions taken at this stage.
  4. Oversight: After execution, the agent reviews its work. Did it meet the intent? Are there any errors? Does the code adhere to best practices? This layer provides a feedback loop, often allowing for self-correction or user interaction for approval.

Custom agents can plug into and influence any of these layers, but they often shine in enhancing the Knowledge and Execution phases, and by adding specific checks in the Oversight phase.

Here’s a simplified view of the Kiro Agent architecture:

flowchart TD User[User Input/Prompt] -->|1. Define Intent| Intent((Intent)) Intent -->|2. Gather Context| Knowledge[Knowledge Base] Knowledge -->|3. Plan & Act| Execution[Execution Logic] Execution -->|4. Review & Refine| Oversight{Oversight & Feedback} Oversight -->|5. Output/Adjust| User

Agent Manifest (agent.yaml)

Every Kiro agent needs a blueprint, and that’s precisely what the agent.yaml file provides. This YAML-formatted file is the heart of your agent, defining its metadata, capabilities, and how it interacts with the Kiro environment. It specifies:

  • Agent Name and Description: How Kiro identifies and describes your agent.
  • Intents: The specific types of tasks or problems your agent is designed to handle. Kiro uses this to decide when to invoke your agent.
  • Hooks: The entry points for your custom code. These are functions that Kiro calls at specific stages of its workflow.
  • Dependencies: Any external libraries or tools your agent requires.

Agent Hooks: Your Custom Logic Injection Points

Hooks are functions or scripts that your custom agent provides, which Kiro invokes at predefined points in its development workflow. Think of them as event listeners for Kiro’s operations. Common hook types include:

  • pre-execution: Runs before Kiro attempts to execute a task (e.g., before generating code). Useful for validating input, setting up context, or performing initial checks.
  • post-execution: Runs after Kiro completes a task. Ideal for reviewing output, running tests, formatting code, or updating external systems.
  • on-error: Runs if an error occurs during execution. Useful for custom error handling or logging.

By implementing these hooks, you can effectively “steer” Kiro’s behavior, ensuring it aligns with your project’s specific needs, quality standards, or integration requirements.

Model Context Protocol (MCP)

The Model Context Protocol (MCP) is Kiro’s standardized way for agents to communicate with each other and with Kiro’s core AI models. It allows agents to:

  • Access Kiro’s internal system prompts and instructions.
  • Inject additional context or constraints into the AI model’s thinking process.
  • Receive structured information about the current project state.

While you might not directly interact with MCP at the lowest level when building simple agents, understanding its role helps grasp how your agent’s input influences Kiro’s powerful underlying models.

Kiro Agent SDK

To make agent development straightforward, Kiro provides Software Development Kits (SDKs), primarily for Python and TypeScript. These SDKs offer utility functions and classes to interact with the Kiro environment, access project files, log information, and return structured results from your hooks. We’ll be using the Python SDK for our examples due to its widespread adoption and ease of use.

Step-by-Step Implementation: Building a Simple Validation Agent

Let’s build a practical custom agent that enforces a simple coding best practice: ensuring all new Python files start with a docstring. This will demonstrate how to define an intent, implement a pre-execution hook, and interact with the Kiro environment.

Step 1: Initialize a New Kiro Agent Project

First, let’s create a new directory for our agent and initialize a Kiro agent project.

  1. Create a Project Directory: Open your terminal or command prompt and navigate to a suitable location.

    mkdir kiro-docstring-agent
    cd kiro-docstring-agent
    
  2. Initialize the Agent: Now, use the Kiro CLI to initialize the agent. This command creates the basic structure for your agent.

    kiro agent init
    

    You’ll be prompted for some information:

    • Agent Name: docstring-validator
    • Description: Ensures Python files have docstrings.
    • Language: Python (or TypeScript if you prefer, but we’ll use Python for this guide)

    After initialization, your directory will look something like this:

    kiro-docstring-agent/
    ├── agent.yaml
    └── src/
        └── main.py (or main.ts)
    

Step 2: Define the Agent’s Intent in agent.yaml

Open agent.yaml in your favorite code editor. You’ll see some boilerplate. We need to define an intent that tells Kiro when to consider activating our agent. For our docstring validator, we want it to run whenever Kiro is about to create or modify a Python file.

Here’s how to modify your agent.yaml. We’ll add a section for intents and specify a trigger based on file changes.

# kiro-docstring-agent/agent.yaml
agent:
  name: docstring-validator
  description: Ensures Python files have docstrings.
  language: python # Or typescript if you chose that
  version: "0.1.0" # Current version of your agent

# This is where we define what our agent cares about
intents:
  - name: validate-python-docstring
    description: "Validate if a Python file has a docstring."
    # We want this agent to be considered whenever Kiro generates or modifies code
    triggers:
      - type: code_generation
      - type: file_modification
    # Specify the hooks that will run when this intent is matched
    hooks:
      pre-execution:
        - src/main.py:validate_docstring # Path to your script and function name

Explanation:

  • intents: This top-level key holds a list of intents your agent can handle.
  • name: A unique identifier for this specific intent.
  • description: A human-readable explanation of what this intent does.
  • triggers: This is crucial. It tells Kiro when to consider activating this intent. We’ve specified code_generation and file_modification, meaning Kiro will evaluate this intent whenever it’s about to create or change code/files.
  • hooks: Inside the intent, we link to our actual code. pre-execution means our validate_docstring function (located in src/main.py) will run before Kiro proceeds with its primary task.

Step 3: Implement the pre-execution Hook

Now, let’s write the Python code for our validate_docstring hook. Open src/main.py.

# kiro-docstring-agent/src/main.py
import os
import logging
from typing import Dict, Any

# Kiro provides a context object with useful information
# You might need to install 'kiro-sdk' if not already installed: pip install kiro-sdk
# For current Kiro versions (as of 2026-01-24), the context object is typically passed
# directly to the hook function, and the SDK provides helpers.
# Let's assume a simplified context for this example.

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

def validate_docstring(context: Dict[str, Any]) -> Dict[str, Any]:
    """
    A pre-execution hook to validate if a Python file has a docstring.
    If Kiro is about to create/modify a Python file, this hook checks its content.
    """
    logger.info("Docstring validator agent: Pre-execution hook activated.")

    # Kiro's context object contains information about the current operation.
    # We're interested in the files Kiro is proposing to change or create.
    # The exact structure of 'files_to_modify' or 'proposed_changes'
    # can vary slightly with Kiro versions, but it generally contains
    # file paths and their proposed content.
    
    # Let's simulate retrieving proposed changes.
    # In a real scenario, context.get('proposed_changes', {}) or similar
    # would provide actual file changes. For this example, we'll assume
    # 'file_path' and 'new_content' are directly available for the target file.

    # For simplicity, let's assume the context contains a 'target_file' key
    # with the path and 'proposed_content' for that file.
    # In a real Kiro environment, you'd iterate through 'proposed_changes'.
    
    target_file_path = context.get('target_file_path')
    proposed_content = context.get('proposed_content')

    if not target_file_path or not proposed_content:
        logger.warning("Docstring validator: No target file path or content found in context.")
        return {"status": "skipped", "message": "No relevant file changes to validate."}

    # Only validate Python files
    if not target_file_path.endswith('.py'):
        logger.info(f"Docstring validator: Skipping non-Python file: {target_file_path}")
        return {"status": "skipped", "message": "Not a Python file."}

    logger.info(f"Docstring validator: Checking file: {target_file_path}")

    # Check for a docstring (very basic check: presence of triple quotes at the start)
    # This is a simplified check. A more robust solution might use AST parsing.
    content_lines = proposed_content.strip().splitlines()
    has_docstring = False
    
    # Look for a docstring within the first few non-empty lines
    for i, line in enumerate(content_lines):
        stripped_line = line.strip()
        if not stripped_line: # Skip empty lines
            continue
        if stripped_line.startswith(('"""', "'''")):
            has_docstring = True
            break
        # If we encounter actual code before a docstring, assume no docstring at the top
        if not stripped_line.startswith(('#', 'from', 'import', 'def', 'class')):
            break # Found code before docstring
        if i > 10: # Don't search too far down
            break
            
    if not has_docstring:
        error_message = f"Error: Python file '{target_file_path}' is missing a top-level docstring."
        logger.error(error_message)
        # If the hook returns 'status': 'fail', Kiro will typically halt the operation
        # and report the error to the user.
        return {
            "status": "fail",
            "message": error_message,
            "suggestion": "Please add a docstring to the beginning of your Python file, e.g., `\"\"\"A brief description.\"\"\"`."
        }
    else:
        logger.info(f"Docstring validator: Docstring found in '{target_file_path}'.")
        return {"status": "success", "message": "Docstring validation passed."}

Explanation of the Python Code:

  • validate_docstring(context: Dict[str, Any]): This is our hook function. Kiro automatically passes a context dictionary containing details about the current operation.
  • logger: We use standard Python logging for better visibility into our agent’s actions when it runs.
  • target_file_path and proposed_content: In a real Kiro hook, the context object would contain structured data about the files Kiro is proposing to change. For this simplified example, we’re assuming these keys are directly available. In practice, you’d likely access context['proposed_changes'] which is a dictionary mapping file paths to their new content.
  • .endswith('.py'): We ensure our check only applies to Python files.
  • content_lines = proposed_content.strip().splitlines(): We get the proposed content of the file and split it into lines.
  • Docstring Check: The core logic is a basic check for """ or ''' at the beginning of the file’s content. A more advanced agent might use Python’s ast module to parse the code and verify a proper docstring structure.
  • Return Value:
    • If a docstring is not found, we return a dictionary with "status": "fail", an error_message, and a suggestion. Kiro interprets "fail" as a signal to halt the current operation and inform the user.
    • If a docstring is found (or the file is not Python), we return "status": "success" or "skipped". This allows Kiro to proceed.

Step 4: Test Your Custom Agent

Kiro provides a way to test your agents locally without fully integrating them into a project.

  1. Navigate to Agent Directory: Ensure you are in the kiro-docstring-agent directory.

  2. Run a Test: We’ll simulate Kiro proposing to create a Python file with and without a docstring.

    Test Case 1: Python file with a docstring (should pass)

    kiro agent test validate-python-docstring --context '{"target_file_path": "my_module.py", "proposed_content": "\"\"\"This is a test module.\n\"\"\"\n\ndef hello():\n    print(\"Hello\")"}'
    

    You should see output indicating “Docstring validation passed.” and the agent returning {"status": "success", ...}.

    Test Case 2: Python file without a docstring (should fail)

    kiro agent test validate-python-docstring --context '{"target_file_path": "no_docstring.py", "proposed_content": "def hello():\n    print(\"Hello, no docstring!\")"}'
    

    This time, you should see an error message from your agent, indicating “Python file ’no_docstring.py’ is missing a top-level docstring.” and the agent returning {"status": "fail", ...}.

    Test Case 3: A non-Python file (should be skipped)

    kiro agent test validate-python-docstring --context '{"target_file_path": "README.md", "proposed_content": "# My Readme"}'
    

    This should show “Docstring validator: Skipping non-Python file: README.md” and return {"status": "skipped", ...}.

What to Observe/Learn:

  • You’ve successfully defined an intent and linked it to a Python hook.
  • You’ve seen how the context object passes information to your agent.
  • You’ve observed how returning "status": "fail" can halt Kiro’s operation and provide feedback.
  • You’ve learned to use kiro agent test to validate your agent’s logic.

Mini-Challenge: Extend the Docstring Agent

You’ve built a basic docstring validator. Now, let’s make it a bit smarter!

Challenge: Modify your docstring-validator agent so that, in addition to checking for a docstring, it also checks if any Python function (def) within the proposed content is missing its own docstring. If a function is found without a docstring, the agent should still fail, but provide a more specific error message, including the function’s name.

Hint:

  • You’ll need to parse the proposed_content more deeply. Regular expressions can be useful for finding def function_name(...): patterns.
  • After finding a def line, you’ll need to look at the lines immediately following it to see if a docstring ("""...""" or '''...''') is present before any actual code.
  • Remember to return "status": "fail" with a clear message and suggestion if a missing function docstring is detected.

What to observe/learn: This challenge will deepen your understanding of parsing code within an agent, handling more complex validation logic, and providing granular feedback to the user.


Common Pitfalls & Troubleshooting

Building custom agents can be tricky at first. Here are some common issues and how to troubleshoot them:

  1. agent.yaml Syntax Errors:

    • Pitfall: YAML is sensitive to indentation and syntax. A misplaced space or incorrect key can prevent your agent from being recognized.
    • Troubleshooting:
      • Use a YAML linter (many IDEs have them built-in).
      • Double-check indentation. YAML uses spaces, not tabs.
      • Ensure all keys are correctly spelled (e.g., intents vs. intent).
      • Kiro CLI often provides helpful error messages if agent.yaml is malformed during kiro agent test or kiro agent deploy.
  2. Agent Not Being Invoked:

    • Pitfall: Your agent is correctly defined, but Kiro never calls your hook function.
    • Troubleshooting:
      • Check triggers in agent.yaml: Does the type of trigger match the scenario you’re expecting (e.g., code_generation, file_modification)?
      • Verify hooks path and function name: Is src/main.py:validate_docstring correctly pointing to your file and function?
      • Examine Kiro’s logs: When Kiro runs, it often logs which agents it’s considering and why it’s activating/deactivating them. Look for messages related to your agent’s name.
      • Ensure the intent is broad enough: If your intent is too specific, Kiro might not match it.
  3. Permissions Issues (if interacting with AWS resources):

    • Pitfall: Your agent tries to call an AWS API (e.g., S3, Lambda) but gets an “Access Denied” error.
    • Troubleshooting:
      • AWS CLI Configuration: Verify your local AWS CLI is configured with credentials that have the necessary permissions. Use aws sts get-caller-identity to check the active principal.
      • Kiro Agent Role: When deploying agents to a shared Kiro environment, they often run with an IAM role. Ensure this role has the required permissions for the AWS services your agent needs to access. This is a common issue for agents that interact with external AWS services.
  4. Debugging Kiro Agents:

    • Pitfall: Your agent runs but produces unexpected results, or silently fails.
    • Troubleshooting:
      • Extensive Logging: Use logging.info(), logging.warning(), and logging.error() liberally within your agent code. These logs will be visible when you run kiro agent test or if the agent runs within Kiro’s environment.
      • Print Statements (for quick checks): While logging is preferred, print() statements can also provide immediate feedback during development with kiro agent test.
      • Inspect the context object: Log the entire context dictionary at the beginning of your hook to understand exactly what information Kiro is providing to your agent. logger.debug(f"Context: {context}")
      • Step-through Debugging: For more complex agents, you might need to attach a debugger (like pdb for Python) to your hook function, though this can be more involved with how Kiro executes hooks.

Summary

Congratulations! You’ve taken a significant step in mastering AWS Kiro by learning how to build and customize your own AI agents.

Here’s a quick recap of what we covered:

  • Kiro Agent Architecture: We explored the Intent, Knowledge, Execution, and Oversight layers that govern Kiro’s operations, understanding where custom logic fits in.
  • Agent Manifest (agent.yaml): You learned to define your agent’s identity, intents, triggers, and hooks in this crucial configuration file.
  • Agent Hooks: We delved into pre-execution hooks, which allow you to inject custom logic before Kiro performs its main task, enabling powerful validation and context manipulation.
  • Step-by-Step Implementation: You successfully initialized a Kiro agent project, defined an intent, implemented a Python pre-execution hook, and tested its behavior with the Kiro CLI.
  • Troubleshooting: We discussed common pitfalls like agent.yaml errors, invocation issues, permissions, and effective debugging strategies.

Building custom agents empowers you to tailor Kiro to your team’s specific coding standards, automate repetitive checks, and integrate seamlessly with your existing toolchain. This capability makes Kiro an incredibly flexible and powerful partner in your development journey.

What’s Next?

In the next chapter, we’ll explore how to deploy your custom agents to a Kiro environment for broader use, delve into more advanced agent functionalities, and discuss strategies for integrating Kiro agents into your CI/CD pipelines for truly automated quality and consistency checks.

References


This page is AI-assisted and reviewed. It references official documentation and recognized resources where relevant.