adapter

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 1, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package adapter provides protocol-agnostic tool format conversion. It enables bidirectional transformation between MCP, OpenAI, and Anthropic tool definitions through a canonical intermediate representation.

This is a pure data-transform library with no I/O, network, or runtime execution.

Overview

The adapter package uses a hub-and-spoke architecture where all conversions pass through a canonical intermediate format (CanonicalTool). This allows N adapters to support N² conversions with only N implementations.

Basic Usage

Use the default registry with all built-in adapters:

registry := adapter.DefaultRegistry()

// Convert an MCP tool to OpenAI format
result, err := registry.Convert(mcpTool, "mcp", "openai")
if err != nil {
    log.Fatal(err)
}

// Check for feature loss warnings
for _, w := range result.Warnings {
    log.Printf("Warning: %s", w)
}

openaiTool := result.Tool.(*adapter.OpenAITool)

Supported Formats

The package includes adapters for three tool formats:

  • MCP (Model Context Protocol) - Full JSON Schema 2020-12 support
  • OpenAI - Function calling format with strict mode support
  • Anthropic - Tool use format with anyOf support

Feature Loss Warnings

Different formats support different JSON Schema features. When converting from a format with more features to one with fewer, the adapter emits warnings indicating which features were lost:

result, _ := registry.Convert(tool, "mcp", "openai")
if len(result.Warnings) > 0 {
    fmt.Println("The following features are not supported by OpenAI:")
    for _, w := range result.Warnings {
        fmt.Printf("  - %s at %s\n", w.Feature, w.Path)
    }
}

Feature Support Matrix

Feature          MCP    OpenAI  Anthropic
─────────────────────────────────────────
$ref/$defs       Yes    No      No
anyOf            Yes    No      Yes
oneOf            Yes    No      No
allOf            Yes    No      No
not              Yes    No      No
pattern          Yes    Yes     Yes
format           Yes    No      Yes
enum/const       Yes    Yes     Yes
min/max          Yes    Yes     Yes

Custom Adapters

Implement the Adapter interface to add support for new formats:

type Adapter interface {
    Name() string
    ToCanonical(raw any) (*CanonicalTool, error)
    FromCanonical(ct *CanonicalTool) (any, error)
    SupportsFeature(feature SchemaFeature) bool
}

Register custom adapters with the registry:

registry := adapter.NewRegistry()
registry.Register(adapter.NewMCPAdapter())
registry.Register(myCustomAdapter)

Type Definitions

The package defines local types for OpenAI and Anthropic formats to avoid SDK coupling:

  • OpenAITool / OpenAIFunction - OpenAI function calling format
  • AnthropicTool - Anthropic tool use format

These types can be serialized directly to JSON for API requests.

Thread Safety

The AdapterRegistry is thread-safe for concurrent reads after initial registration. Register all adapters during initialization before concurrent use.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Adapter

type Adapter interface {
	// Name returns the adapter's identifier (e.g., "mcp", "openai", "anthropic")
	Name() string

	// ToCanonical converts a protocol-specific tool to canonical format.
	// The raw parameter type depends on the adapter (e.g., mcp.Tool, OpenAIFunction).
	ToCanonical(raw any) (*CanonicalTool, error)

	// FromCanonical converts a canonical tool to the protocol-specific format.
	// The returned type depends on the adapter.
	FromCanonical(tool *CanonicalTool) (any, error)

	// SupportsFeature returns whether this adapter supports a schema feature.
	// Features not supported will generate warnings during conversion.
	SupportsFeature(feature SchemaFeature) bool
}

Adapter defines the contract for protocol-specific tool adapters. Each adapter handles bidirectional conversion between a specific tool format (MCP, OpenAI, Anthropic) and the canonical representation.

Contract:

  • Thread-safety: implementations must be safe for concurrent use unless documented otherwise.
  • Ownership: must not mutate caller-owned inputs; return new objects on conversion.
  • Errors: ToCanonical/FromCanonical must return typed errors (e.g., *ConversionError) for invalid input.
  • Determinism: same input yields structurally equivalent canonical output.

type AdapterRegistry

type AdapterRegistry struct {
	// contains filtered or unexported fields
}

AdapterRegistry is a thread-safe registry of protocol adapters.

func DefaultRegistry added in v0.2.0

func DefaultRegistry() *AdapterRegistry

DefaultRegistry returns a registry pre-configured with all built-in adapters. The registry includes MCP, OpenAI, and Anthropic adapters.

Example
package main

import (
	"fmt"

	"github.com/jonwraymond/toolfoundation/adapter"
)

func main() {
	registry := adapter.DefaultRegistry()

	// List available adapters
	adapters := registry.List()
	fmt.Printf("Adapter count: %d\n", len(adapters))
}
Output:

Adapter count: 3

func NewRegistry

func NewRegistry() *AdapterRegistry

NewRegistry creates a new empty adapter registry.

Example
package main

import (
	"fmt"

	"github.com/jonwraymond/toolfoundation/adapter"
)

func main() {
	// Create a custom registry with only specific adapters
	registry := adapter.NewRegistry()

	// Register only the adapters you need
	_ = registry.Register(adapter.NewMCPAdapter())
	_ = registry.Register(adapter.NewOpenAIAdapter())

	fmt.Printf("Custom registry has %d adapters\n", len(registry.List()))
}
Output:

Custom registry has 2 adapters

func (*AdapterRegistry) Convert

func (r *AdapterRegistry) Convert(tool any, fromFormat, toFormat string) (*ConversionResult, error)

Convert transforms a tool from one format to another. It uses the source adapter's ToCanonical and the target adapter's FromCanonical. Returns warnings if schema features are lost during conversion.

Example
package main

import (
	"fmt"
	"log"

	"github.com/modelcontextprotocol/go-sdk/mcp"

	"github.com/jonwraymond/toolfoundation/adapter"
	"github.com/jonwraymond/toolfoundation/model"
)

func main() {
	// Create a tool
	tool := &model.Tool{
		Tool: mcp.Tool{
			Name:        "get_weather",
			Description: "Get current weather for a location",
			InputSchema: map[string]any{
				"type": "object",
				"properties": map[string]any{
					"location": map[string]any{
						"type":        "string",
						"description": "City name",
					},
				},
				"required": []string{"location"},
			},
		},
	}

	// Convert MCP to OpenAI format
	registry := adapter.DefaultRegistry()
	result, err := registry.Convert(tool, "mcp", "openai")
	if err != nil {
		log.Fatal(err)
	}

	openaiTool := result.Tool.(*adapter.OpenAITool)
	fmt.Println("Type:", openaiTool.Type)
	fmt.Println("Function name:", openaiTool.Function.Name)
}
Output:

Type: function
Function name: get_weather
Example (WithWarnings)
package main

import (
	"fmt"

	"github.com/modelcontextprotocol/go-sdk/mcp"

	"github.com/jonwraymond/toolfoundation/adapter"
	"github.com/jonwraymond/toolfoundation/model"
)

func main() {
	// Create a tool with features not supported by all adapters
	tool := &model.Tool{
		Tool: mcp.Tool{
			Name:        "search",
			Description: "Search with flexible input",
			InputSchema: map[string]any{
				"type": "object",
				"properties": map[string]any{
					"query": map[string]any{
						// anyOf is not supported by OpenAI
						"anyOf": []any{
							map[string]any{"type": "string"},
							map[string]any{"type": "array", "items": map[string]any{"type": "string"}},
						},
					},
				},
			},
		},
	}

	registry := adapter.DefaultRegistry()
	result, _ := registry.Convert(tool, "mcp", "openai")

	if len(result.Warnings) > 0 {
		fmt.Println("Feature loss detected")
		for _, w := range result.Warnings {
			fmt.Printf("  - %s\n", w.Feature)
		}
	}
}
Output:

Feature loss detected
  - anyOf

func (*AdapterRegistry) Get

func (r *AdapterRegistry) Get(name string) (Adapter, error)

Get retrieves an adapter by name. Returns an error if the adapter is not found.

func (*AdapterRegistry) List

func (r *AdapterRegistry) List() []string

List returns the names of all registered adapters.

func (*AdapterRegistry) Register

func (r *AdapterRegistry) Register(a Adapter) error

Register adds an adapter to the registry. Returns an error if an adapter with the same name is already registered.

func (*AdapterRegistry) Unregister

func (r *AdapterRegistry) Unregister(name string) error

Unregister removes an adapter from the registry. Returns an error if the adapter is not found.

type AnthropicAdapter added in v0.2.0

type AnthropicAdapter struct{}

AnthropicAdapter converts between Anthropic tool format and CanonicalTool.

func NewAnthropicAdapter added in v0.2.0

func NewAnthropicAdapter() *AnthropicAdapter

NewAnthropicAdapter creates a new Anthropic adapter.

func (*AnthropicAdapter) FromCanonical added in v0.2.0

func (a *AnthropicAdapter) FromCanonical(ct *CanonicalTool) (any, error)

FromCanonical converts a canonical tool to Anthropic format. Returns *AnthropicTool.

func (*AnthropicAdapter) Name added in v0.2.0

func (a *AnthropicAdapter) Name() string

Name returns the adapter's identifier.

func (*AnthropicAdapter) SupportsFeature added in v0.2.0

func (a *AnthropicAdapter) SupportsFeature(feature SchemaFeature) bool

SupportsFeature returns whether this adapter supports a schema feature.

func (*AnthropicAdapter) ToCanonical added in v0.2.0

func (a *AnthropicAdapter) ToCanonical(raw any) (*CanonicalTool, error)

ToCanonical converts an Anthropic tool to the canonical format. Accepts *AnthropicTool or AnthropicTool.

type AnthropicCacheControl added in v0.2.0

type AnthropicCacheControl struct {
	Type string `json:"type"` // "ephemeral"
}

AnthropicCacheControl for prompt caching.

type AnthropicTool added in v0.2.0

type AnthropicTool struct {
	Name         string                 `json:"name"`
	Description  string                 `json:"description,omitempty"`
	InputSchema  map[string]any         `json:"input_schema"`
	CacheControl *AnthropicCacheControl `json:"cache_control,omitempty"`
}

AnthropicTool represents the Anthropic tool format. Based on Anthropic API spec - defined locally to avoid SDK coupling.

Example
package main

import (
	"encoding/json"
	"fmt"

	"github.com/jonwraymond/toolfoundation/adapter"
)

func main() {
	anthropicTool := &adapter.AnthropicTool{
		Name:        "search_docs",
		Description: "Search documentation",
		InputSchema: map[string]any{
			"type": "object",
			"properties": map[string]any{
				"query": map[string]any{"type": "string"},
			},
			"required": []string{"query"},
		},
	}

	// Serialize to JSON for API request
	data, _ := json.MarshalIndent(anthropicTool, "", "  ")
	fmt.Println(string(data))
}
Output:

{
  "name": "search_docs",
  "description": "Search documentation",
  "input_schema": {
    "properties": {
      "query": {
        "type": "string"
      }
    },
    "required": [
      "query"
    ],
    "type": "object"
  }
}

type CanonicalTool

type CanonicalTool struct {
	// Namespace groups related tools (e.g., "github", "slack")
	Namespace string

	// Name is the tool's identifier (required)
	Name string

	// Version is the semantic version of the tool
	Version string

	// Description explains what the tool does
	Description string

	// Category classifies the tool's purpose
	Category string

	// Tags are keywords for discovery
	Tags []string

	// InputSchema defines the tool's input parameters (required)
	InputSchema *JSONSchema

	// OutputSchema defines the tool's output format
	OutputSchema *JSONSchema

	// Timeout is the maximum execution time
	Timeout time.Duration

	// SourceFormat is the original format (e.g., "mcp", "openai", "anthropic")
	SourceFormat string

	// SourceMeta contains format-specific metadata for round-trip conversion
	SourceMeta map[string]any

	// RequiredScopes are authorization scopes needed to use the tool
	RequiredScopes []string
}

CanonicalTool is the protocol-agnostic representation of a tool definition. It serves as the intermediate format for converting between MCP, OpenAI, and Anthropic tool formats.

func (*CanonicalTool) ID

func (t *CanonicalTool) ID() string

ID returns the tool's fully qualified identifier. If Namespace is set, returns "namespace:name", otherwise just "name".

func (*CanonicalTool) Validate

func (t *CanonicalTool) Validate() error

Validate checks that the tool has all required fields. Returns an error if Name or InputSchema is missing.

type ConversionError

type ConversionError struct {
	// Adapter is the name of the adapter that encountered the error
	Adapter string

	// Direction is "to_canonical" or "from_canonical"
	Direction string

	// Cause is the underlying error
	Cause error
}

ConversionError represents an error during tool format conversion.

func (*ConversionError) Error

func (e *ConversionError) Error() string

Error returns a formatted error message including adapter, direction, and cause.

func (*ConversionError) Unwrap

func (e *ConversionError) Unwrap() error

Unwrap returns the underlying cause for use with errors.Is and errors.As.

type ConversionResult

type ConversionResult struct {
	// Tool is the converted tool in the target format
	Tool any

	// Warnings lists features that may have been lost during conversion
	Warnings []FeatureLossWarning
}

ConversionResult contains the result of a format conversion.

type FeatureLossWarning

type FeatureLossWarning struct {
	// Feature is the schema feature that will be lost
	Feature SchemaFeature

	// FromAdapter is the source adapter name
	FromAdapter string

	// ToAdapter is the target adapter name
	ToAdapter string
}

FeatureLossWarning indicates that a schema feature will be lost during conversion. This is a warning, not an error - the conversion proceeds but with reduced fidelity.

func (FeatureLossWarning) String

func (w FeatureLossWarning) String() string

String returns a human-readable warning message.

type JSONSchema

type JSONSchema struct {
	// Type is the JSON type (object, array, string, number, integer, boolean, null)
	Type string

	// Properties maps property names to their schemas (for object types)
	Properties map[string]*JSONSchema

	// Required lists property names that must be present
	Required []string

	// Items is the schema for array elements
	Items *JSONSchema

	// Description explains the schema
	Description string

	// Enum restricts values to a fixed set
	Enum []any

	// Const restricts to a single value
	Const any

	// Default is the default value
	Default any

	// Minimum is the minimum numeric value
	Minimum *float64

	// Maximum is the maximum numeric value
	Maximum *float64

	// MinLength is the minimum string length
	MinLength *int

	// MaxLength is the maximum string length
	MaxLength *int

	// Pattern is a regex pattern for string validation
	Pattern string

	// Format is a semantic format (e.g., "email", "uri", "date-time")
	Format string

	// Ref is a JSON Pointer reference to another schema ($ref)
	Ref string

	// Defs contains schema definitions ($defs)
	Defs map[string]*JSONSchema

	// AdditionalProperties controls whether extra properties are allowed
	AdditionalProperties *bool

	// AnyOf allows any of the listed schemas
	AnyOf []*JSONSchema

	// OneOf requires exactly one of the listed schemas
	OneOf []*JSONSchema

	// AllOf requires all of the listed schemas
	AllOf []*JSONSchema

	// Not disallows the specified schema
	Not *JSONSchema
}

JSONSchema represents a JSON Schema definition. It is a superset supporting features from MCP, OpenAI, and Anthropic formats.

func (*JSONSchema) DeepCopy

func (s *JSONSchema) DeepCopy() *JSONSchema

DeepCopy creates a deep copy of the JSONSchema. Returns nil if the receiver is nil.

func (*JSONSchema) ToMap

func (s *JSONSchema) ToMap() map[string]any

ToMap converts the JSONSchema to a map[string]any representation. Zero-valued fields are omitted from the output.

type MCPAdapter added in v0.2.0

type MCPAdapter struct{}

MCPAdapter converts between model.Tool and CanonicalTool. MCP supports all JSON Schema 2020-12 features.

func NewMCPAdapter added in v0.2.0

func NewMCPAdapter() *MCPAdapter

NewMCPAdapter creates a new MCP adapter.

func (*MCPAdapter) FromCanonical added in v0.2.0

func (a *MCPAdapter) FromCanonical(ct *CanonicalTool) (any, error)

FromCanonical converts a canonical tool to model.Tool.

func (*MCPAdapter) Name added in v0.2.0

func (a *MCPAdapter) Name() string

Name returns the adapter's identifier.

func (*MCPAdapter) SupportsFeature added in v0.2.0

func (a *MCPAdapter) SupportsFeature(feature SchemaFeature) bool

SupportsFeature returns whether this adapter supports a schema feature. MCP supports all JSON Schema 2020-12 features.

Example
package main

import (
	"fmt"

	"github.com/jonwraymond/toolfoundation/adapter"
)

func main() {
	mcp := adapter.NewMCPAdapter()
	openai := adapter.NewOpenAIAdapter()

	// MCP supports all JSON Schema features
	fmt.Println("MCP supports $ref:", mcp.SupportsFeature(adapter.FeatureRef))
	fmt.Println("MCP supports anyOf:", mcp.SupportsFeature(adapter.FeatureAnyOf))

	// OpenAI has limited support
	fmt.Println("OpenAI supports $ref:", openai.SupportsFeature(adapter.FeatureRef))
	fmt.Println("OpenAI supports anyOf:", openai.SupportsFeature(adapter.FeatureAnyOf))
}
Output:

MCP supports $ref: true
MCP supports anyOf: true
OpenAI supports $ref: false
OpenAI supports anyOf: false

func (*MCPAdapter) ToCanonical added in v0.2.0

func (a *MCPAdapter) ToCanonical(raw any) (*CanonicalTool, error)

ToCanonical converts an MCP tool to the canonical format. Accepts *model.Tool, model.Tool, *mcp.Tool, or mcp.Tool.

type OpenAIAdapter added in v0.2.0

type OpenAIAdapter struct{}

OpenAIAdapter converts between OpenAI function format and CanonicalTool.

func NewOpenAIAdapter added in v0.2.0

func NewOpenAIAdapter() *OpenAIAdapter

NewOpenAIAdapter creates a new OpenAI adapter.

func (*OpenAIAdapter) FromCanonical added in v0.2.0

func (a *OpenAIAdapter) FromCanonical(ct *CanonicalTool) (any, error)

FromCanonical converts a canonical tool to OpenAI format. Returns *OpenAITool.

func (*OpenAIAdapter) Name added in v0.2.0

func (a *OpenAIAdapter) Name() string

Name returns the adapter's identifier.

func (*OpenAIAdapter) SupportsFeature added in v0.2.0

func (a *OpenAIAdapter) SupportsFeature(feature SchemaFeature) bool

SupportsFeature returns whether this adapter supports a schema feature.

func (*OpenAIAdapter) ToCanonical added in v0.2.0

func (a *OpenAIAdapter) ToCanonical(raw any) (*CanonicalTool, error)

ToCanonical converts an OpenAI tool to the canonical format. Accepts *OpenAITool, OpenAITool, *OpenAIFunction, or OpenAIFunction.

type OpenAIFunction added in v0.2.0

type OpenAIFunction struct {
	Name        string         `json:"name"`
	Description string         `json:"description,omitempty"`
	Parameters  map[string]any `json:"parameters"`
	Strict      *bool          `json:"strict,omitempty"`
}

OpenAIFunction represents the OpenAI function calling format. Based on OpenAI API spec - defined locally to avoid SDK coupling.

type OpenAITool added in v0.2.0

type OpenAITool struct {
	Type     string         `json:"type"` // always "function"
	Function OpenAIFunction `json:"function"`
}

OpenAITool wraps a function for the tools array format.

Example
package main

import (
	"encoding/json"
	"fmt"

	"github.com/jonwraymond/toolfoundation/adapter"
)

func main() {
	openaiTool := &adapter.OpenAITool{
		Type: "function",
		Function: adapter.OpenAIFunction{
			Name:        "calculate",
			Description: "Perform a calculation",
			Parameters: map[string]any{
				"type": "object",
				"properties": map[string]any{
					"expression": map[string]any{"type": "string"},
				},
				"required": []string{"expression"},
			},
		},
	}

	// Serialize to JSON for API request
	data, _ := json.MarshalIndent(openaiTool, "", "  ")
	fmt.Println(string(data))
}
Output:

{
  "type": "function",
  "function": {
    "name": "calculate",
    "description": "Perform a calculation",
    "parameters": {
      "properties": {
        "expression": {
          "type": "string"
        }
      },
      "required": [
        "expression"
      ],
      "type": "object"
    }
  }
}

type SchemaFeature

type SchemaFeature int

SchemaFeature represents a JSON Schema feature that may or may not be supported by a particular protocol adapter.

const (
	// FeatureRef is the $ref keyword for schema references
	FeatureRef SchemaFeature = iota
	// FeatureDefs is the $defs keyword for schema definitions
	FeatureDefs
	// FeatureAnyOf allows any of the listed schemas
	FeatureAnyOf
	// FeatureOneOf requires exactly one of the listed schemas
	FeatureOneOf
	// FeatureAllOf requires all of the listed schemas
	FeatureAllOf
	// FeatureNot disallows the specified schema
	FeatureNot
	// FeaturePattern is regex pattern validation
	FeaturePattern
	// FeatureFormat is semantic format validation
	FeatureFormat
	// FeatureAdditionalProperties controls extra property handling
	FeatureAdditionalProperties
	// FeatureMinimum is minimum numeric value
	FeatureMinimum
	// FeatureMaximum is maximum numeric value
	FeatureMaximum
	// FeatureMinLength is minimum string length
	FeatureMinLength
	// FeatureMaxLength is maximum string length
	FeatureMaxLength
	// FeatureEnum restricts to a set of values
	FeatureEnum
	// FeatureConst restricts to a single value
	FeatureConst
	// FeatureDefault provides a default value
	FeatureDefault
)

func AllFeatures

func AllFeatures() []SchemaFeature

AllFeatures returns all known schema features in a stable order.

func (SchemaFeature) String

func (f SchemaFeature) String() string

String returns the JSON Schema keyword name for this feature.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL