A minimal example of an agent built with Embabel for generating technical Markdown drafts.
- Understand the architecture of an Embabel agent in Java.
- Have a valid and working configuration for the project.
- Run the project locally and generate a sample article.
- Modify or extend actions and tool groups (search, scraping, memory, reasoning).
- Java 24+ and Maven 3.8+
- Git
- Docker to run MCP tools locally
- Environment variables:
OPENAI_API_KEY(if using OpenAI),BRAVE_API_KEY(if using Brave search)
src/main/java/es/omarall/agents/MinimalArticleAgent.java— main bean with agent actions.CustomToolGroupsConfiguration.java— tool group configuration (web-search, scrapping, memory, reasoning).SlugService.java— utility for slug generation.
src/main/resources/application.yml— MCP and LLM configuration.
-
Clone the repository and navigate to the project folder:
git clone https://github.com/oalles/minimal-article-agent.git cd minimal-article-agent -
Export the required environment variables (example):
export OPENAI_API_KEY=... export BRAVE_API_KEY=...
-
If you want to use local MCP tools with Docker, make sure Docker is installed.
Build and run: mvn spring-boot:run
In the agent console, run a sample prompt:
embabel> x "Generate an article in English explaining why Java developers should learn and use WebAssembly"
The agent is designed with atomic actions (GOAP-like). DTOs are defined as records for easy serialization/deserialization with PromptRunner.
Main DTOs (located as nested classes in MinimalArticleAgent or as records):
Topics(List<String> items)Reference(String url, String title, String summary)References(List<Reference> items)DocOutline(List<String> sections)Fact(String topic, String statement, List<String> sources)KnowledgePack(List<Fact> facts)Draft(String title, String body, List<String> references)DraftWithMeta(Draft draft, Summary summary, FAQ faq, Keywords keywords, String slug)FinalArticle(String markdown)Summary(String summary)FAQ(List<String> questions, List<String> answers)Keywords(List<String> keywords)
- Each tool group is declared as a
McpToolGroupbean with:ToolGroupDescription(description and id)- a readable name and origin (e.g., "Docker")
- permissions (
ToolGroupPermission), e.g.,INTERNET_ACCESS - a list of
McpSyncClient(injected automatically) - a predicate filtering tools by
toolDefinition().name()(e.g., contains "fetch" or "memory")
- Constant names in
CustomToolGroupsConfigurationindicate the ids to use in actions:web-search,academic-search,scrapping,memory-knowledge,reasoning-sequential
-
Annotate actions with
@Action(toolGroups = {"toolgroup-name"}) -
Example method in
MinimalArticleAgent:@Action(toolGroups = {CustomToolGroupsConfiguration.WEB_SEARCH_TOOL_GROUP, CustomToolGroupsConfiguration.SCRAPPING_TOOL_GROUP}) public References searchReferences(Topics topics, OperationContext ctx) { ... }
This indicates that, to execute
searchReferences, the planner can invoke tools belonging to those tool groups.
- Rod Johnson - articles on Embabel.
