# How to Create Custom Agents with Specific Tools and System Prompts in Forge

> Learn to create custom agents in Forge by defining tools and system prompts in YAML files. Forge loads and renders your agents effectively for enhanced automation.

- Repository: [Forge Code/forgecode](https://github.com/antinomyhq/forgecode)
- Tags: how-to-guide
- Published: 2026-04-08

---

**You can create custom agents in Forge by adding Markdown files with YAML front-matter to `./.forge/agents/` or `~/.forge/agents/`, defining the agent's `id`, `tools` whitelist, and `system_prompt` template, which Forge loads via `ForgeAgentRepository` and renders at runtime using the current environment context.**

Forge ships with three built-in agents—**forge**, **muse**, and **sage**—but the antinomyhq/forgecode repository allows you to create custom agents with specific tools and system prompts tailored to your workflow. These custom agents are defined in Markdown files with YAML front-matter that specify everything from tool access to model parameters. When you start a conversation, Forge parses these definitions, renders the system prompt template against the current environment, and enforces tool restrictions without requiring any code changes.

## Where Forge Discovers Custom Agents

Forge searches for agent definitions in three locations, with later sources overriding earlier ones:

1. **Project-local agents** – `./.forge/agents/*.md` relative to the current working directory.
2. **Global custom agents** – `~/.forge/agents/*.md` in your home directory.
3. **Built-in agents** – Compiled into the binary.

The resolution logic lives in **[`ForgeAgentRepository`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_repo/src/agent.rs)**, which walks these directories and keeps the last occurrence of each `id`. This precedence ensures that **CWD > Global > Built-in**, meaning a project-local definition always wins.

## Agent File Format and Front-Matter Schema

Each custom agent is a Markdown file parsed by **[`gray_matter`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_repo/src/agent_definition.rs)** into an **`AgentDefinition`**. The front-matter supports the following keys:

- `id` *(required)* – Unique identifier for addressing the agent (e.g., `:my-agent`).
- `title` – Human-readable name for listings.
- `description` – Summary shown in `forge agents list`.
- `system_prompt` – Template string for the system message.
- `tools` – Array of permitted tool names (supports glob patterns like `mcp_*`).
- `tool_supported` – Boolean override for model-level tool support.
- `custom_rules` – Additional style guidelines injected into the prompt.
- `provider`, `model`, `temperature`, `max_tokens` – Model configuration overrides.
- `max_turns`, `compact` – Execution control settings.

The file body after the front-matter serves as the **system prompt template** if `system_prompt` is omitted. Templates use Handlebars syntax and can reference variables like `{{env.cwd}}` and `{{tool_names.read}}`.

## How System Prompts Are Rendered at Runtime

When a conversation initiates, **[`SystemPrompt::add_system_message`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_app/src/system_prompt.rs)** constructs a `SystemContext` containing:

- The current `Environment` (working directory, OS, etc.).
- Available tool definitions from `ToolCatalog::iter()`.
- A `tool_names` mapping for template interpolation.
- Aggregated **custom rules** from both agent and repository levels.

The `TemplateEngine` renders the agent’s `system_prompt` template against this context, producing the final system message visible to the LLM.

## Restricting Tool Access with Whitelists

The **`tools`** array in your agent definition acts as a whitelist. During execution, **[`ToolResolver::is_allowed`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_app/src/tool_registry.rs)** (called via `validate_tool_call`) checks each tool invocation against this list, supporting glob patterns like `mcp_*` to match multiple tools. If a tool isn't listed, the call is blocked before reaching the executor.

## Creating Your First Custom Agent

Follow these steps to create custom agents with specific tools and system prompts in Forge:

1. **Create a Markdown file** with the agent definition (see examples below).
2. **Place the file** in `./.forge/agents/` for project-specific agents or `~/.forge/agents/` for global access.
3. **Verify discovery** by running `forge agents list` to see your agent alongside built-ins.
4. **Invoke the agent** using its `id`: `:agent-id your prompt here`.

Forge automatically resolves naming conflicts via **`resolve_agent_conflicts`** in [`agent.rs`](https://github.com/antinomyhq/forgecode/blob/main/agent.rs), ensuring local definitions override global ones.

## Practical Agent Configuration Examples

### Minimal Custom Agent

Create a code reviewer agent with limited tool access:

```markdown
---
id: code-reviewer
title: Code Reviewer
description: Reviews Rust code and suggests improvements.
tools:
  - fs_search
  - read
  - write
custom_rules: |
  - Be concise.
  - Prefer using `derive_setters` where appropriate.
---

You are a meticulous Rust code reviewer.  
Focus on correctness, safety, and idiomatic style.  
When you suggest a change, include an inline diff in markdown.

```

Save this as `~/.forge/agents/code-reviewer.md`. The body serves as the template, with access to `{{env.cwd}}` and tool name variables.

### Agent with Glob Tool Patterns

Whitelist entire tool categories using wildcards:

```markdown
---
id: data-fetcher
title: Data Fetcher
description: Retrieves remote data and stores it locally.
tools:
  - fetch
  - mcp_*
---

You are a data-fetching assistant. Use the `fetch` tool for HTTP GET requests.
When invoking any `mcp_*` tool, include a short justification.

```

The **`ToolResolver`** expands `mcp_*` to match all MCP-related tools automatically.

### Model Override Configuration

Force specific model settings for specialized tasks:

```markdown
---
id: vision-assistant
title: Vision Assistant
description: Works with images using a vision-capable model.
provider: openai
model: gpt-4o
temperature: 0.7
tools:
  - read
  - image_read
---

You are an assistant that can analyse images.  
When the user supplies an image path, use the `read` tool to retrieve its contents.
If the model supports image input, also call `image_read` to get a visual description.

```

These settings override global defaults for sessions using this agent.

### CLI Usage

List and invoke your custom agents:

```bash

# List all available agents

forge agents list

# Use the custom agent

:code-reviewer Review the error handling in src/main.rs

```

## Summary

- Forge loads custom agents from `./.forge/agents/` and `~/.forge/agents/`, with project-local files taking precedence over global and built-in definitions.
- Define agents using Markdown with YAML front-matter parsed into **`AgentDefinition`** structs; the `id` and `tools` fields are required for basic functionality.
- System prompts are Handlebars templates rendered by **[`SystemPrompt::add_system_message`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_app/src/system_prompt.rs)** with access to environment variables and tool catalogs.
- Tool access is restricted via whitelists processed by **[`ToolResolver::is_allowed`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_app/src/tool_registry.rs)**, supporting glob patterns for flexible categorization.
- Invoke custom agents using the `:agent-id` syntax in the Forge CLI.

## Frequently Asked Questions

### Can I override a built-in Forge agent like "forge" or "muse"?

Yes. Create a custom agent file with the same `id` as a built-in agent (e.g., `id: forge`) in `./.forge/agents/` or `~/.forge/agents/`. The **[`resolve_agent_conflicts`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_repo/src/agent.rs)** function ensures your definition takes precedence over the compiled default.

### What happens if I don't specify a tools list in the agent definition?

If the `tools` field is omitted or empty, the agent likely inherits default permissions or restrictions based on the **[`ToolResolver`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_app/src/tool_registry.rs)** implementation. However, for security and predictability, you should explicitly whitelist required tools using the `tools` array.

### Can I use custom variables in my system prompt templates?

Yes. The template engine passes a `SystemContext` that includes `env` (environment details), `tool_names` (mappings), and any custom variables defined in `TemplateConfig`. Reference them using Handlebars syntax like `{{env.cwd}}` or `{{tool_names.read}}` within your system prompt body or `system_prompt` front-matter field.

### How do I debug why my custom agent isn't loading?

Run `forge agents list` to verify the agent appears. If missing, check that:
1. The file is in `./.forge/agents/` or `~/.forge/agents/` with a `.md` extension.
2. The YAML front-matter is valid and the `id` field is present (required by **[`parse_agent_file`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_repo/src/agent.rs)**).
3. The file name doesn't contain spaces or special characters that might interfere with path resolution via **[`EnvironmentInfra::agent_path`](https://github.com/antinomyhq/forgecode/blob/main/crates/forge_app/src/infra.rs)**.