Skip to content

Tools

Tools are functions that agents can call during conversation to interact with external systems, search memory, manage execution plans, and more. They follow the Vercel AI SDK tool() pattern with Zod schemas for input validation.

Built-in Tools

The SDK ships a complete set of tool factories in @lota-sdk/core/tools/*:

ToolFactoryDescription
Memory SearchcreateMemorySearchToolSemantic search across organization and agent memories
Conversation SearchcreateConversationSearchToolFull-text search across prior chat messages by role
Remember MemorycreateRememberMemoryToolSaves a durable memory fact with automatic importance scoring
Memory BlockcreateMemoryBlockToolAppends a short-term note to the workstream memory block
Consult TeamcreateTeamThinkToolFan-out consultation to all team agents in parallel
Research TopicresearchTopicToolDelegates web research to a dedicated researcher agent
Fetch WebpagefetchWebpageToolFetches and extracts content from a single webpage via Firecrawl
Search WebsearchWebToolWeb search via Firecrawl
Read File PartsreadFilePartsToolReads file parts from indexed repositories
User QuestionsuserQuestionsToolPresents structured questions to the user
Create Execution PlancreateCreateExecutionPlanToolCreates a contract-driven execution plan and starts a run
Replace Execution PlancreateReplaceExecutionPlanToolReplaces the active run with a newly compiled plan
Submit Node ResultcreateSubmitExecutionNodeResultToolSubmits the result for a running execution node
Get Active PlancreateGetActiveExecutionPlanToolRetrieves the current execution run and its state
Resume RuncreateResumeExecutionPlanRunToolResumes an interrupted execution run from its latest checkpoint
Log Hello WorldcreateLogHelloWorldToolDemo tool with needsApproval: true for testing the approval flow

Import Paths

ts
// Search tools
import { createMemorySearchTool, createConversationSearchTool } from '@lota-sdk/core/tools/search-tools'

// Memory tools
import { createRememberMemoryTool } from '@lota-sdk/core/tools/remember-memory.tool'
import { createMemoryBlockTool } from '@lota-sdk/core/tools/memory-block.tool'

// Execution plan tools
import {
  createCreateExecutionPlanTool,
  createReplaceExecutionPlanTool,
  createSubmitExecutionNodeResultTool,
  createGetActiveExecutionPlanTool,
  createResumeExecutionPlanRunTool,
} from '@lota-sdk/core/tools/execution-plan.tool'

// Delegated tools (pre-built, no factory needed)
import { researchTopicTool } from '@lota-sdk/core/tools/research-topic.tool'
import { fetchWebpageTool } from '@lota-sdk/core/tools/fetch-webpage.tool'
import { searchWebTool } from '@lota-sdk/core/tools/search-web.tool'
import { readFilePartsTool } from '@lota-sdk/core/tools/read-file-parts.tool'

// User interaction
import { userQuestionsTool } from '@lota-sdk/core/tools/user-questions.tool'

Tool Anatomy

Every tool follows the Vercel AI SDK tool() pattern: a Zod input schema, a description string, and an execute function.

ts
import { tool } from 'ai'
import { z } from 'zod'

const myTool = tool({
  description: 'Searches the knowledge base for relevant information',
  inputSchema: z.object({
    query: z.string().describe('The search query'),
    limit: z.number().optional().default(5).describe('Maximum results to return'),
  }),
  execute: async ({ query, limit }) => {
    const results = await knowledgeBase.search(query, limit)
    return { query, count: results.length, results }
  },
})

Key conventions:

  • inputSchema: Zod schema defining the tool parameters
  • description: prompt guidance for the model about when to use the tool
  • execute: implementation function, either async or generator-based
  • toModelOutput: optional transformation that sends a simplified result back to the model while keeping the full payload for the client

Factory Pattern

Most built-in tools use a factory pattern because they need runtime context such as organization ID, workstream ID, and agent name:

ts
const memorySearch = createMemorySearchTool(orgIdString, agentName, { fastMode: false })
const conversationSearch = createConversationSearchTool(workstreamId)
const rememberMemory = createRememberMemoryTool({ orgId, agentName })
const memoryBlock = createMemoryBlockTool({ workstreamId, agentLabel: 'CEO' })

Registering Custom Tools

There are two ways to provide tools to agents.

Via toolProviders

Register tools globally for all agents through the runtime config:

ts
const runtime = await createLotaRuntime({
  toolProviders: {
    myCustomTool: tool({
      description: 'A custom tool available to all agents',
      inputSchema: z.object({ input: z.string() }),
      execute: async ({ input }) => ({ result: `Processed: ${input}` }),
    }),
  },
})

Tools in toolProviders are merged into the tool set for every agent on every turn.

Via buildAgentTools

For per-agent tool assignment, use buildAgentTools in the agents config:

ts
agents: {
  buildAgentTools: (agentId, context) => {
    const baseTools = {
      memorySearch: createMemorySearchTool(context.orgIdString, agentId),
      rememberMemory: createRememberMemoryTool({ orgId: context.orgId, agentName: agentId }),
    }

    if (agentId === 'chief') {
      return {
        ...baseTools,
        consultTeam: createTeamThinkTool(context),
        createExecutionPlan: createCreateExecutionPlanTool(context),
      }
    }

    return baseTools
  },
}

Both mechanisms can be used together. toolProviders sets the baseline; buildAgentTools adds agent-specific tools.

Tool Approval

Native SDK Approval (needsApproval)

Tools can declare needsApproval: true to require user approval before execution. This uses the Vercel AI SDK's built-in approval flow:

ts
import { tool } from 'ai'
import { z } from 'zod'

const dangerousTool = tool({
  description: 'Deletes a resource. Requires user confirmation.',
  inputSchema: z.object({ resourceId: z.string() }),
  needsApproval: true,
  execute: async ({ resourceId }) => {
    await deleteResource(resourceId)
    return { deleted: resourceId }
  },
})

Flow:

  1. The agent calls the tool — it enters approval-requested state.
  2. The client renders approve/deny buttons via addToolApprovalResponse.
  3. The user clicks approve or deny.
  4. The sendAutomaticallyWhen callback (set to lastAssistantMessageIsCompleteWithApprovalResponses by default in useThreadChat) auto-submits the response.
  5. The server routes this as native-tool-approval kind, which continues the agent turn with the approval attached.
  6. If approved, the tool executes. If denied, the agent receives the denial and responds accordingly.

The logHelloWorld demo tool (createLogHelloWorldTool) demonstrates this pattern.

Execution Plan Approval

Execution plan tools (createExecutionPlan, replaceExecutionPlan) use a separate approval mechanism based on plan node states. These are routed as approval-continuation kind on the server, which persists the approval response without re-running the agent turn.

Generator Tools

Tools can be async generators that yield intermediate results before returning the final output:

ts
const progressiveTool = tool({
  description: 'A tool that yields progress updates',
  inputSchema: z.object({ task: z.string() }),
  execute: async function* ({ task }) {
    yield { status: 'starting', message: 'Beginning work...' }

    const intermediate = await doFirstStep(task)
    yield { status: 'in-progress', message: 'First step complete', data: intermediate }

    const result = await doFinalStep(intermediate)
    return { status: 'complete', result }
  },
  toModelOutput: ({ output }) => ({ type: 'text', value: output.result }),
})

consultTeam uses this pattern to stream individual agent responses as they complete.

Shared Tool Schemas

The @lota-sdk/shared/schemas/tools module exports Zod schemas and TypeScript types for all core tool inputs and outputs. These are used by both the server and the client.

ts
import {
  UserQuestionsArgsSchema,
  ConsultTeamArgsSchema,
  ConsultTeamResultDataSchema,
  CreateExecutionPlanArgsSchema,
  ReplaceExecutionPlanArgsSchema,
  SubmitExecutionNodeResultArgsSchema,
  GetActiveExecutionPlanArgsSchema,
  ResumeExecutionPlanRunArgsSchema,
  ExecutionPlanToolResultDataSchema,
} from '@lota-sdk/shared/schemas/tools'

import type {
  CoreChatTools,
  UserQuestionsArgs,
  ConsultTeamArgs,
  ConsultTeamResultData,
  CreateExecutionPlanArgs,
  ReplaceExecutionPlanArgs,
  SubmitExecutionNodeResultArgs,
  GetActiveExecutionPlanArgs,
  ResumeExecutionPlanRunArgs,
  ExecutionPlanToolResultData,
} from '@lota-sdk/shared/schemas/tools'

CoreChatTools

The CoreChatTools type map defines the input and output contract for each SDK-owned tool:

ts
type CoreChatTools = {
  userQuestions: { input: UserQuestionsArgs; output: unknown }
  consultSpecialist: { input: ConsultSpecialistArgs; output: ConsultSpecialistResultData }
  consultTeam: { input: ConsultTeamArgs; output: ConsultTeamResultData }
  createExecutionPlan: { input: CreateExecutionPlanArgs; output: ExecutionPlanToolResultData }
  replaceExecutionPlan: { input: ReplaceExecutionPlanArgs; output: ExecutionPlanToolResultData }
  submitExecutionNodeResult: { input: SubmitExecutionNodeResultArgs; output: ExecutionPlanToolResultData }
  getActiveExecutionPlan: { input: GetActiveExecutionPlanArgs; output: ExecutionPlanToolResultData }
  resumeExecutionPlanRun: { input: ResumeExecutionPlanRunArgs; output: ExecutionPlanToolResultData }
}

Tool Name Constants

ts
import {
  USER_QUESTIONS_TOOL_NAME,               // 'userQuestions'
  CONSULT_TEAM_TOOL_NAME,                 // 'consultTeam'
  CONSULT_SPECIALIST_TOOL_NAME,           // 'consultSpecialist'
  CREATE_EXECUTION_PLAN_TOOL_NAME,        // 'createExecutionPlan'
  REPLACE_EXECUTION_PLAN_TOOL_NAME,       // 'replaceExecutionPlan'
  SUBMIT_EXECUTION_NODE_RESULT_TOOL_NAME, // 'submitExecutionNodeResult'
  GET_ACTIVE_EXECUTION_PLAN_TOOL_NAME,    // 'getActiveExecutionPlan'
  RESUME_EXECUTION_PLAN_RUN_TOOL_NAME,    // 'resumeExecutionPlanRun'
} from '@lota-sdk/shared/schemas/tools'

Use these constants instead of hardcoding tool names in clients.

Citations

Tools that return sourced data can attach citations using the CitationSchema:

ts
import { CitationSchema } from '@lota-sdk/core/tools/tool-contract'
import type { Citation } from '@lota-sdk/core/tools/tool-contract'

const citation: Citation = {
  source: 'Company Wiki',
  sourceId: 'wiki:article:42',
  version: 'v3',
  retrievedAt: new Date().toISOString(),
}