Java Client & Maven Plugin
The Java client library (com.retailcortex.castor:castor-client) is implemented as both a runtime library and a native Maven Plugin (castor-client).
By hooking directly into Maven’s build lifecycle (generate-resources, compile), the plugin validates skill dependencies, enforces 5-point SDLC compliance, and packages pre-compiled skills_manifest.json resources into target application JARs automatically.
Configure the plugin in your application’s pom.xml:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.company.agent</groupId>
<artifactId>agent-service</artifactId>
<version>1.0.0</version>
<build>
<plugins>
<!-- Enterprise Skills Loader Maven Plugin -->
<plugin>
<groupId>com.retailcortex.castor</groupId>
<artifactId>castor-client</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>generate-manifest</goal>
</goals>
</execution>
</executions>
<configuration>
<!-- Qualified skill root URIs to resolve during build -->
<roots>
<root>castor://skills/example.com/testing/test-skill/1.0.0</root>
<root>github://google/skills@main/tree/main/skills/cloud/gemini-api</root>
<root>file://${project.basedir}/skills</root>
</roots>
<!-- Target directory for generated resources -->
<outputDirectory>${project.build.directory}/generated-resources/skills</outputDirectory>
<outputFilename>skills_manifest.json</outputFilename>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- Runtime Client Library -->
<dependency>
<groupId>com.retailcortex.castor</groupId>
<artifactId>castor-client</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>
- Pre-Processing Execution: The
GenerateManifestMojofires during thegenerate-resourceslifecycle phase. - Skill Resolution: Resolves all specified
rootsfrom central Castor Registry servers (castor://,cstr://), GitHub (github://), local filesystem (file://), or local Maven artifacts (~/.m2). - Build Validation: Validates SDLC invariants (YAML frontmatter, CWE rules, retry policies). If validation fails, the Maven build fails.
- Resource Injection: Generates
skills_manifest.jsonand automatically registers${project.build.directory}/generated-resources/skillsinto the Maven project resources so it is bundled directly inside the final target.jar.
In MODULE.bazel:
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
maven.install(
artifacts = [
"com.fasterxml.jackson.core:jackson-databind:2.17.1",
"org.slf4j:slf4j-api:2.0.12",
],
)
use_repo(maven, "maven")
In BUILD.bazel:
java_library(
name = "agent_service_java",
srcs = glob(["src/main/java/**/*.java"]),
deps = [
"//clients/java:castor_client_java",
"@maven//:org_slf4j_slf4j_api",
],
)
Because the Maven plugin automatically injects skills_manifest.json into classloader resources, your Spring Boot or Javalin runtime loads skills with zero file I/O:
package com.company.agent;
import com.retailcortex.castor.loader.SkillLoader;
import com.retailcortex.castor.loader.SkillDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.file.Path;
import java.util.Map;
public class AgentApplication {
private static final Logger logger = LoggerFactory.getLogger(AgentApplication.class);
public static void main(String[] args) throws Exception {
// Load pre-compiled manifest from target directory
Path manifestPath = Path.of("target/classes/skills_manifest.json");
Map<String, SkillDefinition> skills = SkillLoader.loadSkillsFromManifest(manifestPath);
logger.info("Instantly loaded {} skills from pre-compiled manifest.", skills.size());
}
}
SkillLoader includes a test helper hook setHttpClient(HttpClient client) for isolated JUnit 5 unit tests:
import com.retailcortex.castor.loader.SkillLoader;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import java.net.http.HttpClient;
import java.net.http.HttpResponse;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
class SkillLoaderTest {
private HttpClient mockHttpClient;
private HttpResponse<String> mockResponse;
@BeforeEach
void setUp() {
mockHttpClient = Mockito.mock(HttpClient.class);
mockResponse = Mockito.mock(HttpResponse.class);
SkillLoader.setHttpClient(mockHttpClient);
}
@Test
void testLoadFromCastorServerMocked() throws Exception {
when(mockResponse.statusCode()).thenReturn(200);
when(mockResponse.body()).thenReturn("""
{
"id": "sk-9b1deb4d",
"name": "mocked-skill",
"description": "Mocked skill for testing",
"version": "1.0.0"
}
""");
when(mockHttpClient.send(any(), any())).thenReturn(mockResponse);
var skills = SkillLoader.loadSkillsFromCastorServer("example.com/testing/test-skill/1.0.0", null, "http://localhost:8080", "key");
assertThat(skills).containsKey("mocked-skill");
}
}
The Java client provides dynamic pre-call tool suggestions for autonomous agents, bounding candidates to the top ≤ 3 skills:
package com.company.agent;
import com.retailcortex.castor.loader.SkillRegistry;
import com.retailcortex.castor.loader.SkillDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
public class SkillSuggester {
private static final Logger logger = LoggerFactory.getLogger(SkillSuggester.class);
public static void main(String[] args) {
SkillRegistry registry = new SkillRegistry();
// Retrieve top 3 skills ranked by vector relevance
List<SkillDefinition> suggested = registry.suggestSkills(
"Generate BigQuery SQL analytics statement for retail orders",
3,
"http://localhost:8000"
);
for (SkillDefinition s : suggested) {
logger.info("- {}: {}", s.getName(), s.getDescription());
}
}
}
Loaded SkillDefinition records map directly to Google ADK system instructions and agent prompt configurations.
package com.company.agent;
import com.retailcortex.castor.loader.SkillRegistry;
import com.retailcortex.castor.loader.SkillDefinition;
import com.google.adk.agent.Agent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
public class ADKAgentRunner {
private static final Logger logger = LoggerFactory.getLogger(ADKAgentRunner.class);
public static void main(String[] args) throws Exception {
String serverUrl = System.getProperty("castor.server.url",
System.getenv().getOrDefault("CASTOR_SERVER_URL", "http://localhost:8000"));
SkillRegistry registry = new SkillRegistry();
// 1. Pre-call prompt grounding: retrieve top 3 skills dynamically
String prompt = "Generate BigQuery analytics statement for retail orders";
List<SkillDefinition> skills = registry.suggestSkills(prompt, 3, serverUrl);
StringBuilder instructions = new StringBuilder("You are an enterprise AI coding agent.\n");
for (SkillDefinition skill : skills) {
instructions.append(String.format("%n### %s%n%s%n", skill.getName(), skill.getInstructions()));
}
// 2. Instantiate Google ADK Agent grounded in suggested skill instructions
Agent agent = Agent.builder()
.name("retail-coding-agent")
.model("gemini-2.0-flash")
.systemInstruction(instructions.toString())
.build();
// 3. Execute ADK agent request
String response = agent.execute(prompt);
logger.info("ADK Agent Response:\n{}", response);
}
}
The Java SDK loads and evaluates test scenarios defined under scenarios/*.md:
package com.company.agent;
import com.retailcortex.castor.loader.ScenarioEvaluationResult;
import com.retailcortex.castor.loader.SkillDefinition;
import com.retailcortex.castor.loader.SkillLoader;
import java.nio.file.Path;
import java.util.List;
public class SkillScenarioRunner {
public static void main(String[] args) {
SkillDefinition skill = SkillLoader.loadSkillFromDir(Path.of("./skills/bazel-modules"));
if (skill == null) return;
skill.getScenarios().forEach((name, scenario) -> {
// Run prompt through agent and capture output and invoked tools
String agentOutput = "To build a Bazel module hermetically: bazel build //...";
List<String> toolsUsed = List.of("bazel");
ScenarioEvaluationResult result = SkillLoader.evaluateScenario(scenario, agentOutput, toolsUsed);
if (result.isPassed()) {
System.out.printf("[PASS] %s (Similarity: %.2f)%n", name, result.getSimilarityScore());
} else {
System.err.printf("[FAIL] %s (Similarity: %.2f, Errors: %s)%n", name, result.getSimilarityScore(), result.getErrors());
}
});
}
}
- Logging Compliance:
SkillLoaderstrictly utilizesSLF4Jfor all diagnostic logging. Never usee.printStackTrace()or stdout for error logging. - Immutable Thread Safety:
SkillDefinitionobjects are immutable Java records, enabling safe multi-threaded sharing across Spring Singletons and Weld CDI beans. - JIT Dynamic Grounding: Use
registry.suggestSkills(prompt, 3, serverUrl)to avoid blowing up agent context windows. - Fail-Fast Build Integrity: Configure
<failOnError>true</failOnError>in the plugin to ensure corrupt skill definitions abort CI/CD pipelines before deployment.