/**
* Registry value produced by `tool_registry()` / `tool_define(...)`.
*
* Packages that accept or forward registries through typed public APIs
* should import this alias from `std/tools` instead of `dict` or a local
* structural copy.
*/
pub type ToolRegistryInfo = {name: string, version?: string, description?: string}
/** Draft 2020-12 schema document, including the standard boolean form. */
pub type JsonSchema202012 = bool | dict<string, unknown>
/** Reusable named JSON Schemas shared by every adapter projection. */
pub type ToolComponents = {schemas: dict<string, JsonSchema202012>}
pub type ToolCliValueHint = "file" \
| "directory" \
| "path" \
| "url" \
| "email" \
| "username" \
| "hostname" \
| "command" \
| "other"
pub type ToolCliBooleanStyle = "value" | "set_true" | "set_false"
pub type ToolCliArgumentSpec = {
long?: string,
short?: string,
aliases?: list<string>,
position?: int,
value_name?: string,
help?: string,
value_hint?: ToolCliValueHint,
boolean_style?: ToolCliBooleanStyle,
repeatable?: bool,
hidden?: bool,
completions?: list<string>,
display_order?: int,
help_group?: string,
}
pub type ToolCliCommandSpec = {
command: list<string>,
title?: string,
description?: string,
aliases?: list<string>,
hidden?: bool,
display_order?: int,
}
pub type ToolCliTreeSpec = {commands: list<ToolCliCommandSpec>}
pub type ToolRegistryOptions = {
info?: ToolRegistryInfo,
components?: ToolComponents,
cli?: ToolCliTreeSpec,
}
pub type ToolRegistry = {
_type: "tool_registry",
tools: list,
info?: ToolRegistryInfo,
components?: ToolComponents,
cli?: ToolCliTreeSpec,
}
/** Deterministic command path projected by `harn tool run`. */
pub type ToolCliSpec = {
command: list<string>,
aliases?: list<string>,
hidden?: bool,
arguments?: dict<string, ToolCliArgumentSpec>,
}
/** Closed set of adapters that may discover and invoke a tool. */
pub type ToolAudience = "cli" | "mcp" | "catalog" | "dashboard" | "agent"
/** Adapter exposure policy projected with the tool catalog. */
pub type ToolGovernance = {audiences: list<ToolAudience>}
/** Origin coordinates retained across generated adapters. */
pub type ToolSource = {kind: string, id?: string, binding?: dict}
/** Harn-owned execution classification used by approval and scheduling policy. */
pub type ToolPolicy = {
kind: "read" | "edit" | "delete" | "move" | "search" | "execute" | "think" | "fetch" | "other",
side_effect_level: "none" \
| "read_only" \
| "workspace_write" \
| "process_exec" \
| "network" \
| "desktop_control",
}
/** MCP task execution contract retained by the shared tool catalog. */
pub type ToolExecution = {taskSupport: "forbidden" | "optional" | "required"}
/** Portable icon metadata used by MCP and future presentation adapters. */
pub type ToolIcon = {src: string, mimeType?: string, sizes?: list<string>, theme?: "light" | "dark"}
/** Static, handler-free projection of one executable registry entry. */
pub type ToolCatalogEntry = {
name: string,
title?: string,
description?: string,
inputSchema: JsonSchema202012,
outputSchema?: JsonSchema202012,
errorSchema?: JsonSchema202012,
annotations?: dict,
icons?: list<ToolIcon>,
execution?: ToolExecution,
governance: ToolGovernance,
cli: ToolCliSpec,
namespace?: string,
deferLoading: bool,
source?: ToolSource,
policy?: ToolPolicy,
_meta?: dict,
}
/** Versioned static projection shared by generated presentation adapters. */
pub type ToolCatalog = {
schema_version: "harn-tools/2.0",
info?: ToolRegistryInfo,
cli?: ToolCliTreeSpec,
tools: list<ToolCatalogEntry>,
components?: ToolComponents,
}
type ToolDefinitionConfig = {
/** Legacy per-parameter shorthand. Prefer input_schema for new adapters. */
parameters?: dict,
/** Complete caller-owned object schema, normalized directly into inputSchema. */
input_schema?: dict<string, unknown>,
returns?: JsonSchema202012,
output_schema?: JsonSchema202012,
error_schema?: JsonSchema202012,
handler?: fn(dict) -> any,
executor?: string,
host_capability?: string,
mcp_server?: string,
defer_loading?: bool,
namespace?: string,
title?: string,
annotations?: dict,
cli?: ToolCliSpec,
source?: ToolSource,
policy?: dict,
execution_policy?: ToolPolicy,
execution?: ToolExecution,
governance?: ToolGovernance,
icons?: list<ToolIcon>,
meta?: dict,
guidance?: string,
}
/**
* Declarative tool spec accepted by `tool_define_many` / `tool_registry_from`.
*
* System-prompt `guidance` is co-located with the tool. When this tool is
* present in the active tool set, the runtime auto-injects this text as a
* capability-gated system-prompt fragment (gated on the tool's own
* presence), so instruction and tool can never drift. Omit it and the
* instruction never appears. See docs/src/prompt-assembly.md.
*/
pub type ToolDefinitionSpec = {
name: string,
description?: string,
desc?: string,
config?: ToolDefinitionConfig,
/** Legacy per-parameter shorthand. Prefer input_schema for new adapters. */
parameters?: dict,
/** Complete caller-owned object schema, normalized directly into inputSchema. */
input_schema?: dict<string, unknown>,
returns?: JsonSchema202012,
output_schema?: JsonSchema202012,
error_schema?: JsonSchema202012,
handler?: fn(dict) -> any,
executor?: string,
host_capability?: string,
mcp_server?: string,
defer_loading?: bool,
namespace?: string,
title?: string,
annotations?: dict,
cli?: ToolCliSpec,
source?: ToolSource,
policy?: dict,
execution_policy?: ToolPolicy,
execution?: ToolExecution,
governance?: ToolGovernance,
icons?: list<ToolIcon>,
meta?: dict,
guidance?: string,
}
type PathScopeMatcherOptions = {
scope?: string,
arg_keys?: list<string>,
mount_modes?: list<string>,
on_violation?: string,
patterns?: list<string>,
}?
/**
* path_scope returns a dynamic-permissions matcher that checks path args
* against the active session workspace anchor.
*
* @effects: []
* @errors: []
* @api_stability: experimental
* @example: path_scope({mount_modes: ["extend"]})
*/
pub fn path_scope(options: PathScopeMatcherOptions = nil) -> dict {
const opts = options ?? {}
let matcher = {
type: "path_scope",
scope: opts?.scope ?? "anchor_plus_mounted",
arg_keys: opts?.arg_keys ?? ["path", "destination", "source", "file"],
on_violation: opts?.on_violation ?? "deny",
}
if opts?.mount_modes != nil {
matcher = matcher + {mount_modes: opts.mount_modes}
}
if opts?.patterns != nil {
matcher = matcher + {patterns: opts.patterns}
}
return matcher
}
fn __tool_spec_config(spec: ToolDefinitionSpec) -> ToolDefinitionConfig {
if type_of(spec) != "dict" {
throw "tool_define_many: each spec must be a dict"
}
let config = spec.config ?? {}
for key in spec.keys() {
if !contains(["name", "description", "desc", "config"], key) {
config = config + {[key]: spec[key]}
}
}
return config
}
/**
* tool_define_many adds a list of tool specs to a registry.
*
* @effects: []
* @errors: []
*/
pub fn tool_define_many(registry: ToolRegistry, specs: list<ToolDefinitionSpec>) -> ToolRegistry {
if type_of(specs) != "list" {
throw "tool_define_many: specs must be a list"
}
let tools = registry ?? tool_registry()
for spec in specs {
const name = spec.name
const description = spec.description ?? spec.desc
if name == nil || name == "" {
throw "tool_define_many: each spec needs a non-empty name"
}
if description == nil || description == "" {
throw "tool_define_many: each spec needs a non-empty description"
}
tools = tool_define(tools, name, description, __tool_spec_config(spec))
}
return tools
}
/**
* tool_registry_from creates a registry from a list of tool specs.
*
* @effects: []
* @errors: []
*/
pub fn tool_registry_from(
specs: list<ToolDefinitionSpec>,
options: ToolRegistryOptions? = nil,
) -> ToolRegistry {
const opts = options ?? {}
return tool_define_many(tool_registry(opts.info, opts.components, opts.cli), specs)
}