Not every tool should be available in every situation. An agent setting up a brand new project needs different tools than one working on an established codebase. A web-based sandbox has different capabilities than a local desktop environment. And some tools only make sense when specific integrations are configured.
Conditional tool availability is about matching your agent's capabilities to the current context. Done right, it prevents mistakes, reduces confusion, and optimizes for the task at hand.
Why Conditional Tools Matter
Imagine giving your agent a "ConfigurationProgress" tool that reports project setup stages. This tool is perfect when the agent is initializing a freshly cloned repository - it lets the UI show "Installing dependencies... Starting dev server... Configuration complete."
But that same tool is useless (and confusing) when working on an established project. The agent might try to call it inappropriately, wasting tokens and attention on a capability that doesn't apply.
Or consider a "KillTerminal" tool that stops running processes. Essential in agent mode where the agent manages long-running tasks. Dangerous in chat mode where the agent should be read-only.
Conditional tool availability ensures agents only see tools that make sense for their current context.
Dimensions of Conditionality
There are several factors that determine which tools should be available:
Project State
- Is this a brand new import that needs setup?
- Is this an established project with existing configuration?
- Has the agent made any file changes yet?
Agent Mode
- Full agent mode with all capabilities?
- Read-only chat mode for exploration?
- Specialized mode for a specific task?
Environment
- Desktop application with native file access?
- Web-based sandbox with limited capabilities?
- Remote server with specific tooling available?
Integrations
- Is a database connected (Supabase, Postgres)?
- Are payment webhooks configured (Stripe)?
- Is authentication set up (OAuth, JWT)?
User Permissions
- Free tier with basic features?
- Paid tier with advanced operations?
- Admin access with destructive capabilities?
Model Capabilities
- Does the model support specific tool formats?
- Does it have multimodal capabilities?
- Does it work better with certain tool patterns?
Each of these dimensions can influence which tools make sense.
Implementing Conditional Tool Loading
Your tool loading function should accept these parameters and return the appropriate set:
function getToolsForContext(
sandboxClient: SandboxClient,
isImportedProject: boolean,
mode: 'agent' | 'chat',
model: string
): Tool[] {
// Start with base tools everyone gets
const baseTools = [
readTool,
grepTool,
globTool,
lsTool,
webSearchTool,
];
// Add mode-specific tools
if (mode === 'agent') {
baseTools.push(
writeTool,
editTool,
bashTool,
sqlTool,
killTerminalTool,
);
}
// Add project-state-specific tools
if (isImportedProject && mode === 'agent') {
baseTools.push(
configurationProgressTool,
configurePreviewUrlTool,
);
}
// Add environment-specific tools
if (sandboxClient.name === 'desktop') {
baseTools.push(desktopTerminalLogsTool);
} else if (sandboxClient.name === 'daytona') {
baseTools.push(webTerminalLogsTool);
}
// Add model-specific tools
if (model === 'gpt-5.1-codex-max') {
baseTools.push(applyPatchTool);
} else {
baseTools.push(editStringReplaceTool);
}
return baseTools;
}This function makes explicit decisions about tool availability based on context.
Project State: Import vs Established
When a user imports an existing project (clone from GitHub, import a zip), that project needs initial setup:
- Install dependencies
- Start dev servers
- Save configuration for future sessions
These imported projects get special tools:
ConfigurationProgress - Reports setup stages to the UI
// Only add for imported projects in agent mode
if (isImportedProject && mode === 'agent') {
baseTools.push(configurationProgressTool);
}This tool lets the agent communicate progress:
- "analyzing" - Exploring the project structure
- "installing" - Installing dependencies
- "starting" - Starting dev servers
- "saving" - Saving configuration
- "complete" - Setup finished
The UI shows a progress indicator, the user knows what's happening, and the agent stays focused on the setup flow.
But once setup is complete and the project is established? This tool is removed. It no longer applies.
ConfigurePreviewUrl - Registers preview tabs for the desktop UI
if (isImportedProject && mode === 'agent') {
baseTools.push(configurePreviewUrlTool);
}This tool tells the desktop app "open a preview tab for localhost:3000" when the dev server starts. Critical during setup, irrelevant afterward.
Mode-Based Tool Filtering
We covered modes in the previous guide, but tool availability is where it's enforced:
Agent Mode Tools (can modify):
- writeTool - Create new files
- editTool - Modify existing files
- bashTool - Run terminal commands
- sqlTool - Execute database queries
- killTerminalTool - Stop running processes
Chat Mode Exclusions (read-only):
const chatModeTools = mode === 'chat'
? [readTool, grepTool, globTool, lsTool, webSearchTool, todoWriteTool]
: [...fullToolSet];Even though writeTool might technically be available, chat mode excludes it. The system prompt also reinforces this, but removing the tool entirely is the strongest guarantee.
Environment-Based Tool Availability
Different execution environments have different capabilities:
Desktop Environment:
- Native file system access
- Can spawn and manage processes
- Terminal logs via stdout/stderr capture
if (sandboxClient.name === 'desktop') {
baseTools.push(desktopTerminalLogsTool);
if (mode === 'agent') {
baseTools.push(killTerminalTool);
}
}Web Sandbox (Daytona):
- Remote file system
- Limited process management
- Terminal logs via WebSocket streams
if (sandboxClient.name === 'daytona') {
baseTools.push(webTerminalLogsTool);
// No kill terminal tool - can't manage processes the same way
}The agent doesn't need to know these differences. It just sees the appropriate terminal logs tool for its environment.
Integration-Based Tool Availability
Some tools only make sense when certain integrations exist:
Database Tools - When Supabase is connected:
if (hasSupabaseConnection) {
baseTools.push(sqlTool);
}Without a database connection, the SQL tool would just fail. Better to not offer it at all.
Payment Tools - When Stripe webhooks are configured:
if (hasStripeWebhooks) {
baseTools.push(paymentsAgentTool);
}The payments agent tool delegates to a specialized sub-agent that handles Stripe integration. Only relevant if Stripe is actually set up.
Model-Specific Tool Formats
Different models have different strengths with tool formats:
GPT-5.1 Codex Max - Works best with patch-based editing:
if (model === 'gpt-5.1-codex-max') {
baseTools.push(applyPatchTool);
} else {
baseTools.push(editStringReplaceTool);
}The patch format is a unified diff that Codex Max excels at generating. Other models do better with string replacement.
This lets you optimize tool selection for the model's capabilities without changing anything else.
Permission-Based Tool Availability
You can gate tools based on user permissions:
if (user.tier === 'paid') {
baseTools.push(
aiImageGenerationTool,
aiVideoGenerationTool,
advancedAnalyticsTool,
);
}
if (user.role === 'admin') {
baseTools.push(
deploymentTool,
userManagementTool,
);
}This prevents free users from accessing premium features and ensures only admins can perform sensitive operations.
Dynamic Tool Addition/Removal
Sometimes tools become available or unavailable during a session:
After database connection:
async connectDatabase(projectId: string) {
// Connect to database
await setupDatabaseConnection(projectId);
// Refresh agent with new tool set
this.agent = this.initializeAgent();
}The agent is reinitialized with the updated tool set. Now SQL queries are possible.
After completing initial setup:
async markSetupComplete() {
this.isImportedProject = false;
// Reinitialize without setup-specific tools
this.agent = this.initializeAgent();
}Configuration tools are removed because setup is done.
Tool Availability in System Prompts
Your system prompt should acknowledge conditional tools:
Based on your current context, you have access to:
- File operations (read, write, edit)
- Search tools (grep, glob)
- Terminal access (bash commands, logs)
- Web search for documentation
You do NOT have access to:
- Database operations (no database connected)
- Payment processing (Stripe not configured)
This sets expectations and prevents the agent from trying to use unavailable capabilities.
Handling Unavailable Tool Requests
What if your agent asks for a tool that isn't available? You have options:
Fail with explanation:
if (!hasDatabaseConnection && toolName === 'sql') {
return "Error: SQL tool unavailable - no database connected. Use the setup tool to connect a database first.";
}Suggest alternatives:
if (mode === 'chat' && toolName === 'write') {
return "Error: Cannot create files in chat mode. Switch to Agent mode to modify the codebase.";
}Redirect to setup:
if (!hasStripeWebhooks && toolName === 'payments_agent') {
return "Error: Payments agent unavailable. Configure Stripe webhooks first using the setup tool.";
}Clear error messages help the agent understand what's missing and how to proceed.
Testing Conditional Logic
How do you verify your conditional tool logic works?
Test each dimension independently:
// Test import project state
const importTools = getToolsForContext(client, true, 'agent', 'claude');
expect(importTools).toContain(configurationProgressTool);
// Test established project
const normalTools = getToolsForContext(client, false, 'agent', 'claude');
expect(normalTools).not.toContain(configurationProgressTool);
// Test chat mode
const chatTools = getToolsForContext(client, false, 'chat', 'claude');
expect(chatTools).not.toContain(writeTool);
// Test desktop environment
const desktopTools = getToolsForContext(desktopClient, false, 'agent', 'claude');
expect(desktopTools).toContain(desktopTerminalLogsTool);Each context should produce the expected tool set.
Common Patterns
Here are patterns that work well:
Progressive Tool Enablement - Start with minimal tools, add as the project is configured
Capability-Based - Tools reflect what the environment can actually do
Mode-Appropriate - Chat mode can't modify, agent mode can't switch modes
Integration-Aware - Database tools when database exists, payment tools when Stripe configured
Permission-Gated - Premium features only for paid users, admin tools only for admins
The Result
When you nail conditional tool availability:
- Agents don't waste time trying to use unavailable tools
- Tool lists are focused and relevant to the task
- Error messages are clear when something isn't available
- The UI can show/hide features based on tool availability
- Users aren't confused by capabilities that don't apply
It's the final piece of behavior control. You've defined how your agent should act (system prompts), what it can do (tools), when it should be cautious (modes), what it knows (context), and now which capabilities are available in each situation (conditional tools).
That's a complete behavior control system for your coding agent.