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

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, 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, an interface implemented by 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:

// 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 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 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 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →