Overview
The Gemini CLI Tools API enables you to create custom tools that extend the model’s capabilities. Tools can interact with the local environment, fetch external data, perform computations, and more.
Core Interfaces
The main interface that defines a tool’s schema and builds executable invocations.
interface ToolBuilder<TParams extends object, TResult extends ToolResult> {
/** Internal name used for API calls */
name: string;
/** User-friendly display name */
displayName: string;
/** Description provided to the model */
description: string;
/** Tool categorization for permissions */
kind: Kind;
/** Whether output should be rendered as markdown */
isOutputMarkdown: boolean;
/** Whether tool supports streaming output */
canUpdateOutput: boolean;
/** Whether tool is read-only (no side effects) */
isReadOnly: boolean;
/** Get the function declaration schema */
getSchema(modelId?: string): FunctionDeclaration;
/** Validate parameters and build an executable invocation */
build(params: TParams): ToolInvocation<TParams, TResult>;
}
Unique internal identifier for the tool. Used in API calls to the Gemini model.
Human-readable name shown to users in confirmations and logs.
Clear explanation of what the tool does. This is provided to the model to help it understand when and how to use the tool.
Tool category for permission management. See Tool Kinds below.
Represents a validated, ready-to-execute tool call.
interface ToolInvocation<TParams extends object, TResult extends ToolResult> {
/** Validated parameters for this invocation */
params: TParams;
/** Get pre-execution description */
getDescription(): string;
/** Determine affected file system paths */
toolLocations(): ToolLocation[];
/** Check if user confirmation is required */
shouldConfirmExecute(
abortSignal: AbortSignal
): Promise<ToolCallConfirmationDetails | false>;
/** Execute the tool */
execute(
signal: AbortSignal,
updateOutput?: (output: ToolLiveOutput) => void,
shellExecutionConfig?: ShellExecutionConfig
): Promise<TResult>;
}
Returns a markdown string describing what this specific invocation will do. Shown to users before execution.
shouldConfirmExecute
(signal: AbortSignal) => Promise<ToolCallConfirmationDetails | false>
required
Determines if user confirmation is needed. Returns confirmation details or false if no confirmation required.
execute
(signal: AbortSignal, updateOutput?, config?) => Promise<ToolResult>
required
Executes the tool’s action and returns the result. Can be aborted via the signal.
The outcome of a tool execution.
interface ToolResult {
/** Content sent back to the LLM for context */
llmContent: PartListUnion;
/** User-friendly display (markdown string or rich object) */
returnDisplay: ToolResultDisplay;
/** Error information if the tool call failed */
error?: {
message: string;
type?: ToolErrorType;
};
/** Optional structured data payload */
data?: Record<string, unknown>;
/** Request to execute another tool immediately */
tailToolCallRequest?: {
name: string;
args: Record<string, unknown>;
};
}
Factual content included in the model’s conversation history. Can be a string or array of Part objects for rich content (images, audio, etc.).
returnDisplay
ToolResultDisplay
required
User-facing output. Can be:
string - Markdown text
FileDiff - File modification preview
AnsiOutput - Terminal output with ANSI codes
TodoList - Task list
SubagentProgress - Sub-agent status
Present if the tool call failed.Human-readable error message.
Machine-readable error classification (e.g., FILE_NOT_FOUND, INVALID_TOOL_PARAMS).
Base Classes
Base class for creating custom tools with automatic validation.
abstract class DeclarativeTool<
TParams extends object,
TResult extends ToolResult
> implements ToolBuilder<TParams, TResult> {
constructor(
readonly name: string,
readonly displayName: string,
readonly description: string,
readonly kind: Kind,
readonly parameterSchema: unknown,
readonly messageBus: MessageBus,
readonly isOutputMarkdown: boolean = true,
readonly canUpdateOutput: boolean = false,
readonly extensionName?: string,
readonly extensionId?: string
);
/** Validate raw parameters (override for custom validation) */
validateToolParams(params: TParams): string | null;
/** Build a validated invocation */
abstract build(params: TParams): ToolInvocation<TParams, TResult>;
/** Convenience: build and execute in one step */
async buildAndExecute(
params: TParams,
signal: AbortSignal,
updateOutput?: (output: ToolLiveOutput) => void,
shellExecutionConfig?: ShellExecutionConfig
): Promise<TResult>;
}
Extends DeclarativeTool with automatic schema validation and a simplified implementation pattern.
abstract class BaseDeclarativeTool<
TParams extends object,
TResult extends ToolResult
> extends DeclarativeTool<TParams, TResult> {
/** Override for additional validation beyond JSON schema */
protected validateToolParamValues(params: TParams): string | null;
/** Create the invocation object after validation */
protected abstract createInvocation(
params: TParams,
messageBus: MessageBus,
toolName?: string,
toolDisplayName?: string
): ToolInvocation<TParams, TResult>;
}
Convenience base class for implementing ToolInvocation.
abstract class BaseToolInvocation<
TParams extends object,
TResult extends ToolResult
> implements ToolInvocation<TParams, TResult> {
constructor(
readonly params: TParams,
protected readonly messageBus: MessageBus,
readonly _toolName?: string,
readonly _toolDisplayName?: string,
readonly _serverName?: string,
readonly _toolAnnotations?: Record<string, unknown>
);
abstract getDescription(): string;
toolLocations(): ToolLocation[] {
return [];
}
/** Override to customize confirmation UI */
protected async getConfirmationDetails(
abortSignal: AbortSignal
): Promise<ToolCallConfirmationDetails | false>;
/** Override to provide policy update options */
protected getPolicyUpdateOptions(
outcome: ToolConfirmationOutcome
): PolicyUpdateOptions | undefined;
abstract execute(
signal: AbortSignal,
updateOutput?: (output: ToolLiveOutput) => void,
shellExecutionConfig?: ShellExecutionConfig
): Promise<TResult>;
}
The Kind enum categorizes tools for permission management and parallel execution.
enum Kind {
Read = 'read', // Read files/data
Edit = 'edit', // Modify files
Delete = 'delete', // Delete resources
Move = 'move', // Move/rename
Search = 'search', // Search operations
Execute = 'execute', // Run commands
Think = 'think', // Reasoning/planning
Agent = 'agent', // Sub-agent invocation
Fetch = 'fetch', // Network requests
Communicate = 'communicate', // User interaction
Plan = 'plan', // Planning operations
SwitchMode = 'switch_mode', // Mode changes
Other = 'other' // Uncategorized
}
Read-only kinds (safe for parallel execution):
Kind.Read
Kind.Search
Kind.Fetch
Mutator kinds (have side effects):
Kind.Edit
Kind.Delete
Kind.Move
Kind.Execute
Step 1: Define Parameters Interface
interface MyToolParams {
query: string;
limit?: number;
}
Step 2: Define Parameter Schema
const MY_TOOL_SCHEMA = {
type: 'object',
properties: {
query: {
type: 'string',
description: 'Search query'
},
limit: {
type: 'number',
description: 'Maximum results to return',
default: 10
}
},
required: ['query']
};
class MyToolInvocation extends BaseToolInvocation<
MyToolParams,
ToolResult
> {
getDescription(): string {
return `Searching for "${this.params.query}" (limit: ${this.params.limit || 10})`;
}
async execute(signal: AbortSignal): Promise<ToolResult> {
try {
// Perform the actual work
const results = await performSearch(
this.params.query,
this.params.limit || 10
);
return {
llmContent: `Found ${results.length} results for "${this.params.query}"`,
returnDisplay: formatResults(results)
};
} catch (error) {
return {
llmContent: `Search failed: ${error.message}`,
returnDisplay: error.message,
error: {
message: error.message,
type: ToolErrorType.EXECUTION_FAILED
}
};
}
}
}
class MyTool extends BaseDeclarativeTool<MyToolParams, ToolResult> {
constructor(messageBus: MessageBus) {
super(
'my_custom_search', // name
'Custom Search', // displayName
'Searches the custom database', // description
Kind.Search, // kind
MY_TOOL_SCHEMA, // parameterSchema
messageBus, // messageBus
true, // isOutputMarkdown
false // canUpdateOutput
);
}
protected validateToolParamValues(params: MyToolParams): string | null {
if (params.limit && params.limit < 1) {
return 'limit must be at least 1';
}
return null;
}
protected createInvocation(
params: MyToolParams,
messageBus: MessageBus,
toolName?: string,
toolDisplayName?: string
): ToolInvocation<MyToolParams, ToolResult> {
return new MyToolInvocation(
params,
messageBus,
toolName,
toolDisplayName
);
}
}
const toolRegistry = config.getToolRegistry();
const myTool = new MyTool(messageBus);
toolRegistry.registerTool(myTool);
Gemini CLI includes several built-in tools:
LSTool - List directory contents
ReadFileTool - Read file content
WriteFileTool - Write to files
EditTool - In-place file modifications
GrepTool - Search for patterns
GlobTool - Find files by glob patterns
ReadManyFilesTool - Read multiple files
ShellTool - Execute shell commands
WebFetchTool - Fetch content from URLs
WebSearchTool - Perform web searches
MemoryTool - Interact with AI memory
- Model Request - Model returns a
FunctionCall with tool name and arguments
- Tool Retrieval - Core looks up the tool in the
ToolRegistry
- Parameter Validation -
validateToolParams() is called
- Build Invocation -
build() creates a ToolInvocation
- Confirmation -
shouldConfirmExecute() checks if user approval is needed
- User Confirmation - If required, CLI prompts the user
- Execution -
execute() performs the tool’s action
- Result Processing -
ToolResult is returned
- Response to Model -
llmContent is sent back to the model
- Display to User -
returnDisplay is shown in the CLI
Advanced Features
Streaming Output
Tools can provide live updates during execution:
async execute(
signal: AbortSignal,
updateOutput?: (output: ToolLiveOutput) => void
): Promise<ToolResult> {
updateOutput?.('Processing step 1...');
await step1();
updateOutput?.('Processing step 2...');
await step2();
return {
llmContent: 'Completed',
returnDisplay: 'All steps completed successfully'
};
}
Rich Content Results
Return structured content to the model:
return {
llmContent: [
'Analysis results:',
{
type: 'image',
image: {
url: 'data:image/png;base64,...'
}
}
],
returnDisplay: ''
};
Chain tool executions:
return {
llmContent: 'Preprocessed data',
returnDisplay: 'Data ready for analysis',
tailToolCallRequest: {
name: 'analyze_data',
args: { data: processedData }
}
};
Custom Confirmation UI
protected async getConfirmationDetails(
abortSignal: AbortSignal
): Promise<ToolCallConfirmationDetails | false> {
return {
type: 'exec',
title: 'Confirm Command Execution',
command: this.params.command,
rootCommand: this.params.command.split(' ')[0],
rootCommands: [this.params.command.split(' ')[0]],
onConfirm: async (outcome: ToolConfirmationOutcome) => {
// Handle confirmation outcome
}
};
}
Tools can be discovered dynamically through:
Command-based Discovery
Configure tools.discoveryCommand in settings.json:
{
"tools": {
"discoveryCommand": "./discover-tools.sh",
"callCommand": "./call-tool.sh"
}
}
The discovery command should output JSON:
[
{
"name": "custom_tool",
"description": "Does something custom",
"parametersJsonSchema": {
"type": "object",
"properties": {
"input": { "type": "string" }
}
}
}
]
MCP-based Discovery
Configure MCP servers in settings.json:
{
"mcpServers": {
"my-server": {
"command": "node",
"args": ["server.js"]
}
}
}
The CLI connects to MCP servers and discovers tools automatically. Tool names are prefixed with the server alias: my-server__tool_name.
Best Practices
- Clear Descriptions - Provide detailed descriptions to help the model understand when to use the tool
- Validate Thoroughly - Validate all parameters before execution
- Handle Errors Gracefully - Return structured errors with helpful messages
- Use Appropriate Kinds - Categorize tools correctly for permission management
- Confirm Destructive Operations - Always require confirmation for mutating operations
- Provide Meaningful Output - Both
llmContent and returnDisplay should be informative
- Support Cancellation - Respect the
AbortSignal for long-running operations
- Stream Progress - Use
updateOutput for operations that take time