Skip to content

Tool Architecture & Execution

The tool architecture is a cornerstone of the Viber framework, designed to safely provide agents with the capabilities they need to perform meaningful work. It directly supports the Vibe-X philosophy by enabling a secure, observable, and extensible system where a human expert can confidently delegate tasks to an AI partner.

The core principles are:

  • Security First: Untrusted, LLM-generated code or commands must never execute directly on a host machine. All tool execution is centralized, validated, and sandboxed.
  • Robust Self-Correction: LLM-generated tool calls can be malformed. The system is designed to detect this, provide clear, corrective feedback to the agent, and enable the agent to fix its own mistakes.
  • Extensible by Design: The framework is built to be easily extended. Adding new tools follows a consistent pattern, and the architecture supports advanced integrations like the Model Context Protocol (MCP).

A “tool” is a capability that an agent can call. Tools are defined using the AI SDK pattern with Zod schemas.

Tools are defined with a name, description, parameter schema, and execute function:

import { z } from "zod";
import type { CoreTool } from "@viber/core";
const writeFileTool: CoreTool = {
name: "write_file",
description: "Writes content to a file at the specified path.",
parameters: z.object({
path: z.string().describe("The file path to write to"),
content: z.string().describe("The content to write"),
}),
execute: async ({ path, content }) => {
// Implementation
return { success: true, message: `File '${path}' written successfully.` };
},
};

Tools are registered with agents through configuration:

import { Agent } from "viber";
const agent = new Agent({
name: "Developer",
llm: { provider: "openai", model: "gpt-4o" },
tools: ["write_file", "read_file", "execute_code"],
requireApproval: ["write_file", "execute_code"], // Human-in-the-loop
});

The following diagram and steps describe the end-to-end flow of a tool call, from the XAgent’s task assignment to the final result.

graph TD
XAgent -- "1. Assign Task" --> Agent
subgraph "Agent"
AgentLLM["LLM"]
AgentCore["Core Logic"]
end
subgraph "Framework"
ToolManager["Tool Manager"]
end
subgraph "Execution Environment"
Tool["Tool (e.g., write_file)"]
end
AgentLLM -- "2. Generate Tool Call" --> AgentCore
AgentCore -- "3. Request Execution" --> ToolManager
alt 4a. Validation Fails
ToolManager -- "Validation Error" --> AgentCore
AgentCore -- "Error Context" --> AgentLLM
AgentLLM -- "Corrected Tool Call" --> AgentCore
AgentCore -- "Retry Request" --> ToolManager
end
ToolManager -- "4b. Validation Succeeds" --> Tool
Tool -- "5. Return Result" --> ToolManager
ToolManager -- "6. Return Structured Result" --> AgentCore
AgentCore -- "7. Report Task Completion" --> XAgent

Step-by-Step Flow:

  1. Task Assignment (XAgent): The process begins when the XAgent, following the project’s plan, assigns a specific task to a specialist Agent (e.g., “Write ‘hello world’ to hello.txt”).

  2. Tool Call Generation (Agent’s LLM): The Agent’s LLM receives the instruction. It determines that the task can be accomplished with a tool and generates the corresponding structured tool call.

  3. Execution Request: The Agent’s core logic receives the generated tool call and passes it to the Tool Manager for secure execution.

  4. Validation: The Tool Manager validates the call against the tool’s registered schema.

    • If Validation Fails: The Tool Manager returns a structured error to the Agent. The Agent’s LLM receives the error context and generates a corrected call. This self-correction loop is a key feature of the framework’s robustness.
    • If Validation Succeeds: The Tool Manager proceeds to execution.
  5. Secure Execution: The Tool Manager securely runs the tool with validated parameters.

  6. Result Capturing: The Tool Manager captures the tool’s output and packages it into a structured result object, which is returned to the Agent.

  7. Task Completion: The Agent has now completed its assigned task. It reports the result back to the XAgent, which updates the project plan and proceeds to the next step in the workflow.

Viber supports native tool approval through AI SDK v6’s approval mechanism:

// Agent configuration with approval requirements
const agent = new Agent({
name: "Developer",
tools: ["write_file", "execute_code", "read_file"],
requireApproval: ["write_file", "execute_code"],
});
// Frontend handles approval
const { messages, approveToolCall, status } = useXChat({
spaceId: "my-space",
});
// When status is "awaiting-approval"
if (status === "awaiting-approval") {
const pendingApprovals = getPendingApprovals(messages[messages.length - 1]);
// Show UI for approval
await approveToolCall(pendingApprovals[0].toolCallId, true);
}

Executing arbitrary, LLM-generated commands is a major security risk. Viber mitigates this through multiple layers:

All tool executions are scoped to the Space’s workspace directory:

  • Tools can only read from and write to the Space’s artifact directory
  • Path traversal attacks are mitigated by resolving all paths relative to the Space root
  • Each Space has complete data isolation from other Spaces
// Tools can specify their permission requirements
const webBrowseTool: CoreTool = {
name: "browse_web",
description: "Browse a webpage and extract content",
parameters: z.object({
url: z.string().url(),
}),
permissions: ["network"], // Requires network access
execute: async ({ url }) => {
// Implementation with Playwright
},
};

Viber provides a core set of tools in @viber/tools:

ToolDescriptionCategory
read_fileRead file contentsFile
write_fileWrite content to fileFile
list_filesList directory contentsFile
web_searchSearch the webSearch
browse_webBrowse and extract web pagesWeb
execute_sqlExecute database queriesDatabase

Users can define custom tools following the same pattern:

import { z } from "zod";
import type { CoreTool } from "@viber/core";
export const customTool: CoreTool = {
name: "my_custom_tool",
description: "Does something custom",
parameters: z.object({
input: z.string(),
}),
execute: async ({ input }) => {
// Custom implementation
return { result: `Processed: ${input}` };
},
};
// Register with agent
const agent = new Agent({
name: "CustomAgent",
tools: [customTool], // Pass tool object directly
});

Viber supports the Model Context Protocol (MCP) for integrating external tool servers:

import { createMCPToolProvider } from "@viber/tools";
// Connect to an MCP server
const mcpTools = await createMCPToolProvider({
server: "mcp://localhost:3000",
});
// Use MCP tools with agents
const agent = new Agent({
name: "MCPAgent",
tools: [...mcpTools.getTools()],
});

This enables agents to access a vast ecosystem of third-party capabilities without requiring custom code for each integration.