mirror of
https://github.com/alibaba/higress.git
synced 2026-04-21 20:17:29 +08:00
refactor: migrate MCP SDK to main repo (#3516)
This commit is contained in:
164
plugins/wasm-go/pkg/mcp/validator/README.md
Normal file
164
plugins/wasm-go/pkg/mcp/validator/README.md
Normal file
@@ -0,0 +1,164 @@
|
||||
# MCP Configuration Validator
|
||||
|
||||
This package provides a configuration validation library for MCP (Model Context Protocol) server configurations. It allows you to validate MCP configurations without requiring the full runtime environment, making it perfect for use in management platforms and frontend applications.
|
||||
|
||||
## Features
|
||||
|
||||
- **Lightweight Validation**: Validates configuration structure and syntax without requiring actual server instances
|
||||
- **REST Tool Support**: Full validation for REST-based MCP tools including request/response templates
|
||||
- **ToolSet Support**: Validates composed server configurations (toolSets)
|
||||
- **Pre-registered Server Handling**: Gracefully handles pre-registered Go-based servers by skipping their validation
|
||||
- **Minimal Dependencies**: Reuses the core parsing logic from the main MCP server implementation
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Validation
|
||||
|
||||
```go
|
||||
import "github.com/higress-group/wasm-go/pkg/mcp/validator"
|
||||
|
||||
// Validate a configuration YAML string
|
||||
yamlConfig := `
|
||||
server:
|
||||
name: my-server
|
||||
config:
|
||||
apiKey: secret
|
||||
tools:
|
||||
- name: my-tool
|
||||
description: A sample tool
|
||||
args:
|
||||
- name: input
|
||||
type: string
|
||||
required: true
|
||||
requestTemplate:
|
||||
url: https://api.example.com/endpoint
|
||||
method: POST
|
||||
responseTemplate:
|
||||
body: "{{.}}"
|
||||
`
|
||||
result, err := validator.ValidateConfigYAML(yamlConfig)
|
||||
if err != nil {
|
||||
// Handle error
|
||||
return
|
||||
}
|
||||
|
||||
if result.IsValid {
|
||||
fmt.Printf("Configuration is valid for server: %s\n", result.ServerName)
|
||||
if result.IsComposed {
|
||||
fmt.Println("This is a composed server (toolSet)")
|
||||
} else {
|
||||
fmt.Println("This is a single server")
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Configuration is invalid: %v\n", result.Error)
|
||||
}
|
||||
```
|
||||
|
||||
## Supported Configuration Types
|
||||
|
||||
### 1. REST Server Configuration
|
||||
|
||||
Validates REST-based MCP servers with tools, security schemes, and templates:
|
||||
|
||||
```yaml
|
||||
server:
|
||||
name: weather-api
|
||||
config:
|
||||
apiKey: your-api-key
|
||||
securitySchemes:
|
||||
- id: bearer-auth
|
||||
type: http
|
||||
scheme: bearer
|
||||
tools:
|
||||
- name: get_weather
|
||||
description: Get current weather
|
||||
args:
|
||||
- name: city
|
||||
type: string
|
||||
required: true
|
||||
requestTemplate:
|
||||
url: "https://api.weather.com/v1/current?city={{.args.city}}"
|
||||
method: GET
|
||||
responseTemplate:
|
||||
body: "Weather: {{.temperature}}°C"
|
||||
```
|
||||
|
||||
### 2. ToolSet Configuration (Composed Server)
|
||||
|
||||
Validates composed servers that aggregate tools from multiple servers:
|
||||
|
||||
```yaml
|
||||
toolSet:
|
||||
name: ai-assistant-tools
|
||||
serverTools:
|
||||
- serverName: weather-api
|
||||
tools: ["get_weather", "get_forecast"]
|
||||
- serverName: search-api
|
||||
tools: ["web_search"]
|
||||
```
|
||||
|
||||
### 3. Pre-registered Go-based Server
|
||||
|
||||
For pre-registered Go-based servers, validation focuses on basic structure and skips server instance validation:
|
||||
|
||||
```yaml
|
||||
server:
|
||||
name: custom-go-server
|
||||
config:
|
||||
database_url: "postgres://localhost:5432/mydb"
|
||||
allowTools: ["query_database"]
|
||||
```
|
||||
|
||||
## Validation Result
|
||||
|
||||
The `ValidationResult` struct provides detailed information about the validation:
|
||||
|
||||
```go
|
||||
type ValidationResult struct {
|
||||
IsValid bool `json:"isValid"` // Whether the configuration is valid
|
||||
Error error `json:"error"` // Validation error if any
|
||||
ServerName string `json:"serverName"` // Parsed server name
|
||||
IsComposed bool `json:"isComposed"` // Whether it's a composed server
|
||||
}
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
The validator reuses the core parsing logic from the main MCP server implementation through dependency injection:
|
||||
|
||||
- **parseConfigCore**: Core parsing logic with configurable dependencies
|
||||
- **ConfigOptions**: Dependency config options
|
||||
- **SkipPreRegisteredServers**: Flag to skip validation of pre-registered Go servers
|
||||
|
||||
This approach ensures:
|
||||
- **Consistency**: Same validation logic as runtime
|
||||
- **Maintainability**: Single source of truth for parsing logic
|
||||
- **Minimal Code Duplication**: Reuses existing implementation
|
||||
|
||||
## Testing
|
||||
|
||||
Run the tests to verify the validator works correctly:
|
||||
|
||||
```bash
|
||||
cd pkg/mcp/validator
|
||||
go test -v
|
||||
```
|
||||
|
||||
The test suite covers:
|
||||
- REST server configuration validation
|
||||
- ToolSet configuration validation
|
||||
- Pre-registered server handling
|
||||
- Invalid configuration detection
|
||||
- Error cases
|
||||
|
||||
## Error Handling
|
||||
|
||||
The validator provides detailed error messages for common configuration issues:
|
||||
|
||||
- Missing required fields (e.g., `server.name`)
|
||||
- Invalid JSON structure
|
||||
- Malformed tool definitions
|
||||
- Invalid template syntax
|
||||
- Missing server or toolSet configuration
|
||||
|
||||
These errors help developers quickly identify and fix configuration problems before deployment.
|
||||
129
plugins/wasm-go/pkg/mcp/validator/config_validator.go
Normal file
129
plugins/wasm-go/pkg/mcp/validator/config_validator.go
Normal file
@@ -0,0 +1,129 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validator
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/higress-group/wasm-go/pkg/log"
|
||||
"github.com/alibaba/higress/plugins/wasm-go/pkg/mcp/server"
|
||||
)
|
||||
|
||||
// validatorLogger is a simple logger implementation for validation mode
|
||||
type validatorLogger struct{}
|
||||
|
||||
func (l *validatorLogger) Trace(msg string) { fmt.Fprintf(os.Stderr, "[TRACE] %s\n", msg) }
|
||||
func (l *validatorLogger) Tracef(format string, args ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, "[TRACE] "+format+"\n", args...)
|
||||
}
|
||||
func (l *validatorLogger) Debug(msg string) { fmt.Fprintf(os.Stderr, "[DEBUG] %s\n", msg) }
|
||||
func (l *validatorLogger) Debugf(format string, args ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, "[DEBUG] "+format+"\n", args...)
|
||||
}
|
||||
func (l *validatorLogger) Info(msg string) { fmt.Fprintf(os.Stderr, "[INFO] %s\n", msg) }
|
||||
func (l *validatorLogger) Infof(format string, args ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, "[INFO] "+format+"\n", args...)
|
||||
}
|
||||
func (l *validatorLogger) Warn(msg string) { fmt.Fprintf(os.Stderr, "[WARN] %s\n", msg) }
|
||||
func (l *validatorLogger) Warnf(format string, args ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, "[WARN] "+format+"\n", args...)
|
||||
}
|
||||
func (l *validatorLogger) Error(msg string) { fmt.Fprintf(os.Stderr, "[ERROR] %s\n", msg) }
|
||||
func (l *validatorLogger) Errorf(format string, args ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, "[ERROR] "+format+"\n", args...)
|
||||
}
|
||||
func (l *validatorLogger) Critical(msg string) { fmt.Fprintf(os.Stderr, "[CRITICAL] %s\n", msg) }
|
||||
func (l *validatorLogger) Criticalf(format string, args ...interface{}) {
|
||||
fmt.Fprintf(os.Stderr, "[CRITICAL] "+format+"\n", args...)
|
||||
}
|
||||
func (l *validatorLogger) ResetID(pluginID string) {}
|
||||
|
||||
// init initializes the validator package
|
||||
func init() {
|
||||
// Set a custom logger for validation mode to prevent panics
|
||||
log.SetPluginLog(&validatorLogger{})
|
||||
}
|
||||
|
||||
// ValidationResult contains the result of configuration validation
|
||||
type ValidationResult struct {
|
||||
IsValid bool `json:"isValid"`
|
||||
Error error `json:"error,omitempty"`
|
||||
ServerName string `json:"serverName,omitempty"`
|
||||
IsComposed bool `json:"isComposed"`
|
||||
}
|
||||
|
||||
// ValidateConfig validates MCP configuration
|
||||
// This function focuses on validating REST tools and toolSet configurations
|
||||
// It skips validation for pre-registered Go-based servers
|
||||
func ValidateConfig(configJSON string) (*ValidationResult, error) {
|
||||
// Create empty dependencies for validation mode
|
||||
// We skip pre-registered servers validation by setting SkipPreRegisteredServers to true
|
||||
toolRegistry := &server.GlobalToolRegistry{}
|
||||
toolRegistry.Initialize() // Initialize the registry to prevent nil map assignment panic
|
||||
|
||||
deps := &server.ConfigOptions{
|
||||
Servers: make(map[string]server.Server), // Empty servers map
|
||||
ToolRegistry: toolRegistry, // Initialized registry
|
||||
SkipPreRegisteredServers: true, // Skip pre-registered servers
|
||||
}
|
||||
|
||||
// Call core parsing logic for validation
|
||||
configGjson := gjson.Parse(configJSON)
|
||||
mockConfig := &server.McpServerConfig{}
|
||||
|
||||
err := server.ParseConfigCore(configGjson, mockConfig, deps)
|
||||
|
||||
result := &ValidationResult{
|
||||
IsValid: err == nil,
|
||||
Error: err,
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
result.ServerName = mockConfig.GetServerName()
|
||||
result.IsComposed = mockConfig.GetIsComposed()
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// ValidateConfigYAML validates MCP configuration from YAML format
|
||||
// This function converts YAML to JSON first, then validates using the same logic
|
||||
func ValidateConfigYAML(configYAML string) (*ValidationResult, error) {
|
||||
// Parse YAML into a generic interface
|
||||
var yamlData interface{}
|
||||
if err := yaml.Unmarshal([]byte(configYAML), &yamlData); err != nil {
|
||||
return &ValidationResult{
|
||||
IsValid: false,
|
||||
Error: fmt.Errorf("failed to parse YAML: %v", err),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Convert to JSON
|
||||
jsonBytes, err := json.Marshal(yamlData)
|
||||
if err != nil {
|
||||
return &ValidationResult{
|
||||
IsValid: false,
|
||||
Error: fmt.Errorf("failed to convert YAML to JSON: %v", err),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Use the existing JSON validation logic
|
||||
return ValidateConfig(string(jsonBytes))
|
||||
}
|
||||
266
plugins/wasm-go/pkg/mcp/validator/config_validator_test.go
Normal file
266
plugins/wasm-go/pkg/mcp/validator/config_validator_test.go
Normal file
@@ -0,0 +1,266 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validator
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestValidateConfig_RestServer(t *testing.T) {
|
||||
// Test REST server configuration
|
||||
configJSON := `{
|
||||
"server": {
|
||||
"name": "test-rest-server",
|
||||
"config": {}
|
||||
},
|
||||
"tools": [
|
||||
{
|
||||
"name": "test-tool",
|
||||
"description": "A test tool",
|
||||
"args": [
|
||||
{
|
||||
"name": "input",
|
||||
"description": "Input parameter",
|
||||
"type": "string",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"requestTemplate": {
|
||||
"url": "https://api.example.com/test",
|
||||
"method": "POST"
|
||||
},
|
||||
"responseTemplate": {
|
||||
"body": "{{.}}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
result, err := ValidateConfig(configJSON)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateConfig returned error: %v", err)
|
||||
}
|
||||
|
||||
if !result.IsValid {
|
||||
t.Errorf("Expected config to be valid, but got invalid with error: %v", result.Error)
|
||||
}
|
||||
|
||||
if result.ServerName != "test-rest-server" {
|
||||
t.Errorf("Expected server name 'test-rest-server', got '%s'", result.ServerName)
|
||||
}
|
||||
|
||||
if result.IsComposed {
|
||||
t.Errorf("Expected single server (not composed), but got composed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateConfig_ToolSet(t *testing.T) {
|
||||
// Test toolSet configuration
|
||||
configJSON := `{
|
||||
"toolSet": {
|
||||
"name": "test-toolset",
|
||||
"serverTools": [
|
||||
{
|
||||
"serverName": "server1",
|
||||
"tools": ["tool1", "tool2"]
|
||||
},
|
||||
{
|
||||
"serverName": "server2",
|
||||
"tools": ["tool3"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}`
|
||||
|
||||
result, err := ValidateConfig(configJSON)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateConfig returned error: %v", err)
|
||||
}
|
||||
|
||||
if !result.IsValid {
|
||||
t.Errorf("Expected config to be valid, but got invalid with error: %v", result.Error)
|
||||
}
|
||||
|
||||
if result.ServerName != "test-toolset" {
|
||||
t.Errorf("Expected server name 'test-toolset', got '%s'", result.ServerName)
|
||||
}
|
||||
|
||||
if !result.IsComposed {
|
||||
t.Errorf("Expected composed server, but got single server")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateConfig_PreRegisteredServer(t *testing.T) {
|
||||
// Test pre-registered Go-based server configuration (should be skipped in validation)
|
||||
configJSON := `{
|
||||
"server": {
|
||||
"name": "some-go-server",
|
||||
"config": {
|
||||
"someParam": "value"
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
result, err := ValidateConfig(configJSON)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateConfig returned error: %v", err)
|
||||
}
|
||||
|
||||
if !result.IsValid {
|
||||
t.Errorf("Expected config to be valid (pre-registered servers should be skipped), but got invalid with error: %v", result.Error)
|
||||
}
|
||||
|
||||
if result.ServerName != "some-go-server" {
|
||||
t.Errorf("Expected server name 'some-go-server', got '%s'", result.ServerName)
|
||||
}
|
||||
|
||||
if result.IsComposed {
|
||||
t.Errorf("Expected single server (not composed), but got composed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateConfig_InvalidConfig(t *testing.T) {
|
||||
// Test invalid configuration (missing required fields)
|
||||
configJSON := `{
|
||||
"server": {
|
||||
"config": {}
|
||||
}
|
||||
}`
|
||||
|
||||
result, err := ValidateConfig(configJSON)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateConfig returned error: %v", err)
|
||||
}
|
||||
|
||||
if result.IsValid {
|
||||
t.Errorf("Expected config to be invalid, but got valid")
|
||||
}
|
||||
|
||||
if result.Error == nil {
|
||||
t.Errorf("Expected validation error, but got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateConfig_MissingServerAndToolSet(t *testing.T) {
|
||||
// Test configuration missing both server and toolSet
|
||||
configJSON := `{
|
||||
"allowTools": ["tool1"]
|
||||
}`
|
||||
|
||||
result, err := ValidateConfig(configJSON)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateConfig returned error: %v", err)
|
||||
}
|
||||
|
||||
if result.IsValid {
|
||||
t.Errorf("Expected config to be invalid, but got valid")
|
||||
}
|
||||
|
||||
if result.Error == nil {
|
||||
t.Errorf("Expected validation error, but got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateConfigYAML_RestServer(t *testing.T) {
|
||||
// Test REST server configuration in YAML format
|
||||
configYAML := `
|
||||
server:
|
||||
name: test-rest-server-yaml
|
||||
config: {}
|
||||
tools:
|
||||
- name: test-tool-yaml
|
||||
description: A test tool from YAML
|
||||
args:
|
||||
- name: input
|
||||
description: Input parameter
|
||||
type: string
|
||||
required: true
|
||||
requestTemplate:
|
||||
url: https://api.example.com/test
|
||||
method: POST
|
||||
responseTemplate:
|
||||
body: "{{.}}"
|
||||
`
|
||||
|
||||
result, err := ValidateConfigYAML(configYAML)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateConfigYAML returned error: %v", err)
|
||||
}
|
||||
|
||||
if !result.IsValid {
|
||||
t.Errorf("Expected config to be valid, but got invalid with error: %v", result.Error)
|
||||
}
|
||||
|
||||
if result.ServerName != "test-rest-server-yaml" {
|
||||
t.Errorf("Expected server name 'test-rest-server-yaml', got '%s'", result.ServerName)
|
||||
}
|
||||
|
||||
if result.IsComposed {
|
||||
t.Errorf("Expected single server (not composed), but got composed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateConfigYAML_ToolSet(t *testing.T) {
|
||||
// Test toolSet configuration in YAML format
|
||||
configYAML := `
|
||||
toolSet:
|
||||
name: test-toolset-yaml
|
||||
serverTools:
|
||||
- serverName: server1
|
||||
tools: ["tool1", "tool2"]
|
||||
- serverName: server2
|
||||
tools: ["tool3"]
|
||||
`
|
||||
|
||||
result, err := ValidateConfigYAML(configYAML)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateConfigYAML returned error: %v", err)
|
||||
}
|
||||
|
||||
if !result.IsValid {
|
||||
t.Errorf("Expected config to be valid, but got invalid with error: %v", result.Error)
|
||||
}
|
||||
|
||||
if result.ServerName != "test-toolset-yaml" {
|
||||
t.Errorf("Expected server name 'test-toolset-yaml', got '%s'", result.ServerName)
|
||||
}
|
||||
|
||||
if !result.IsComposed {
|
||||
t.Errorf("Expected composed server, but got single server")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateConfigYAML_InvalidYAML(t *testing.T) {
|
||||
// Test invalid YAML syntax
|
||||
configYAML := `
|
||||
server:
|
||||
name: test-server
|
||||
config: {
|
||||
invalid yaml syntax here
|
||||
`
|
||||
|
||||
result, err := ValidateConfigYAML(configYAML)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateConfigYAML returned error: %v", err)
|
||||
}
|
||||
|
||||
if result.IsValid {
|
||||
t.Errorf("Expected config to be invalid due to YAML syntax error, but got valid")
|
||||
}
|
||||
|
||||
if result.Error == nil {
|
||||
t.Errorf("Expected YAML parsing error, but got nil")
|
||||
}
|
||||
}
|
||||
250
plugins/wasm-go/pkg/mcp/validator/example_usage.go
Normal file
250
plugins/wasm-go/pkg/mcp/validator/example_usage.go
Normal file
@@ -0,0 +1,250 @@
|
||||
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package validator
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ExampleUsage demonstrates how to use the ValidateConfig function
|
||||
func ExampleUsage() {
|
||||
// Example 1: REST server configuration
|
||||
restServerConfig := `{
|
||||
"server": {
|
||||
"name": "weather-api",
|
||||
"config": {
|
||||
"apiKey": "your-api-key"
|
||||
}
|
||||
},
|
||||
"tools": [
|
||||
{
|
||||
"name": "get_weather",
|
||||
"description": "Get current weather for a city",
|
||||
"args": [
|
||||
{
|
||||
"name": "city",
|
||||
"description": "City name",
|
||||
"type": "string",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "units",
|
||||
"description": "Temperature units",
|
||||
"type": "string",
|
||||
"enum": ["celsius", "fahrenheit"],
|
||||
"default": "celsius"
|
||||
}
|
||||
],
|
||||
"requestTemplate": {
|
||||
"url": "https://api.weather.com/v1/current?city={{.args.city}}&units={{.args.units}}",
|
||||
"method": "GET",
|
||||
"headers": [
|
||||
{
|
||||
"key": "Authorization",
|
||||
"value": "Bearer {{.config.apiKey}}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"responseTemplate": {
|
||||
"body": "Current weather in {{.args.city}}: {{.temperature}}°{{.args.units}}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"allowTools": ["get_weather"]
|
||||
}`
|
||||
|
||||
result, err := ValidateConfig(restServerConfig)
|
||||
if err != nil {
|
||||
fmt.Printf("Error validating REST server config: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("REST Server Config Validation:\n")
|
||||
fmt.Printf(" Valid: %t\n", result.IsValid)
|
||||
fmt.Printf(" Server Name: %s\n", result.ServerName)
|
||||
fmt.Printf(" Is Composed: %t\n", result.IsComposed)
|
||||
if result.Error != nil {
|
||||
fmt.Printf(" Error: %v\n", result.Error)
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
// Example 2: ToolSet configuration
|
||||
toolSetConfig := `{
|
||||
"toolSet": {
|
||||
"name": "ai-assistant-tools",
|
||||
"serverTools": [
|
||||
{
|
||||
"serverName": "weather-api",
|
||||
"tools": ["get_weather", "get_forecast"]
|
||||
},
|
||||
{
|
||||
"serverName": "search-api",
|
||||
"tools": ["web_search", "image_search"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"allowTools": ["weather-api/get_weather", "search-api/web_search"]
|
||||
}`
|
||||
|
||||
result, err = ValidateConfig(toolSetConfig)
|
||||
if err != nil {
|
||||
fmt.Printf("Error validating toolSet config: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("ToolSet Config Validation:\n")
|
||||
fmt.Printf(" Valid: %t\n", result.IsValid)
|
||||
fmt.Printf(" Server Name: %s\n", result.ServerName)
|
||||
fmt.Printf(" Is Composed: %t\n", result.IsComposed)
|
||||
if result.Error != nil {
|
||||
fmt.Printf(" Error: %v\n", result.Error)
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
// Example 3: Pre-registered Go-based server (validation skipped)
|
||||
goServerConfig := `{
|
||||
"server": {
|
||||
"name": "custom-go-server",
|
||||
"config": {
|
||||
"database_url": "postgres://localhost:5432/mydb",
|
||||
"max_connections": 10
|
||||
}
|
||||
},
|
||||
"allowTools": ["query_database", "update_record"]
|
||||
}`
|
||||
|
||||
result, err = ValidateConfig(goServerConfig)
|
||||
if err != nil {
|
||||
fmt.Printf("Error validating Go server config: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Go Server Config Validation (skipped):\n")
|
||||
fmt.Printf(" Valid: %t\n", result.IsValid)
|
||||
fmt.Printf(" Server Name: %s\n", result.ServerName)
|
||||
fmt.Printf(" Is Composed: %t\n", result.IsComposed)
|
||||
if result.Error != nil {
|
||||
fmt.Printf(" Error: %v\n", result.Error)
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
// Example 4: Invalid configuration
|
||||
invalidConfig := `{
|
||||
"server": {
|
||||
"config": {}
|
||||
}
|
||||
}`
|
||||
|
||||
result, err = ValidateConfig(invalidConfig)
|
||||
if err != nil {
|
||||
fmt.Printf("Error validating invalid config: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Invalid Config Validation:\n")
|
||||
fmt.Printf(" Valid: %t\n", result.IsValid)
|
||||
if result.Error != nil {
|
||||
fmt.Printf(" Error: %v\n", result.Error)
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateConfigFromBytes validates configuration from byte array
|
||||
func ValidateConfigFromBytes(configBytes []byte) (*ValidationResult, error) {
|
||||
return ValidateConfig(string(configBytes))
|
||||
}
|
||||
|
||||
// ValidateConfigFromMap validates configuration from a map
|
||||
func ValidateConfigFromMap(configMap map[string]interface{}) (*ValidationResult, error) {
|
||||
configBytes, err := json.Marshal(configMap)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal config map: %v", err)
|
||||
}
|
||||
return ValidateConfig(string(configBytes))
|
||||
}
|
||||
|
||||
// ExampleYAMLUsage demonstrates how to use the ValidateConfigYAML function
|
||||
func ExampleYAMLUsage() {
|
||||
// Example YAML configuration for REST server
|
||||
yamlConfig := `
|
||||
server:
|
||||
name: weather-api-yaml
|
||||
config:
|
||||
apiKey: your-api-key
|
||||
tools:
|
||||
- name: get_weather
|
||||
description: Get current weather for a city
|
||||
args:
|
||||
- name: city
|
||||
description: City name
|
||||
type: string
|
||||
required: true
|
||||
- name: units
|
||||
description: Temperature units
|
||||
type: string
|
||||
enum: ["celsius", "fahrenheit"]
|
||||
default: celsius
|
||||
requestTemplate:
|
||||
url: "https://api.weather.com/v1/current?city={{.args.city}}&units={{.args.units}}"
|
||||
method: GET
|
||||
headers:
|
||||
- key: Authorization
|
||||
value: "Bearer {{.config.apiKey}}"
|
||||
responseTemplate:
|
||||
body: "Current weather in {{.args.city}}: {{.temperature}}°{{.args.units}}"
|
||||
allowTools: ["get_weather"]
|
||||
`
|
||||
|
||||
result, err := ValidateConfigYAML(yamlConfig)
|
||||
if err != nil {
|
||||
fmt.Printf("Error validating YAML config: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("YAML Config Validation:\n")
|
||||
fmt.Printf(" Valid: %t\n", result.IsValid)
|
||||
fmt.Printf(" Server Name: %s\n", result.ServerName)
|
||||
fmt.Printf(" Is Composed: %t\n", result.IsComposed)
|
||||
if result.Error != nil {
|
||||
fmt.Printf(" Error: %v\n", result.Error)
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
// Example YAML configuration for ToolSet
|
||||
yamlToolSetConfig := `
|
||||
toolSet:
|
||||
name: ai-assistant-tools-yaml
|
||||
serverTools:
|
||||
- serverName: weather-api
|
||||
tools: ["get_weather", "get_forecast"]
|
||||
- serverName: search-api
|
||||
tools: ["web_search", "image_search"]
|
||||
allowTools: ["weather-api/get_weather", "search-api/web_search"]
|
||||
`
|
||||
|
||||
result, err = ValidateConfigYAML(yamlToolSetConfig)
|
||||
if err != nil {
|
||||
fmt.Printf("Error validating YAML toolSet config: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("YAML ToolSet Config Validation:\n")
|
||||
fmt.Printf(" Valid: %t\n", result.IsValid)
|
||||
fmt.Printf(" Server Name: %s\n", result.ServerName)
|
||||
fmt.Printf(" Is Composed: %t\n", result.IsComposed)
|
||||
if result.Error != nil {
|
||||
fmt.Printf(" Error: %v\n", result.Error)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user