diff --git a/spring-ai-modules/pom.xml b/spring-ai-modules/pom.xml
index 09fc0ed8f1ae..0bafd5b562c5 100644
--- a/spring-ai-modules/pom.xml
+++ b/spring-ai-modules/pom.xml
@@ -21,6 +21,7 @@
spring-ai-3
spring-ai-4
spring-ai-agentic-patterns
+ spring-ai-agent-skills
spring-ai-chat-stream
spring-ai-introduction
spring-ai-mcp
diff --git a/spring-ai-modules/spring-ai-agent-skills/pom.xml b/spring-ai-modules/spring-ai-agent-skills/pom.xml
new file mode 100644
index 000000000000..31a662800a00
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agent-skills/pom.xml
@@ -0,0 +1,65 @@
+
+
+ 4.0.0
+
+
+ com.baeldung
+ spring-ai-modules
+ 0.0.1
+ ../pom.xml
+
+
+ spring-ai-agent-skills
+ spring-ai-agent-skills
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.ai
+ spring-ai-starter-model-anthropic
+
+
+ org.apache.tika
+ tika-core
+ ${tika.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+ org.springframework.ai
+ spring-ai-bom
+ ${spring-ai.version}
+ pom
+ import
+
+
+
+
+
+ 21
+ 1.1.4
+ 3.5.13
+ 2.0.17
+ 1.5.18
+ 3.3.0
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/spring-ai-modules/spring-ai-agent-skills/src/main/java/com/baeldung/springai/agentskills/anthropic/AgentSkillsController.java b/spring-ai-modules/spring-ai-agent-skills/src/main/java/com/baeldung/springai/agentskills/anthropic/AgentSkillsController.java
new file mode 100644
index 000000000000..555107e1c7ca
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agent-skills/src/main/java/com/baeldung/springai/agentskills/anthropic/AgentSkillsController.java
@@ -0,0 +1,42 @@
+package com.baeldung.springai.agentskills.anthropic;
+
+import javax.validation.Valid;
+
+import org.apache.tika.Tika;
+
+import org.springframework.http.ContentDisposition;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/agent-skills")
+@Validated
+public class AgentSkillsController {
+
+ private final AgentSkillsService agentSkillsService;
+ private final Tika tika = new Tika();
+
+ public AgentSkillsController(AgentSkillsService agentSkillsService) {
+ this.agentSkillsService = agentSkillsService;
+ }
+
+ @GetMapping("/report")
+ public ResponseEntity genReport(@RequestBody @Valid ReportRequest reportRequest) {
+ AnthropicDocument document = agentSkillsService.genReport(reportRequest);
+ return ResponseEntity.ok()
+ .contentType(MediaType.parseMediaType(tika.detect(document.content())))
+ .header(HttpHeaders.CONTENT_DISPOSITION,
+ ContentDisposition.attachment()
+ .filename(document.fileName())
+ .build()
+ .toString())
+ .body(document.content());
+ }
+
+}
diff --git a/spring-ai-modules/spring-ai-agent-skills/src/main/java/com/baeldung/springai/agentskills/anthropic/AgentSkillsService.java b/spring-ai-modules/spring-ai-agent-skills/src/main/java/com/baeldung/springai/agentskills/anthropic/AgentSkillsService.java
new file mode 100644
index 000000000000..d9adb75dd853
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agent-skills/src/main/java/com/baeldung/springai/agentskills/anthropic/AgentSkillsService.java
@@ -0,0 +1,56 @@
+package com.baeldung.springai.agentskills.anthropic;
+
+import java.util.List;
+
+import org.springframework.ai.anthropic.AnthropicChatModel;
+import org.springframework.ai.anthropic.AnthropicChatOptions;
+import org.springframework.ai.anthropic.AnthropicSkillsResponseHelper;
+import org.springframework.ai.anthropic.api.AnthropicApi;
+import org.springframework.ai.chat.client.ChatClient;
+import org.springframework.ai.chat.model.ChatResponse;
+import org.springframework.stereotype.Service;
+
+@Service
+public class AgentSkillsService {
+
+ private final AnthropicChatModel chatModel;
+ private final AnthropicApi anthropicApi;
+ private final MonthlySalesService monthlySalesService;
+
+ public AgentSkillsService(AnthropicChatModel chatModel, AnthropicApi anthropicApi, MonthlySalesService monthlySalesService) {
+ this.chatModel = chatModel;
+ this.anthropicApi = anthropicApi;
+ this.monthlySalesService = monthlySalesService;
+ }
+
+ public AnthropicDocument genReport(ReportRequest reportRequest) {
+ ChatResponse response = ChatClient.create(chatModel)
+ .prompt()
+ .system("Given the dataset of monthly sales for our product: " + monthlySalesService.getMonthlySalesForYear(2025))
+ .user(reportRequest.prompt())
+ .options(AnthropicChatOptions.builder()
+ .model("claude-sonnet-4-5")
+ .skill(AnthropicApi.AnthropicSkill.DOCX)
+ .skill(AnthropicApi.AnthropicSkill.PDF)
+ .skill(AnthropicApi.AnthropicSkill.PPTX)
+ .skill(AnthropicApi.AnthropicSkill.XLSX)
+ .maxTokens(4096)
+ .build())
+ .call()
+ .chatResponse();
+
+ List fileIds = AnthropicSkillsResponseHelper.extractFileIds(response);
+ if (fileIds.isEmpty()) {
+ throw new IllegalStateException("No document was generated by the DOCX skill");
+ }
+
+ return downloadReport(fileIds.get(0));
+ }
+
+ public AnthropicDocument downloadReport(String fileId) {
+ AnthropicApi.FileMetadata metadata = anthropicApi.getFileMetadata(fileId);
+ byte[] content = anthropicApi.downloadFile(fileId);
+ return new AnthropicDocument(metadata.filename(), content);
+ }
+
+}
diff --git a/spring-ai-modules/spring-ai-agent-skills/src/main/java/com/baeldung/springai/agentskills/anthropic/AnthropicDocument.java b/spring-ai-modules/spring-ai-agent-skills/src/main/java/com/baeldung/springai/agentskills/anthropic/AnthropicDocument.java
new file mode 100644
index 000000000000..9b05f18921de
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agent-skills/src/main/java/com/baeldung/springai/agentskills/anthropic/AnthropicDocument.java
@@ -0,0 +1,4 @@
+package com.baeldung.springai.agentskills.anthropic;
+
+public record AnthropicDocument(String fileName, byte[] content) {
+}
\ No newline at end of file
diff --git a/spring-ai-modules/spring-ai-agent-skills/src/main/java/com/baeldung/springai/agentskills/anthropic/Application.java b/spring-ai-modules/spring-ai-agent-skills/src/main/java/com/baeldung/springai/agentskills/anthropic/Application.java
new file mode 100644
index 000000000000..a89b1ca1f0b4
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agent-skills/src/main/java/com/baeldung/springai/agentskills/anthropic/Application.java
@@ -0,0 +1,12 @@
+package com.baeldung.springai.agentskills.anthropic;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+}
diff --git a/spring-ai-modules/spring-ai-agent-skills/src/main/java/com/baeldung/springai/agentskills/anthropic/MonthlySale.java b/spring-ai-modules/spring-ai-agent-skills/src/main/java/com/baeldung/springai/agentskills/anthropic/MonthlySale.java
new file mode 100644
index 000000000000..857037672249
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agent-skills/src/main/java/com/baeldung/springai/agentskills/anthropic/MonthlySale.java
@@ -0,0 +1,7 @@
+package com.baeldung.springai.agentskills.anthropic;
+
+import java.math.BigDecimal;
+import java.time.Month;
+
+public record MonthlySale(String product, int year, Month month, BigDecimal amount) {
+}
diff --git a/spring-ai-modules/spring-ai-agent-skills/src/main/java/com/baeldung/springai/agentskills/anthropic/MonthlySalesService.java b/spring-ai-modules/spring-ai-agent-skills/src/main/java/com/baeldung/springai/agentskills/anthropic/MonthlySalesService.java
new file mode 100644
index 000000000000..db5a3d3261c9
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agent-skills/src/main/java/com/baeldung/springai/agentskills/anthropic/MonthlySalesService.java
@@ -0,0 +1,41 @@
+package com.baeldung.springai.agentskills.anthropic;
+
+import java.math.BigDecimal;
+import java.time.Month;
+import java.util.List;
+
+import org.springframework.stereotype.Service;
+
+@Service
+public class MonthlySalesService {
+
+ public List getMonthlySalesForYear(int year) {
+ return List.of(
+ new MonthlySale("Product A", year, Month.JANUARY, new BigDecimal("1200")),
+ new MonthlySale("Product A", year, Month.FEBRUARY, new BigDecimal("1325")),
+ new MonthlySale("Product A", year, Month.MARCH, new BigDecimal("1410")),
+ new MonthlySale("Product A", year, Month.APRIL, new BigDecimal("1380")),
+ new MonthlySale("Product A", year, Month.MAY, new BigDecimal("1495")),
+ new MonthlySale("Product A", year, Month.JUNE, new BigDecimal("1560")),
+ new MonthlySale("Product A", year, Month.JULY, new BigDecimal("1620")),
+ new MonthlySale("Product A", year, Month.AUGUST, new BigDecimal("1585")),
+ new MonthlySale("Product A", year, Month.SEPTEMBER, new BigDecimal("1660")),
+ new MonthlySale("Product A", year, Month.OCTOBER, new BigDecimal("1715")),
+ new MonthlySale("Product A", year, Month.NOVEMBER, new BigDecimal("1780")),
+ new MonthlySale("Product A", year, Month.DECEMBER, new BigDecimal("1850")),
+ new MonthlySale("Product B", year, Month.JANUARY, new BigDecimal("950")),
+ new MonthlySale("Product B", year, Month.FEBRUARY, new BigDecimal("990")),
+ new MonthlySale("Product B", year, Month.MARCH, new BigDecimal("1045")),
+ new MonthlySale("Product B", year, Month.APRIL, new BigDecimal("1015")),
+ new MonthlySale("Product B", year, Month.MAY, new BigDecimal("1090")),
+ new MonthlySale("Product B", year, Month.JUNE, new BigDecimal("1135")),
+ new MonthlySale("Product B", year, Month.JULY, new BigDecimal("1180")),
+ new MonthlySale("Product B", year, Month.AUGUST, new BigDecimal("1160")),
+ new MonthlySale("Product B", year, Month.SEPTEMBER, new BigDecimal("1215")),
+ new MonthlySale("Product B", year, Month.OCTOBER, new BigDecimal("1270")),
+ new MonthlySale("Product B", year, Month.NOVEMBER, new BigDecimal("1330")),
+ new MonthlySale("Product B", year, Month.DECEMBER, new BigDecimal("1395"))
+ );
+ }
+
+}
diff --git a/spring-ai-modules/spring-ai-agent-skills/src/main/java/com/baeldung/springai/agentskills/anthropic/ReportRequest.java b/spring-ai-modules/spring-ai-agent-skills/src/main/java/com/baeldung/springai/agentskills/anthropic/ReportRequest.java
new file mode 100644
index 000000000000..29d6999bc846
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agent-skills/src/main/java/com/baeldung/springai/agentskills/anthropic/ReportRequest.java
@@ -0,0 +1,6 @@
+package com.baeldung.springai.agentskills.anthropic;
+
+import javax.validation.constraints.NotNull;
+
+public record ReportRequest(@NotNull String prompt) {
+}
diff --git a/spring-ai-modules/spring-ai-agent-skills/src/main/resources/application-anthropic.yml b/spring-ai-modules/spring-ai-agent-skills/src/main/resources/application-anthropic.yml
new file mode 100644
index 000000000000..c4b337a8578d
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agent-skills/src/main/resources/application-anthropic.yml
@@ -0,0 +1,8 @@
+spring:
+
+ application:
+ name: agentskills-anthropic
+
+ ai:
+ anthropic:
+ api-key: "${ANTHROPIC_API_KEY}"
diff --git a/spring-ai-modules/spring-ai-agent-skills/src/main/resources/application.yml b/spring-ai-modules/spring-ai-agent-skills/src/main/resources/application.yml
new file mode 100644
index 000000000000..c81b1a1fd268
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agent-skills/src/main/resources/application.yml
@@ -0,0 +1,3 @@
+spring:
+ profiles:
+ active: anthropic
\ No newline at end of file
diff --git a/spring-ai-modules/spring-ai-agent-skills/src/test/java/com/baeldung/springai/agentskills/anthropic/AgentSkillsControllerIntegrationTest.java b/spring-ai-modules/spring-ai-agent-skills/src/test/java/com/baeldung/springai/agentskills/anthropic/AgentSkillsControllerIntegrationTest.java
new file mode 100644
index 000000000000..3f27109c733f
--- /dev/null
+++ b/spring-ai-modules/spring-ai-agent-skills/src/test/java/com/baeldung/springai/agentskills/anthropic/AgentSkillsControllerIntegrationTest.java
@@ -0,0 +1,55 @@
+package com.baeldung.springai.agentskills.anthropic;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.bean.override.mockito.MockitoBean;
+import org.springframework.test.web.servlet.MockMvc;
+
+@SpringBootTest(properties = {
+ "spring.profiles.active=test",
+ "spring.ai.anthropic.api-key=test-key"
+})
+@AutoConfigureMockMvc
+class AgentSkillsControllerIntegrationTest {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @MockitoBean
+ private AgentSkillsService agentSkillsService;
+
+ @Test
+ void whenReportRequestIsValid_thenEndpointReturnsGeneratedDocument() throws Exception {
+ byte[] documentContent = "%PDF-1.7\nMock PDF".getBytes();
+ ReportRequest reportRequest = new ReportRequest("Generate a monthly sales summary");
+ AnthropicDocument generatedDocument = new AnthropicDocument("sales-report.pdf", documentContent);
+
+ when(agentSkillsService.genReport(reportRequest)).thenReturn(generatedDocument);
+
+ mockMvc.perform(get("/agent-skills/report")
+ .contentType(MediaType.APPLICATION_JSON)
+ .content("""
+ {
+ "prompt": "Generate a monthly sales summary"
+ }
+ """))
+ .andExpect(status().isOk())
+ .andExpect(content().contentType(MediaType.APPLICATION_PDF))
+ .andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"sales-report.pdf\""))
+ .andExpect(content().bytes(documentContent));
+
+ verify(agentSkillsService).genReport(reportRequest);
+ }
+
+}