Sitemap

Spring AI Recipe: Getting Started with Embabel

5 min readJun 22, 2026

--

What if your agent could figure out its own plan instead of following a predefined workflow? That’s the idea behind Embabel. In this recipe, you’ll create a simple goal-driven agent using Embabel.

Press enter or click to view image in full size

Embabel is an agentic framework created by Rod Johnson, the creator of Spring, and built on top of Spring AI. It uses Goal-Oriented Action Planning (GOAP) to autonomously determine how to achieve a goal from a given input and the current state of the world.

This differs from workflow-based approaches such as the graph workflows we’ve built in previous recipes using Spring AI Alibaba Graph. Rather than following a predefined sequence of steps, Embabel determines which actions should be performed to achieve a desired outcome.

One of Embabel’s most interesting characteristics is that its planning is deterministic. Given the same input and world state, it will produce the same plan every time. And for Spring developers, the programming model will feel familiar, relying heavily on annotations and strongly-typed objects.

In this recipe, we’ll build a simple Embabel agent that greets someone by name. Despite its simplicity, it demonstrates the key concepts you’ll use when developing more sophisticated Embabel agents.

Creating an Embabel Agent

At the time of writing, Embabel 0.4.0 targets Spring Boot 3.x and Spring AI 1.x. Therefore, create a Spring Boot 3.5.x project rather than a Spring Boot 4 project.

Also, although Embabel itself is developed in Kotlin and provides a rich Kotlin development experience, we’re going to stick with Java to develop this recipe’s agent.

Create a brand new Spring Boot project. Select Spring Boot 3.5.15 (or the latest release in the Spring Boot 3.5.x line). No additional dependencies are required from the Initializr. We’ll add the Embabel dependencies manually.

Once the project has been created, add the following dependencies to the build:

ext {
embabelVersion = '0.4.0'
}

dependencies {
implementation("com.embabel.agent:embabel-agent-starter:$embabelVersion")
implementation "com.embabel.agent:embabel-agent-starter-openai:$embabelVersion"
implementation "com.embabel.agent:embabel-agent-starter-shell:$embabelVersion"
//...dependencies from the Initializr go here ...
}

Next create a new agent class:

package com.example.embabelagent;

import com.embabel.agent.api.annotation.AchievesGoal;
import com.embabel.agent.api.annotation.Action;
import com.embabel.agent.api.annotation.Agent;
import com.embabel.agent.api.common.Ai;
import com.embabel.agent.domain.io.UserInput;

@Agent(name = "helloAgent",
description = "Says hello to someone.")
public class HelloAgent {
@Action(description = "Derive Person from input")
public Person derivePerson(UserInput userInput, Ai ai) {
var prompt = String.format(
"Derive the person from the input: %s",
userInput.getContent());
return ai
.withDefaultLlm()
.createObject(prompt, Person.class);
}

@Action(description = "Say hello to someone")
@AchievesGoal(
description = "Someone has been greeted with a friendly hello")
public Greeting sayHello(Person person) {
return new Greeting(
String.format("Hello, %s!", person.name()));
}

}

This agent contains two actions:

  • derivePerson() uses an LLM to extract a Person object from the user's input.
  • sayHello() creates a greeting for that person.

The sayHello() method is annotated with @AchievesGoal, indicating that it fulfills the goal of greeting someone.

This simple example illustrates several core Embabel concepts:

  • Methods annotated with @Action represent actions the agent can perform.
  • Methods annotated with @AchievesGoal represent goal-achieving actions.
  • Actions communicate through strongly-typed Java objects rather than strings.

Embabel uses these types to determine dependencies between actions. Because sayHello() requires a Person and derivePerson() produces a Person, Embabel can automatically determine that it must invoke derivePerson() before calling sayHello().

We’ll also need two simple domain types.

Greeting:

public record Greeting(String greetingText) {}

Person:

public record Person(String name) {}

Trying It Out

Run the application. After startup completes, you’ll be presented with the Embabel shell prompt:

embabel>

From this prompt you can explore the agent and its capabilities.

  • Type agents to see the available agents.
  • Type actions to see available actions and their preconditions and postconditions.
  • Type goals to see the goals that can be achieved.
  • Type help to see additional commands.
  • Type exit or quit to leave the shell.

You can also interact with the agent using the execute command:

embabel> execute "Say hello to Rod"

Or use the shorter x alias:

embabel> x "Say hello to Rod"

Before executing the request, Embabel determines which actions are required to achieve the goal. In this case, it first derives a Person from the input and then invokes the action that produces the greeting.

You’ll see quite a bit of logging as Embabel plans and executes the request. Eventually you’ll receive the final result:

{
"greetingText" : "Hello, Rod!"
}

We won’t examine the log output in detail in this recipe, but it’s worth spending some time looking through it. The logs provide valuable insight into how Embabel plans and executes actions to achieve a goal.

Using the Agent via MCP

Working with agents through Embabel’s shell is useful during development, but in a real-world environment you’ll typically expose them through MCP.

To do that, replace the shell dependency with Embabel’s MCP server dependency:

implementation(
"com.embabel.agent:embabel-agent-starter-mcpserver:$embabelVersion")

This allows Embabel to expose selected goals as MCP tools that can be invoked by any MCP-compatible client.

Next, configure the MCP server to use the Streamable HTTP transport and listen on port 3000:

server.port=3000
spring.ai.mcp.server.protocol=streamable

Finally, update the @AchievesGoal annotation on the sayHello() method:

@Action(description = "Say hello to someone")
@AchievesGoal(
description = "Someone has been greeted with a friendly hello",
export = @Export(
name = "sayHello",
remote = true,
startingInputTypes = Person.class))
public Greeting sayHello(Person person) {
return new Greeting(
String.format("Hello, %s!", person.name()));
}

The @Export configuration tells Embabel to expose this goal as a remote MCP tool named sayHello. The startingInputTypes attribute identifies the type that MCP clients should provide when invoking the tool.

Start the application again. This time you won’t see the Embabel shell prompt. Instead, the application is running as an MCP server. To interact with it, connect using an MCP client such as MCP Inspector or MCPJam. In either case, connect to the MCP server at the following URL: http://localhost:3000/mcp.

Once connected, you can inspect the available tools. In MCP Inspector you’ll see something like this:

Press enter or click to view image in full size

Notice that several tools are available. For now, we’ll focus on the sayHello tool. The others support more advanced Embabel functionality that we'll explore in future recipes.

Select the sayHello tool and enter a value into the name field. Then click Run Tool. The response should look something like this:

Press enter or click to view image in full size

Summary

In this recipe, you created a simple Embabel agent, executed it through Embabel’s shell interface, and then exposed it as an MCP tool. Although the example was intentionally simple, it introduced the key concepts behind Embabel: actions, goals, strongly-typed planning, and deterministic goal-oriented execution.

In future recipes, we’ll build more sophisticated agents that perform multi-step planning and reason over richer world models.

Get the code for this and other Spring AI recipes at https://github.com/habuma/spring-ai-recipes.

If you’re exploring Spring AI, I’ve written a book (Spring AI in Action) that covers the core foundations — and I’ll continue sharing newer patterns like this here as the ecosystem evolves. You can find my book on the publisher’s website, at Amazon, or anywhere you buy technical books.

--

--

Craig Walls
Craig Walls

Written by Craig Walls

Author Spring AI in Action, Spring in Action, and Build Talking Apps for Alexa