tools

package
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Jun 10, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

Documentation

Overview

Package tools defines Harbor's unified tool catalog surface — the single planner-addressable concept that hides whether a tool is an in-process Go function, an HTTP endpoint, an MCP server, an A2A remote agent, or a Flow (a typed DAG of Nodes registered as one Tool — see internal/runtime/flow).

The unification is at the **type level** (RFC §6.4, brief 03 §1): every Tool is the same struct regardless of Source / Transport; the dispatch is one switch in one place. Adding a new transport (Phase 27 HTTP, Phase 28 MCP, Phase 29 A2A) is a ToolProvider driver; nothing else changes.

Identity is mandatory. CatalogFilter keys on the (tenant, user, session) triple plus GrantedScopes; every ToolDescriptor.Invoke reads identity from ctx and stamps it in audit-emitted events (RFC §4, D-001).

Reliability shell. Every tool invocation (regardless of Transport) is wrapped in the ToolPolicy shell — timeout, retry-with- exponential-backoff, validation. Defaults fire on a zero-valued ToolPolicy so a plain `tools.RegisterFunc(name, fn)` is production-resilient with no ceremony (RFC §6.4, D-024).

Concurrent reuse contract (D-025). A constructed *catalog is safe to share across N concurrent goroutines: descriptors are immutable after Register; storage is RWMutex-guarded; per-invocation state lives in the call's ctx + ToolResult, never on the catalog or the Tool itself.

Index

Constants

View Source
const (
	// EventTypeToolInvoked — emitted at the start of every tool
	// invocation (after argument validation succeeds; before the
	// policy shell's first attempt). Carries identity + tool name
	// + transport.
	EventTypeToolInvoked events.EventType = "tool.invoked"
	// EventTypeToolCompleted — emitted on a successful invocation
	// (the policy shell returned a non-nil ToolResult). Carries
	// identity + tool name + transport + attempts taken.
	EventTypeToolCompleted events.EventType = "tool.completed"
	// EventTypeToolFailed — emitted on a terminal invocation
	// failure (policy retries exhausted or a permanent class
	// error). Carries identity + tool name + transport + last
	// error class + attempts taken.
	EventTypeToolFailed events.EventType = "tool.failed"
	// EventTypeToolInvalidArgs — emitted when argument validation
	// fails at the catalog edge. NOT a tool error; the planner is
	// expected to reformulate. Carries identity + tool name + the
	// validation error detail.
	EventTypeToolInvalidArgs events.EventType = "tool.invalid_args"
	// EventTypeToolPolicyExhausted — emitted when the policy's
	// retry budget exhausts. (A distinct shape from tool.failed so
	// operators can quantify "tool was unhealthy" vs "tool was
	// permanently broken".)
	EventTypeToolPolicyExhausted events.EventType = "tool.policy_exhausted"
)

Phase 26 tool-side event types. Registered via init() so the canonical events registry stays the single source of truth (see internal/events/events.go).

Variables

View Source
var (
	// ErrToolNotFound — Resolve returned (_, false); typically the
	// planner asked for a tool name the catalog never registered.
	ErrToolNotFound = errors.New("tools: tool not found")
	// ErrToolInvalidArgs — argument validation failed at the
	// catalog edge. The planner is expected to reformulate via LLM
	// retry feedback; this is NOT a tool error.
	ErrToolInvalidArgs = errors.New("tools: invalid arguments")
	// ErrToolPolicyExhausted — the policy's retry budget was
	// exhausted; the wrapped cause carries the last attempt's
	// failure.
	ErrToolPolicyExhausted = errors.New("tools: policy retries exhausted")
	// ErrToolDuplicateName — Register called with a Name already
	// in the catalog.
	ErrToolDuplicateName = errors.New("tools: duplicate tool name")
)

Sentinel errors. Callers compare via errors.Is.

View Source
var ErrToolExampleInvalid = errors.New("tools: invalid tool example")

ErrToolExampleInvalid — a ToolExample registered on a Tool references an argument key that is not declared in the tool's Tool.ArgsSchema `properties`. The catalog rejects the registration loudly (AGENTS.md §5 fail-loudly): a passing example is a working example, so an example whose `Args` cannot match the schema would teach the planner a shape the catalog edge would then reject.

Functions

func VisibleNames added in v1.3.0

func VisibleNames(cat ToolCatalog, filter CatalogFilter) []string

VisibleNames returns the sorted names of every tool visible under `filter` — both `LoadingAlways` and `LoadingDeferred` modes, so the result is the run's FULL reachable tool set (the prompt-time set plus everything `tool_search` can surface).

Phase 111d (D-201): this is the ONE producer of the "allowed tools" capability input the skills surface consumes — the builtin `skill_search` / `skill_get` / `skill_list` delegations and the run loop's `skills.Directory.View` call both derive their `CapabilityContext.AllowedTools` / `DirectoryCapability.AllowedTools` from it. Centralised here so the three call sites cannot drift (the SDK friction audit's triple-duplication finding, read forward).

The supplied filter's LoadingModes is OVERRIDDEN to the full two-mode set; every other predicate (identity triple, GrantedScopes, NameRegex) applies as given. A nil catalog returns nil — callers on catalog-less stacks get the empty (default-deny) capability set.

Concurrent reuse (D-025): pure function over the catalog's concurrent-safe List; no shared state.

func WithCatalog

func WithCatalog(ctx context.Context, cat ToolCatalog) context.Context

WithCatalog attaches cat to ctx so downstream handlers can recover it via MustCatalog / Catalog.

Types

type CatalogFilter

type CatalogFilter struct {
	TenantID, UserID, SessionID string
	GrantedScopes               []string
	LoadingModes                []LoadingMode
	NameRegex                   *regexp.Regexp
}

CatalogFilter is the server-enforced subscription predicate on ToolCatalog.List. Keys on the (tenant, user, session) triple plus GrantedScopes — every component participates in visibility:

  • TenantID / UserID / SessionID: typically mandatory at the dispatch boundary, but List is tolerant of empty fields so callers can build admin-view filters (an empty triple matches every tool — see HasFullTriple). Production wiring (Phase 60+ Protocol surface) MUST gate the empty-triple case behind an elevated scope.

  • GrantedScopes: a Tool is visible only if every entry in its AuthScopes is contained in GrantedScopes. Tools with no AuthScopes (the in-process default) are always visible.

  • LoadingModes: defaults to LoadingAlways when empty (the prompt-time view); pass [LoadingAlways, LoadingDeferred] for a full-discovery view.

  • NameRegex: optional final filter for operator queries; nil matches every tool.

func (CatalogFilter) HasFullTriple

func (f CatalogFilter) HasFullTriple() bool

HasFullTriple reports whether the filter has all three identity components. Phase 60+ Protocol surface uses this to reject empty- triple non-admin reads.

type CatalogOption

type CatalogOption func(*catalogConfig)

CatalogOption configures a catalog at construction.

func WithSearchCache added in v1.2.0

func WithSearchCache(sc ToolSearchCache) CatalogOption

WithSearchCache attaches a search index to the catalog (Phase 107c / D-167).

type CatalogReplacer

type CatalogReplacer interface {
	// Replace atomically swaps each named descriptor in `wrapped`
	// with its wrapped version. Returns ErrToolNotFound (wrapped)
	// when any name does not resolve.
	Replace(wrapped []ToolDescriptor) error
}

CatalogReplacer is the optional surface a ToolCatalog exposes when it supports atomic per-tool descriptor replacement at boot. The Phase 64a catalog wiring (internal/tools/catalog.Builder) uses this to install wrapped descriptors over previously-registered names without a Deregister+Register round trip (which would race concurrent Resolve calls).

Replacement semantics:

  • Each descriptor in `wrapped` MUST name an already-registered Tool (the builder enforces this via Resolve BEFORE calling Replace). Otherwise Replace returns ErrToolNotFound naming the offending tool.
  • Replacement is atomic from the catalog's external view: a concurrent Resolve sees EITHER every old descriptor OR every new descriptor, never a half-applied mix.
  • Descriptors NOT named in `wrapped` are untouched.

A catalog implementation that does not support replacement skips this interface. Callers branch on the type assertion; the absence is the signal.

type DescriptorOption

type DescriptorOption func(*descriptorConfig)

DescriptorOption configures a ToolDescriptor at registration. Drivers (Phase 26 in-process, Phase 27+ HTTP / MCP / A2A) accept `opts ...DescriptorOption` so the same option surface is reused across transports.

func WithAuthScopes

func WithAuthScopes(scopes ...string) DescriptorOption

WithAuthScopes adds required auth scopes — the CatalogFilter MUST grant every scope listed here for the Tool to be visible.

func WithBus

func WithBus(bus events.EventBus) DescriptorOption

WithBus wires a canonical event bus into a tool registration so the driver's Invoke wrapper publishes tool.invoked / tool.completed / tool.failed around each invocation. Identity in the published payload is read from the invocation ctx via identity.From; if no identity is present the publish is skipped (a tool boundary that rejects missing identity already errored before this point).

Drivers (inproc, http, mcp, a2a) honour this option in their Invoke closures so the Phase 26 event surface fires regardless of transport. Zero value (nil bus) keeps the previous no-op behaviour, so legacy registrations continue to work.

func WithCostHint

func WithCostHint(s string) DescriptorOption

WithCostHint sets a free-form cost annotation.

func WithDescription

func WithDescription(s string) DescriptorOption

WithDescription overrides the Tool's planner-facing description. Default is the function name when registering via RegisterFunc.

func WithExamples

func WithExamples(examples ...ToolExample) DescriptorOption

WithExamples attaches canonical argument-shape examples surfaced to the planner.

func WithLatencyHint

func WithLatencyHint(d time.Duration) DescriptorOption

WithLatencyHint sets a non-authoritative latency annotation.

func WithLoading

func WithLoading(m LoadingMode) DescriptorOption

WithLoading overrides LoadingMode (default: LoadingAlways).

func WithPolicy

func WithPolicy(p ToolPolicy) DescriptorOption

WithPolicy overrides the ToolPolicy applied to the registered Tool. Pass tools.ToolPolicy{} (the zero value) to opt back into DefaultPolicy().

func WithSafetyNotes

func WithSafetyNotes(s string) DescriptorOption

WithSafetyNotes attaches operator-supplied safety text surfaced to the planner.

func WithSideEffect

func WithSideEffect(s SideEffect) DescriptorOption

WithSideEffect declares the tool's side-effect class.

func WithSource

func WithSource(id ToolSourceID) DescriptorOption

WithSource overrides the descriptor's ToolSourceID (for provider-driven registrations).

func WithTags

func WithTags(tags ...string) DescriptorOption

WithTags adds operator-facing tags.

type ErrorClass

type ErrorClass string

ErrorClass categorises a tool invocation failure. The policy's RetryOn allowlist references these classes; the shell's classifier maps Go errors to a class on each failed attempt.

const (
	// ErrClassTransient covers retryable infrastructure issues —
	// network blips, "connection reset", "EOF", etc. The shell's
	// classifier maps common error strings here.
	ErrClassTransient ErrorClass = "transient"
	// ErrClassTimeout — the per-attempt context.DeadlineExceeded
	// or the tool's own timeout signal.
	ErrClassTimeout ErrorClass = "timeout"
	// ErrClass5xx — HTTP / RPC server-side 5xx (transient-side
	// failures distinct from network errors).
	ErrClass5xx ErrorClass = "5xx"
	// ErrClassPermanent — non-retryable. A tool that returns a
	// 4xx, ErrToolInvalidArgs, or a context.Canceled gets this
	// class; the shell stops retrying immediately.
	ErrClassPermanent ErrorClass = "permanent"
)

func ClassifyError

func ClassifyError(err error, perAttemptTimeout bool) ErrorClass

ClassifyError maps a Go error to an ErrorClass. Used by the shell to decide retryability. Public so drivers can return a pre-classified error if they want to bypass the default heuristics.

Heuristics (V1):

  • context.DeadlineExceeded + perAttemptTimeout: ErrClassTimeout (per-attempt timeout fired).
  • context.DeadlineExceeded without perAttemptTimeout: parent ctx died → ErrClassPermanent (caller-driven).
  • context.Canceled: ErrClassPermanent.
  • ErrToolInvalidArgs / ErrToolNotFound wrapped: ErrClassPermanent.
  • HTTP-status-shaped error strings ("status 5xx", "500", "503"): ErrClass5xx.
  • "timeout" / "deadline exceeded" / "context canceled" string match in error: ErrClassTimeout.
  • everything else: ErrClassTransient (the conservative default so tools opt OUT of retry rather than opting in).

type InvokeHooks

type InvokeHooks struct {
	// OnAttempt fires after each invoke attempt with the attempt
	// index (0-based: 0 is the initial call, 1 is the first
	// retry) and the attempt's error (nil on success).
	OnAttempt func(attempt int, err error)
}

InvokeHooks lets drivers observe per-attempt outcomes (e.g. to emit per-attempt audit events) without coupling the policy shell to the events package.

type LoadingMode

type LoadingMode string

LoadingMode controls when a Tool appears in the planner's prompt- time catalog. Always-loaded tools live in every planner step; Deferred tools are loaded on demand (lazy resolution).

const (
	LoadingAlways   LoadingMode = "always"
	LoadingDeferred LoadingMode = "deferred"
)

The LoadingMode values.

type PlannerView added in v1.3.0

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

PlannerView is the planner-facing, schema-only projection of a ToolCatalog under one run's identity scope (Phase 110a — D-194; originally Phase 83i / D-152 as a `cmd/harbor` adapter).

`planner.RunContext.Catalog` is the surface that renders into the `<available_tools>` prompt section. PlannerView wraps the production catalog + a per-run visibility filter (keyed on the run's identity triple + any GrantedScopes the operator declared). The planner sees the FILTERED set; the catalog's internal store is immutable from the planner's perspective (the planner only reads), and Resolve returns the schema-only Tool value — never the dispatch-side ToolDescriptor.

PlannerView satisfies `planner.ToolCatalogView` STRUCTURALLY (Resolve(name) (Tool, bool) + List() []Tool). This package cannot name that interface — `internal/planner` imports `internal/tools` (ToolCatalogView's methods return Tool) — so the compile-time assertion lives in `internal/planner`'s tests, where the import is legal.

Per-run construction discipline (D-025): the view is a value type with two read-only fields. Each run constructs its own view via NewPlannerView — the filter depends on the run's identity. Sharing one view across runs would cross-contaminate visibility across tenants / users / sessions — DO NOT cache.

func NewPlannerView added in v1.3.0

func NewPlannerView(cat ToolCatalog, filter CatalogFilter) PlannerView

NewPlannerView constructs the per-run view over `cat` with `filter`'s visibility predicate. The filter's GrantedScopes is the operator-configured `tools.granted_scopes` list (Phase 83m / Item 6 / D-156): tools whose declared AuthScopes are entirely contained in the granted set are visible; tools that require a missing scope are filtered out. An empty / nil GrantedScopes keeps the "no scopes granted" default — tools with AuthScopes are invisible to the planner; tools without AuthScopes are always visible (the standard CatalogFilter rule). The GrantedScopes slice is copied so the caller's backing array cannot mutate the view after construction.

func (PlannerView) List added in v1.3.0

func (v PlannerView) List() []Tool

List implements the planner's ToolCatalogView contract. Returns the filtered slice of Tools visible to the run's identity.

func (PlannerView) Resolve added in v1.3.0

func (v PlannerView) Resolve(name string) (Tool, bool)

Resolve implements the planner's ToolCatalogView contract. Returns the schema-only Tool value the planner uses to build a CallTool decision — never the dispatch-side ToolDescriptor.

type ResolvedDescriptorConfig

type ResolvedDescriptorConfig struct {
	Policy      ToolPolicy
	Description string
	Tags        []string
	AuthScopes  []string
	Examples    []ToolExample
	SideEffect  SideEffect
	Loading     LoadingMode
	CostHint    string
	LatencyHint time.Duration
	SafetyNotes string
	Source      ToolSourceID
	Bus         events.EventBus
}

ResolvedDescriptorConfig is the resolved option set after applying DescriptorOptions. Drivers consume this when building their ToolDescriptor — they read what the operator declared at registration time + the package-set defaults.

Exported so transports (Phase 27 HTTP, Phase 28 MCP, Phase 29 A2A) can reuse the option-resolution pipeline without re-implementing it. The fields mirror the unexported descriptorConfig in this file.

func ResolveOptions

func ResolveOptions(opts ...DescriptorOption) ResolvedDescriptorConfig

ResolveOptions applies opts to a fresh ResolvedDescriptorConfig + sets per-driver defaults the inproc registrar (and future drivers) consume.

type SideEffect

type SideEffect string

SideEffect is the declared side-effect class. Operators reason about which tools are safe to retry from this field; the policy's retry behaviour is orthogonal (a tool with SideEffectWrite may still have RetryOn = []ErrorClass{ErrClassTransient} if the caller knows the write is idempotent).

const (
	SideEffectPure     SideEffect = "pure"
	SideEffectRead     SideEffect = "read"
	SideEffectWrite    SideEffect = "write"
	SideEffectExternal SideEffect = "external"
	SideEffectStateful SideEffect = "stateful"
)

The SideEffect classes, ordered from safest to most consequential.

type Tool

type Tool struct {
	// Name is the unique catalog key. Two descriptors with the same
	// Name return ErrToolDuplicateName from Register.
	Name string
	// Description is the planner-facing summary. Surfaced in the
	// prompt-time catalog so the LLM can choose between tools.
	Description string
	// ArgsSchema is the JSON-Schema (object) describing the tool's
	// argument shape. Catalogs validate against this before dispatch
	// — failures yield ErrToolInvalidArgs.
	ArgsSchema json.RawMessage
	// OutSchema is the JSON-Schema (object) describing the tool's
	// result shape. Used for output validation when policy enables it.
	OutSchema json.RawMessage
	// SideEffects classifies the tool's effect domain — operators
	// gate which classes are safe to retry / parallelize.
	SideEffects SideEffect
	// Tags allow operators to filter / categorise tools.
	Tags []string
	// AuthScopes lists the scopes a planner step's identity MUST
	// carry (via CatalogFilter.GrantedScopes) for the Tool to be
	// visible. Empty = no scope requirement.
	AuthScopes []string
	// CostHint is free-form (cheap / normal / expensive) and
	// non-authoritative — Governance owns real cost gates.
	CostHint string
	// LatencyHint is non-authoritative; surfaced for prompt ordering.
	LatencyHint time.Duration
	// SafetyNotes is free-form text the planner sees alongside
	// Description; used for "this tool writes to production" hints.
	SafetyNotes string
	// Loading mode: Always (visible in every planner step) or
	// Deferred (loaded on demand).
	Loading LoadingMode
	// Examples surface canonical argument shapes to the planner.
	Examples []ToolExample
	// Source identifies the provider (empty for in-process tools).
	Source ToolSourceID
	// Transport discriminates the tool's source. Determines which
	// driver's Invoke implementation runs.
	Transport TransportKind
	// Policy is the reliability shell wrapping every invocation.
	// Zero value → DefaultPolicy is applied at dispatch time.
	Policy ToolPolicy
	// HandlesMIME declares the MIME types this tool can consume from
	// the artifact store (Round-7 F11 / D-166). Used by the runtime's
	// multimodal materializer to populate `ArtifactStub.Fetch.Tool`
	// when an operator-uploaded input artifact's MIME matches — giving
	// the planner an explicit "use this tool for this ref" hint
	// instead of forcing the LLM to discover the binding from the
	// catalog. Wildcards are supported (`image/*`, `audio/*`); empty
	// means the tool advertises no MIME affinity (the LLM still picks
	// it from the catalog by description).
	HandlesMIME []string
}

Tool is Harbor's planner-addressable unit. Same struct regardless of Transport. The planner reasons about this; the dispatcher uses the matching ToolDescriptor.

Concurrent reuse: Tool is a value type; all fields are read-only after Register. The Tool itself never carries per-invocation state.

func (Tool) MatchesMIME

func (t Tool) MatchesMIME(mime string) bool

MatchesMIME reports whether the tool's HandlesMIME declaration covers the supplied MIME type. Wildcards on the type half are supported (`image/*` matches `image/png`, `image/webp`, etc.). Round-7 F11 / D-166 — used by the runtime materializer when populating `ArtifactStub.Fetch.Tool`.

type ToolCatalog

type ToolCatalog interface {
	// Register adds a descriptor. Returns ErrToolDuplicateName when
	// the Tool's Name is already registered.
	Register(d ToolDescriptor) error
	// Resolve returns the descriptor for name. found=false when
	// absent; the caller compares against the boolean (no error).
	Resolve(name string) (ToolDescriptor, bool)
	// List returns Tool *views* (never ToolDescriptors) matching
	// filter. The slice is owned by the caller; mutations on it do
	// not affect the catalog.
	List(filter CatalogFilter) []Tool
	// Search (Phase 107c / D-167) runs a full-text + tag-filter scan
	// over the catalog's search index. Returns an empty slice when no
	// SearchCache is attached (honest "discovery unavailable").
	Search(ctx context.Context, query string, tags []string, limit int) []Tool
}

ToolCatalog is Harbor's planner-addressable registry. Three methods. Concrete V1 implementation is the in-memory catalog (catalog.go); future drivers (remote-catalog, persistent-catalog) plug in behind the interface.

Concurrent reuse (D-025): implementations MUST be safe for N concurrent goroutines. The in-memory catalog uses a single RWMutex; descriptors are immutable after Register.

func Catalog

func Catalog(ctx context.Context) (ToolCatalog, bool)

Catalog returns the ToolCatalog in ctx and a presence bool. Use when absence is recoverable.

func MustCatalog

func MustCatalog(ctx context.Context) ToolCatalog

MustCatalog returns the ToolCatalog in ctx. Panics with ErrToolNotFound (used as the sentinel for "no catalog configured") when absent. Use in handler/runtime paths where a catalog is mandatory.

func NewCatalog

func NewCatalog(opts ...CatalogOption) ToolCatalog

NewCatalog constructs the canonical in-memory ToolCatalog. Safe for N concurrent goroutines after construction (D-025). The catalog is empty; callers register descriptors via Register.

`opts` is reserved for future configuration; Phase 26 ships without options but the variadic surface is stable so future fields land without breaking signatures.

type ToolCompletedPayload

type ToolCompletedPayload struct {
	events.SafeSealed
	Identity   identity.Quadruple
	ToolName   string
	Transport  TransportKind
	Attempts   int
	DurationMS int64
}

ToolCompletedPayload is the typed payload for EventTypeToolCompleted. SafePayload.

type ToolDescriptor

type ToolDescriptor struct {
	Tool     Tool
	Invoke   func(ctx context.Context, args json.RawMessage) (ToolResult, error)
	Validate func(args json.RawMessage) error
}

ToolDescriptor is the callable binding produced by a driver. The planner sees Tool; the dispatcher uses ToolDescriptor.

Invoke is wrapped by the ToolPolicy shell at dispatch time — the descriptor's Invoke is the inner-most function, called once per retry attempt by the shell. Drivers populate Invoke with the transport-specific code; the shell handles timeout / retry / backoff / validation uniformly.

Validate is the cached compiled JSON-Schema validator. Drivers build it once at Register; the shell calls it once before the first Invoke attempt (validation BEFORE retry — failing args don't get retried).

type ToolExample

type ToolExample struct {
	Args        map[string]any
	Description string
	Tags        []string
}

ToolExample is a usage example surfaced to the planner. The `Args` map is JSON-marshalled into the prompt.

type ToolFailedPayload

type ToolFailedPayload struct {
	events.SafeSealed
	Identity     identity.Quadruple
	ToolName     string
	Transport    TransportKind
	Attempts     int
	ErrorClass   ErrorClass
	ErrorMessage string
}

ToolFailedPayload is the typed payload for EventTypeToolFailed. SafePayload: ErrorClass is enum-shaped; ErrorMessage is operator-controlled (or wraps a sentinel).

type ToolInvalidArgsPayload

type ToolInvalidArgsPayload struct {
	events.SafeSealed
	Identity        identity.Quadruple
	ToolName        string
	Transport       TransportKind
	ValidationError string
}

ToolInvalidArgsPayload is the typed payload for EventTypeToolInvalidArgs. SafePayload: the validation error describes the schema mismatch (e.g. "expected string, got int"), not the offending arg value. Producers MUST NOT include raw arg bytes here — those go through the audit redactor.

type ToolInvokedPayload

type ToolInvokedPayload struct {
	events.SafeSealed
	Identity  identity.Quadruple
	ToolName  string
	Transport TransportKind
	StartedAt time.Time
}

ToolInvokedPayload is the typed payload for EventTypeToolInvoked. SafePayload: carries no secret-shaped data (the tool name and transport are operator-supplied identifiers, not user input).

type ToolPolicy

type ToolPolicy struct {
	// TimeoutMS is the per-attempt deadline in milliseconds. 0
	// means "use DefaultPolicy.TimeoutMS (30000)". When the policy
	// is overridden via WithPolicy, 0 means "no per-attempt
	// timeout" (the ctx's deadline still applies).
	TimeoutMS int
	// MaxRetries is the count of retry attempts AFTER the initial
	// invocation. Total invocations = MaxRetries + 1. 0 (zero
	// value) means "use DefaultPolicy.MaxRetries (3)".
	MaxRetries int
	// BackoffBase is the first-retry sleep before retry attempt 1.
	// Subsequent retries multiply by BackoffMult, capped at
	// BackoffMax. 0 means "use DefaultPolicy.BackoffBase (100ms)".
	BackoffBase time.Duration
	// BackoffMult is the multiplier between successive retry
	// sleeps. 0 means "use DefaultPolicy.BackoffMult (2)".
	BackoffMult float64
	// BackoffMax caps the per-retry sleep regardless of mult. 0
	// means "use DefaultPolicy.BackoffMax (30s)".
	BackoffMax time.Duration
	// RetryOn lists which ErrorClasses are retryable. Empty (zero
	// value) means "use DefaultPolicy.RetryOn ([transient, timeout,
	// 5xx])". An empty non-nil slice (explicitly set to []) means
	// "retry on nothing" (one attempt only).
	RetryOn []ErrorClass
	// Validate selects which side(s) of the invocation the shell
	// validates. Zero value means "use DefaultPolicy.Validate
	// (ValidateBoth)".
	Validate ValidateMode
}

ToolPolicy is the per-tool reliability shell. Every invocation regardless of Transport wraps in ToolPolicy (RFC §6.4, D-024). Zero value is "use defaults": DefaultPolicy fires on a fresh Tool.Policy at dispatch time.

Mirrors engine.NodePolicy (RFC §6.1): same backoff math, same validation modes. A flow registered as a tool gets BOTH layers — the outer ToolPolicy wraps the whole flow invocation; the per-node NodePolicy wraps each step inside the flow's engine. No double-wrapping at any single layer.

func DefaultPolicy

func DefaultPolicy() ToolPolicy

DefaultPolicy returns the policy applied when ToolPolicy is zero-valued. 3 retries / 100ms→30s exponential backoff (mult=2) / 30s timeout / Validate=ValidateBoth / RetryOn=[transient, timeout, 5xx]. Per D-024 acceptance criteria.

type ToolPolicyExhaustedPayload

type ToolPolicyExhaustedPayload struct {
	events.SafeSealed
	Identity  identity.Quadruple
	ToolName  string
	Transport TransportKind
	Attempts  int
	LastClass ErrorClass
	LastError string
}

ToolPolicyExhaustedPayload is the typed payload for EventTypeToolPolicyExhausted. SafePayload.

type ToolProvider

type ToolProvider interface {
	// Connect is called once at provider attach. Drivers establish
	// long-lived connections, authenticate, etc.
	Connect(ctx context.Context) error
	// Discover returns the current set of descriptors. May be
	// called periodically by the catalog manager.
	Discover(ctx context.Context) ([]ToolDescriptor, error)
	// Close releases provider resources. Must be idempotent.
	Close(ctx context.Context) error
	// SourceID is the stable identifier for this provider.
	SourceID() ToolSourceID
}

ToolProvider is the seam for external tool sources. Phase 27+ drivers (HTTP, MCP, A2A) implement Connect / Discover / Close to pull in remote tools as ToolDescriptors. The in-process registrar does not need a provider lifecycle (it's a thin wrapper around ToolCatalog.Register), so Phase 26 ships the interface shape without a default driver.

Identity-mandatory: Connect / Discover propagate identity via ctx so transports can scope their authentication.

type ToolResult

type ToolResult struct {
	Value any
	Meta  map[string]any
}

ToolResult is the canonical result type returned by every ToolDescriptor.Invoke. Heavy outputs route through the ArtifactStore upstream (D-022, D-026); ToolResult.Value carries either typed Go values or ArtifactRef-shaped placeholders.

func RunWithPolicy

func RunWithPolicy(
	ctx context.Context,
	args json.RawMessage,
	invoke func(ctx context.Context, args json.RawMessage) (ToolResult, error),
	validateIn func(args json.RawMessage) error,
	validateOut func(result ToolResult) error,
	policy ToolPolicy,
) (ToolResult, error)

RunWithPolicy is the externally-visible executor — drivers (in-process, HTTP, MCP, A2A, Flow) call this from their Invoke closures so every tool invocation regardless of Transport wraps in the same reliability shell.

`validateIn` / `validateOut` may be nil; the shell uses the resolved policy's Validate mode to decide whether to call them. `policy` is the Tool's policy (per-Tool); zero-valued → defaults.

Concurrent reuse (D-025): RunWithPolicy is stateless — no shared mutable state across calls. Safe for N concurrent invocations.

func RunWithPolicyHooked

func RunWithPolicyHooked(
	ctx context.Context,
	args json.RawMessage,
	invoke func(ctx context.Context, args json.RawMessage) (ToolResult, error),
	validateIn func(args json.RawMessage) error,
	validateOut func(result ToolResult) error,
	policy ToolPolicy,
	hooks ...InvokeHooks,
) (ToolResult, error)

RunWithPolicyHooked is the externally-visible executor with observability hooks. Equivalent to RunWithPolicy with an InvokeHooks pointer. Drivers (esp. those that publish per-attempt events) consume this surface.

type ToolSearchCache added in v1.2.0

type ToolSearchCache interface {
	Search(ctx context.Context, query string, tags []string, limit int) ([]Tool, error)
	Sync(ctx context.Context, tools []Tool) error
	Close() error
}

ToolSearchCache is the tool search index surface the catalog delegates to. Defined here to avoid import cycles between internal/tools and internal/tools/drivers/searchcache.

type ToolSourceID

type ToolSourceID string

ToolSourceID identifies the provider that produced a Tool. Empty for in-process tools (no provider lifecycle to track); populated for HTTP / MCP / A2A providers.

type TransportKind

type TransportKind string

TransportKind discriminates a Tool's source. Same Tool struct regardless of Transport; the value lets observability + audit filter or annotate without re-shaping the Tool.

const (
	// TransportInProcess — a Go function registered via
	// drivers/inproc.RegisterFunc.
	TransportInProcess TransportKind = "inprocess"
	// TransportHTTP — Phase 27 (UTCP-style HTTP tool).
	TransportHTTP TransportKind = "http"
	// TransportMCP — Phase 28 (MCP southbound).
	TransportMCP TransportKind = "mcp"
	// TransportA2A — Phase 29 (A2A southbound).
	TransportA2A TransportKind = "a2a"
	// TransportFlow — Phase 26a (a typed DAG of Nodes registered as
	// a Tool via internal/runtime/flow.RegisterAsTool).
	TransportFlow TransportKind = "flow"
)

type ValidateMode

type ValidateMode string

ValidateMode selects which side of an invocation the policy validates: input, output, both, or none. Mirrors engine.NodePolicy (RFC §6.1) so a developer who learned NodePolicy already knows ToolPolicy (D-024).

const (
	// ValidateNone disables validation. Escape hatch for hot paths.
	ValidateNone ValidateMode = ""
	// ValidateBoth runs validation on input AND output. Default for
	// production tools.
	ValidateBoth ValidateMode = "both"
	// ValidateIn runs validation on the input only.
	ValidateIn ValidateMode = "in"
	// ValidateOut runs validation on the output only.
	ValidateOut ValidateMode = "out"
)

Directories

Path Synopsis
Package approval ships Harbor's tool-side synchronous approval-gate subsystem — the second consumer of the unified pause/resume primitive (Phase 50 / D-067), layered on the same Coordinator + bus + steering inbox seams Phase 30 (tool-side OAuth) built.
Package approval ships Harbor's tool-side synchronous approval-gate subsystem — the second consumer of the unified pause/resume primitive (Phase 50 / D-067), layered on the same Coordinator + bus + steering inbox seams Phase 30 (tool-side OAuth) built.
Package auth ships Harbor's tool-side OAuth subsystem — the TokenStore + OAuthProvider seam every Phase 27 (HTTP) / Phase 28 (MCP) / Phase 29 (A2A) southbound driver consults when a tool call needs a bearer token.
Package auth ships Harbor's tool-side OAuth subsystem — the TokenStore + OAuthProvider seam every Phase 27 (HTTP) / Phase 28 (MCP) / Phase 29 (A2A) southbound driver consults when a tool call needs a bearer token.
conformancetest
Package conformancetest exposes the shared TokenStore + Sealer conformance suite Phase 30 ships.
Package conformancetest exposes the shared TokenStore + Sealer conformance suite Phase 30 ships.
drivers/oauth2
Package oauth2 ships Harbor's V1 default OAuth provider driver (D-095, closes issue #116 and D-090's deferred construction gap).
Package oauth2 ships Harbor's V1 default OAuth provider driver (D-095, closes issue #116 and D-090's deferred construction gap).
Package builtin ships the small set of opt-in tools that travel with the Harbor binary.
Package builtin ships the small set of opt-in tools that travel with the Harbor binary.
Package catalog ships Harbor's operator-config-driven tool-catalog wiring (Phase 64a / D-090).
Package catalog ships Harbor's operator-config-driven tool-catalog wiring (Phase 64a / D-090).
Package conformancetest is Harbor's shared conformance suite for any ToolCatalog implementation.
Package conformancetest is Harbor's shared conformance suite for any ToolCatalog implementation.
drivers
a2a
Package a2a is Harbor's southbound A2A integration with the tool catalog (Phase 29).
Package a2a is Harbor's southbound A2A integration with the tool catalog (Phase 29).
http
Package http is Harbor's HTTP transport driver for the unified tool catalog (Phase 27).
Package http is Harbor's HTTP transport driver for the unified tool catalog (Phase 27).
inproc
Package inproc is Harbor's in-process tool driver.
Package inproc is Harbor's in-process tool driver.
mcp
attach.go — the exported boot-time MCP server attachment helper (Phase 110d, D-197; absorbs cmd/harbor's attachDevMCPServer from Phase 83g / D-150 INCLUDING the Phase 26b config→ToolPolicy projection that the devstack mirror had silently dropped).
attach.go — the exported boot-time MCP server attachment helper (Phase 110d, D-197; absorbs cmd/harbor's attachDevMCPServer from Phase 83g / D-150 INCLUDING the Phase 26b config→ToolPolicy projection that the devstack mirror had silently dropped).
searchcache
Package searchcache is Harbor's SQLite FTS5-backed tool search cache (Phase 107c / D-167).
Package searchcache is Harbor's SQLite FTS5-backed tool search cache (Phase 107c / D-167).
Package protocol implements the seven `tools.*` Protocol methods the Console Tools page (Phase 73f / D-116) consumes:
Package protocol implements the seven `tools.*` Protocol methods the Console Tools page (Phase 73f / D-116) consumes:

Jump to

Keyboard shortcuts

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