diff --git a/internal/services/toolkit/client/client.go b/internal/services/toolkit/client/client.go index 2e8b091d..34ca1975 100644 --- a/internal/services/toolkit/client/client.go +++ b/internal/services/toolkit/client/client.go @@ -49,12 +49,10 @@ func NewAIClient( // toolRegistry.Register("always_exception", tools.AlwaysExceptionToolDescription, tools.AlwaysExceptionTool) // toolRegistry.Register("greeting", tools.GreetingToolDescription, tools.GreetingTool) - // toolRegistry.Register("paper_score", toolPaperScore.Description, toolPaperScore.Call) - // toolRegistry.Register("paper_score_comment", toolPaperScoreComment.Description, toolPaperScoreComment.Call) - // Load tools dynamically from backend (TODO: Make URL configurable / Xtramcp url) - xtraMCPLoader := xtramcp.NewXtraMCPLoader(db, projectService, "http://localhost:8080/mcp") - + // Load tools dynamically from backend + xtraMCPLoader := xtramcp.NewXtraMCPLoader(db, projectService, "http://paperdebugger-xtra-mcp-server.com/mcp") + // initialize MCP session first and log session ID sessionID, err := xtraMCPLoader.InitializeMCP() if err != nil { @@ -62,7 +60,7 @@ func NewAIClient( // TODO: Fallback to static tools or exit? } else { logger.Info("[AI Client] XtraMCP session initialized", "sessionID", sessionID) - + // dynamically load all tools from XtraMCP backend err = xtraMCPLoader.LoadToolsFromBackend(toolRegistry) if err != nil { diff --git a/internal/services/toolkit/tools/xtramcp/helper.go b/internal/services/toolkit/tools/xtramcp/helper.go new file mode 100644 index 00000000..c7ba6137 --- /dev/null +++ b/internal/services/toolkit/tools/xtramcp/helper.go @@ -0,0 +1,24 @@ +package xtramcp + +import ( + "fmt" + "strings" +) + +// extracts JSON data from SSE format response +// SSE format: +// +// event: message +// data: { ... } +func parseSSEResponse(body []byte) (string, error) { + lines := strings.Split(string(body), "\n") + + for _, line := range lines { + if strings.HasPrefix(line, "data: ") { + jsonData := strings.TrimPrefix(line, "data: ") + return jsonData, nil + } + } + + return "", fmt.Errorf("no data line found in SSE response") +} diff --git a/internal/services/toolkit/tools/xtramcp/loader.go b/internal/services/toolkit/tools/xtramcp/loader.go index 974ff71b..a47b95c4 100644 --- a/internal/services/toolkit/tools/xtramcp/loader.go +++ b/internal/services/toolkit/tools/xtramcp/loader.go @@ -6,7 +6,6 @@ import ( "fmt" "io" "net/http" - "strings" "paperdebugger/internal/libs/db" "paperdebugger/internal/services" "paperdebugger/internal/services/toolkit/registry" @@ -55,10 +54,10 @@ func (loader *XtraMCPLoader) LoadToolsFromBackend(toolRegistry *registry.ToolReg // Register each tool dynamically, passing the session ID for _, toolSchema := range toolSchemas { dynamicTool := NewDynamicTool(loader.db, loader.projectService, toolSchema, loader.baseURL, loader.sessionID) - + // Register the tool with the registry toolRegistry.Register(toolSchema.Name, dynamicTool.Description, dynamicTool.Call) - + fmt.Printf("Registered dynamic tool: %s\n", toolSchema.Name) } @@ -196,18 +195,9 @@ func (loader *XtraMCPLoader) fetchAvailableTools() ([]ToolSchema, error) { return nil, fmt.Errorf("failed to read response body: %w", err) } - // Parse SSE format - extract JSON from "data: " lines - lines := strings.Split(string(bodyBytes), "\n") - var extractedJSON string - for _, line := range lines { - if strings.HasPrefix(line, "data: ") { - extractedJSON = strings.TrimPrefix(line, "data: ") - break - } - } - - if extractedJSON == "" { - return nil, fmt.Errorf("no data line found in SSE response") + extractedJSON, err := parseSSEResponse(bodyBytes) + if err != nil { + return nil, fmt.Errorf("failed to parse SSE response: %w", err) } // Parse the extracted JSON diff --git a/internal/services/toolkit/tools/xtramcp/tool.go b/internal/services/toolkit/tools/xtramcp/tool.go index 12156837..f9a4e470 100644 --- a/internal/services/toolkit/tools/xtramcp/tool.go +++ b/internal/services/toolkit/tools/xtramcp/tool.go @@ -10,13 +10,11 @@ import ( "paperdebugger/internal/libs/db" "paperdebugger/internal/services" toolCallRecordDB "paperdebugger/internal/services/toolkit/db" - "strings" "time" "github.com/openai/openai-go/v2" "github.com/openai/openai-go/v2/packages/param" "github.com/openai/openai-go/v2/responses" - "github.com/samber/lo" ) // ToolSchema represents the schema from your backend @@ -155,16 +153,11 @@ func (t *DynamicTool) executeTool(args map[string]interface{}) (string, error) { if err != nil { return "", fmt.Errorf("failed to read response: %w", err) } - - // Parse response (assuming stream format) - lines := strings.Split(string(body), "\n") - lines = lo.Filter(lines, func(line string, _ int) bool { - return strings.HasPrefix(line, "data:") - }) - if len(lines) == 0 { - return "", fmt.Errorf("no data line found") + + extractedJSON, err := parseSSEResponse(body) + if err != nil { + return "", fmt.Errorf("failed to parse SSE response: %w", err) } - line := lines[0] - line = strings.TrimPrefix(line, "data: ") - return line, nil + + return extractedJSON, nil }