exec

package
v0.2.1 Latest Latest
Warning

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

Go to latest
Published: Feb 3, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package exec provides a unified facade for tool execution in the toolexec ecosystem.

The exec package simplifies tool execution by combining discovery, execution, and result handling into a single, cohesive API. It integrates with tooldiscovery for tool registration and search, and with run for the underlying execution pipeline.

Overview

Instead of working with multiple packages directly, users can create an Exec instance that handles the complete workflow:

  • Tool registration via tooldiscovery's index
  • Local handler management
  • Tool search and discovery
  • Single tool and chain execution
  • Documentation retrieval

Basic Usage

// Create discovery index and documentation store
idx := index.NewInMemoryIndex(index.IndexOptions{
    Searcher: search.NewBM25Searcher(search.DefaultConfig()),
})
docs := tooldoc.NewInMemoryStore(tooldoc.StoreOptions{Index: idx})

// Register a tool
tool := model.Tool{
    Tool: mcp.Tool{Name: "greet", Description: "Greets a user"},
    Namespace: "demo",
}
idx.RegisterTool(tool, model.NewLocalBackend("greet-handler"))

// Create executor with local handler
executor, err := exec.New(exec.Options{
    Index: idx,
    Docs:  docs,
    LocalHandlers: map[string]exec.Handler{
        "greet-handler": func(ctx context.Context, args map[string]any) (any, error) {
            return fmt.Sprintf("Hello, %s!", args["name"]), nil
        },
    },
})

// Execute the tool
result, err := executor.RunTool(ctx, "demo:greet", map[string]any{"name": "World"})

Search and Execute

The package supports a search-then-execute workflow:

results, _ := executor.SearchTools(ctx, "greeting tools", 5)
if len(results) > 0 {
    result, _ := executor.RunTool(ctx, results[0].ID, args)
}

Chain Execution

Execute multiple tools in sequence, optionally passing results between steps:

result, steps, err := executor.RunChain(ctx, []exec.Step{
    {ToolID: "ns:tool1", Args: map[string]any{"x": 1}},
    {ToolID: "ns:tool2", UsePrevious: true}, // receives tool1's result
})

Integration

The exec package integrates with:

Index

Examples

Constants

View Source
const (
	DefaultMaxToolCalls = 100
	DefaultLanguage     = "go"
	DefaultTimeout      = 30 * time.Second
)

Default configuration values.

Variables

View Source
var (
	ErrIndexRequired = errors.New("exec: Index is required")
	ErrDocsRequired  = errors.New("exec: Docs store is required")
)

Errors returned by Options validation.

Functions

This section is empty.

Types

type CodeParams

type CodeParams struct {
	// Language specifies the programming language.
	// If empty, Options.DefaultLanguage is used.
	Language string

	// Code is the source code to execute.
	Code string

	// Timeout overrides Options.DefaultTimeout for this execution.
	// If zero, the default timeout is used.
	Timeout time.Duration

	// MaxToolCalls overrides Options.MaxToolCalls for this execution.
	// If zero, the default limit is used.
	MaxToolCalls int

	// AllowedTools restricts which tools the code can call.
	// If nil or empty, all registered tools are allowed.
	AllowedTools []string

	// Env provides environment variables for the execution.
	Env map[string]string
}

CodeParams configures a code execution request.

type CodeResult

type CodeResult struct {
	// Value is the final return value from the code.
	Value any

	// ToolCalls contains information about each tool call made during execution.
	ToolCalls []ToolCall

	// Duration is the total execution time.
	Duration time.Duration

	// Stdout contains captured standard output.
	Stdout string

	// Stderr contains captured standard error.
	Stderr string

	// Error is non-nil if execution failed.
	Error error
}

CodeResult represents the outcome of code execution with tool access.

func (CodeResult) OK

func (c CodeResult) OK() bool

OK returns true if code execution succeeded.

type Exec

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

Exec is the unified facade for tool execution. It combines discovery, execution, and result handling into a single API.

func New

func New(opts Options) (*Exec, error)

New creates a new Exec instance with the given options.

Example
package main

import (
	"context"
	"fmt"

	"github.com/jonwraymond/tooldiscovery/index"
	"github.com/jonwraymond/tooldiscovery/search"
	"github.com/jonwraymond/tooldiscovery/tooldoc"
	"github.com/jonwraymond/toolexec/exec"
)

func main() {
	// Create discovery index and documentation store
	idx := index.NewInMemoryIndex(index.IndexOptions{
		Searcher: search.NewBM25Searcher(search.BM25Config{}),
	})
	docs := tooldoc.NewInMemoryStore(tooldoc.StoreOptions{Index: idx})

	// Create executor with local handler
	executor, err := exec.New(exec.Options{
		Index: idx,
		Docs:  docs,
		LocalHandlers: map[string]exec.Handler{
			"greet-handler": func(ctx context.Context, args map[string]any) (any, error) {
				name, _ := args["name"].(string)
				return fmt.Sprintf("Hello, %s!", name), nil
			},
		},
	})
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Println("Executor created:", executor != nil)
}
Output:

Executor created: true

func (*Exec) DocStore

func (e *Exec) DocStore() tooldoc.Store

DocStore returns the underlying documentation store.

func (*Exec) GetToolDoc

func (e *Exec) GetToolDoc(ctx context.Context, toolID string, level tooldoc.DetailLevel) (tooldoc.ToolDoc, error)

GetToolDoc retrieves tool documentation at the specified detail level.

func (*Exec) Index

func (e *Exec) Index() index.Index

Index returns the underlying tool index. This allows advanced usage patterns like direct tool registration.

func (*Exec) RunChain

func (e *Exec) RunChain(ctx context.Context, steps []Step) (Result, []StepResult, error)

RunChain executes a sequence of tools. Returns the final result, a slice of step results, and any error.

Example
package main

import (
	"context"
	"fmt"

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

	"github.com/jonwraymond/tooldiscovery/index"
	"github.com/jonwraymond/tooldiscovery/search"
	"github.com/jonwraymond/tooldiscovery/tooldoc"
	"github.com/jonwraymond/toolexec/exec"
	"github.com/jonwraymond/toolfoundation/model"
)

func main() {
	// Setup
	idx := index.NewInMemoryIndex(index.IndexOptions{
		Searcher: search.NewBM25Searcher(search.BM25Config{}),
	})
	docs := tooldoc.NewInMemoryStore(tooldoc.StoreOptions{Index: idx})

	// Register an add tool
	addTool := model.Tool{
		Tool: mcp.Tool{
			Name:        "add",
			Description: "Adds two numbers",
			InputSchema: map[string]any{
				"type": "object",
				"properties": map[string]any{
					"a": map[string]any{"type": "number"},
					"b": map[string]any{"type": "number"},
				},
			},
		},
		Namespace: "math",
	}
	_ = idx.RegisterTool(addTool, model.NewLocalBackend("add-handler"))

	// Create executor
	executor, _ := exec.New(exec.Options{
		Index: idx,
		Docs:  docs,
		LocalHandlers: map[string]exec.Handler{
			"add-handler": func(ctx context.Context, args map[string]any) (any, error) {
				a, _ := args["a"].(float64)
				b, _ := args["b"].(float64)
				return a + b, nil
			},
		},
		ValidateInput:  false,
		ValidateOutput: false,
	})

	// Execute a chain of operations
	ctx := context.Background()
	result, steps, err := executor.RunChain(ctx, []exec.Step{
		{ToolID: "math:add", Args: map[string]any{"a": float64(5), "b": float64(3)}},
		{ToolID: "math:add", Args: map[string]any{"a": float64(10), "b": float64(2)}},
	})
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Println("Steps completed:", len(steps))
	fmt.Println("Final result:", result.Value)
}
Output:

Steps completed: 2
Final result: 12

func (*Exec) RunTool

func (e *Exec) RunTool(ctx context.Context, toolID string, args map[string]any) (Result, error)

RunTool executes a single tool by ID and returns the result.

Example
package main

import (
	"context"
	"fmt"

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

	"github.com/jonwraymond/tooldiscovery/index"
	"github.com/jonwraymond/tooldiscovery/search"
	"github.com/jonwraymond/tooldiscovery/tooldoc"
	"github.com/jonwraymond/toolexec/exec"
	"github.com/jonwraymond/toolfoundation/model"
)

func main() {
	// Setup
	idx := index.NewInMemoryIndex(index.IndexOptions{
		Searcher: search.NewBM25Searcher(search.BM25Config{}),
	})
	docs := tooldoc.NewInMemoryStore(tooldoc.StoreOptions{Index: idx})

	// Register a greeting tool
	tool := model.Tool{
		Tool: mcp.Tool{
			Name:        "greet",
			Description: "Greets a user",
			InputSchema: map[string]any{
				"type": "object",
				"properties": map[string]any{
					"name": map[string]any{"type": "string"},
				},
			},
		},
		Namespace: "demo",
	}
	_ = idx.RegisterTool(tool, model.NewLocalBackend("greet-handler"))

	// Create executor
	executor, _ := exec.New(exec.Options{
		Index: idx,
		Docs:  docs,
		LocalHandlers: map[string]exec.Handler{
			"greet-handler": func(ctx context.Context, args map[string]any) (any, error) {
				name, _ := args["name"].(string)
				return fmt.Sprintf("Hello, %s!", name), nil
			},
		},
		ValidateInput:  false,
		ValidateOutput: false,
	})

	// Execute the tool
	ctx := context.Background()
	result, err := executor.RunTool(ctx, "demo:greet", map[string]any{"name": "World"})
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Println("Result:", result.Value)
}
Output:

Result: Hello, World!

func (*Exec) SearchTools

func (e *Exec) SearchTools(ctx context.Context, query string, limit int) ([]ToolSummary, error)

SearchTools finds tools matching a query.

Example
package main

import (
	"context"
	"fmt"

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

	"github.com/jonwraymond/tooldiscovery/index"
	"github.com/jonwraymond/tooldiscovery/tooldoc"
	"github.com/jonwraymond/toolexec/exec"
	"github.com/jonwraymond/toolfoundation/model"
)

func main() {
	// Setup - use default lexical searcher for simplicity
	idx := index.NewInMemoryIndex()
	docs := tooldoc.NewInMemoryStore(tooldoc.StoreOptions{Index: idx})

	// Register some tools (InputSchema is required)
	tools := []model.Tool{
		{
			Tool: mcp.Tool{
				Name:        "greet",
				Description: "Greets a user with a friendly message",
				InputSchema: map[string]any{"type": "object"},
			},
			Namespace: "demo",
		},
		{
			Tool: mcp.Tool{
				Name:        "farewell",
				Description: "Says goodbye to a user",
				InputSchema: map[string]any{"type": "object"},
			},
			Namespace: "demo",
		},
	}
	for _, t := range tools {
		_ = idx.RegisterTool(t, model.NewLocalBackend("handler"))
	}

	// Create executor
	executor, _ := exec.New(exec.Options{
		Index: idx,
		Docs:  docs,
	})

	// Search for greeting-related tools
	ctx := context.Background()
	results, err := executor.SearchTools(ctx, "greet", 5)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Println("Found tools:", len(results))
	if len(results) > 0 {
		fmt.Println("Top result:", results[0].ID)
	}
}
Output:

Found tools: 1
Top result: demo:greet

type Handler

type Handler func(ctx context.Context, args map[string]any) (any, error)

Handler is the function signature for local tool handlers. It matches run.LocalHandler and local.HandlerFunc for compatibility.

type Options

type Options struct {
	// Index provides tool discovery and registration.
	// Required.
	Index index.Index

	// Docs provides tool documentation.
	// Required.
	Docs tooldoc.Store

	// LocalHandlers maps handler names to handler functions.
	// These are used when a tool's backend is a local backend
	// referencing the handler by name.
	LocalHandlers map[string]Handler

	// MCPExecutor executes MCP backend tools.
	// Optional; if nil, MCP tools cannot be executed.
	MCPExecutor run.MCPExecutor

	// ProviderExecutor executes provider backend tools.
	// Optional; if nil, provider tools cannot be executed.
	ProviderExecutor run.ProviderExecutor

	// SecurityProfile determines the runtime backend for code execution.
	// Default: runtime.ProfileDev
	SecurityProfile runtime.SecurityProfile

	// EnableCodeExecution enables the code execution subsystem.
	// Default: false (tool execution only)
	EnableCodeExecution bool

	// MaxToolCalls limits tool calls in code execution.
	// Default: 100
	MaxToolCalls int

	// DefaultLanguage for code execution.
	// Default: "go"
	DefaultLanguage string

	// DefaultTimeout for tool and code execution.
	// Default: 30s
	DefaultTimeout time.Duration

	// ValidateInput enables input validation before execution.
	// Default: true
	ValidateInput bool

	// ValidateOutput enables output validation after execution.
	// Default: true
	ValidateOutput bool
}

Options configures an Exec instance.

type Result

type Result struct {
	// Value is the return value from the tool.
	Value any

	// ToolID is the canonical ID of the executed tool.
	ToolID string

	// Duration is how long the tool took to execute.
	Duration time.Duration

	// Error is non-nil if the tool execution failed.
	// This is set when the tool itself returns an error,
	// not for resolution or validation errors (which are
	// returned from RunTool directly).
	Error error
}

Result represents the outcome of a single tool execution.

func (Result) OK

func (r Result) OK() bool

OK returns true if the result has no error.

type Step

type Step struct {
	// ToolID is the canonical ID of the tool to execute.
	ToolID string

	// Args are the arguments to pass to the tool.
	// If UsePrevious is true and Args is nil, the previous
	// step's result is used as the argument map.
	Args map[string]any

	// UsePrevious indicates that this step should receive
	// the previous step's result. If Args is also set,
	// the previous result is merged into Args under the
	// key "previous" (unless Args already has that key).
	UsePrevious bool

	// StopOnError determines whether chain execution should
	// stop if this step fails. Default is true.
	StopOnError *bool
}

Step defines a single step in a chain execution.

type StepResult

type StepResult struct {
	// StepIndex is the zero-based index of this step in the chain.
	StepIndex int

	// ToolID is the canonical ID of the executed tool.
	ToolID string

	// Args are the arguments passed to this step.
	Args map[string]any

	// Value is the return value from this step.
	Value any

	// Duration is how long this step took to execute.
	Duration time.Duration

	// Error is non-nil if this step failed.
	Error error

	// Skipped is true if this step was skipped due to a prior failure.
	Skipped bool
}

StepResult represents the outcome of a single step in a chain execution.

func (StepResult) OK

func (s StepResult) OK() bool

OK returns true if the step completed successfully.

type ToolCall

type ToolCall struct {
	// ToolID is the canonical ID of the called tool.
	ToolID string

	// Args are the arguments passed to the tool.
	Args map[string]any

	// Result is the tool's return value.
	Result any

	// Duration is how long the tool call took.
	Duration time.Duration

	// Error is non-nil if the tool call failed.
	Error error
}

ToolCall represents a tool invocation made during code execution.

type ToolSummary

type ToolSummary = index.Summary

ToolSummary is an alias to index.Summary for search results.

Jump to

Keyboard shortcuts

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