Add MCP integration with MCPServer for tool-based interactions

- Introduce `MCPServer` to support connecting to MCP servers via stdio, SSE, or HTTP.
- Implement tool fetching, management, and invocation through MCP.
- Add `WithMCPServer` method to `ToolBox` for seamless tool integration.
- Extend schema package to handle raw JSON schemas for MCP tools.
- Update documentation with MCP usage guidelines and examples.
This commit is contained in:
2026-01-24 16:25:28 -05:00
parent 97d54c10ae
commit 2cf75ae07d
7 changed files with 446 additions and 2 deletions

134
schema/raw.go Normal file
View File

@@ -0,0 +1,134 @@
package schema
import (
"encoding/json"
"fmt"
"reflect"
"github.com/openai/openai-go"
"google.golang.org/genai"
)
// Raw represents a raw JSON schema that is passed through directly.
// This is used for MCP tools where we receive the schema from the server.
type Raw struct {
schema map[string]any
}
// NewRaw creates a new Raw schema from a map.
func NewRaw(schema map[string]any) Raw {
if schema == nil {
schema = map[string]any{
"type": "object",
"properties": map[string]any{},
}
}
return Raw{schema: schema}
}
// NewRawFromJSON creates a new Raw schema from JSON bytes.
func NewRawFromJSON(data []byte) (Raw, error) {
var schema map[string]any
if err := json.Unmarshal(data, &schema); err != nil {
return Raw{}, fmt.Errorf("failed to parse JSON schema: %w", err)
}
return NewRaw(schema), nil
}
func (r Raw) OpenAIParameters() openai.FunctionParameters {
return openai.FunctionParameters(r.schema)
}
func (r Raw) GoogleParameters() *genai.Schema {
return mapToGenaiSchema(r.schema)
}
func (r Raw) AnthropicInputSchema() map[string]any {
return r.schema
}
func (r Raw) Required() bool {
return false
}
func (r Raw) Description() string {
if desc, ok := r.schema["description"].(string); ok {
return desc
}
return ""
}
func (r Raw) FromAny(val any) (reflect.Value, error) {
return reflect.ValueOf(val), nil
}
func (r Raw) SetValueOnField(obj reflect.Value, val reflect.Value) {
// No-op for raw schemas
}
// mapToGenaiSchema converts a map[string]any JSON schema to genai.Schema
func mapToGenaiSchema(m map[string]any) *genai.Schema {
if m == nil {
return nil
}
schema := &genai.Schema{}
// Type
if t, ok := m["type"].(string); ok {
switch t {
case "string":
schema.Type = genai.TypeString
case "number":
schema.Type = genai.TypeNumber
case "integer":
schema.Type = genai.TypeInteger
case "boolean":
schema.Type = genai.TypeBoolean
case "array":
schema.Type = genai.TypeArray
case "object":
schema.Type = genai.TypeObject
}
}
// Description
if desc, ok := m["description"].(string); ok {
schema.Description = desc
}
// Enum
if enum, ok := m["enum"].([]any); ok {
for _, e := range enum {
if s, ok := e.(string); ok {
schema.Enum = append(schema.Enum, s)
}
}
}
// Properties (for objects)
if props, ok := m["properties"].(map[string]any); ok {
schema.Properties = make(map[string]*genai.Schema)
for k, v := range props {
if vm, ok := v.(map[string]any); ok {
schema.Properties[k] = mapToGenaiSchema(vm)
}
}
}
// Required
if req, ok := m["required"].([]any); ok {
for _, r := range req {
if s, ok := r.(string); ok {
schema.Required = append(schema.Required, s)
}
}
}
// Items (for arrays)
if items, ok := m["items"].(map[string]any); ok {
schema.Items = mapToGenaiSchema(items)
}
return schema
}