Commit 3bdfe803 authored by Irene Denazi's avatar Irene Denazi
Browse files

Addition of agent skils

parent 127f46a0
Loading
Loading
Loading
Loading
Loading
+24 −2
Original line number Diff line number Diff line
@@ -23,9 +23,9 @@
	</organization>
	
	<properties>
		<spring.boot-version>3.5.5</spring.boot-version>
		<spring.boot-version>4.0.0</spring.boot-version>
		<java.version>17</java.version>
		<spring-ai.version>1.1.0</spring-ai.version>
		<spring-ai.version>2.0.0-M2</spring-ai.version>
		<camel.version>4.0.0-RC2</camel.version>
	</properties>

@@ -34,6 +34,19 @@
			<id>gitlab-maven</id>
			<url>https://labs.etsi.org/rep/api/v4/groups/260/-/packages/maven</url>
		</repository>
		<repository>
			<id>spring-snapshots</id>
			<name>Spring Snapshots</name>
			<url>https://repo.spring.io/snapshot</url>
			<snapshots>
				<enabled>true</enabled>
			</snapshots>
		</repository>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/milestone</url>
		</repository>
	</repositories>
	<distributionManagement>
		<repository>
@@ -97,6 +110,11 @@
		<dependency>
			<groupId>org.springframework.ai</groupId>
			<artifactId>spring-ai-starter-model-ollama</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springaicommunity</groupId>
			<artifactId>spring-ai-agent-utils</artifactId>
			<version>0.4.2</version>
		</dependency>
				<dependency>
			<groupId>org.commonmark</groupId>
@@ -164,6 +182,10 @@
			<groupId>org.apache.camel.springboot</groupId>
			<artifactId>camel-service-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.ai</groupId>
			<artifactId>spring-ai-retry</artifactId>
		</dependency>
	</dependencies>

	  <build>
+35 −38
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@ package org.etsi.osl.mcp.backend;

import java.util.Map;
import java.util.UUID;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.client.ChatClient;
@@ -10,7 +11,11 @@ import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.ChatMemoryRepository;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.tool.ToolCallback; 
import org.springframework.ai.chat.client.advisor.ToolCallAdvisor;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springaicommunity.agent.tools.SkillsTool;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@@ -28,28 +33,40 @@ public class ChatController {
    @Value("${spring.ai.chat.maxMessages}")
    private final int maxMessages = 100;
    
    ChatMemoryRepository chatMemoryRepository;

    ChatMemory chatMemory = MessageWindowChatMemory.builder()
      .chatMemoryRepository(chatMemoryRepository)
      .maxMessages( maxMessages )
      .build();
    
    public ChatController(ChatClient.Builder chatClientBuilder,
                           ToolCallbackProvider tools,
                           ToolCallbackProvider mcpTools,
                           @Value("${spring.ai.chat.system-prompt}") String systemPrompt,
                           ChatMemory chatMemory) {
                           @Value("${agent.skills.dirs}") String agentSkillsDir,
                           ChatMemoryRepository chatMemoryRepository) {
        // Validate that system prompt is not empty
        if (systemPrompt == null || systemPrompt.trim().isEmpty()) {
            throw new IllegalArgumentException("System prompt cannot be null or empty. Please configure SPRING_AI_CHAT_SYSTEM_PROMPT with a valid value.");
        }
        
        this.systemPrompt = systemPrompt;
        
        ChatMemory chatMemory = MessageWindowChatMemory.builder()
            .chatMemoryRepository(chatMemoryRepository)
            .maxMessages(maxMessages)
            .build();
        
        // Build SkillsTool - build() returns a ToolCallback, not SkillsTool
        ToolCallback skillsTool = SkillsTool.builder()  // FIXED TYPE HERE
            .addSkillsDirectory(agentSkillsDir)
            .build();
        

        this.chatClient = chatClientBuilder
            .defaultAdvisors(new SimpleLoggerAdvisor(), MessageChatMemoryAdvisor.builder(chatMemory).build()) 
                .defaultToolCallbacks(tools)
            .defaultAdvisors(
                new SimpleLoggerAdvisor(), 
                MessageChatMemoryAdvisor.builder(chatMemory).build()
            )
            .defaultToolCallbacks(mcpTools)
            .defaultToolCallbacks(skillsTool)  // Now this works!
            .defaultSystem(systemPrompt)
            .build();
            
        log.info("ChatController initialized with skills from: {}", agentSkillsDir);
        log.info("ChatController initialized with system prompt: {}", systemPrompt);
        this.conversationId = UUID.randomUUID().toString();
    }
@@ -64,12 +81,9 @@ public class ChatController {
        log.debug("Response from AI: {}", response);
        String markdownAnswer = formatResponse(question, response);
        log.debug("Markdown formatted response: {}", markdownAnswer);
        // var htmlResponse = MarkdownHelper.toHTML(markdownAnswer);
        // log.debug("HTML formatted response: {}", htmlResponse);
        return new Answer(markdownAnswer);
    }


    public String simpleAsk(Map<String, Object> headers, String question) {
        var response = chatClient.prompt()
                .user(question)
@@ -82,23 +96,6 @@ public class ChatController {

    private String formatResponse(Question question, String answer) {
        return answer;
//        var prompt = """
//                Following are the question and answer:
//                
//                Question: {question}
//                
//                Answer: {answer}
//                
//                Format the answer into plain human readable text and return only the formatted response.
//                """;
//        return chatClient
//                .prompt()
//                .user(u -> u.text(prompt)
//                        .param("question", question.question())
//                        .param("answer", answer)
//                )
//                .call()
//                .content();
    }

    public record Question(String question) {}
+45 −0
Original line number Diff line number Diff line
package org.etsi.osl.mcp.backend.configuration;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.modelcontextprotocol.spec.McpSchema;
import org.springframework.ai.mcp.McpToolFilter;
import org.springframework.ai.mcp.McpConnectionInfo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.regex.Pattern;

/**
 * Configurable MCP Tool Filter
 * 
 * Filters MCP tools based on a configurable regex pattern.
 * Only tools matching the pattern will be available to the chat client.
 * 
 * @author OpenSlice Team
 */
@Component
public class CustomMcpToolFilter implements McpToolFilter {
    
    private static final Logger log = LoggerFactory.getLogger(CustomMcpToolFilter.class);
    
    private final Pattern allowedPattern;
    
    public CustomMcpToolFilter(@Value("${mcp.allowed.tool.pattern:.*}") String patternString) {
        this.allowedPattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE);
        log.info("MCP Tool Filter initialized with pattern: {}", patternString);
    }

    @Override
    public boolean test(McpConnectionInfo connectionInfo, McpSchema.Tool tool) {
        boolean matches = allowedPattern.matcher(tool.name()).find();
        
        if (matches) {
            log.debug("MCP Tool ALLOWED: {}", tool.name());
        } else {
            log.info("MCP Tool FILTERED OUT: {}", tool.name());
        }
        
        return matches;
    }
}
+0 −3
Original line number Diff line number Diff line
@@ -35,12 +35,9 @@ class McpConfiguration {
            // Get the current authentication from SecurityContext
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            
            // Check if the authentication is JWT-based (from Bearer token)
            if (authentication instanceof JwtAuthenticationToken jwtAuth) {
                // Extract the JWT token value
                String tokenValue = jwtAuth.getToken().getTokenValue();
                
                // Add as Bearer token to the outgoing MCP Server request
                builder.header("Authorization", "Bearer " + tokenValue);
            }
        };
+9 −1
Original line number Diff line number Diff line
  
server:
  port: ${SERVER_PORT:11880}

agent:
  skills:
    dirs: ${AGENT_SKILLS_DIRS:skills}

mcp:
  allowed:
    tool:
      pattern: ${MCP_ALLOWED_TOOL_PATTERN:.*product.*}
 
spring:
  application:
    name: ${SPRING_APPLICATION_NAME:osl-mcp-backend}