> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/QwenLM/qwen-code/llms.txt
> Use this file to discover all available pages before exploring further.

# Tools System Architecture

> Deep dive into Qwen Code's extensible tools system

# Tools System Architecture

Qwen Code's tools system provides the AI model with capabilities to interact with the local environment. This guide explains how the tools system works and how to extend it.

## Overview

The tools system is built on these core principles:

* **Self-describing:** Tools define their parameters using JSON Schema
* **Type-safe:** Full TypeScript type safety throughout
* **Extensible:** Easy to add new tools
* **Secure:** Validation and approval mechanisms
* **Testable:** Comprehensive test coverage

## Architecture

### Core Components

```
ToolRegistry
│
├── BaseTool (abstract)
│   ├── name: string
│   ├── displayName: string
│   ├── description: string
│   ├── schema: JSONSchema
│   └── createInvocation(params): ToolInvocation
│
├── BaseDeclarativeTool (extends BaseTool)
│   ├── Kind: Read | Edit | Other
│   ├── validateToolParamValues()
│   └── createInvocation()
│
└── Tool Implementations
    ├── ReadFileTool
    ├── WriteFileTool
    ├── EditTool
    ├── ShellTool
    ├── TaskTool
    └── ...
```

### Tool Lifecycle

1. **Registration:** Tool registered in `ToolRegistry`
2. **Discovery:** Model receives tool definitions (name, description, schema)
3. **Invocation:** Model requests tool use with parameters
4. **Validation:** Parameters validated against schema and business rules
5. **Confirmation:** User approval for dangerous operations (if needed)
6. **Execution:** Tool executes and returns results
7. **Response:** Results sent back to model

## Base Tool Class

All tools extend `BaseDeclarativeTool`:

```typescript theme={null}
// packages/core/src/tools/tools.ts
export abstract class BaseDeclarativeTool<
  TParams extends object,
  TResult extends ToolResult
> {
  constructor(
    public readonly name: string,
    public readonly displayName: string,
    public readonly description: string,
    public readonly kind: Kind,
    public readonly schema: object,
    public readonly isOutputMarkdown: boolean = false,
    public readonly canUpdateOutput: boolean = false,
  ) {}

  // Validate parameters beyond schema validation
  protected validateToolParamValues(params: TParams): string | null {
    return null; // Override to add custom validation
  }

  // Create a tool invocation instance
  protected abstract createInvocation(
    params: TParams,
  ): ToolInvocation<TParams, TResult>;
}
```

### Tool Kinds

```typescript theme={null}
export enum Kind {
  Read = 'read',    // Read-only operations (no confirmation)
  Edit = 'edit',    // Modifying operations (require confirmation)
  Other = 'other',  // Special tools (custom behavior)
}
```

## Tool Invocation

Each tool execution creates a `ToolInvocation` instance:

```typescript theme={null}
export interface ToolInvocation<
  TParams extends object,
  TResult extends ToolResult
> {
  // Get display description for UI
  getDescription(): string;

  // Get file locations involved (for IDE integration)
  toolLocations?(): ToolLocation[];

  // Check if confirmation needed
  shouldConfirmExecute?(
    signal: AbortSignal,
  ): Promise<ToolCallConfirmationDetails | false>;

  // Execute the tool
  execute(
    signal: AbortSignal,
    updateOutput?: (output: ToolResultDisplay) => void,
    ...args: any[]
  ): Promise<TResult>;
}
```

## Tool Result

Tools return a `ToolResult`:

```typescript theme={null}
export interface ToolResult {
  // Content sent to the model
  llmContent: PartUnion;

  // Content displayed to user (optional)
  returnDisplay?: ToolResultDisplay;

  // Error information (optional)
  error?: {
    message: string;
    type: ToolErrorType;
  };
}
```

## Creating a New Tool

### Step 1: Create Tool File

Create a new file in `packages/core/src/tools/`:

```typescript theme={null}
// packages/core/src/tools/my-tool.ts
import { BaseDeclarativeTool, BaseToolInvocation, Kind } from './tools.js';
import type { ToolInvocation, ToolResult } from './tools.js';
import { ToolNames, ToolDisplayNames } from './tool-names.js';
import type { Config } from '../config/config.js';

// 1. Define parameter interface
export interface MyToolParams {
  requiredParam: string;
  optionalParam?: number;
}

// 2. Create invocation class
class MyToolInvocation extends BaseToolInvocation<
  MyToolParams,
  ToolResult
> {
  constructor(
    private config: Config,
    params: MyToolParams,
  ) {
    super(params);
  }

  getDescription(): string {
    return `My tool: ${this.params.requiredParam}`;
  }

  async execute(signal: AbortSignal): Promise<ToolResult> {
    // Implement tool logic
    try {
      const result = await this.doSomething();
      return {
        llmContent: `Success: ${result}`,
        returnDisplay: result,
      };
    } catch (error) {
      return {
        llmContent: `Error: ${error.message}`,
        error: {
          message: error.message,
          type: 'EXECUTION_ERROR',
        },
      };
    }
  }

  private async doSomething(): Promise<string> {
    // Tool implementation
    return 'result';
  }
}

// 3. Create tool class
export class MyTool extends BaseDeclarativeTool<
  MyToolParams,
  ToolResult
> {
  static readonly Name = 'my_tool';

  constructor(private config: Config) {
    super(
      MyTool.Name,
      'MyTool',  // Display name
      'Description of what my tool does',
      Kind.Other,  // or Kind.Read, Kind.Edit
      {
        type: 'object',
        properties: {
          requiredParam: {
            type: 'string',
            description: 'Description for the model',
          },
          optionalParam: {
            type: 'number',
            description: 'Optional parameter',
          },
        },
        required: ['requiredParam'],
      },
    );
  }

  protected override validateToolParamValues(
    params: MyToolParams,
  ): string | null {
    // Add custom validation
    if (params.requiredParam.length === 0) {
      return 'requiredParam must not be empty';
    }
    return null;
  }

  protected createInvocation(
    params: MyToolParams,
  ): ToolInvocation<MyToolParams, ToolResult> {
    return new MyToolInvocation(this.config, params);
  }
}
```

### Step 2: Add Tool Names

Update `packages/core/src/tools/tool-names.ts`:

```typescript theme={null}
export class ToolNames {
  // ... existing tools ...
  static readonly MY_TOOL = 'my_tool';
}

export class ToolDisplayNames {
  // ... existing tools ...
  static readonly MY_TOOL = 'MyTool';
}
```

### Step 3: Register Tool

Register in `packages/core/src/config/config.ts`:

```typescript theme={null}
private initializeTools(): void {
  // ... existing tools ...
  
  // Add your tool
  const myTool = new MyTool(this);
  this.toolRegistry.registerTool(myTool);
}
```

### Step 4: Add Tests

Create `packages/core/src/tools/my-tool.test.ts`:

```typescript theme={null}
import { describe, it, expect, beforeEach } from 'vitest';
import { MyTool } from './my-tool.js';
import type { Config } from '../config/config.js';

describe('MyTool', () => {
  let tool: MyTool;
  let mockConfig: Config;

  beforeEach(() => {
    mockConfig = {} as Config;  // Mock as needed
    tool = new MyTool(mockConfig);
  });

  it('should create invocation', () => {
    const params = { requiredParam: 'test' };
    const invocation = tool.invoke(params);
    expect(invocation).toBeDefined();
  });

  it('should validate parameters', async () => {
    const params = { requiredParam: '' };
    const result = await tool.invoke(params).execute(
      new AbortController().signal,
    );
    expect(result.error).toBeDefined();
  });

  it('should execute successfully', async () => {
    const params = { requiredParam: 'test' };
    const result = await tool.invoke(params).execute(
      new AbortController().signal,
    );
    expect(result.llmContent).toContain('Success');
  });
});
```

## Advanced Features

### Confirmation Dialogs

For tools that need user approval:

```typescript theme={null}
class MyToolInvocation extends BaseToolInvocation<MyToolParams, ToolResult> {
  override async shouldConfirmExecute(
    signal: AbortSignal,
  ): Promise<ToolCallConfirmationDetails | false> {
    // Return false for no confirmation needed
    if (this.isSafeOperation()) {
      return false;
    }

    // Return confirmation details
    return {
      type: 'exec',
      title: 'Confirm Dangerous Operation',
      command: this.params.command,
      onConfirm: async (outcome: ToolConfirmationOutcome) => {
        if (outcome === ToolConfirmationOutcome.ProceedAlways) {
          // User chose "Always allow"
          this.addToAllowlist();
        }
      },
    };
  }
}
```

### Progress Updates

For long-running operations:

```typescript theme={null}
async execute(
  signal: AbortSignal,
  updateOutput?: (output: ToolResultDisplay) => void,
): Promise<ToolResult> {
  for (let i = 0; i < steps.length; i++) {
    // Update UI with progress
    if (updateOutput) {
      updateOutput({
        status: `Step ${i + 1}/${steps.length}`,
        progress: i / steps.length,
      });
    }

    await this.executeStep(steps[i], signal);
  }

  return { llmContent: 'Complete' };
}
```

### File Path Validation

Common pattern for file-based tools:

```typescript theme={null}
protected override validateToolParamValues(
  params: MyToolParams,
): string | null {
  const filePath = params.path;

  // Check if absolute path
  if (!path.isAbsolute(filePath)) {
    return `Path must be absolute: ${filePath}`;
  }

  // Check if within workspace
  const workspaceContext = this.config.getWorkspaceContext();
  if (!workspaceContext.isPathWithinWorkspace(filePath)) {
    return `Path must be within workspace: ${filePath}`;
  }

  // Check against .qwenignore
  const fileService = this.config.getFileService();
  if (fileService.shouldQwenIgnoreFile(filePath)) {
    return `Path is ignored by .qwenignore: ${filePath}`;
  }

  return null;
}
```

## Built-in Tools

Qwen Code includes these built-in tools:

### File System Tools

* `read_file` - Read file contents
* `write_file` - Write to files
* `edit` - Edit files with string replacement
* `glob` - Find files by pattern
* `grep_search` - Search file contents
* `list_directory` - List directory contents

See [File System Tools](./tools/file-system.mdx)

### Execution Tools

* `run_shell_command` - Execute shell commands

See [Shell Tool](./tools/shell.mdx)

### Delegation Tools

* `task` - Delegate to subagents

See [Task Tool](./tools/task.mdx)

### Memory Tools

* `save_memory` - Save long-term memories

See [Memory Tool](./tools/memory.mdx)

### Integration Tools

* MCP tools (dynamically loaded)
* LSP tools (language server integration)
* Web fetch and search

## Tool Registry

The `ToolRegistry` manages all available tools:

```typescript theme={null}
// packages/core/src/tools/tool-registry.ts
export class ToolRegistry {
  private tools = new Map<string, BaseTool>();

  registerTool(tool: BaseTool): void {
    this.tools.set(tool.name, tool);
  }

  getTool(name: string): BaseTool | undefined {
    return this.tools.get(name);
  }

  getAllTools(): BaseTool[] {
    return Array.from(this.tools.values());
  }

  getToolsForModel(): FunctionDeclaration[] {
    return this.getAllTools().map(tool => ({
      name: tool.name,
      description: tool.description,
      parameters: tool.schema,
    }));
  }
}
```

## Best Practices

1. **Clear Descriptions:** Write clear, detailed descriptions for the model
2. **Validation:** Validate all parameters thoroughly
3. **Error Handling:** Provide helpful error messages
4. **Testing:** Write comprehensive tests
5. **Documentation:** Document parameters and behavior
6. **Security:** Validate paths, sanitize inputs
7. **Performance:** Optimize for common cases
8. **Telemetry:** Log usage for analytics (respecting privacy)

## Next Steps

* [Subagents System](./subagents-system.mdx) - Task delegation
* [File System Tools Reference](./tools/file-system.mdx)
* [Shell Tool Reference](./tools/shell.mdx)
* [Task Tool Reference](./tools/task.mdx)
