feat: add Google Vertex AI support for Claude models (#146)

Add support for using Claude models via Google Cloud Vertex AI through
the `google-vertex-anthropic` provider. This enables users who have
Claude access through their Google Cloud account to use mcphost with
Vertex AI authentication.

Changes:
- Add `google-vertex-anthropic` provider case and createVertexAnthropicProvider()
- Support multiple env var names for project/region to match eino-claude:
  - Project: ANTHROPIC_VERTEX_PROJECT_ID, GOOGLE_CLOUD_PROJECT, GCLOUD_PROJECT
  - Region: CLOUD_ML_REGION (defaults to "global" if not set)
- Upgrade eino from v0.5.11 to v0.7.11 (required by eino-claude v0.1.12)
- Migrate schema API from OpenAPI v3 to JSON Schema (eino v0.7.11 change)

Usage:
  # Authenticate with Google Cloud
  gcloud auth application-default login

  # Set required environment variables
  export ANTHROPIC_VERTEX_PROJECT_ID="your-project-id"
  export CLOUD_ML_REGION="us-east5"  # or use default "global"

  # Run mcphost
  mcphost --model google-vertex-anthropic:claude-sonnet-4@20250514

Reference: https://docs.anthropic.com/en/docs/claude-code/google-vertex-ai

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Shane McDonald
2026-01-10 05:01:18 -05:00
committed by GitHub
parent 7ece291d9a
commit c4aa911e3a
7 changed files with 126 additions and 33 deletions
+9 -4
View File
@@ -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.44.0-beta.2
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
+16 -6
View File
@@ -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=
+13 -15
View File
@@ -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
}
+67
View File
@@ -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 == "" {
+13
View File
@@ -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 {
+4 -4
View File
@@ -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"
@@ -228,7 +228,7 @@ func (m *MCPToolManager) loadServerTools(ctx context.Context, serverName string,
// Pre-process the schema to convert numeric exclusive bounds to boolean format.
marshaledInputSchema = convertExclusiveBoundsToBoolean(marshaledInputSchema)
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)
@@ -238,7 +238,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
@@ -258,7 +258,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,
}
+4 -4
View File
@@ -7,7 +7,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"
)
@@ -132,7 +132,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
}
@@ -144,7 +144,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
@@ -154,7 +154,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")
}