Comprehensive developer toolkit providing reusable skills for Java/Spring Boot, TypeScript/NestJS/React/Next.js, Python, PHP, AWS CloudFormation, AI/RAG, DevOps, and more.
82
82%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Risky
Do not use without reviewing
Define tools and enable AI agents to interact with external systems, APIs, and services using LangChain4j's annotation-based and programmatic tool system.
LangChain4j's tool system enables AI agents to execute external functions through declarative annotations and programmatic interfaces. Tools are defined using the @Tool annotation and automatically registered with AI services, allowing LLMs to perform actions beyond text generation such as database queries, API calls, and calculations.
Use this skill when:
Follow these steps to implement tools with LangChain4j:
Create methods annotated with @Tool in a class:
public class WeatherTools {
@Tool("Get current weather for a city")
public String getWeather(
@P("City name") String city,
@P("Temperature unit (celsius or fahrenheit)", required = false) String unit) {
// Implementation
return weatherService.getWeather(city, unit);
}
}Use @P annotation for clear parameter descriptions that help the LLM understand how to call the tool:
@Tool("Calculate total order amount")
public double calculateOrderTotal(
@P("List of product IDs") List<String> productIds,
@P("Customer discount percentage", required = false) Double discount) {
// Implementation
}Connect tools to an AI service using the AiServices builder:
MathAssistant assistant = AiServices.builder(MathAssistant.class)
.chatModel(chatModel)
.tools(new Calculator(), new WeatherService())
.build();Implement error handling for tool failures:
AiServices.builder(Assistant.class)
.chatModel(chatModel)
.tools(new ExternalServiceTools())
.toolExecutionErrorHandler((request, exception) -> {
log.error("Tool execution failed: {}", exception.getMessage());
return "An error occurred while processing your request";
})
.build();Track tool calls for debugging and analytics:
Result<String> result = assistant.chat(question);
result.toolExecutions().forEach(execution ->
log.info("Executed tool: {} in {}ms",
execution.request().name(),
execution.duration().toMillis())
);// Define tools using @Tool annotation
public class CalculatorTools {
@Tool("Add two numbers")
public double add(double a, double b) {
return a + b;
}
}
// Register with AiServices builder
interface MathAssistant {
String ask(String question);
}
MathAssistant assistant = AiServices.builder(MathAssistant.class)
.chatModel(chatModel)
.tools(new CalculatorTools())
.build();AiServices.builder(AssistantInterface.class)
// Static tool registration
.tools(new Calculator(), new WeatherService())
// Dynamic tool provider
.toolProvider(new DynamicToolProvider())
// Concurrent execution
.executeToolsConcurrently()
// Error handling
.toolExecutionErrorHandler((request, exception) -> {
return "Error: " + exception.getMessage();
})
// Memory for context
.chatMemoryProvider(userId -> MessageWindowChatMemory.withMaxMessages(20))
.build();Use @Tool annotation to define methods as executable tools:
public class BasicTools {
@Tool("Add two numbers")
public int add(@P("first number") int a, @P("second number") int b) {
return a + b;
}
@Tool("Get greeting")
public String greet(@P("name to greet") String name) {
return "Hello, " + name + "!";
}
}Provide clear parameter descriptions using @P annotation:
public class WeatherService {
@Tool("Get current weather conditions")
public String getCurrentWeather(
@P("City name or coordinates") String location,
@P("Temperature unit (celsius, fahrenheit)", required = false) String unit) {
// Implementation with validation
if (location == null || location.trim().isEmpty()) {
return "Location is required";
}
return weatherClient.getCurrentWeather(location, unit);
}
}Use Java records and descriptions for complex objects:
public class OrderService {
@Description("Customer order information")
public record OrderRequest(
@Description("Customer ID") String customerId,
@Description("List of items") List<OrderItem> items,
@JsonProperty(required = false) @Description("Delivery instructions") String instructions
) {}
@Tool("Create customer order")
public String createOrder(OrderRequest order) {
return orderService.processOrder(order);
}
}Access user context using @ToolMemoryId:
public class PersonalizedTools {
@Tool("Get user preferences")
public String getPreferences(
@ToolMemoryId String userId,
@P("Preference category") String category) {
return preferenceService.getPreferences(userId, category);
}
}Create tools that change based on context:
public class ContextAwareToolProvider implements ToolProvider {
@Override
public ToolProviderResult provideTools(ToolProviderRequest request) {
String message = request.userMessage().singleText().toLowerCase();
var builder = ToolProviderResult.builder();
if (message.contains("weather")) {
builder.add(weatherToolSpec, weatherExecutor);
}
if (message.contains("calculate")) {
builder.add(calcToolSpec, calcExecutor);
}
return builder.build();
}
}Return results immediately without full AI response:
public class QuickTools {
@Tool(value = "Get current time", returnBehavior = ReturnBehavior.IMMEDIATE)
public String getCurrentTime() {
return LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
}Handle tool execution errors gracefully:
AiServices.builder(Assistant.class)
.chatModel(chatModel)
.tools(new ExternalServiceTools())
.toolExecutionErrorHandler((request, exception) -> {
if (exception instanceof ApiException) {
return "Service temporarily unavailable: " + exception.getMessage();
}
return "An error occurred while processing your request";
})
.build();Implement circuit breakers and retries:
public class ResilientService {
private final CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("external-api");
@Tool("Get external data")
public String getExternalData(@P("Data identifier") String id) {
return circuitBreaker.executeSupplier(() -> {
return externalApi.getData(id);
});
}
}@Service
public class MultiDomainToolService {
public String processRequest(String userId, String request, String domain) {
String contextualRequest = String.format("[Domain: %s] %s", domain, request);
Result<String> result = assistant.chat(userId, contextualRequest);
// Log tool usage
result.toolExecutions().forEach(execution ->
analyticsService.recordToolUsage(userId, domain, execution.request().name()));
return result.content();
}
}interface StreamingAssistant {
TokenStream chat(String message);
}
StreamingAssistant assistant = AiServices.builder(StreamingAssistant.class)
.streamingChatModel(streamingChatModel)
.tools(new Tools())
.build();
TokenStream stream = assistant.chat("What's the weather and calculate 15*8?");
stream
.onToolExecuted(execution ->
System.out.println("Executed: " + execution.request().name()))
.onPartialResponse(System.out::print)
.onComplete(response -> System.out.println("Complete!"))
.start();executeToolsConcurrently() for independent toolspublic class CalculatorTools {
@Tool("Add two numbers")
public double add(@P("First number") double a,
@P("Second number") double b) {
return a + b;
}
@Tool("Multiply two numbers")
public double multiply(@P("First number") double a,
@P("Second number") double b) {
return a * b;
}
}
// Usage
interface MathAssistant {
String ask(String question);
}
MathAssistant assistant = AiServices.builder(MathAssistant.class)
.chatModel(chatModel)
.tools(new CalculatorTools())
.build();
String result = assistant.ask("What is 15 times 7 plus 3?");@Component
public class DatabaseTools {
private final CustomerRepository repository;
@Tool("Get customer information by ID")
public Customer getCustomer(@P("Customer ID") Long customerId) {
return repository.findById(customerId)
.orElseThrow(() -> new IllegalArgumentException("Customer not found"));
}
@Tool("Update customer email address")
public String updateEmail(
@P("Customer ID") Long customerId,
@P("New email address") String newEmail) {
Customer customer = repository.findById(customerId)
.orElseThrow(() -> new IllegalArgumentException("Customer not found"));
customer.setEmail(newEmail);
repository.save(customer);
return "Email updated successfully";
}
}@Component
public class ApiTools {
private final WebClient webClient;
@Tool("Get current stock price")
public String getStockPrice(@P("Stock symbol") String symbol) {
return webClient.get()
.uri("/api/stocks/{symbol}", symbol)
.retrieve()
.bodyToMono(String.class)
.block();
}
}public class UserPreferencesTools {
@Tool("Get user preferences for a category")
public String getPreferences(
@ToolMemoryId String userId,
@P("Preference category (e.g., theme, language)") String category) {
return preferencesService.getPreferences(userId, category);
}
@Tool("Set user preference")
public String setPreference(
@ToolMemoryId String userId,
@P("Preference category") String category,
@P("Preference value") String value) {
preferencesService.setPreference(userId, category, value);
return "Preference saved";
}
}public class DynamicToolProvider implements ToolProvider {
private final Map<String, Object> availableTools = new HashMap<>();
public void registerTool(String name, ToolSpecification spec, ToolExecutor executor) {
availableTools.put(name, new ToolWithSpec(spec, executor));
}
@Override
public ToolProviderResult provideTools(ToolProviderRequest request) {
var builder = ToolProviderResult.builder();
String message = request.userMessage().singleText().toLowerCase();
// Dynamically filter tools based on user message
if (message.contains("weather")) {
builder.add(weatherToolSpec, weatherExecutor);
}
if (message.contains("calculate") || message.contains("math")) {
builder.add(calculatorToolSpec, calculatorExecutor);
}
return builder.build();
}
}For detailed API reference, examples, and advanced patterns, see:
Problem: LLM calls tools that don't exist
Solution: Implement hallucination handler:
.hallucinatedToolNameStrategy(request -> {
return ToolExecutionResultMessage.from(request,
"Error: Tool '" + request.name() + "' does not exist");
})Problem: Tools receive invalid parameters
Solution: Add input validation and error handlers:
.toolArgumentsErrorHandler((error, context) -> {
return ToolErrorHandlerResult.text("Invalid arguments: " + error.getMessage());
})Problem: Tools are slow or timeout
Solution: Use concurrent execution and resilience patterns:
.executeToolsConcurrently(Executors.newFixedThreadPool(5))
.toolExecutionTimeout(Duration.ofSeconds(30))langchain4j-ai-services-patternslangchain4j-rag-implementation-patternslangchain4j-spring-boot-integrationplugins
developer-kit-ai
skills
chunking-strategy
prompt-engineering
developer-kit-aws
skills
aws
aws-cli-beast
aws-cost-optimization
aws-drawio-architecture-diagrams
aws-sam-bootstrap
aws-cloudformation
aws-cloudformation-auto-scaling
references
aws-cloudformation-bedrock
references
aws-cloudformation-cloudfront
references
aws-cloudformation-cloudwatch
references
aws-cloudformation-dynamodb
references
aws-cloudformation-ec2
aws-cloudformation-ecs
references
aws-cloudformation-elasticache
aws-cloudformation-iam
references
aws-cloudformation-lambda
references
aws-cloudformation-rds
aws-cloudformation-s3
references
aws-cloudformation-security
references
aws-cloudformation-task-ecs-deploy-gh
aws-cloudformation-vpc
developer-kit-core
skills
developer-kit-java
skills
aws-lambda-java-integration
aws-rds-spring-boot-integration
aws-sdk-java-v2-bedrock
aws-sdk-java-v2-core
aws-sdk-java-v2-dynamodb
aws-sdk-java-v2-kms
aws-sdk-java-v2-lambda
aws-sdk-java-v2-messaging
aws-sdk-java-v2-rds
aws-sdk-java-v2-s3
aws-sdk-java-v2-secrets-manager
graalvm-native-image
langchain4j
langchain4j-mcp-server-patterns
langchain4j-ai-services-patterns
references
langchain4j-mcp-server-patterns
references
langchain4j-rag-implementation-patterns
references
langchain4j-spring-boot-integration
langchain4j-testing-strategies
langchain4j-tool-function-calling-patterns
langchain4j-vector-stores-configuration
references
qdrant
references
spring-ai-mcp-server-patterns
references
spring-boot-actuator
spring-boot-cache
spring-boot-crud-patterns
spring-boot-dependency-injection
spring-boot-event-driven-patterns
spring-boot-openapi-documentation
spring-boot-project-creator
spring-boot-resilience4j
spring-boot-rest-api-standards
spring-boot-saga-pattern
spring-boot-security-jwt
assets
references
scripts
spring-boot-test-patterns
spring-data-jpa
references
spring-data-neo4j
references
unit-test-application-events
unit-test-bean-validation
unit-test-boundary-conditions
unit-test-caching
unit-test-config-properties
unit-test-controller-layer
unit-test-exception-handler
unit-test-json-serialization
unit-test-mapper-converter
unit-test-parameterized
unit-test-scheduled-async
unit-test-service-layer
unit-test-utility-methods
unit-test-wiremock-rest-api
developer-kit-php
skills
aws-lambda-php-integration
developer-kit-python
skills
aws-lambda-python-integration
developer-kit-tools
developer-kit-typescript
skills
aws-lambda-typescript-integration
better-auth
drizzle-orm-patterns
dynamodb-toolbox-patterns
references
nestjs
nestjs-best-practices
nestjs-code-review
nestjs-drizzle-crud-generator
scripts
nextjs-app-router
nextjs-authentication
nextjs-code-review
nextjs-data-fetching
references
nextjs-deployment
nextjs-performance
nx-monorepo
react-code-review
react-patterns
references
shadcn-ui
tailwind-css-patterns
references
tailwind-design-system
references
turborepo-monorepo
typescript-docs
typescript-security-review
zod-validation-utilities