toolexec¶
Execution layer providing tool running, code orchestration, and runtime isolation. This repository handles the actual execution of tools across different backend types.
Packages¶
| Package | Purpose |
|---|---|
exec | Unified facade combining discovery + execution |
run | Core tool execution and chaining |
code | Code-based tool orchestration |
runtime | Sandbox and runtime isolation |
backend | Backend registry and resolution |
Motivation¶
- Execute tools with proper validation and error handling
- Support multiple backend types (local, provider, MCP server)
- Enable tool chaining and orchestration
- Provide secure runtime isolation
run Package¶
The run package provides the core tool execution engine.
Core Responsibilities¶
- Validate input against tool schema
- Resolve backend and execute
- Normalize results
- Validate output against schema
Example¶
import "github.com/jonwraymond/toolexec/run"
runner := run.NewRunner(run.Config{
Index: idx,
Backends: backends,
})
result, err := runner.Run(ctx, "github:create_issue", map[string]any{
"owner": "jonwraymond",
"repo": "toolexec",
"title": "New issue",
})
Execution Pipeline¶
flowchart LR
Input["Tool ID + Args"] --> Validate1["Validate Input"]
Validate1 --> Resolve["Resolve Backend"]
Resolve --> Execute["Execute"]
Execute --> Normalize["Normalize Result"]
Normalize --> Validate2["Validate Output"]
Validate2 --> Result["RunResult"] exec Package¶
The exec facade composes discovery, documentation, and execution behind a single API for most callers.
Example¶
import (
"github.com/jonwraymond/toolexec/exec"
"github.com/jonwraymond/tooldiscovery/index"
"github.com/jonwraymond/tooldiscovery/tooldoc"
)
idx := index.NewInMemoryIndex()
docs := tooldoc.NewInMemoryStore(tooldoc.StoreOptions{Index: idx})
executor, err := exec.New(exec.Options{Index: idx, Docs: docs})
result, err := executor.RunTool(ctx, "math:add", map[string]any{"a": 5, "b": 3})
code Package¶
The code package provides code-based tool orchestration for complex workflows.
Features¶
- Multi-tool orchestration
- Conditional execution
- Result aggregation
- Error handling with retries
Example¶
import "github.com/jonwraymond/toolexec/code"
executor := code.NewExecutor(runner)
// Execute a workflow
result, err := executor.Execute(ctx, `
issue := run("github:create_issue", {title: "Bug fix"})
run("github:add_labels", {issue: issue.number, labels: ["bug"]})
`)
runtime Package¶
The runtime package provides sandbox and runtime isolation for tool execution.
Supported Runtimes¶
| Runtime | Isolation | Use Case |
|---|---|---|
unsafe_host | Process | Trusted tools (dev only) |
docker | Container | Standard isolation |
containerd | Container | Infra-native runtime |
kubernetes | Pod/Job | Cluster scheduling + quotas |
gvisor | Container sandbox | Hardened isolation |
kata | MicroVM | VM-level isolation |
firecracker | MicroVM | Strongest isolation |
wasm | In-process sandbox | Constrained SDK surface |
remote | Remote service | Dedicated runtime fleet |
proxmox_lxc | LXC container | Proxmox-backed runtime |
temporal | Orchestrator | Long-running workflows |
Runtime Backends (Readiness)¶
| Backend | Readiness | Notes |
|---|---|---|
unsafe_host | prod | Development only; no isolation |
docker | prod | Default container isolation |
containerd | beta | Direct CRI runtime |
kubernetes | beta | Jobs/pods + runtimeClass |
gvisor | beta | runsc sandbox |
kata | beta | VM-level isolation |
firecracker | beta | MicroVM runtime |
wasm | beta | Wazero execution |
remote | beta | Signed HTTP runtime |
proxmox_lxc | beta | Proxmox API + runtime service |
temporal | stub | Orchestration only; compose with sandbox |
Concrete runtime clients (Kubernetes, Proxmox, remote HTTP) live in toolexec-integrations and are injected into these backends via interfaces.
Example¶
import (
"github.com/jonwraymond/tooldiscovery/tooldoc"
"github.com/jonwraymond/toolexec/runtime"
"github.com/jonwraymond/toolexec/runtime/backend/unsafe"
"github.com/jonwraymond/toolexec/runtime/gateway/direct"
)
docs := tooldoc.NewInMemoryStore(tooldoc.StoreOptions{Index: idx})
gateway := direct.New(direct.Config{Index: idx, Docs: docs, Runner: runner})
rt := runtime.NewDefaultRuntime(runtime.RuntimeConfig{
Backends: map[runtime.SecurityProfile]runtime.Backend{
runtime.ProfileDev: unsafe.New(unsafe.Config{RequireOptIn: true}),
},
DefaultProfile: runtime.ProfileDev,
})
result, err := rt.Execute(ctx, runtime.ExecuteRequest{
Language: "go",
Code: `__out = "ok"`,
Profile: runtime.ProfileDev,
Gateway: gateway,
Metadata: map[string]any{"unsafeOptIn": true},
})
backend Package¶
The backend package provides backend registry and resolution.
Backend Types¶
| Type | Description |
|---|---|
local | In-process handler function |
provider | External tool provider |
mcp | Remote MCP server |
Example¶
import "github.com/jonwraymond/toolexec/backend"
registry := backend.NewRegistry()
// Register a local backend
registry.Register("calculator", backend.Local(func(ctx context.Context, args any) (any, error) {
// Implementation
}))
// Resolve backend for tool
b, err := registry.Resolve(tool.Backend)
Schemas and Contracts¶
toolexec enforces the canonical tool schema from toolfoundation. It does not define new input/output schemas; instead it validates against each model.Tool schema at execution time.
Key guarantees:
- InputSchema is required for all tools.
- OutputSchema is optional; output validation runs only when present.
- Execution results are normalized into
run.RunResultandexec.Result. - Streaming uses
run.StreamEventwithprogress,chunk,done,error.
See the full schema and contract details in: - toolexec schemas - toolfoundation schemas
Examples¶
Runnable examples cover execution, chaining, discovery, and runtimes:
go run ./examples/basic
go run ./examples/chain
go run ./examples/discovery
go run ./examples/streaming
go run ./examples/runtime
go run ./examples/full
Diagram¶
Architecture Plan Summary¶
The toolexec architecture plan focused on delivering a unified execution surface while keeping each layer isolated and testable:
- Facade-first API:
exec.Execprovides the primary entry point, composing discovery, documentation, and execution behind a single interface. - Deterministic execution:
run.DefaultRunnerenforces a strict pipeline (resolve → validate → execute → normalize → validate) with structured results. - Runtime isolation: the
runtimepackage allows sandboxed execution for code workflows without coupling to tool execution paths. - Schema contracts: execution never invents schemas; it validates against
toolfoundationand surfaces structured results for downstream chaining.
See the full plan for rationale and milestones: Architecture plan.
Key Design Decisions¶
- Schema validation: Both input and output are validated
- Backend abstraction: Execution is decoupled from backend type
- Unified facade: Most callers use exec instead of wiring packages
- Pluggable runtimes: Security profiles are configurable
- Chaining support: Tools can call other tools