# How Tool Groups Work in Embabel Actions: Core Concepts and Implementation

> Explore how Embabel actions use tool groups for external capabilities. Learn about discovery, resolution, and security controls in this core concept guide.

- Repository: [Embabel/embabel-agent](https://github.com/embabel/embabel-agent)
- Tags: deep-dive
- Published: 2026-08-09

---

**Embabel actions consume external capabilities through logical collections called tool groups, which are discovered via role-based requirements, resolved by a registry resolver, and injected into the execution context with permission-aware security controls.**

The `embabel/embabel-agent` repository implements a sophisticated capability system that allows AI agents to interact with external tools through structured groupings. Understanding how tool groups work in Embabel actions requires examining the core abstractions that define, resolve, and secure these collections. This article explores the complete lifecycle from metadata definition to runtime injection.

## Core Components of Embabel Tool Groups

### ToolGroupDescription and ToolGroupMetadata

In [`embabel-agent-api/src/main/kotlin/com/embabel/agent/core/ToolGroup.kt`](https://github.com/embabel/embabel-agent/blob/main/embabel-agent-api/src/main/kotlin/com/embabel/agent/core/ToolGroup.kt), the hierarchy begins with `ToolGroupDescription` (lines 26-48), which encapsulates human-readable descriptions, role identifiers, and usage notes. The concrete `ToolGroupMetadata` class extends this description (lines 98-105) to add artifact coordinates, versioning, and critical **permissions** such as `HOST_ACCESS` and `INTERNET_ACCESS`.

### ToolGroupRequirement

Actions declare their needs through `ToolGroupRequirement` (lines 51-55), which specifies a required role, optionally limits available tool names, and defines termination behavior if the requirement cannot be satisfied.

### ToolGroupResolver and RegistryToolGroupResolver

Resolution logic resides in [`ToolGroupResolver.kt`](https://github.com/embabel/embabel-agent/blob/main/ToolGroupResolver.kt), an interface implemented by [`RegistryToolGroupResolver.kt`](https://github.com/embabel/embabel-agent/blob/main/RegistryToolGroupResolver.kt). This default resolver maintains an in-memory registry, matching `ToolGroupRequirement` roles against registered groups and returning a `ToolGroupResolution` containing the actual tools.

## The Tool Group Lifecycle in Action Execution

The platform orchestrates tool groups through five distinct phases:

1. **Definition** – Developers instantiate `ToolGroup` using the `ToolGroup.ofTools` factory, supplying `ToolGroupMetadata` and native `Tool` implementations.

2. **Registration** – Groups are added to the `ToolGroupResolver`, typically via Spring bean registration or programmatic `addToolGroup` calls.

3. **Resolution** – When an action declares a requirement (e.g., `ToolGroupRequirement(role = "web")`), the resolver searches its registry and attaches the matching group to the execution context.

4. **Injection** – Resolved tools become part of the LLM's tool-use capability, available for invocation only when the model requests them.

5. **Info Reporting** – The `PlatformInfoController` exposes available groups via `/api/platform/tool-groups`, enabling runtime inspection of roles and permissions.

## Practical Implementation Example

The following Kotlin example demonstrates the complete workflow from definition to usage:

```kotlin
// 1️⃣ Define a custom tool group for web search
val webSearchMetadata = ToolGroupMetadata(
    description = "Web search tools",
    role = "web",
    name = "embabel-web-search",
    provider = "embabel",
    permissions = setOf(ToolGroupPermission.INTERNET_ACCESS)
)
val webSearchTools = listOf(SearchTool(), OpenPageTool())
val webSearchGroup = ToolGroup.ofTools(webSearchMetadata, webSearchTools)

// 2️⃣ Register the group (e.g., in a Spring @Configuration)
@Bean
fun toolGroupResolver(): ToolGroupResolver =
    RegistryToolGroupResolver("default", listOf(webSearchGroup))

// 3️⃣ Use the group in a PromptRunner
val result = promptRunner
    .withToolGroup(webSearchGroup)          // injects the group
    .run("""Find the latest news about Kotlin.""")
    
// 4️⃣ Retrieve available groups via REST (for debugging)
GET /api/platform/tool-groups
// → returns JSON with role, description, permissions, etc.

```

## Integration Points and Security

### PromptRunner Integration

The fluent API method `PromptRunner.withToolGroup(toolGroup)` records the `ToolGroupRequirement` and delegates resolution to the configured resolver at runtime. The test file [`FakePromptRunnerTest.kt`](https://github.com/embabel/embabel-agent/blob/main/FakePromptRunnerTest.kt) demonstrates this pattern in `embabel-agent-api/src/test/kotlin/com/embabel/agent/test/unit/`.

### REST API Exposure

`PlatformInfoController.getToolGroups()` in [`embabel-agent-common/embabel-agent-webmvc/src/main/kotlin/com/embabel/agent/web/rest/PlatformInfoController.kt`](https://github.com/embabel/embabel-agent/blob/main/embabel-agent-common/embabel-agent-webmvc/src/main/kotlin/com/embabel/agent/web/rest/PlatformInfoController.kt) surfaces the resolver's `availableToolGroups()` method via the `/api/platform/tool-groups` endpoint, returning JSON representations of role, description, and permissions.

### Permission Enforcement

`ToolGroupPermission` flags defined in [`ToolGroup.kt`](https://github.com/embabel/embabel-agent/blob/main/ToolGroup.kt) are consulted by security layers to enforce sandboxing rules. A group with `HOST_ACCESS` cannot execute in sandboxed actions unless explicitly permitted, preventing unauthorized system access.

## Summary

- **Tool groups** are logical collections of related tools defined by `ToolGroupMetadata` and instantiated via `ToolGroup.ofTools`.
- The **registry-based resolver** matches action requirements to available groups using role identifiers.
- **Permissions** like `INTERNET_ACCESS` and `HOST_ACCESS` are declared in metadata and enforced by the platform security layer.
- The **PromptRunner** fluent API enables direct injection of tool groups into action execution contexts.
- Runtime visibility is provided through the **PlatformInfoController** REST endpoint at `/api/platform/tool-groups`.

## Frequently Asked Questions

### What is the difference between ToolGroup and ToolGroupMetadata?

`ToolGroupMetadata` contains static descriptive data including permissions and artifact coordinates, while `ToolGroup` is the runtime representation that publishes the actual list of native `Tool` instances and provides an `infoString` for debugging purposes.

### How does RegistryToolGroupResolver match requirements to tool groups?

The resolver compares the `role` property of a `ToolGroupRequirement` against the roles defined in registered groups' metadata, returning a `ToolGroupResolution` containing the matching tools when found.

### Can an action use multiple tool groups simultaneously?

Yes, actions can declare multiple `ToolGroupRequirement` instances or chain multiple `withToolGroup()` calls on the PromptRunner, allowing the LLM to access tools from different logical groups within a single execution context.

### How are permissions enforced for tool groups?

Security layers inspect the `ToolGroupPermission` set stored in `ToolGroupMetadata` (such as `HOST_ACCESS` or `INTERNET_ACCESS`) to determine whether a group's tools can execute within the current sandbox or network restrictions.