feat: 工具层及 mcp 大重构 (#252)

* feat: 第一版大重构

* fix: 修复类型问题

* chore: 更新版本到 1.3.2

* Add brave as alternative WebSearchTool

* fix: 修正顺序

* fix: 修复对穷鬼模式的 auto dream 和 session memory 越过

* feat: 穷鬼模式去除 session-summary

* feat: 创建 builtin-tools 包,搬运所有工具实现

将 src/tools/ 下的全部 60 个工具目录迁移至 packages/builtin-tools/src/tools/,
内部导入路径已更新为 src/ alias 模式。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: 更新 src/ 中所有工具引用至 builtin-tools 包,删除 src/tools/

- src/tools.ts 及 178 个 src/ 文件的 import 路径从 ./tools/ 改为 builtin-tools/tools/
- 删除 src/tools/ 整个目录(已迁移至 packages/builtin-tools/)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: 添加 builtin-tools 路径别名至 tsconfig,更新 bun.lock

- tsconfig.json 新增 builtin-tools/* 和 builtin-tools 路径映射
- 新增 packages/builtin-tools/src 至 include

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: 为 builtin-tools、mcp-client、agent-tools 添加 @claude-code-best 作用域前缀

所有包名及 import 路径统一添加 @claude-code-best/ 前缀:
- builtin-tools → @claude-code-best/builtin-tools
- mcp-client → @claude-code-best/mcp-client
- agent-tools → @claude-code-best/agent-tools

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: 修复 node 环境没有 bun 的问题

---------

Co-authored-by: Eric-Guo <eric.guocz@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
claude-code-best
2026-04-13 09:52:05 +08:00
committed by GitHub
parent bbb8b613a9
commit 2fb1c9dcd8
559 changed files with 9346 additions and 1837 deletions

View File

@@ -0,0 +1,123 @@
import { z } from 'zod/v4'
import {
ensureConnectedClient,
fetchResourcesForClient,
} from 'src/services/mcp/client.js'
import { buildTool, type ToolDef } from 'src/Tool.js'
import { errorMessage } from 'src/utils/errors.js'
import { lazySchema } from 'src/utils/lazySchema.js'
import { logMCPError } from 'src/utils/log.js'
import { jsonStringify } from 'src/utils/slowOperations.js'
import { isOutputLineTruncated } from 'src/utils/terminal.js'
import { DESCRIPTION, LIST_MCP_RESOURCES_TOOL_NAME, PROMPT } from './prompt.js'
import { renderToolResultMessage, renderToolUseMessage } from './UI.js'
const inputSchema = lazySchema(() =>
z.object({
server: z
.string()
.optional()
.describe('Optional server name to filter resources by'),
}),
)
type InputSchema = ReturnType<typeof inputSchema>
const outputSchema = lazySchema(() =>
z.array(
z.object({
uri: z.string().describe('Resource URI'),
name: z.string().describe('Resource name'),
mimeType: z.string().optional().describe('MIME type of the resource'),
description: z.string().optional().describe('Resource description'),
server: z.string().describe('Server that provides this resource'),
}),
),
)
type OutputSchema = ReturnType<typeof outputSchema>
export type Output = z.infer<OutputSchema>
export const ListMcpResourcesTool = buildTool({
isConcurrencySafe() {
return true
},
isReadOnly() {
return true
},
toAutoClassifierInput(input) {
return input.server ?? ''
},
shouldDefer: true,
name: LIST_MCP_RESOURCES_TOOL_NAME,
searchHint: 'list resources from connected MCP servers',
maxResultSizeChars: 100_000,
async description() {
return DESCRIPTION
},
async prompt() {
return PROMPT
},
get inputSchema(): InputSchema {
return inputSchema()
},
get outputSchema(): OutputSchema {
return outputSchema()
},
async call(input, { options: { mcpClients } }) {
const { server: targetServer } = input
const clientsToProcess = targetServer
? mcpClients.filter(client => client.name === targetServer)
: mcpClients
if (targetServer && clientsToProcess.length === 0) {
throw new Error(
`Server "${targetServer}" not found. Available servers: ${mcpClients.map(c => c.name).join(', ')}`,
)
}
// fetchResourcesForClient is LRU-cached (by server name) and already
// warm from startup prefetch. Cache is invalidated on onclose and on
// resources/list_changed notifications, so results are never stale.
// ensureConnectedClient is a no-op when healthy (memoize hit), but after
// onclose it returns a fresh connection so the re-fetch succeeds.
const results = await Promise.all(
clientsToProcess.map(async client => {
if (client.type !== 'connected') return []
try {
const fresh = await ensureConnectedClient(client)
return await fetchResourcesForClient(fresh)
} catch (error) {
// One server's reconnect failure shouldn't sink the whole result.
logMCPError(client.name, errorMessage(error))
return []
}
}),
)
return {
data: results.flat(),
}
},
renderToolUseMessage,
userFacingName: () => 'listMcpResources',
renderToolResultMessage,
isResultTruncated(output: Output): boolean {
return isOutputLineTruncated(jsonStringify(output))
},
mapToolResultToToolResultBlockParam(content, toolUseID) {
if (!content || content.length === 0) {
return {
tool_use_id: toolUseID,
type: 'tool_result',
content:
'No resources found. MCP servers may still provide tools even if they have no resources.',
}
}
return {
tool_use_id: toolUseID,
type: 'tool_result',
content: jsonStringify(content),
}
},
} satisfies ToolDef<InputSchema, Output>)

View File

@@ -0,0 +1,35 @@
import * as React from 'react'
import { MessageResponse } from 'src/components/MessageResponse.js'
import { OutputLine } from 'src/components/shell/OutputLine.js'
import { Text } from '@anthropic/ink'
import type { ToolProgressData } from 'src/Tool.js'
import type { ProgressMessage } from 'src/types/message.js'
import { jsonStringify } from 'src/utils/slowOperations.js'
import type { Output } from './ListMcpResourcesTool.js'
export function renderToolUseMessage(
input: Partial<{ server?: string }>,
): React.ReactNode {
return input.server
? `List MCP resources from server "${input.server}"`
: `List all MCP resources`
}
export function renderToolResultMessage(
output: Output,
_progressMessagesForMessage: ProgressMessage<ToolProgressData>[],
{ verbose }: { verbose: boolean },
): React.ReactNode {
if (!output || output.length === 0) {
return (
<MessageResponse height={1}>
<Text dimColor>(No resources found)</Text>
</MessageResponse>
)
}
// eslint-disable-next-line no-restricted-syntax -- human-facing UI, not tool_result
const formattedOutput = jsonStringify(output, null, 2)
return <OutputLine content={formattedOutput} verbose={verbose} />
}

View File

@@ -0,0 +1,20 @@
export const LIST_MCP_RESOURCES_TOOL_NAME = 'ListMcpResourcesTool'
export const DESCRIPTION = `
Lists available resources from configured MCP servers.
Each resource object includes a 'server' field indicating which server it's from.
Usage examples:
- List all resources from all servers: \`listMcpResources\`
- List resources from a specific server: \`listMcpResources({ server: "myserver" })\`
`
export const PROMPT = `
List available resources from configured MCP servers.
Each returned resource will include all standard MCP resource fields plus a 'server' field
indicating which server the resource belongs to.
Parameters:
- server (optional): The name of a specific MCP server to get resources from. If not provided,
resources from all servers will be returned.
`