mcpField
T
tools

tools

github.com/assistant-ui开发296.3k 次安装1 天前更新
代码仓库

关于 tools

assistant-ui tools 为开发者提供编写模型可调用工具及其界面的工具包系统。编译器将 `"use generative"` 文件拆分为服务端和客户端构建,并支持人工交互与 MCP 工具包。

主要功能

  1. 01编译生成服务端和客户端工具包
  2. 02支持后端、前端、人工及 Provider 工具
  3. 03渲染工具调用和流式状态
  4. 04支持人工输入与审批门控
  5. 05支持 MCP 工具包和应用

适用场景

  1. 01为聊天模型定义工具
  2. 02在聊天界面呈现工具调用
  3. 03因人工输入或审批暂停运行

MCP 相关

工具包可通过 `AISDKToolkit` 和 `defineMcpToolkit` 打开 MCP 服务。该技能也介绍了如何渲染 MCP App 的 `ui://` 小部件。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/assistant-ui/skills --skill tools

安装后在 Claude Code 中输入/tools确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
human-in-loop.md
legacy-component-apis.md
mcp-apps.md
mcp-server.md
multi-agent.md
tool-ui.md
toolkits.md
webmcp.md

详细介绍

assistant-ui 工具

请始终查阅 assistant-ui.com/llms.txt,以获取最新 API。

工具是模型可以调用的具名能力。在 assistant-ui 中,你需要在工具包中声明工具。工具包是一个映射,其键是模型可见的工具名称,值则包含架构、执行器和渲染器。受支持的编写方式是使用由构建插件编译的 "use generative" 文件,该插件会将一个文件拆分为服务端构建(架构和后端执行器)与客户端构建(架构、渲染器和浏览器执行器)。

参考资料

编写模型

1. 添加构建插件

没有编译器,该指令不会产生任何作用。

import { withAui } from "@assistant-ui/next";

export default withAui({
  /* your Next config */
});

对于 Vite 和 TanStack Start,请将 @assistant-ui/vite 中的 aui() 添加到 plugins;对于 Expo 和原生 React Native,请使用 @assistant-ui/metro 中的 withAui 包装 Metro 配置。这三种方式都接受一个 aui 选项对象,详见 toolkits.md。

2. 编写工具包
"use generative";

import { defineToolkit } from "@assistant-ui/react";
import { z } from "zod";

export default defineToolkit({
  get_weather: {
    description: "Get current weather for a location.",
    parameters: z.object({
      location: z.string().describe("City name or zip code"),
      unit: z.enum(["celsius", "fahrenheit"]).default("celsius"),
    }),
    execute: async ({ location, unit }) => {
      "use client";
      return fetchWeatherAPI(location, unit);
    },
    render: ({ args, result }) =>
      result ? (
        <div>
          {result.temperature} {args.unit}
        </div>
      ) : (
        <div>Fetching weather for {args.location}</div>
      ),
  },
});
3. 在客户端挂载
"use client";

import { AssistantRuntimeProvider, AuiConfig, Tools } from "@assistant-ui/react";
import { useChatRuntime } from "@assistant-ui/ai-sdk";
import toolkit from "./toolkit";

export function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
  const runtime = useChatRuntime();
  const config = AuiConfig({ tools: Tools({ toolkit }) });
  return (
    <AssistantRuntimeProvider runtime={runtime} config={config}>
      {children}
    </AssistantRuntimeProvider>
  );
}

如果只想将工具包作用域限定在树的一部分,可以用 <AuiProvider extends={aui} config={config}> 包装该子树,并使用 const aui = useAui()。useChatRuntime() 默认指向 /api/chat。

4. 将工具提供给模型

在路由处理程序中,同一个导入会解析为服务端构建。

import { AISDKToolkit } from "@assistant-ui/ai-sdk";
import { streamText, convertToModelMessages } from "ai";
import { openai } from "@ai-sdk/openai";
import toolkit from "../../toolkit";

const aiToolkit = new AISDKToolkit({ toolkit });

export async function POST(req: Request) {
  const { messages, system, tools } = await req.json();

  const result = streamText({
    model: openai("gpt-5.6-luna"),
    system,
    messages: await convertToModelMessages(messages),
    tools: await aiToolkit.tools({ frontend: tools }),
  });

  return result.toUIMessageStreamResponse();
}

AISDKToolkit.tools() 会向模型注册工具包中的所有工具;如果服务端构建包含后端 execute,则会将其接入;它还会合并客户端在请求正文中上传的前端工具,并启动工具包展开包含的任何 MCP 服务。服务端 execute 的优先级高于同名的已上传条目。

工具类型

类型根据 execute 推断,并写回为 type。你不会在 "use generative" 文件中编写 type。

你编写的 execute推断出的类型服务端构建保留客户端构建保留
普通的 async () => ...backendschema 和 execute,并由 server-only 保护schema 和 render
async () => { "use client"; ... }frontend仅 schemaschema、execute 以及 render 或 renderText
humanTool()human仅 schemaschema 和 render
stubTool()frontend,执行器在运行时提供仅 schemaschema 以及 render 或 renderText
providerTool({ ... })providerschema 和 provider 配置schema 和 provider 配置
externalTool()backend,定义在其他位置省略type: "backend" 以及 render 或 renderText

编译器会在构建时强制要求每个工具都声明 execute,frontend 工具声明 render 或 renderText,human 工具声明 render。humanTool() 和 stubTool() 没有运行时实现,执行到它们时会抛出异常,这意味着该文件从未被编译;externalTool() 同样是一个编译时标记。

渲染工具调用

render 接收作为 ToolCallMessagePartProps 的实时调用。

字段类型说明
argsTArgs已解析的参数;流式传输期间可能不完整
argsTextstring原始 JSON,可能不完整
resultTResult | undefined调用有结果后才会存在
isErrorboolean | undefined结果是否表示失败
statusToolCallMessagePartStatusrunning、complete、带有 reason 的 incomplete,或带有 reason: "tool-calls" | "interrupt" 的 requires-action
toolName、toolCallIdstring模型可见的名称,以及此次调用的稳定 ID
timingToolCallTiming | undefined记录时的实际开始和完成时间
interrupt{ type: "human"; payload: unknown } | undefined来自前端执行器的暂停状态 human() 请求
approvalobject | undefined服务端门控:id、approved?、options?、optionId?、resolution?
addResult(result) => void在 UI 中完成 human 工具
resume(payload: unknown) => void响应 interrupt
respondToApproval(response: ToolApprovalResponse) => Promise<void>响应审批门控

如果要显示单行状态而不是组件,请设置 renderText,并提供 running 和 complete 值;每个值可以是字符串,也可以是 ({ args, result }) 的函数。在条目上设置 display: "standalone",使 UI 保持在折叠的工具组之外。没有渲染器的工具会回退到 ToolFallback 元素。

审批关卡

某些运行时会暂停服务,并发出客户端必须在工具运行前答复的审批请求。AI SDK v7 运行时会为调用级别 toolApproval 选项中列出的每个工具发出审批请求。

import { useState } from "react";
import { defineToolkit, type ToolApprovalResponse } from "@assistant-ui/react";

const toolkit = defineToolkit({
  deploy: {
    type: "backend",
    render: ({ args, approval, respondToApproval, result }) => {
      const [error, setError] = useState<string | null>(null);

      const answer = async (response: ToolApprovalResponse) => {
        setError(null);
        try {
          await respondToApproval(response);
        } catch (failure) {
          setError(failure instanceof Error ? failure.message : String(failure));
        }
      };

      if (approval?.approved === undefined) {
        if (approval?.isAutomatic) return <p>Auto approved by policy</p>;
        return (
          <div>
            <p>Approve deploy to {args.target}?</p>
            <button onClick={() => void answer({ approved: true })}>Approve</button>
            <button onClick={() => void answer({ approved: false, reason: "user denied" })}>
              Deny
            </button>
            {error && <p role="alert">{error}</p>}
          </div>
        );
      }

      if (approval?.approved === false) {
        return <p>Denied{approval.reason ? `: ${approval.reason}` : ""}</p>;
      }
      return result === undefined ? <p>Approved, running</p> : <p>Deployed</p>;
    },
  },
});

approval.approved 有三种状态。undefined 表示关卡处于开放状态,且这是唯一可以调用 respondToApproval 的状态。true 表示已记录为允许,服务正在生成结果。false 表示已记录为拒绝;运行时会记录错误结果,并公开 approval.reason。当服务端策略而非用户授予审批时,approval.isAutomatic 为 true,此时应显示徽章,而不是按钮。

respondToApproval 会返回一个 promise:运行时接受答复后,该 promise 会兑现;无法记录答复时(例如关卡已过期或答复被拒绝),则会拒绝。应等待它完成后再禁用控件,这样答复被拒绝时,请求仍可重试。toolApprovalAcceptsText(approval) 会报告请求是否接受自由格式答复(单独接受或与选项同时接受),以便渲染器判断是否需要提供文本框。完整的选项、问题和处理结果接口见 human-in-loop.md。

人工工具

人工工具没有执行器:运行会暂停,直到渲染器提供结果。

select_date: {
  description: "Ask the user to select a date.",
  parameters: z.object({ prompt: z.string() }),
  execute: humanTool(),
  render: ({ args, result, addResult }) => {
    if (result) return <p>Selected {result.date}</p>;
    return <DatePicker prompt={args.prompt} onChange={(date) => addResult({ date })} />;
  },
},

准确调用一次 addResult。当用户直接提供工具结果时,使用人工工具;当后端负责执行操作、只需要获得许可时,则使用审批关卡。

常见问题

humanTool() 或 stubTool() 在运行时抛出错误

  • 文件未经过编译器处理。添加构建插件,并确保 "use generative" 是文件的第一行。

工具 UI 从未渲染

  • 工具包键必须与模型可见的工具名称完全一致,包括任何 MCP prefix。
  • 必须挂载工具包:将 const config = AuiConfig({ tools: Tools({ toolkit }) }) 作为 config 传给 provider。请从模块作用域或 useMemo 传入稳定的工具包。

模型从未获知前端工具或人工工具

  • 客户端构建会跳过上传这些 schema,因为它假定你的后端导入了同一文件的服务端构建版本。如果你没有自己的后端,请使用 aui: { backendless: true } 进行编译。

前端工具的结果从未传递给模型

  • 使用来自 ai 的 sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls 配置运行时;对于审批关卡,还需配置 lastAssistantMessageIsCompleteWithApprovalResponses。

往返传递的结果忽略了 toModelOutput

  • 将工具注册表同时传给 convertToModelMessages(messages, { tools }) 和 streamText。

构建时出现工具名称警告

  • 名称重复表示两个展开片段定义了相同的键,而对象展开会保留后者;请重命名其中一个条目。无法通过 makeTool() 处理的工具来自不透明的工厂调用:请将其写成内联对象,或展开编译器可见的 defineToolkit(...) 或 defineMcpToolkit(...) 片段。

respondToApproval 被拒绝

  • 只有当 approval.approved 为 undefined 时才可调用。对于未声明 display: "text" 或 allowFreeform 的请求,提供 text 答复会抛出错误;未知的 optionId 也会导致错误。

MCP 连接不断累积

  • 将 AISDKToolkit 保留在模块作用域,以便客户端跨请求复用连接池,并在 onFinish 中调用 aiToolkit.close()。

相关技能

  • elements —— 样式化的 ToolFallback 和 ToolGroup 文件及其余目录内容
  • generative-ui —— 模型使用你提供的词汇组合而成的 UI
  • react-mcp —— 终端用户在浏览器中添加并进行身份验证的 MCP 服务
  • runtime —— 工具包接入的 useChatRuntime 和 AI SDK 路由
  • copilots —— interactables,即可由模型编辑的应用状态方案,是存根工具的替代方案
  • update —— 将旧版工具代码迁移到工具包

评论

支持 Markdown