diff --git a/go.mod b/go.mod index 62c08dc..049759a 100644 --- a/go.mod +++ b/go.mod @@ -10,11 +10,11 @@ require ( github.com/bytedance/sonic v1.14.1 github.com/charmbracelet/fang v0.4.0 github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 - github.com/cloudwego/eino v0.5.11 - github.com/cloudwego/eino-ext/components/model/claude v0.1.0 + github.com/cloudwego/eino v0.7.11 + github.com/cloudwego/eino-ext/components/model/claude v0.1.12 github.com/cloudwego/eino-ext/components/model/ollama v0.1.2 github.com/cloudwego/eino-ext/components/model/openai v0.0.0-20250903035842-96774a3ec845 - github.com/getkin/kin-openapi v0.120.0 + github.com/eino-contrib/jsonschema v1.0.3 github.com/mark3labs/mcp-filesystem-server v0.11.1 github.com/mark3labs/mcp-go v0.43.0 github.com/ollama/ollama v0.11.8 @@ -29,6 +29,7 @@ require ( require ( cloud.google.com/go v0.121.6 // indirect cloud.google.com/go/auth v0.16.5 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.8.0 // indirect github.com/alecthomas/chroma/v2 v2.20.0 // indirect github.com/andybalholm/cascadia v1.3.3 // indirect @@ -65,11 +66,11 @@ require ( github.com/djherbis/times v1.6.0 // indirect github.com/dlclark/regexp2 v1.11.5 // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/eino-contrib/jsonschema v1.0.2 // indirect github.com/evanphx/json-patch v0.5.2 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.10 // indirect + github.com/getkin/kin-openapi v0.120.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.22.0 // indirect @@ -122,6 +123,7 @@ require ( github.com/yuin/goldmark v1.7.13 // indirect github.com/yuin/goldmark-emoji v1.0.6 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect go.opentelemetry.io/otel v1.38.0 // indirect go.opentelemetry.io/otel/metric v1.38.0 // indirect @@ -129,6 +131,9 @@ require ( golang.org/x/arch v0.20.0 // indirect golang.org/x/crypto v0.41.0 // indirect golang.org/x/net v0.43.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/time v0.12.0 // indirect + google.golang.org/api v0.246.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 // indirect google.golang.org/grpc v1.75.0 // indirect google.golang.org/protobuf v1.36.8 // indirect diff --git a/go.sum b/go.sum index a6bb89d..5db4db7 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ cloud.google.com/go v0.121.6 h1:waZiuajrI28iAf40cWgycWNgaXPO06dupuS+sgibK6c= cloud.google.com/go v0.121.6/go.mod h1:coChdst4Ea5vUpiALcYKXEpR1S9ZgXbhEzzMcMR66vI= cloud.google.com/go/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI= cloud.google.com/go/auth v0.16.5/go.mod h1:utzRfHMP+Vv0mpOkTRQoWD2q3BatTOoWbA7gCc2dUhQ= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcaobyVfZWqRLA= cloud.google.com/go/compute/metadata v0.8.0/go.mod h1:sYOGTp851OV9bOFJ9CH7elVvyzopvWQFNNghtDQ/Biw= github.com/JohannesKaufmann/html-to-markdown v1.6.0 h1:04VXMiE50YYfCfLboJCLcgqF5x+rHJnb1ssNmqpLH/k= @@ -108,10 +110,10 @@ github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQ github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= -github.com/cloudwego/eino v0.5.11 h1:R/BPZJPiMrGm1kA4ql2T0P8SlqLC16Pbxev+v6a6AGY= -github.com/cloudwego/eino v0.5.11/go.mod h1:N6E+toMzWw/3ql0IVM5n5lbYFCeblCYx7ebH16kt1JQ= -github.com/cloudwego/eino-ext/components/model/claude v0.1.0 h1:UZVwYzV7gOBCBKHGdAT2fZzm/+2TBEfDDYn713EvLF0= -github.com/cloudwego/eino-ext/components/model/claude v0.1.0/go.mod h1:lacy0WE3yKuOSxrhJQKqWAxn3LiUy/CJ91jU7nLDNNQ= +github.com/cloudwego/eino v0.7.11 h1:QQ3Ik4/nW1462CuvFsmH3gWAqNI/70BXRDmsYyvXyds= +github.com/cloudwego/eino v0.7.11/go.mod h1:nA8Vacmuqv3pqKBQbTWENBLQ8MmGmPt/WqiyLeB8ohQ= +github.com/cloudwego/eino-ext/components/model/claude v0.1.12 h1:c66gFH9J5Ku2/v1f7jPwI9R4CYw5TiAlIVzsfzjsF1g= +github.com/cloudwego/eino-ext/components/model/claude v0.1.12/go.mod h1:a9oQkf4Ib+/VqjsLRdRETytt2m/C4fbcvfjPNu6nVAg= github.com/cloudwego/eino-ext/components/model/ollama v0.1.2 h1:WxJ+7oXnr3AhM6u4VbFF3L2ionxCrPfmLetx7V+zthw= github.com/cloudwego/eino-ext/components/model/ollama v0.1.2/go.mod h1:OgGMCiR/G/RnOWaJvdK8pVSxAzoz2SlCqim43oFTuwo= github.com/cloudwego/eino-ext/components/model/openai v0.0.0-20250903035842-96774a3ec845 h1:nxflfiBwWNPoKS9X4SMhmT+si7rtYv+lQzIyPJik4DM= @@ -128,8 +130,8 @@ github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZ github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/eino-contrib/jsonschema v1.0.2 h1:HaxruBMUdnXa7Lg/lX8g0Hk71ZIfdTZXmBQz0e3esr8= -github.com/eino-contrib/jsonschema v1.0.2/go.mod h1:cpnX4SyKjWjGC7iN2EbhxaTdLqGjCi0e9DxpLYxddD4= +github.com/eino-contrib/jsonschema v1.0.3 h1:2Kfsm1xlMV0ssY2nuxshS4AwbLFuqmPmzIjLVJ1Fsp0= +github.com/eino-contrib/jsonschema v1.0.3/go.mod h1:cpnX4SyKjWjGC7iN2EbhxaTdLqGjCi0e9DxpLYxddD4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= @@ -361,6 +363,8 @@ github.com/yuin/goldmark-emoji v1.0.6 h1:QWfF2FYaXwL74tfGOW5izeiZepUDroDJfWubQI9 github.com/yuin/goldmark-emoji v1.0.6/go.mod h1:ukxJDKFpdFb5x0a5HqbdlcKtebh086iJpI31LTKmWuA= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= @@ -408,6 +412,8 @@ golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -463,6 +469,8 @@ golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= @@ -472,6 +480,8 @@ golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxb golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/api v0.246.0 h1:H0ODDs5PnMZVZAEtdLMn2Ul2eQi7QNjqM2DIFp8TlTM= +google.golang.org/api v0.246.0/go.mod h1:dMVhVcylamkirHdzEBAIQWUCgqY885ivNeZYd7VAVr8= google.golang.org/genai v1.22.0 h1:5hrEhXXWJQZa3tdPocl4vQ/0w6myEAxdNns2Kmx0f4Y= google.golang.org/genai v1.22.0/go.mod h1:QPj5NGJw+3wEOHg+PrsWwJKvG6UC84ex5FR7qAYsN/M= google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 h1:pmJpJEvT846VzausCQ5d7KreSROcDqmO388w5YbnltA= diff --git a/internal/models/gemini/gemini.go b/internal/models/gemini/gemini.go index 0b1da68..a1ef7f5 100644 --- a/internal/models/gemini/gemini.go +++ b/internal/models/gemini/gemini.go @@ -11,6 +11,7 @@ import ( "github.com/cloudwego/eino/components" "github.com/cloudwego/eino/components/model" "github.com/cloudwego/eino/schema" + "github.com/eino-contrib/jsonschema" "github.com/getkin/kin-openapi/openapi3" "google.golang.org/genai" ) @@ -83,7 +84,7 @@ type Config struct { // ResponseSchema defines the structure for JSON responses // Optional. Used when you want structured output in JSON format - ResponseSchema *openapi3.Schema + ResponseSchema *jsonschema.Schema // EnableCodeExecution allows the model to execute code // Warning: Be cautious with code execution in production @@ -103,7 +104,7 @@ type options struct { // TopK limits the number of tokens to sample from TopK *int32 // ResponseSchema defines the expected JSON structure for responses - ResponseSchema *openapi3.Schema + ResponseSchema *jsonschema.Schema } // ChatModel implements the Gemini chat model for the eino framework. @@ -124,7 +125,7 @@ type ChatModel struct { // topK limits token sampling topK *int32 // responseSchema for structured JSON output - responseSchema *openapi3.Schema + responseSchema *jsonschema.Schema // tools converted to Gemini format tools []*genai.Tool // origTools stores the original tool definitions @@ -448,7 +449,7 @@ func (cm *ChatModel) buildGenerateConfig(opts ...model.Option) (*genai.GenerateC // Set response schema for JSON mode if geminiOptions.ResponseSchema != nil { - gSchema, err := cm.convertOpenAPISchema(geminiOptions.ResponseSchema) + gSchema, err := cm.convertJSONSchema(geminiOptions.ResponseSchema) if err != nil { return nil, nil, fmt.Errorf("convert response schema failed: %w", err) } @@ -466,12 +467,12 @@ func (cm *ChatModel) convertToGeminiTools(tools []*schema.ToolInfo) ([]*genai.To var functionDeclarations []*genai.FunctionDeclaration for _, tool := range tools { - openSchema, err := tool.ToOpenAPIV3() + openSchema, err := tool.ToJSONSchema() if err != nil { return nil, fmt.Errorf("get open schema failed: %w", err) } - gSchema, err := cm.convertOpenAPISchema(openSchema) + gSchema, err := cm.convertJSONSchema(openSchema) if err != nil { return nil, fmt.Errorf("convert open schema failed: %w", err) } @@ -487,7 +488,7 @@ func (cm *ChatModel) convertToGeminiTools(tools []*schema.ToolInfo) ([]*genai.To return []*genai.Tool{{FunctionDeclarations: functionDeclarations}}, nil } -func (cm *ChatModel) convertOpenAPISchema(schema *openapi3.Schema) (*genai.Schema, error) { +func (cm *ChatModel) convertJSONSchema(schema *jsonschema.Schema) (*genai.Schema, error) { if schema == nil { return nil, nil } @@ -501,15 +502,12 @@ func (cm *ChatModel) convertOpenAPISchema(schema *openapi3.Schema) (*genai.Schem result.Type = genai.TypeObject if schema.Properties != nil { properties := make(map[string]*genai.Schema) - for name, prop := range schema.Properties { - if prop == nil || prop.Value == nil { - continue - } - propSchema, err := cm.convertOpenAPISchema(prop.Value) + for pair := schema.Properties.Oldest(); pair != nil; pair = pair.Next() { + propSchema, err := cm.convertJSONSchema(pair.Value) if err != nil { return nil, err } - properties[name] = propSchema + properties[pair.Key] = propSchema } result.Properties = properties } @@ -518,8 +516,8 @@ func (cm *ChatModel) convertOpenAPISchema(schema *openapi3.Schema) (*genai.Schem } case openapi3.TypeArray: result.Type = genai.TypeArray - if schema.Items != nil && schema.Items.Value != nil { - itemSchema, err := cm.convertOpenAPISchema(schema.Items.Value) + if schema.Items != nil { + itemSchema, err := cm.convertJSONSchema(schema.Items) if err != nil { return nil, err } diff --git a/internal/models/providers.go b/internal/models/providers.go index 4f2caed..a4d21cd 100644 --- a/internal/models/providers.go +++ b/internal/models/providers.go @@ -218,6 +218,12 @@ func CreateProvider(ctx context.Context, config *ProviderConfig) (*ProviderResul return nil, err } return &ProviderResult{Model: model, Message: ""}, nil + case "google-vertex-anthropic": + model, err := createVertexAnthropicProvider(ctx, config, modelName) + if err != nil { + return nil, err + } + return &ProviderResult{Model: model, Message: ""}, nil default: return nil, fmt.Errorf("unsupported provider: %s", provider) } @@ -350,6 +356,67 @@ func createAnthropicProvider(ctx context.Context, config *ProviderConfig, modelN return anthropic.NewCustomChatModel(ctx, claudeConfig) } +func createVertexAnthropicProvider(ctx context.Context, config *ProviderConfig, modelName string) (model.ToolCallingChatModel, error) { + projectID := os.Getenv("GOOGLE_VERTEX_PROJECT") + if projectID == "" { + projectID = os.Getenv("ANTHROPIC_VERTEX_PROJECT_ID") + } + if projectID == "" { + projectID = os.Getenv("GOOGLE_CLOUD_PROJECT") + } + if projectID == "" { + projectID = os.Getenv("GCLOUD_PROJECT") + } + if projectID == "" { + projectID = os.Getenv("CLOUDSDK_CORE_PROJECT") + } + if projectID == "" { + return nil, fmt.Errorf("Google Vertex project ID not provided. Set ANTHROPIC_VERTEX_PROJECT_ID, GOOGLE_CLOUD_PROJECT, or GCLOUD_PROJECT environment variable") + } + + region := os.Getenv("GOOGLE_VERTEX_LOCATION") + if region == "" { + region = os.Getenv("ANTHROPIC_VERTEX_REGION") + } + if region == "" { + region = os.Getenv("CLOUD_ML_REGION") + } + if region == "" { + region = "global" + } + + maxTokens := config.MaxTokens + if maxTokens == 0 { + maxTokens = 4096 // Default value + } + + claudeConfig := &einoclaude.Config{ + ByVertex: true, + VertexProjectID: projectID, + VertexRegion: region, + Model: modelName, + MaxTokens: maxTokens, + } + + if config.Temperature != nil { + claudeConfig.Temperature = config.Temperature + } + + if config.TopP != nil { + claudeConfig.TopP = config.TopP + } + + if config.TopK != nil { + claudeConfig.TopK = config.TopK + } + + if len(config.StopSequences) > 0 { + claudeConfig.StopSequences = config.StopSequences + } + + return anthropic.NewCustomChatModel(ctx, claudeConfig) +} + func createOpenAIProvider(ctx context.Context, config *ProviderConfig, modelName string) (model.ToolCallingChatModel, error) { apiKey := config.ProviderAPIKey if apiKey == "" { diff --git a/internal/models/registry.go b/internal/models/registry.go index 3ac93df..53f67b1 100644 --- a/internal/models/registry.go +++ b/internal/models/registry.go @@ -100,6 +100,19 @@ func (r *ModelsRegistry) ValidateEnvironment(provider string, apiKey string) err return nil } + // Add alternative environment variable names for google-vertex-anthropic + // These match the env vars checked by eino-claude and other tools + if provider == "google-vertex-anthropic" { + envVars = append(envVars, + "ANTHROPIC_VERTEX_PROJECT_ID", + "GOOGLE_CLOUD_PROJECT", + "GCLOUD_PROJECT", + "CLOUDSDK_CORE_PROJECT", + "ANTHROPIC_VERTEX_REGION", + "CLOUD_ML_REGION", + ) + } + // Check if at least one environment variable is set var foundVar bool for _, envVar := range envVars { diff --git a/internal/tools/mcp.go b/internal/tools/mcp.go index 2da192d..fa32489 100644 --- a/internal/tools/mcp.go +++ b/internal/tools/mcp.go @@ -11,7 +11,7 @@ import ( "github.com/cloudwego/eino/components/model" "github.com/cloudwego/eino/components/tool" "github.com/cloudwego/eino/schema" - "github.com/getkin/kin-openapi/openapi3" + "github.com/eino-contrib/jsonschema" "github.com/mark3labs/mcp-go/client" "github.com/mark3labs/mcp-go/client/transport" "github.com/mark3labs/mcp-go/mcp" @@ -221,7 +221,7 @@ func (m *MCPToolManager) loadServerTools(ctx context.Context, serverName string, if err != nil { return fmt.Errorf("conv mcp tool input schema fail(marshal): %w, tool name: %s", err, mcpTool.Name) } - inputSchema := &openapi3.Schema{} + inputSchema := &jsonschema.Schema{} err = sonic.Unmarshal(marshaledInputSchema, inputSchema) if err != nil { return fmt.Errorf("conv mcp tool input schema fail(unmarshal): %w, tool name: %s", err, mcpTool.Name) @@ -231,7 +231,7 @@ func (m *MCPToolManager) loadServerTools(ctx context.Context, serverName string, // OpenAI function calling requires object schemas to have a "properties" field // even if it's empty, otherwise it throws "object schema missing properties" error if inputSchema.Type == "object" && inputSchema.Properties == nil { - inputSchema.Properties = make(openapi3.Schemas) + inputSchema.Properties = jsonschema.NewProperties() } // Create prefixed tool name @@ -251,7 +251,7 @@ func (m *MCPToolManager) loadServerTools(ctx context.Context, serverName string, info: &schema.ToolInfo{ Name: prefixedName, Desc: mcpTool.Description, - ParamsOneOf: schema.NewParamsOneOfByOpenAPIV3(inputSchema), + ParamsOneOf: schema.NewParamsOneOfByJSONSchema(inputSchema), }, mapping: mapping, } diff --git a/internal/tools/mcp_test.go b/internal/tools/mcp_test.go index 9b4e72f..5932453 100644 --- a/internal/tools/mcp_test.go +++ b/internal/tools/mcp_test.go @@ -6,7 +6,7 @@ import ( "time" "github.com/cloudwego/eino/schema" - "github.com/getkin/kin-openapi/openapi3" + "github.com/eino-contrib/jsonschema" "github.com/mark3labs/mcphost/internal/config" ) @@ -131,7 +131,7 @@ func TestMCPToolManager_ToolWithoutProperties(t *testing.T) { func TestIssue89_ObjectSchemaMissingProperties(t *testing.T) { // Create a schema that would cause the OpenAI validation error // This simulates what might happen with tools that have no input properties - brokenSchema := &openapi3.Schema{ + brokenSchema := &jsonschema.Schema{ Type: "object", // Properties is nil - this causes "object schema missing properties" error in OpenAI } @@ -143,7 +143,7 @@ func TestIssue89_ObjectSchemaMissingProperties(t *testing.T) { // Apply the fix from issue #89 if brokenSchema.Type == "object" && brokenSchema.Properties == nil { - brokenSchema.Properties = make(openapi3.Schemas) + brokenSchema.Properties = jsonschema.NewProperties() } // Verify the fix worked @@ -153,7 +153,7 @@ func TestIssue89_ObjectSchemaMissingProperties(t *testing.T) { // Test that we can create a ParamsOneOf from the fixed schema // This is what would fail before the fix - paramsOneOf := schema.NewParamsOneOfByOpenAPIV3(brokenSchema) + paramsOneOf := schema.NewParamsOneOfByJSONSchema(brokenSchema) if paramsOneOf == nil { t.Error("Failed to create ParamsOneOf from fixed schema - OpenAI function calling would fail") }