diff --git a/go.mod b/go.mod index 49c2a546..b8e16e0c 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,13 @@ module github.com/mark3labs/mcphost -go 1.23.3 +go 1.23 require ( github.com/charmbracelet/huh v0.3.0 github.com/charmbracelet/huh/spinner v0.0.0-20241127125741-aad810dfbce6 github.com/charmbracelet/lipgloss v1.0.0 github.com/charmbracelet/log v0.4.0 - github.com/mark3labs/mcp-go v0.8.1 + github.com/mark3labs/mcp-go v0.8.2 github.com/ollama/ollama v0.5.1 github.com/spf13/cobra v1.8.1 golang.org/x/term v0.22.0 diff --git a/go.sum b/go.sum index c0876c9c..83291ba5 100644 --- a/go.sum +++ b/go.sum @@ -57,8 +57,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mark3labs/mcp-go v0.8.1 h1:41sD6WY2vwXACNpcUtZzJ7etrjtu1EajyTcrNsm8sgE= -github.com/mark3labs/mcp-go v0.8.1/go.mod h1:ePkDSyplFbA306xRgyp587+q/vpdgxuswwjZqTQ+I8Q= +github.com/mark3labs/mcp-go v0.8.2 h1:OtqqXlRqjXs6zuMhf1uiuQ2iqBrhMGgLpDeVDUWMKFc= +github.com/mark3labs/mcp-go v0.8.2/go.mod h1:cjMlBU0cv/cj9kjlgmRhoJ5JREdS7YX83xeIG9Ko/jE= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= diff --git a/pkg/llm/anthropic/provider.go b/pkg/llm/anthropic/provider.go index f726dd11..69e47bbf 100644 --- a/pkg/llm/anthropic/provider.go +++ b/pkg/llm/anthropic/provider.go @@ -24,106 +24,106 @@ func NewProvider(apiKey string) *Provider { } func (p *Provider) CreateMessage( - ctx context.Context, - prompt string, - messages []llm.Message, - tools []llm.Tool, + ctx context.Context, + prompt string, + messages []llm.Message, + tools []llm.Tool, ) (llm.Message, error) { - anthropicMessages := make([]MessageParam, 0, len(messages)) + anthropicMessages := make([]MessageParam, 0, len(messages)) - for _, msg := range messages { - content := []ContentBlock{} + for _, msg := range messages { + content := []ContentBlock{} - // Add regular text content if present - if textContent := strings.TrimSpace(msg.GetContent()); textContent != "" { - content = append(content, ContentBlock{ - Type: "text", - Text: textContent, - }) - } + // Add regular text content if present + if textContent := strings.TrimSpace(msg.GetContent()); textContent != "" { + content = append(content, ContentBlock{ + Type: "text", + Text: textContent, + }) + } - // Add tool calls if present - for _, call := range msg.GetToolCalls() { - input, _ := json.Marshal(call.GetArguments()) - content = append(content, ContentBlock{ - Type: "tool_use", - ID: call.GetID(), - Name: call.GetName(), - Input: input, - }) - } + // Add tool calls if present + for _, call := range msg.GetToolCalls() { + input, _ := json.Marshal(call.GetArguments()) + content = append(content, ContentBlock{ + Type: "tool_use", + ID: call.GetID(), + Name: call.GetName(), + Input: input, + }) + } - // Handle tool responses - if msg.IsToolResponse() { - if historyMsg, ok := msg.(*history.HistoryMessage); ok { - for _, block := range historyMsg.Content { - if block.Type == "tool_result" { - content = append(content, ContentBlock{ - Type: "tool_result", - ToolUseID: block.ToolUseID, - Content: block.Content, - }) - } - } - } else { - // Always include tool response content - content = append(content, ContentBlock{ - Type: "tool_result", - ToolUseID: msg.GetToolResponseID(), - Content: msg.GetContent(), - }) - } - } + // Handle tool responses + if msg.IsToolResponse() { + if historyMsg, ok := msg.(*history.HistoryMessage); ok { + for _, block := range historyMsg.Content { + if block.Type == "tool_result" { + content = append(content, ContentBlock{ + Type: "tool_result", + ToolUseID: block.ToolUseID, + Content: block.Content, + }) + } + } + } else { + // Always include tool response content + content = append(content, ContentBlock{ + Type: "tool_result", + ToolUseID: msg.GetToolResponseID(), + Content: msg.GetContent(), + }) + } + } - // Always append the message, even if content is empty - // This maintains conversation flow - anthropicMessages = append(anthropicMessages, MessageParam{ - Role: msg.GetRole(), - Content: content, - }) - } + // Always append the message, even if content is empty + // This maintains conversation flow + anthropicMessages = append(anthropicMessages, MessageParam{ + Role: msg.GetRole(), + Content: content, + }) + } - // Add the new prompt if provided - if prompt != "" { - anthropicMessages = append(anthropicMessages, MessageParam{ - Role: "user", - Content: []ContentBlock{{ - Type: "text", - Text: prompt, - }}, - }) - } + // Add the new prompt if provided + if prompt != "" { + anthropicMessages = append(anthropicMessages, MessageParam{ + Role: "user", + Content: []ContentBlock{{ + Type: "text", + Text: prompt, + }}, + }) + } - // Convert tools to Anthropic format - anthropicTools := make([]Tool, len(tools)) - for i, tool := range tools { - anthropicTools[i] = Tool{ - Name: tool.Name, - Description: tool.Description, - InputSchema: InputSchema{ - Type: tool.InputSchema.Type, - Properties: tool.InputSchema.Properties, - Required: tool.InputSchema.Required, - }, - } - } + // Convert tools to Anthropic format + anthropicTools := make([]Tool, len(tools)) + for i, tool := range tools { + anthropicTools[i] = Tool{ + Name: tool.Name, + Description: tool.Description, + InputSchema: InputSchema{ + Type: tool.InputSchema.Type, + Properties: tool.InputSchema.Properties, + Required: tool.InputSchema.Required, + }, + } + } - // Add debug logging for message structure - debugJSON, _ := json.MarshalIndent(anthropicMessages, "", " ") - log.Debug("sending messages to Anthropic", "messages", string(debugJSON)) + // Add debug logging for message structure + debugJSON, _ := json.MarshalIndent(anthropicMessages, "", " ") + log.Debug("sending messages to Anthropic", "messages", string(debugJSON)) - // Make the API call - resp, err := p.client.CreateMessage(ctx, CreateRequest{ - Model: p.model, - Messages: anthropicMessages, - MaxTokens: 4096, - Tools: anthropicTools, - }) - if err != nil { - return nil, err - } + // Make the API call + resp, err := p.client.CreateMessage(ctx, CreateRequest{ + Model: p.model, + Messages: anthropicMessages, + MaxTokens: 4096, + Tools: anthropicTools, + }) + if err != nil { + return nil, err + } - return &Message{Msg: *resp}, nil + return &Message{Msg: *resp}, nil } func (p *Provider) SupportsTools() bool { @@ -135,43 +135,43 @@ func (p *Provider) Name() string { } func (p *Provider) CreateToolResponse( - toolCallID string, - content interface{}, + toolCallID string, + content interface{}, ) (llm.Message, error) { - log.Debug("creating tool response", - "toolCallID", toolCallID, - "content", content) + log.Debug("creating tool response", + "toolCallID", toolCallID, + "content", content) - var contentStr string - var structuredContent interface{} = content + var contentStr string + var structuredContent interface{} = content - // Convert content to string if needed - switch v := content.(type) { - case string: - contentStr = v - case []byte: - contentStr = string(v) - default: - // For structured content, create JSON representation - if jsonBytes, err := json.Marshal(content); err == nil { - contentStr = string(jsonBytes) - } else { - contentStr = fmt.Sprintf("%v", content) - } - } + // Convert content to string if needed + switch v := content.(type) { + case string: + contentStr = v + case []byte: + contentStr = string(v) + default: + // For structured content, create JSON representation + if jsonBytes, err := json.Marshal(content); err == nil { + contentStr = string(jsonBytes) + } else { + contentStr = fmt.Sprintf("%v", content) + } + } - msg := &Message{ - Msg: APIMessage{ - Role: "tool", - Content: []ContentBlock{{ - Type: "tool_result", - ToolUseID: toolCallID, - Content: structuredContent, // Original structure - Text: contentStr, // String representation - }}, - }, - } + msg := &Message{ + Msg: APIMessage{ + Role: "tool", + Content: []ContentBlock{{ + Type: "tool_result", + ToolUseID: toolCallID, + Content: structuredContent, // Original structure + Text: contentStr, // String representation + }}, + }, + } - log.Debug("created tool response message", "message", msg) - return msg, nil + log.Debug("created tool response message", "message", msg) + return msg, nil } diff --git a/pkg/llm/openai/provider.go b/pkg/llm/openai/provider.go index 5e3edb1b..9dedb5e4 100644 --- a/pkg/llm/openai/provider.go +++ b/pkg/llm/openai/provider.go @@ -216,7 +216,7 @@ func (p *Provider) CreateToolResponse( texts = append(texts, text) continue } - + // Then try array of text if textArray, ok := block["text"].([]interface{}); ok { for _, t := range textArray { @@ -226,7 +226,7 @@ func (p *Provider) CreateToolResponse( } continue } - + // Finally try nested content array if contentArray, ok := block["content"].([]interface{}); ok { for _, c := range contentArray { diff --git a/pkg/llm/openai/types.go b/pkg/llm/openai/types.go index 34018271..5b8ce5dd 100644 --- a/pkg/llm/openai/types.go +++ b/pkg/llm/openai/types.go @@ -9,12 +9,12 @@ type CreateRequest struct { } type MessageParam struct { - Role string `json:"role"` - Content *string `json:"content"` + Role string `json:"role"` + Content *string `json:"content"` FunctionCall *FunctionCall `json:"function_call,omitempty"` ToolCalls []ToolCall `json:"tool_calls,omitempty"` - Name string `json:"name,omitempty"` - ToolCallID string `json:"tool_call_id,omitempty"` + Name string `json:"name,omitempty"` + ToolCallID string `json:"tool_call_id,omitempty"` } type ToolCall struct {