进阶
给 dsh 接入 MCP server 或自定义 tool
两条路径让模型获得真实能力:用 ctx.tools.register 注册自定义 tool,或把现成 MCP server 包成 dsh 插件。
Tool 与 command:谁调用谁
command 由用户在 UI 里触发;tool 由模型自己决定需要时调用。想让 agent 自主行动,就注册 tool。
路径 A — 注册自定义 tool
最小注册节点:
ctx.tools.register({
name: 'read_local_file',
description: 'Read a text file from the workspace',
parameters: {
type: 'object',
properties: {
path: { type: 'string', description: 'Relative path' },
},
required: ['path'],
},
async execute(args) {
try {
return { ok: true, content: await fs.readFile(args.path, 'utf8') };
} catch (err) {
// 错误必须回传给模型,不能吞掉
return { ok: false, error: String(err) };
}
},
});三个细节很关键:清晰的 description(它决定模型选不选这个 tool)、严格的 JSON Schema parameters、以及结构化错误返回(让模型能恢复并重试)。
路径 B — 包一个现成 MCP server
MCP (Model Context Protocol) server 通过 stdio 或 HTTP 暴露工具。与其重新实现,不如在插件里启动 server 并把它的工具注册到 ctx:
import { startMcpClient } from '@dsh/mcp';
export async function apply(ctx) {
const client = await startMcpClient({
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-filesystem', './'],
});
const tools = await client.listTools();
for (const tool of tools) {
ctx.tools.register({
name: `mcp_${tool.name}`,
description: tool.description,
parameters: tool.inputSchema,
execute: (args) => client.callTool(tool.name, args),
});
}
}远程 server 只需传 url 代替 command/args。无论哪种方式,模型的工具箱里都多了一个工具,协议层完全不用模型操心。
端到端验证
启动 dsh,开一个会话,让模型做一件需要工具的事(比如"package.json 里有什么?")。观察模型自主调用 read_local_file(或 mcp_filesystem_*),然后基于结果作答。
相关拆解
真实插件在更大规模上展示了同一模式:modlens 把结构化视觉证据作为 tool 结果返回,dsh-at-file 用 Codex 风格的 @file 引用扩展输入通道。