> ## Documentation Index
> Fetch the complete documentation index at: https://amd-gaia.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom Agents

> Create your own AI agents and add them to the GAIA Agent UI

<Info>
  **Prerequisites:** Complete the [Setup](/docs/setup) guide and have the [Agent UI](/docs/guides/agent-ui) running before you start.
</Info>

## Overview

GAIA's agent registry lets you extend the Agent UI with your own custom agents. Each agent lives in its own directory under `~/.gaia/agents/` as a Python module. Once placed there, the agent appears automatically in the **agent selector** dropdown of the Gaia Agent UI.

Custom agents can have their own:

* **Personality and instructions** (system prompt)
* **Tools** (RAG, file search, shell, image generation, vision, plus your own `@tool` functions)
* **Preferred models** (override the server default)
* **Conversation starters** (suggestion chips in the UI)
* **MCP servers** (any Model Context Protocol server)
* **Connectors** ([Google, GitHub, and more](/docs/connectors)) — give your agent permission to read your real email, calendar, repos, etc.

<Note>
  This guide builds a **local** custom agent in `~/.gaia/agents/` for your own Agent UI.
  When it's ready to **share** — so anyone can install it from the [Agent Hub](https://amd-gaia.ai/hub) —
  jump to [Publish and share your agent](#publish-and-share-your-agent).
</Note>

<Tip>
  **Using Claude Code in the GAIA repo?** Invoke the **`gaia-build-agent`** skill — it
  walks scaffolding → tools → model → test end-to-end, then hands off to the
  **`agent-hub-release`** skill for shipping.
</Tip>

***

## Quick Start: Gaia Builder Agent

The fastest way to create an agent is to use the built-in **Gaia Builder Agent**.

<Steps>
  <Step title="Open the Agent UI">
    Start the GAIA Agent UI: `gaia chat --ui`
  </Step>

  <Step title="Click the + button">
    In the chat input footer, click the **+** icon (to the left of the agent picker).
    On the welcome screen, click **Build a Custom Agent Template**.
  </Step>

  <Step title="Follow the prompts">
    The Builder (an **alpha** feature) greets you, asks what to name the agent and what it should do, and asks whether you want MCP support — then scaffolds a starter agent with a personality and conversation starters tailored to your description.
  </Step>

  <Step title="Use your new agent">
    Your agent is immediately available in the agent selector dropdown — no restart needed.
  </Step>
</Steps>

The Builder creates a **starter template** at `~/.gaia/agents/<your-agent-name>/agent.py` — a tailored persona and conversation starters (plus MCP wiring if you asked for it). It's a starting point you extend, not a turnkey agent: it won't fetch data or perform complex tasks on its own until you add tools. Open the file to refine the instructions and add capabilities (see [Python Agent](#python-agent) below).

<Tip>
  Need your agent to read real data — Gmail, GitHub, your calendar? Skip ahead to [Using GAIA connectors](#using-gaia-connectors).
</Tip>

***

## Python Agent

For full control — custom tools, built-in tool mixins, MCP servers, or complex logic — write a Python module.

### Directory structure

```
~/.gaia/agents/
└── my-agent/
    ├── agent.py          # required — defines the Agent subclass
    └── agent.yaml        # optional sidecar — only the `models:` list is read
```

### Minimal Python agent

```python theme={null}
from gaia.agents.base.agent import Agent
from gaia.agents.base.tools import _TOOL_REGISTRY, tool


class MyPythonAgent(Agent):
    AGENT_ID = "my-python-agent"
    AGENT_NAME = "My Python Agent"
    AGENT_DESCRIPTION = "An agent with a custom tool"

    def _get_system_prompt(self) -> str:
        return "You are a helpful assistant with a custom greet tool."

    def _register_tools(self) -> None:
        _TOOL_REGISTRY.clear()

        @tool
        def greet(name: str) -> str:
            """Greet a person by name."""
            return f"Hello, {name}! Welcome to GAIA."
```

This mirrors what the Builder Agent scaffolds. The only method a subclass **must**
implement is `_register_tools()` (it's abstract on the base `Agent`); the other two
have sensible defaults you can override:

| Method                 | Required? | Purpose                                                                                      |
| ---------------------- | --------- | -------------------------------------------------------------------------------------------- |
| `_register_tools()`    | **Yes**   | Registers tools into `_TOOL_REGISTRY`                                                        |
| `_get_system_prompt()` | Optional  | Returns the system prompt string (defaults to `""`, i.e. mixin prompts only)                 |
| `_create_console()`    | Optional  | Returns the console handler (defaults to `AgentConsole`, or a silent console in silent mode) |

### Adding built-in tools

GAIA ships with ready-to-use tool mixins. To enable most of them, inherit the mixin and call its `register_*_tools()` method from `_register_tools()`. The `sd` and `vlm` mixins are the exception — call `init_sd()` / `init_vlm()` (which set up the client **and** register the tools in one step):

| Tool          | Mixin                                                     | Register call                       |
| ------------- | --------------------------------------------------------- | ----------------------------------- |
| `rag`         | `gaia.agents.tools.rag_tools.RAGToolsMixin`               | `self.register_rag_tools()`         |
| `file_search` | `gaia.agents.tools.file_tools.FileSearchToolsMixin`       | `self.register_file_search_tools()` |
| `file_io`     | `gaia.agents.tools.file_io_tools.FileIOToolsMixin`        | `self.register_file_io_tools()`     |
| `shell`       | `gaia.agents.tools.shell_tools.ShellToolsMixin`           | `self.register_shell_tools()`       |
| `screenshot`  | `gaia.agents.tools.screenshot_tools.ScreenshotToolsMixin` | `self.register_screenshot_tools()`  |
| `sd`          | `gaia.sd.mixin.SDToolsMixin`                              | `self.init_sd()`                    |
| `vlm`         | `gaia.vlm.mixin.VLMToolsMixin`                            | `self.init_vlm()`                   |

<Tip>
  To call out to external services (Gmail, GitHub, Slack, etc.) without baking API keys into your code, use [GAIA connectors](#using-gaia-connectors) instead of writing your own auth.
</Tip>

### Overriding the default model (optional)

If your agent needs a specific model, set `model_id` in `__init__()`:

```python theme={null}
def __init__(self, **kwargs):
    kwargs.setdefault("model_id", "Gemma-4-E4B-it-GGUF")
    super().__init__(**kwargs)
```

Alternatively, create a companion `agent.yaml` next to `agent.py` with a `models:` list — the registry reads that list as an ordered preference. The sidecar only honours the `models:` key; any other top-level key is ignored.

***

## Using GAIA connectors

If your agent needs to act on your behalf — read your inbox, list your
calendar events, query GitHub, post to Slack, etc. — wire it up to a
**GAIA connector** rather than asking users to paste API tokens into
your code.

A connector lets users:

1. **Authenticate once** in Settings → Connections (OAuth flow for
   Google-style providers, paste-an-API-key for MCP servers).
2. **Grant scopes per agent** so each agent only sees the data it
   actually needs. The Agent UI surfaces this when a user first picks
   your agent.
3. **Trust that secrets stay in the OS keyring**, never in plaintext
   files or env vars baked into your code.

Declare what your agent needs by setting `REQUIRED_CONNECTORS` on the
class, and call `get_credential_sync(connector_id, agent_id, required_scopes=[...])`
from inside a tool to get a usable token. The
[`connectors-demo` agent](https://github.com/amd/gaia/blob/main/hub/agents/python/connectors-demo/gaia_agent_connectors_demo/agent.py)
is a working reference for both `oauth_pkce` (Google) and `mcp_server`
(GitHub) connectors.

<Card title="Read the connectors guide" icon="plug" href="/docs/connectors">
  Walks through what connectors are, how the OAuth + MCP flows differ,
  per-agent grants, and a step-by-step setup for Google and GitHub.
</Card>

***

## Export and import agents

Custom agents can be packaged into a single `.zip` bundle and shared between machines, teammates, or environments. GAIA supports export and import from both the Agent UI and the CLI. Exports include every `~/.gaia/agents/*` directory with an `agent.py` — the installer-seeded bundled example included; imported copies land as user-owned agents the seeder never touches.

### From the Agent UI

Open **Settings → Custom Agents**.

* **Export All** — packages every custom agent under `~/.gaia/agents/` into a single `.zip` file and downloads it. Disabled when you have no custom agents.
* **Import** — opens a file picker for a `.zip` bundle and shows a confirmation dialog listing the agent IDs that will be installed. Imported agents register into the live registry; in most cases no restart is required.

### From the CLI

```bash theme={null}
# Export every custom agent to ~/.gaia/export.zip
gaia agent export

# Export to a specific path
gaia agent export --output ./my-agents.zip

# Import a bundle (shows IDs and prompts for confirmation)
gaia agent import ./my-agents.zip

# Import non-interactively (CI / scripts)
gaia agent import ./my-agents.zip --yes
```

See the [CLI reference](/docs/reference/cli#agent-command) for full option details.

### What's in the bundle

Each `.zip` contains a `bundle.json` manifest at the root and one directory per agent:

```
my-agents.zip
├── bundle.json                  # { format_version, exported_at, gaia_version, agent_ids[] }
├── my-research-bot/
│   ├── agent.py
│   └── agent.yaml               # if present
└── my-greeter/
    └── agent.py
```

Bundles are validated on import. The receiving end enforces conservative limits (max 1000 entries, 500 MB uncompressed, 50 MB per file, 100 MB upload via the UI) and rejects symlinks or path-traversal entries.

### Security

<Warning>
  **Bundles ship your agent source as-is.** Any API keys, hardcoded tokens, or secrets embedded in `agent.py` will be included in the export. Review the bundle — or better, move secrets to [GAIA connectors](#using-gaia-connectors) — before sharing.
</Warning>

<Note>
  **Importing runs third-party code.** A bundle's `agent.py` files execute inside your GAIA process. Both the CLI (`gaia agent import` without `--yes`) and the UI surface the agent IDs and require explicit confirmation before installing. Only import bundles from sources you trust.
</Note>

The UI endpoints (`POST /api/agents/export` and `POST /api/agents/import`) are localhost-only, CSRF-guarded with the `X-Gaia-UI` header, and disabled while a [tunnel](/docs/guides/agent-ui) is active.

***

## Examples

<AccordionGroup>
  <Accordion title="Example Agent — simple personality">
    ```python theme={null}
    from gaia.agents.base.agent import Agent
    from gaia.agents.base.console import AgentConsole


    class ExampleAgent(Agent):
        AGENT_ID = "example-agent"
        AGENT_NAME = "Example Agent"
        AGENT_DESCRIPTION = (
            "A bundled example agent that shows how installer-seeded custom agents work"
        )
        CONVERSATION_STARTERS = [
            "What are you an example of?",
            "How do I build my own agent like you?",
        ]

        def _get_system_prompt(self) -> str:
            return (
                "You are GAIA's bundled Example Agent — a working demonstration of a "
                "custom agent seeded by the installer. Explain plainly that you exist "
                "to show how custom agents are packaged and loaded, and point anyone "
                "who wants to build their own to "
                "https://amd-gaia.ai/docs/guides/custom-agent."
            )

        def _create_console(self) -> AgentConsole:
            return AgentConsole()

        def _register_tools(self) -> None:
            pass
    ```
  </Accordion>

  <Accordion title="Research Agent — RAG + file search">
    ```python theme={null}
    from gaia.agents.base.agent import Agent
    from gaia.agents.base.console import AgentConsole
    from gaia.agents.base.tools import _TOOL_REGISTRY
    from gaia.agents.tools.rag_tools import RAGToolsMixin
    from gaia.agents.tools.file_tools import FileSearchToolsMixin


    class ResearchAgent(Agent, RAGToolsMixin, FileSearchToolsMixin):
        AGENT_ID = "research-agent"
        AGENT_NAME = "Research Agent"
        AGENT_DESCRIPTION = "Searches documents and files to answer questions"
        CONVERSATION_STARTERS = [
            "Summarize my project notes",
            "Find files related to...",
            "What does the documentation say about...?",
        ]

        def _get_system_prompt(self) -> str:
            return (
                "You are a thorough research assistant. When asked a question, "
                "first search available documents and files before answering. "
                "Always cite your sources."
            )

        def _create_console(self) -> AgentConsole:
            return AgentConsole()

        def _register_tools(self) -> None:
            _TOOL_REGISTRY.clear()
            self.register_rag_tools()
            self.register_file_search_tools()
    ```
  </Accordion>

  <Accordion title="Code Review Agent — shell + file I/O">
    ```python theme={null}
    from gaia.agents.base.agent import Agent
    from gaia.agents.base.console import AgentConsole
    from gaia.agents.base.tools import _TOOL_REGISTRY
    from gaia.agents.tools.shell_tools import ShellToolsMixin
    from gaia.agents.tools.file_io_tools import FileIOToolsMixin


    class CodeReviewAgent(Agent, FileIOToolsMixin, ShellToolsMixin):
        AGENT_ID = "code-review-agent"
        AGENT_NAME = "Code Review Agent"
        AGENT_DESCRIPTION = "Reviews code and runs linters"

        def _get_system_prompt(self) -> str:
            return (
                "You are an expert code reviewer. You read files, run linters, "
                "and provide actionable feedback. Be specific about file names "
                "and line numbers. Prioritize correctness and security."
            )

        def _create_console(self) -> AgentConsole:
            return AgentConsole()

        def _register_tools(self) -> None:
            _TOOL_REGISTRY.clear()
            self.register_file_io_tools()
            self.register_shell_tools()
    ```
  </Accordion>
</AccordionGroup>

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="My agent doesn't appear in the selector">
    * Ensure the directory is under `~/.gaia/agents/<id>/` and contains `agent.py`.
    * The directory name does not need to match the `AGENT_ID` class attribute, but it helps.
    * Check the server logs for warnings like `Failed to load agent from ...`.
    * If you created the agent manually (not via the Builder), **restart the GAIA server** — discovery runs at boot. Agents created via the Builder Agent are loaded immediately without a restart.
  </Accordion>

  <Accordion title="Agent fails to load">
    Check the server logs for load errors. Ensure `AGENT_ID`, `AGENT_NAME`, and `AGENT_DESCRIPTION` are set as class attributes, and that the required `_register_tools()` method is implemented (`_get_system_prompt()` and `_create_console()` are optional overrides).
  </Accordion>

  <Accordion title="Imported agent doesn't appear or shows errors">
    * The import response (UI status banner or CLI output) lists per-agent errors under `errors[]` — check those first.
    * Confirm each agent directory in the bundle contains an `agent.py` at its root. Bundles missing `agent.py` are rejected.
    * If `requires_restart: true` is reported, restart the GAIA server to pick up the new agent.
    * Bundles produced on a different GAIA version may reference imports that don't exist locally — check the server logs for `ImportError`.
  </Accordion>

  <Accordion title="MCP server not connecting">
    * Ensure `npx` or the MCP server command is installed and accessible in `$PATH`.
    * Check the server logs for `MCP` connection errors.
    * Test the MCP server standalone before adding it to your agent.
  </Accordion>

  <Accordion title="Agent uses wrong model">
    If your preferred model isn't loaded on the Lemonade server, GAIA falls back to the server default. Run `gaia init` to download additional models.
  </Accordion>
</AccordionGroup>

***

## Publish and share your agent

The agent you built lives in `~/.gaia/agents/` — yours, on your machine. To let
**anyone install it** from the [Agent Hub](https://amd-gaia.ai/hub) (and PyPI/npm), package it as a hub
agent and publish it through the curated PR route:

<Steps>
  <Step title="Package it">
    `gaia agent init <id>` scaffolds a publishable package under
    `hub/agents/python/<id>/` — a `gaia-agent.yaml` manifest, your agent code, a
    README, and `pyproject.toml`. Move your `agent.py` logic in and fill the manifest.
  </Step>

  <Step title="Validate it">
    `gaia agent test --lint` checks the manifest and that the agent loads. Add tests
    and a README — both are required for review.
  </Step>

  <Step title="Open a PR">
    Commit the package under `hub/agents/python/<id>/` and open a PR to
    [`amd/gaia`](https://github.com/amd/gaia). The **PR route needs no token** —
    maintainers review, and the automated pipeline publishes it to the Hub + PyPI on
    merge. (A direct-publish token route exists too, for maintainers.)
  </Step>
</Steps>

**What's required:** a valid manifest, a README, tests, and passing review. The full
contract — manifest fields, the four package parts, versioning + immutability, and
both publish routes — is in the publishing guide.

<CardGroup cols={2}>
  <Card title="Publishing guide → Hub + PyPI" icon="upload" href="/docs/guides/hub-publishing">
    The complete walkthrough: package layout, manifest, the no-token PR route vs
    direct token publish, requirements, and verification.
  </Card>

  <Card title="Claude Code skills" icon="robot" href="https://github.com/amd/gaia/tree/main/.claude/skills">
    In the GAIA repo, the **`gaia-build-agent`** skill builds and the
    **`agent-hub-release`** skill cuts a frozen-binary + npm sidecar release (like the
    email agent). Invoke them with the Skill tool.
  </Card>
</CardGroup>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Agent System SDK" icon="code" href="/docs/sdk/core/agent-system">
    Deep dive into the base Agent class, tool registry, and state machine
  </Card>

  <Card title="MCP Integration" icon="plug" href="/docs/sdk/infrastructure/mcp">
    Connect any MCP server to extend your agent with external tools
  </Card>

  <Card title="GAIA Connectors" icon="key" href="/docs/connectors">
    Give your agent permission to read Gmail, GitHub, Slack, and more — without baking API keys into code
  </Card>

  <Card title="RAG SDK" icon="magnifying-glass" href="/docs/sdk/sdks/rag">
    Add document Q\&A to your agent with the RAG SDK
  </Card>

  <Card title="Agent UI Guide" icon="browser" href="/docs/guides/agent-ui">
    Learn about the GAIA Agent UI and how agents are surfaced there
  </Card>

  <Card title="Custom Installer" icon="box-archive" href="/docs/playbooks/custom-installer/index">
    Ship your agent pre-loaded in a branded, signed GAIA installer
  </Card>
</CardGroup>

***

<small style={{ color: '#666' }}>
  **License**
  Copyright(C) 2024-2026 Advanced Micro Devices, Inc. All rights reserved.
  SPDX-License-Identifier: MIT
</small>
