Tools & Remote Config

Register tool metadata once at startup, then tune retries, budgets, and timeouts per tool from the dashboard. Changes propagate to the SDK within 30 seconds without redeployment.

Register at startup

import { configure, registerTools } from 'fuze-ai'

configure({
  cloud:   { apiKey: process.env.FUZE_API_KEY },
  project: { projectId: 'my-agent' },
})

registerTools([
  {
    name: 'search',
    description: 'Vector database search over company docs',
    sideEffect: false,
    defaults: { maxRetries: 3, maxBudget: 0.10, timeout: 10_000 },
  },
  {
    name: 'sendEmail',
    description: 'Send transactional email via SES',
    sideEffect: true,
    defaults: { maxRetries: 1, maxBudget: 0.02, timeout: 8_000 },
  },
])

Call this once during application startup, before any agents run. If no API key is configured, registerTools() is a no-op, nothing breaks.

Tool name matching

The name in registerTools() must match the function name that guard() wraps. Fuze uses fn.name automatically:

typescript
// ✅ name matches, remote config applies
registerTools([{ name: 'search', ... }])
const search = guard(async function search(q: string) { ... })

// ✅ also works with named arrow functions assigned to const
const search = guard(search_impl) // fn.name = 'search_impl', register as 'search_impl'

// ❌ anonymous function, remote config won't apply (no name to match)
const search = guard(async (q: string) => { ... })

registerTools() API

typescript
registerTools(tools: ToolRegistration[]): void
FieldTypeRequiredDescription
namestringFunction name, must match fn.name in guard()
descriptionstring,Human-readable description shown in the dashboard
schemaobject,JSON Schema of the function's parameters
sideEffectbooleanWhether the tool modifies external state
defaults.maxRetriesnumberDefault retry count (editable from dashboard)
defaults.maxBudgetnumberDefault USD budget cap per call (editable from dashboard)
defaults.timeoutnumberDefault timeout in ms (editable from dashboard)

How remote config works

code
1. SDK starts → registerTools() → POST /v1/tools/register
   Creates default tool_configs in the database if absent

2. SDK starts → GET /v1/tools/config → populates in-memory config cache
   Refreshed automatically every 30 seconds

3. Dashboard user edits tool config → PUT /api/tools/:name/config
   Written to database immediately

4. Within 30 seconds → SDK's background refresh picks up new config
   No restart needed

5. guard(fn) is called → getToolConfig(fn.name) reads from cache synchronously
   Zero added latency on the execution hot path

Override precedence

The tighter limit always wins:

LevelApplied
guard(fn, { maxCost: X })Highest, never overridden remotely
Dashboard maxBudgetTakes minimum of local and remote
Dashboard maxRetries / timeoutReplaces local values entirely
defaults in registerTools()Baseline, used when dashboard hasn't been configured
fuze.toml / configure()Global baseline

Disabling a tool remotely

Set enabled: false for a tool in the dashboard. Any call to that tool will immediately throw a FuzeError, no LLM request is made, and the error propagates to your agent for handling:

typescript
import { FuzeError } from 'fuze-ai'

try {
  await search('query')
} catch (err) {
  if (err instanceof FuzeError) {
    // Tool was disabled from the dashboard
    console.log(err.message) // "Tool 'search' is disabled via remote configuration"
  }
}

This is useful for emergency stops: if a tool is causing unexpected costs or failures, disable it from the dashboard without a deployment.

Without an API key

registerTools() is always safe to call. When no FUZE_API_KEY is set:

  • The call is a no-op, no network request is made
  • guard() uses local defaults from fuze.toml and per-function options
  • All protection logic (budget, loop detection, tracing) works exactly as normal
  • Dashboard tool configuration simply isn't available