Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions internal/services/toolkit/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,18 @@ 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 {
logger.Errorf("[AI Client] Failed to initialize XtraMCP session: %v", err)
// 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 {
Expand Down
24 changes: 24 additions & 0 deletions internal/services/toolkit/tools/xtramcp/helper.go
Original file line number Diff line number Diff line change
@@ -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")
}
20 changes: 5 additions & 15 deletions internal/services/toolkit/tools/xtramcp/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"fmt"
"io"
"net/http"
"strings"
"paperdebugger/internal/libs/db"
"paperdebugger/internal/services"
"paperdebugger/internal/services/toolkit/registry"
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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
Expand Down
19 changes: 6 additions & 13 deletions internal/services/toolkit/tools/xtramcp/tool.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}