Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
6665afc
{tool/claudecode, docs/mkdocs}: add Claude Code toolset
Flash-LHR Apr 20, 2026
91ca004
tool/claudecode: tidy module dependencies
Flash-LHR Apr 20, 2026
a120560
tool/claudecode: raise test coverage
Flash-LHR Apr 20, 2026
be2b295
tool/claudecode: stabilize and extend tests
Flash-LHR Apr 20, 2026
66744f7
tool/claudecode: cover remaining helper branches
Flash-LHR Apr 20, 2026
eeecd8d
tool/claudecode: make ripgrep path test environment independent
Flash-LHR Apr 20, 2026
332aba9
tool/claudecode: cover remaining patch branches
Flash-LHR Apr 20, 2026
d982a17
tool/claudecode: cover remaining helper paths
Flash-LHR Apr 20, 2026
b15cf0f
tool/claudecode: cover common and read helpers
Flash-LHR Apr 20, 2026
2be7fa3
tool/claudecode: cover search helpers
Flash-LHR Apr 20, 2026
ae04f5b
tool/claudecode: cover helper branches for patch checks
Flash-LHR Apr 20, 2026
d4a9d66
{tool/claudecode, docs/mkdocs}: adopt focused review fixes
Flash-LHR Apr 20, 2026
ed32bcb
tool/claudecode: tighten web search and notebook edge cases
Flash-LHR Apr 21, 2026
385abdc
tool/claudecode: align google search offset semantics
Flash-LHR Apr 21, 2026
fd3cd53
docs
Flash-LHR Apr 23, 2026
6f1556c
remove tool search
Flash-LHR Apr 23, 2026
2b4e018
del ask user
Flash-LHR Apr 23, 2026
68413e4
tool/claudecode: add coverage for web fetch and bash branches
Flash-LHR Apr 23, 2026
1a3bcb4
tool/claudecode: add coverage for additional web fetch branches
Flash-LHR Apr 23, 2026
d4cbcb6
tool/claudecode: stabilize web fetch coverage tests
Flash-LHR Apr 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions docs/mkdocs/en/tool.md
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,70 @@ searchTool := duckduckgo.NewTool(
)
```

### Claude Code ToolSet

`tool/claudecode` provides a code-oriented ToolSet that exposes a Claude Code-style tool surface inside the framework. It covers file editing, repository search, command execution, and web retrieval, and can be attached directly to `LLMAgent` or other runtimes. If your goal is to invoke the local Claude Code CLI and consume its execution trace and tool events, see the [Claude Code Agent guide](claudecode.md).

By default, `claudecode` exposes a core set of workflow tools: `Bash`, `TaskStop`, `TaskOutput`, `Read`, `Glob`, `Grep`, `WebFetch`, and `WebSearch`. When read-only mode is disabled, it also exposes `Write`, `Edit`, and `NotebookEdit`.

The following table lists the tools currently exposed by `claudecode`:

| Tool | Description |
| --- | --- |
| `Bash` | Executes local shell commands. |
| `TaskStop` | Stops a background task started by `Bash`. |
| `TaskOutput` | Reads the current or final output of a background task. |
| `Read` | Reads file contents. |
| `Glob` | Finds files by path pattern. |
| `Grep` | Searches repository content. |
| `WebFetch` | Fetches the content of a specific URL. |
| `WebSearch` | Performs an open web search. |
| `Write` | Creates a file or overwrites a file with complete contents. Only exposed when read-only mode is disabled. |
| `Edit` | Performs targeted replacement in an existing text file. Only exposed when read-only mode is disabled. |
| `NotebookEdit` | Edits `.ipynb` files at the cell level. Only exposed when read-only mode is disabled. |

#### Basic Usage

```go
import (
"log"

"trpc.group/trpc-go/trpc-agent-go/agent/llmagent"
"trpc.group/trpc-go/trpc-agent-go/tool"
"trpc.group/trpc-go/trpc-agent-go/tool/claudecode"
)

toolSet, err := claudecode.NewToolSet(
claudecode.WithBaseDir("."),
)
if err != nil {
log.Fatal(err)
}
defer toolSet.Close()

agent := llmagent.New(
"claude-style-agent",
llmagent.WithToolSets([]tool.ToolSet{toolSet}),
)
```

`llmagent.WithToolSets(...)` attaches these tools as a ToolSet. Calling `Tools()` returns the flattened list of individual tools.

#### Common Options

The main `tool/claudecode` options focus on working directory, read-only mode, and web behavior:

| Option | Description |
| --- | --- |
| `WithName(name)` | Overrides the ToolSet name. The default name is `claudecode`. |
| `WithBaseDir(dir)` | Sets the base directory used by file, search, and command execution tools. |
| `WithReadOnly(readOnly)` | Removes `Write`, `Edit`, and `NotebookEdit` when enabled. |
| `WithMaxFileSize(size)` | Limits the maximum readable file size. |
| `WithWebFetchOptions(opts)` | Configures domain policy, timeout, and content handling for `WebFetch`. |
| `WithWebSearchOptions(opts)` | Configures backend, paging, and request options for `WebSearch`. |

`WithBaseDir` defines the working scope for `Read`, `Write`, `Edit`, `Glob`, and `Grep`, and also determines the default working directory for `Bash`. When read-only mode is enabled, the toolset keeps only read, search, command, and web capabilities. When read-only mode is disabled, it also exposes `Write`, `Edit`, and `NotebookEdit`.

## MCP Tools

MCP (Model Context Protocol) is an open protocol that standardizes how applications provide context to LLMs. MCP tools are based on JSON-RPC 2.0 and provide standardized integration with external services for Agents.
Expand Down
63 changes: 63 additions & 0 deletions docs/mkdocs/zh/tool.md
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,69 @@ searchTool := duckduckgo.NewTool(
)
```

### Claude Code ToolSet

`tool/claudecode` 提供了一组面向代码工作的 ToolSet,用于在框架内部暴露与 Claude Code 接近的工具接口。它覆盖文件读写、代码检索、命令执行和网页获取等能力,可以直接挂接到 `LLMAgent` 或其他运行时。如果你的目标是调用本地 Claude Code CLI,并消费 CLI 的执行轨迹与工具事件,请参考 [Claude Code Agent 使用指南](claudecode.md)。

从能力组成上看,`claudecode` 默认会提供一组代码工作流工具,包括 `Bash`、`TaskStop`、`TaskOutput`、`Read`、`Glob`、`Grep`、`WebFetch` 和 `WebSearch`。在非只读模式下,还会额外提供 `Write`、`Edit` 和 `NotebookEdit`。

下表列出了当前 `claudecode` 工具集中的主要工具及其用途:

| 工具名 | 说明 |
| --- | --- |
| `Bash` | 执行本地 Shell 命令。 |
| `TaskStop` | 停止由 `Bash` 以后台模式启动的任务。 |
| `TaskOutput` | 读取后台任务的当前输出或最终输出。 |
| `Read` | 读取文件内容。 |
| `Glob` | 按路径模式查找文件。 |
| `Grep` | 按内容搜索仓库。 |
| `WebFetch` | 抓取指定 URL 的页面内容。 |
| `WebSearch` | 进行开放式网页搜索。 |
| `Write` | 创建文件或用完整内容覆盖文件,仅在非只读模式下暴露。 |
| `Edit` | 对已有文本文件做局部替换,仅在非只读模式下暴露。 |
| `NotebookEdit` | 按 cell 粒度编辑 `.ipynb` 文件,仅在非只读模式下暴露。 |

#### 基本用法

```go
import (
"log"
"trpc.group/trpc-go/trpc-agent-go/agent/llmagent"
"trpc.group/trpc-go/trpc-agent-go/tool"
"trpc.group/trpc-go/trpc-agent-go/tool/claudecode"
)

toolSet, err := claudecode.NewToolSet(
claudecode.WithBaseDir("."),
)
if err != nil {
log.Fatal(err)
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
defer toolSet.Close()

agent := llmagent.New(
"claude-style-agent",
llmagent.WithToolSets([]tool.ToolSet{toolSet}),
)
```

`llmagent.WithToolSets(...)` 会以 ToolSet 形式接入这组工具;如果调用 `Tools()`,则会得到展开后的单个工具列表。

#### 常用配置

`tool/claudecode` 的配置重点围绕工作目录、只读模式和 Web 能力展开:

| Option | 说明 |
| --- | --- |
| `WithName(name)` | 覆盖 ToolSet 名称,默认值为 `claudecode`。 |
| `WithBaseDir(dir)` | 指定工具集的基础目录。文件、检索和命令执行都会以此为基准。 |
| `WithReadOnly(readOnly)` | 启用只读模式后,不再暴露 `Write`、`Edit`、`NotebookEdit`。 |
| `WithMaxFileSize(size)` | 限制单个文件可读取的最大尺寸。 |
| `WithWebFetchOptions(opts)` | 配置 `WebFetch` 的域名策略、超时与内容处理方式。 |
| `WithWebSearchOptions(opts)` | 配置 `WebSearch` 的后端、分页参数与请求选项。 |

`WithBaseDir` 定义了 `Read`、`Write`、`Edit`、`Glob`、`Grep` 等文件相关工具的工作范围,也决定了 `Bash` 的默认执行目录。启用只读模式后,工具集只保留读取、检索、命令执行和 Web 相关能力;关闭只读模式后,会额外暴露 `Write`、`Edit` 与 `NotebookEdit`。

## MCP Tools 协议工具

MCP(Model Context Protocol)是一个开放协议,标准化了应用程序向 LLM 提供上下文的方式。MCP 工具基于 JSON-RPC 2.0 协议,为 Agent 提供了与外部服务的标准化集成能力。
Expand Down
167 changes: 167 additions & 0 deletions tool/claudecode/bash.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
//
// Tencent is pleased to support the open source community by making
// trpc-agent-go available.
//
// Copyright (C) 2025 Tencent. All rights reserved.
//
// trpc-agent-go is licensed under the Apache License Version 2.0.
//

package claudecode

import (
"context"
"fmt"
"os"
"path/filepath"
"strconv"
"time"

"github.com/google/uuid"
"trpc.group/trpc-go/trpc-agent-go/tool"
"trpc.group/trpc-go/trpc-agent-go/tool/function"
)

func newBashTool(runtime *runtime) (tool.Tool, error) {
return function.NewFunctionTool(
func(ctx context.Context, in bashInput) (bashOutput, error) {
if in.RunInBackground {
return runBackgroundCommand(runtime, in.Command)
}
return runForegroundCommand(ctx, runtime, in)
},
function.WithName(toolBash),
function.WithDescription(bashDescription()),
), nil
}

func runForegroundCommand(ctx context.Context, runtime *runtime, in bashInput) (bashOutput, error) {
timeoutMs := bashTimeout(in.Timeout)
start := time.Now()
runCtx, cancel := context.WithTimeout(ctx, time.Duration(timeoutMs)*time.Millisecond)
defer cancel()
result, err := runCapturedProcess(runCtx, runtime.currentBaseDir(), nil, "bash", "-lc", in.Command)
durationMs := time.Since(start).Milliseconds()
timedOut := errorsIsDeadlineExceeded(runCtx.Err())
exitCode := result.ExitCode
if err != nil && exitCode == 0 {
if timedOut {
exitCode = 124
} else {
exitCode = 1
}
}
stdout := string(result.Stdout)
stderr := string(result.Stderr)
return bashOutput{
Command: in.Command,
ExitCode: exitCode,
Stdout: stdout,
Stderr: stderr,
Output: joinOutput(stdout, stderr),
DurationMs: durationMs,
TimedOut: timedOut,
}, nil
}

func bashTimeout(timeout *int) int {
timeoutMs := defaultBashTimeoutMs
if raw := os.Getenv("BASH_DEFAULT_TIMEOUT_MS"); raw != "" {
if parsed, err := strconv.Atoi(raw); err == nil && parsed > 0 {
timeoutMs = parsed
}
}
if timeout != nil {
timeoutMs = *timeout
}
if timeoutMs <= 0 {
timeoutMs = defaultBashTimeoutMs
}
if timeoutMs > maxBashTimeoutMs {
timeoutMs = maxBashTimeoutMs
}
return timeoutMs
}

func runBackgroundCommand(runtime *runtime, command string) (bashOutput, error) {
taskID := uuid.NewString()
outputDir := filepath.Join(os.TempDir(), "trpc-agent-go-claudecode")
if err := os.MkdirAll(outputDir, 0o755); err != nil {
return bashOutput{}, err
}
outputPath := filepath.Join(outputDir, taskID+".log")
outputFile, err := os.Create(outputPath)
if err != nil {
return bashOutput{}, err
}
process, err := startProcess(runtime.currentBaseDir(), nil, outputFile, outputFile, "bash", "-lc", command)
if err != nil {
_ = outputFile.Close()
return bashOutput{}, err
}
runtime.taskState.mu.Lock()
runtime.taskState.tasks[taskID] = &backgroundTask{
ID: taskID,
Command: command,
Type: toolBash,
OutputPath: outputPath,
Process: process,
Status: "running",
}
runtime.taskState.mu.Unlock()
go func() {
state, waitErr := process.Wait()
_ = outputFile.Close()
runtime.taskState.mu.Lock()
task := runtime.taskState.tasks[taskID]
if task != nil && task.Status == "running" {
task.Status = backgroundTaskStatus(waitErr, state)
exitCode := backgroundTaskExitCode(waitErr, state)
task.ExitCode = &exitCode
}
runtime.taskState.mu.Unlock()
}()
return bashOutput{
Command: command,
ExitCode: 0,
Output: fmt.Sprintf("Command is running in the background. Read %s later to inspect the output.", outputPath),
BackgroundTaskID: taskID,
OutputPath: outputPath,
}, nil
}
Comment thread
Flash-LHR marked this conversation as resolved.

func backgroundTaskStatus(waitErr error, state *os.ProcessState) string {
if waitErr != nil {
return "exited"
}
if state == nil || !state.Success() {
return "exited"
}
return "completed"
}

func backgroundTaskExitCode(waitErr error, state *os.ProcessState) int {
if state != nil {
return state.ExitCode()
}
if waitErr != nil {
return 1
}
return 0
}

func errorsIsDeadlineExceeded(err error) bool {
return err == context.DeadlineExceeded
}

func bashDescription() string {
return fmt.Sprintf(`Execute a local shell command.

Usage:
- Use %s for shell-native tasks such as git, build, test, lint, package managers, and project scripts.
- Prefer dedicated tools when they fit better: use %s to read files, %s to create or overwrite files, %s for targeted text replacements, %s for notebook cell edits, %s for filename search, %s for repository content search, %s to fetch a specific URL, and %s for broad web discovery.
- NEVER use bash grep or rg for repository search when %s can answer the question.
- Commands run from the current workspace base directory.
- Use run_in_background for long-running commands that do not need an immediate result. Inspect them later with %s or stop them with %s.
- timeout is measured in milliseconds and is capped at %d ms.`, toolBash, toolRead, toolWrite, toolEdit, toolNotebookEdit, toolGlob, toolGrep, toolWebFetch, toolWebSearch, toolGrep, toolTaskOutput, toolTaskStop, maxBashTimeoutMs)
}
Loading
Loading