Tools And Context Tools
Tools are the action surface of the runtime. They should be typed, explicit, and easy to inspect in traces.
createTool(...)
Use createTool(...) for the standard SDK tool shape.
import { createTool } from "@cognipeer/agent-sdk";
import { z } from "zod";
const search = createTool({
name: "search_docs",
description: "Search the docs index",
schema: z.object({ query: z.string().min(1) }),
func: async ({ query }) => ({ hits: [`result for ${query}`] }),
});Important fields
namedescriptionschemafuncneedsApproval?approvalPrompt?approvalDefaults?maxExecutionsPerRun?cache?— opt-in result cache (trueor{ keyFn?, ttlMs?, scope? }). Identical args inside the same invoke skip re-execution and the cached value is replayed fromstate.toolCache.retry?— per-tool retry policy:{ maxRetries?, backoffMs?, shouldRetry?, circuitBreakerThreshold? }. Exponential backoff doublesbackoffMsper attempt. AftercircuitBreakerThresholdconsecutive failures the tool short-circuits with a breaker error for the rest of the invoke.
Approval-gated tools
If a tool is risky, mark it for approval:
const writeFile = createTool({
name: "dangerous_write",
description: "Write content to disk",
schema: z.object({ path: z.string(), content: z.string() }),
needsApproval: true,
approvalPrompt: "Confirm the write is safe.",
func: async ({ path, content }) => ({ ok: true, path, bytesWritten: content.length }),
});The runtime pauses before execution, records a pending approval, and resumes after resolveToolApproval(...) is applied.
Execution limits per tool
Use maxExecutionsPerRun when one specific tool should not be called indefinitely even if the global tool budget is still available.
Idempotent tool caching
When a tool is deterministic for a given input (e.g. file reads, web fetches, lookups), opt into the per-invoke cache:
const fetchPage = createTool({
name: "fetch_page",
description: "Fetch a static page",
schema: z.object({ url: z.string().url() }),
func: async ({ url }) => fetch(url).then((r) => r.text()),
cache: true, // identical { url } within the same invoke replays the result
});Advanced form:
cache: {
keyFn: (args) => args.url, // canonicalize cache key
ttlMs: 30_000, // expire cached entries after 30s
}The cache lives on state.toolCache and is scoped to the current invoke. Cached results are flagged with fromCache: true in state.toolHistory.
Retry and circuit breaker
Per-tool retry policy with exponential backoff:
const search = createTool({
name: "search",
description: "External search API",
schema: z.object({ q: z.string() }),
func: async ({ q }) => searchClient.query(q),
retry: {
maxRetries: 3,
backoffMs: 500, // doubles each attempt (500, 1000, 2000, ...)
shouldRetry: (err) => !`${err?.message}`.includes("UNAUTHORIZED"),
circuitBreakerThreshold: 5, // open the breaker after 5 consecutive failures
},
});Once the breaker opens, subsequent calls to that tool within the same invoke short-circuit with a breaker error message — no model retries, no extra latency.
Non-SDK tools
You can also pass objects that expose invoke, call, run, or func. This is how LangChain and MCP-adapted tools integrate cleanly.
Built-in context tools
createSmartAgent(...) may append runtime-managed tools:
manage_todo_listget_tool_responseopen_skillandbind_skill_toolswhenskillsare configuredresponsewhenoutputSchemais active
manage_todo_list
This is the planning tool for autonomous multi-step work.
writecreates or replaces the full planupdatepatches existing items by idreadreturns the current planexpectedVersionprevents stale writes from overwriting a newer plan
get_tool_response
This is the recovery tool for summarized history. Use it when a large tool output has been archived and you need the raw execution payload again.
open_skill and bind_skill_tools
These are the progressive disclosure meta-tools registered by createSmartAgent({ skills }).
open_skill({ skillKey, query? })loads a skill's prompt. Small skills bind all tools immediately; large skills return a tool index and optional default bindings.bind_skill_tools({ skillKey, toolNames })binds a selected subset from an already-open large skill.
Bound skill tools are appended to the live runtime tool set for later turns in the same invoke. See Guide → Skills for authoring guidance.
response
This finalize tool is injected only when outputSchema is active. The model is expected to call it exactly once with the final JSON object.
Tool authoring guidance
- validate inputs tightly with Zod
- return structured objects instead of long prose blobs
- throw actionable errors
- include source ids, cache flags, or other metadata when it improves downstream reasoning
- keep large outputs intentional, because they interact directly with summarization pressure