first commit
This commit is contained in:
171
core/mcp/utils_test.go
Normal file
171
core/mcp/utils_test.go
Normal file
@@ -0,0 +1,171 @@
|
||||
package mcp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/maximhq/bifrost/core/schemas"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestConvertMCPToolToBifrostSchema_EmptyParameters tests that tools with no parameters
|
||||
// get an empty properties map instead of nil, which is required by some providers like OpenAI
|
||||
func TestConvertMCPToolToBifrostSchema_EmptyParameters(t *testing.T) {
|
||||
// Create a tool with no parameters (like return_special_chars or return_null)
|
||||
mcpTool := &mcp.Tool{
|
||||
Name: "test_tool_no_params",
|
||||
Description: "A test tool with no parameters",
|
||||
InputSchema: mcp.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: map[string]interface{}{}, // Empty properties
|
||||
Required: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
// Convert the tool
|
||||
bifrostTool := convertMCPToolToBifrostSchema(mcpTool, defaultLogger)
|
||||
|
||||
// Verify the function was created
|
||||
if bifrostTool.Function == nil {
|
||||
t.Fatal("Function should not be nil")
|
||||
}
|
||||
|
||||
// Verify parameters were created
|
||||
if bifrostTool.Function.Parameters == nil {
|
||||
t.Fatal("Parameters should not be nil")
|
||||
}
|
||||
|
||||
// Verify properties is not nil (this is the key fix)
|
||||
if bifrostTool.Function.Parameters.Properties == nil {
|
||||
t.Error("Properties should not be nil for object type, even if empty")
|
||||
}
|
||||
|
||||
// Verify it's an empty map
|
||||
if bifrostTool.Function.Parameters.Properties != nil && bifrostTool.Function.Parameters.Properties.Len() != 0 {
|
||||
t.Errorf("Expected empty properties map, got %d properties", bifrostTool.Function.Parameters.Properties.Len())
|
||||
}
|
||||
|
||||
// Verify the type is preserved
|
||||
if bifrostTool.Function.Parameters.Type != "object" {
|
||||
t.Errorf("Expected type 'object', got '%s'", bifrostTool.Function.Parameters.Type)
|
||||
}
|
||||
}
|
||||
|
||||
// TestConvertMCPToolToBifrostSchema_WithAnnotations tests that MCP tool annotations
|
||||
// are preserved on ChatTool.Annotations (not ChatToolFunction) and are absent from JSON.
|
||||
func TestConvertMCPToolToBifrostSchema_WithAnnotations(t *testing.T) {
|
||||
readOnly := true
|
||||
destructive := false
|
||||
|
||||
mcpTool := &mcp.Tool{
|
||||
Name: "read_resource",
|
||||
Description: "Reads a resource",
|
||||
InputSchema: mcp.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: map[string]interface{}{},
|
||||
},
|
||||
Annotations: mcp.ToolAnnotation{
|
||||
Title: "Resource Reader",
|
||||
ReadOnlyHint: &readOnly,
|
||||
DestructiveHint: &destructive,
|
||||
IdempotentHint: schemas.Ptr(true),
|
||||
},
|
||||
}
|
||||
|
||||
bifrostTool := convertMCPToolToBifrostSchema(mcpTool, defaultLogger)
|
||||
|
||||
// Annotations must be on ChatTool, not buried in Function
|
||||
require.NotNil(t, bifrostTool.Annotations, "Annotations should be set on ChatTool")
|
||||
assert.Equal(t, "Resource Reader", bifrostTool.Annotations.Title)
|
||||
require.NotNil(t, bifrostTool.Annotations.ReadOnlyHint)
|
||||
assert.True(t, *bifrostTool.Annotations.ReadOnlyHint)
|
||||
require.NotNil(t, bifrostTool.Annotations.DestructiveHint)
|
||||
assert.False(t, *bifrostTool.Annotations.DestructiveHint)
|
||||
require.NotNil(t, bifrostTool.Annotations.IdempotentHint)
|
||||
assert.True(t, *bifrostTool.Annotations.IdempotentHint)
|
||||
assert.Nil(t, bifrostTool.Annotations.OpenWorldHint)
|
||||
|
||||
// The JSON sent to providers must not contain annotations
|
||||
toolJSON, err := json.Marshal(bifrostTool)
|
||||
require.NoError(t, err)
|
||||
s := string(toolJSON)
|
||||
assert.NotContains(t, s, "annotations", "annotations must be absent from provider JSON")
|
||||
assert.NotContains(t, s, "readOnlyHint", "readOnlyHint must be absent from provider JSON")
|
||||
assert.NotContains(t, s, "Resource Reader", "annotation title must be absent from provider JSON")
|
||||
}
|
||||
|
||||
// TestConvertMCPToolToBifrostSchema_NilAnnotationsWhenAllZero verifies the nil guard:
|
||||
// when all annotation fields are zero-valued, ChatTool.Annotations must remain nil.
|
||||
func TestConvertMCPToolToBifrostSchema_NilAnnotationsWhenAllZero(t *testing.T) {
|
||||
mcpTool := &mcp.Tool{
|
||||
Name: "no_hints_tool",
|
||||
Description: "A tool with no annotation hints",
|
||||
InputSchema: mcp.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: map[string]interface{}{},
|
||||
},
|
||||
Annotations: mcp.ToolAnnotation{}, // All zero values — Title empty, all hints nil
|
||||
}
|
||||
|
||||
bifrostTool := convertMCPToolToBifrostSchema(mcpTool, defaultLogger)
|
||||
|
||||
assert.Nil(t, bifrostTool.Annotations,
|
||||
"Annotations should be nil when all MCP annotation fields are zero")
|
||||
}
|
||||
|
||||
// TestConvertMCPToolToBifrostSchema_WithParameters tests the normal case with parameters
|
||||
func TestConvertMCPToolToBifrostSchema_WithParameters(t *testing.T) {
|
||||
// Create a tool with parameters
|
||||
mcpTool := &mcp.Tool{
|
||||
Name: "test_tool_with_params",
|
||||
Description: "A test tool with parameters",
|
||||
InputSchema: mcp.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: map[string]interface{}{
|
||||
"param1": map[string]interface{}{
|
||||
"type": "string",
|
||||
"description": "A string parameter",
|
||||
},
|
||||
"param2": map[string]interface{}{
|
||||
"type": "number",
|
||||
"description": "A number parameter",
|
||||
},
|
||||
},
|
||||
Required: []string{"param1"},
|
||||
},
|
||||
}
|
||||
|
||||
// Convert the tool
|
||||
bifrostTool := convertMCPToolToBifrostSchema(mcpTool, defaultLogger)
|
||||
|
||||
// Verify the function was created
|
||||
if bifrostTool.Function == nil {
|
||||
t.Fatal("Function should not be nil")
|
||||
}
|
||||
|
||||
// Verify parameters were created
|
||||
if bifrostTool.Function.Parameters == nil {
|
||||
t.Fatal("Parameters should not be nil")
|
||||
}
|
||||
|
||||
// Verify properties is not nil
|
||||
if bifrostTool.Function.Parameters.Properties == nil {
|
||||
t.Fatal("Properties should not be nil")
|
||||
}
|
||||
|
||||
// Verify the correct number of properties
|
||||
if bifrostTool.Function.Parameters.Properties.Len() != 2 {
|
||||
t.Errorf("Expected 2 properties, got %d", bifrostTool.Function.Parameters.Properties.Len())
|
||||
}
|
||||
|
||||
// Verify required fields
|
||||
if len(bifrostTool.Function.Parameters.Required) != 1 {
|
||||
t.Errorf("Expected 1 required field, got %d", len(bifrostTool.Function.Parameters.Required))
|
||||
}
|
||||
|
||||
if bifrostTool.Function.Parameters.Required[0] != "param1" {
|
||||
t.Errorf("Expected required field 'param1', got '%s'", bifrostTool.Function.Parameters.Required[0])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user