Documentation
¶
Overview ¶
Package backend provides tool execution backend abstractions and registry.
This package defines the core Backend interface and provides infrastructure for managing multiple backend sources:
- Backend interface for tool sources (local, MCP, HTTP, gRPC)
- Registry for managing and discovering backends
- Aggregator for multi-backend tool execution
Backend Types ¶
Backends can be:
- Local: In-process handlers registered directly
- MCP: Model Context Protocol servers (stdio, SSE, etc.)
- HTTP: RESTful tool APIs
- gRPC: High-performance tool services
Registry ¶
The Registry manages backend lifecycle:
registry := backend.NewRegistry()
registry.Register(localBackend)
registry.Register(mcpBackend)
// List all registered backends
for _, b := range registry.List() {
fmt.Printf("%s: %s\n", b.Kind(), b.Name())
}
Aggregator ¶
The Aggregator combines multiple backends for unified tool access:
agg := backend.NewAggregator(registry) tools, _ := agg.ListAllTools(ctx) result, _ := agg.Execute(ctx, "backend:tool", args)
Index ¶
- Variables
- func FormatToolID(backendName, tool string) string
- func ParseToolID(id string) (backendName, tool string, err error)
- type Aggregator
- type Backend
- type ConfigurableBackend
- type Factory
- type Info
- type Registry
- func (r *Registry) Get(name string) (Backend, bool)
- func (r *Registry) List() []Backend
- func (r *Registry) ListByKind(kind string) []Backend
- func (r *Registry) ListEnabled() []Backend
- func (r *Registry) Names() []string
- func (r *Registry) Register(b Backend) error
- func (r *Registry) RegisterFactory(kind string, factory Factory)
- func (r *Registry) StartAll(ctx context.Context) error
- func (r *Registry) StopAll() error
- func (r *Registry) Unregister(name string)
- type StreamingBackend
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrBackendNotFound = errors.New("backend not found") ErrBackendDisabled = errors.New("backend disabled") ErrToolNotFound = errors.New("tool not found in backend") )
Common errors for backend operations.
var ErrBackendExists = errors.New("backend already registered")
ErrBackendExists is returned when registering a duplicate backend.
var ErrInvalidToolID = errors.New("invalid tool ID format")
ErrInvalidToolID is returned for malformed tool IDs.
Functions ¶
func FormatToolID ¶
FormatToolID builds a tool ID from backend and tool name.
func ParseToolID ¶
ParseToolID splits a tool ID into backend and tool name.
Types ¶
type Aggregator ¶
type Aggregator struct {
// contains filtered or unexported fields
}
Aggregator combines tools from multiple backends.
Example ¶
package main
import (
"context"
"fmt"
"github.com/jonwraymond/toolexec/backend"
"github.com/jonwraymond/toolexec/backend/local"
)
func main() {
// Create registry and backends
reg := backend.NewRegistry()
backend1 := local.New("math")
backend1.RegisterHandler("add", local.ToolDef{
Name: "add",
Description: "Adds two numbers",
InputSchema: map[string]any{"type": "object"},
Handler: func(ctx context.Context, args map[string]any) (any, error) {
a, _ := args["a"].(float64)
b, _ := args["b"].(float64)
return a + b, nil
},
})
backend2 := local.New("text")
backend2.RegisterHandler("upper", local.ToolDef{
Name: "upper",
Description: "Converts to uppercase",
InputSchema: map[string]any{"type": "object"},
Handler: func(ctx context.Context, args map[string]any) (any, error) {
return "HELLO", nil
},
})
_ = reg.Register(backend1)
_ = reg.Register(backend2)
// Create aggregator
agg := backend.NewAggregator(reg)
// List tools from all backends
ctx := context.Background()
tools, _ := agg.ListAllTools(ctx)
fmt.Printf("Total tools: %d\n", len(tools))
// Execute through aggregator (using backend:tool format)
result, _ := agg.Execute(ctx, "math:add", map[string]any{"a": float64(5), "b": float64(3)})
fmt.Printf("5 + 3 = %v\n", result)
}
Output: Total tools: 2 5 + 3 = 8
func NewAggregator ¶
func NewAggregator(registry *Registry) *Aggregator
NewAggregator creates a new tool aggregator.
func (*Aggregator) ListAllTools ¶
ListAllTools returns tools from all enabled backends.
type Backend ¶
type Backend interface {
// Kind returns the backend type (e.g., "local", "mcp", "http").
Kind() string
// Name returns the unique instance name for this backend.
Name() string
// Enabled returns whether this backend is currently enabled.
Enabled() bool
// ListTools returns all tools available from this backend.
ListTools(ctx context.Context) ([]model.Tool, error)
// Execute invokes a tool on this backend.
Execute(ctx context.Context, tool string, args map[string]any) (any, error)
// Start initializes the backend (connect to remote, start subprocess, etc.).
Start(ctx context.Context) error
// Stop gracefully shuts down the backend.
Stop() error
}
Backend defines a source of tools. Backends can be local handlers, MCP servers, HTTP APIs, or custom implementations.
Contract: - Concurrency: implementations must be safe for concurrent use. - Context: methods must honor cancellation/deadlines. - Errors: use ErrBackendNotFound/ErrBackendDisabled/ErrToolNotFound/ErrBackendUnavailable where applicable.
Example (Lifecycle) ¶
package main
import (
"context"
"fmt"
"github.com/jonwraymond/toolexec/backend/local"
)
func main() {
// Create a local backend
b := local.New("example")
// Register a tool
b.RegisterHandler("echo", local.ToolDef{
Name: "echo",
Description: "Echoes input",
InputSchema: map[string]any{"type": "object"},
Handler: func(ctx context.Context, args map[string]any) (any, error) {
return args["message"], nil
},
})
ctx := context.Background()
// Start the backend
if err := b.Start(ctx); err != nil {
fmt.Printf("Start failed: %v\n", err)
return
}
// Check if enabled
fmt.Printf("Enabled: %v\n", b.Enabled())
// List available tools
tools, _ := b.ListTools(ctx)
fmt.Printf("Tools: %d\n", len(tools))
// Execute a tool
result, _ := b.Execute(ctx, "echo", map[string]any{"message": "hello"})
fmt.Printf("Result: %v\n", result)
// Stop the backend
_ = b.Stop()
}
Output: Enabled: true Tools: 1 Result: hello
type ConfigurableBackend ¶
ConfigurableBackend can be configured from raw bytes (YAML/JSON).
Contract: - Configure must validate config and return error on invalid input.
type Info ¶
Info contains metadata about a backend.
Example ¶
package main
import (
"fmt"
"github.com/jonwraymond/toolexec/backend"
"github.com/jonwraymond/toolexec/backend/local"
)
func main() {
b := local.New("my-backend")
info := backend.Info{
Kind: b.Kind(),
Name: b.Name(),
Enabled: b.Enabled(),
Description: "A demo backend",
Version: "1.0.0",
}
fmt.Printf("Kind: %s\n", info.Kind)
fmt.Printf("Name: %s\n", info.Name)
fmt.Printf("Enabled: %v\n", info.Enabled)
}
Output: Kind: local Name: my-backend Enabled: true
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry manages backend instances.
Example ¶
package main
import (
"context"
"fmt"
"github.com/jonwraymond/toolexec/backend"
"github.com/jonwraymond/toolexec/backend/local"
)
func main() {
// Create a registry
reg := backend.NewRegistry()
// Create and register a local backend
localBackend := local.New("demo")
localBackend.RegisterHandler("greet", local.ToolDef{
Name: "greet",
Description: "Greets a user",
InputSchema: map[string]any{"type": "object"},
Handler: func(ctx context.Context, args map[string]any) (any, error) {
name, _ := args["name"].(string)
return fmt.Sprintf("Hello, %s!", name), nil
},
})
_ = reg.Register(localBackend)
// List registered backends
backends := reg.List()
fmt.Printf("Registered backends: %d\n", len(backends))
// Get a specific backend
b, ok := reg.Get("demo")
fmt.Printf("Found 'demo': %v\n", ok)
fmt.Printf("Backend kind: %s\n", b.Kind())
}
Output: Registered backends: 1 Found 'demo': true Backend kind: local
func (*Registry) ListByKind ¶
ListByKind returns backends matching the given kind.
func (*Registry) ListEnabled ¶
ListEnabled returns enabled backends only.
func (*Registry) RegisterFactory ¶
RegisterFactory registers a factory for a backend kind.
func (*Registry) Unregister ¶
Unregister removes a backend from the registry.