给智能体的工具(MCP)
卡片可以赋予智能体新的能力。在清单中声明一个工具并在卡片中实现它,所有通过箭头与卡片相连的智能体都会在工具列表中看到它—— 经由应用本来就为智能体运行的 MCP 服务器(Claude Code、Codex、Qwen Code)。不需要任何权限:用户画出的箭头就是同意。
声明
"tools": [
{
"name": "run_tests",
"title": "Run the tests",
"description": "Runs the project's tests in the connected terminal and returns the failing tests with their messages, or 'All tests passed'.",
"inputSchema": {
"type": "object",
"properties": {
"filter": { "type": "string", "maxLength": 200, "description": "Only tests whose name contains this" }
}
},
"timeoutMs": 120000
}
]智能体看到的名称是 <card name with - → _>_<tool>——名为 test-radar 的卡片对应 test_radar_run_tests——
参数是你的 inputSchema,外加应用添加的可选参数 card(id 或名称,用于在连了多张副本时指定其中一张)。
描述在交给模型时会加上前缀 [Custom card "<displayName>" by <author>, community code]。如果两个卡片包会产生同一个名称,先安装的那个保留它,后安装的会加上 _2、_3。
应用在安装时会拒绝的名称:与 NeuroSquad 自有工具(canvas_spawn_card、terminal_send_keys、browser_navigate……)相同的对外名称、
任何包含 __ 的名称(为用户安装的 MCP 服务器保留),以及属于内置工具族的卡片 name,例如 telegram、squad、ports 或 mcp
(参见保留名)。新增工具或改变工具描述措辞的更新会再次征求用户同意。
描述是写给模型看的:这个工具做什么、何时使用、返回什么。参数要少且有约束(enum、maxLength、minimum……);应用会在你的卡片看到之前校验它们。
实现
import { toolError } from '@neurosquad/card-sdk'
card.tools.handle<{ filter?: string }>('run_tests', async ({ filter }, call) => {
call.progress('running the tests…') // shown on the arrow
const terminal = (await card.agents.list()).find((a) => a.kind === 'shell' && a.connected)
if (!terminal) return toolError('Connect a terminal card to the Test radar card first.')
const result = await card.terminals.run(terminal.id, `npm test -- ${filter ?? ''}`, {
timeoutMs: 110_000
})
if (call.signal.aborted) return // the agent gave up; nothing is sent
return result.exitCode === 0 ? 'All tests passed' : result.output
})处理函数会收到校验过的参数和一个 call:
call. | 说明 |
|---|---|
callId | 本次调用的 id。 |
tool | 清单中的工具名。 |
agent | 发起调用的智能体的 { id, name }。 |
deadline | 应用放弃等待的时间戳(毫秒)。 |
signal | 一个 AbortSignal,在取消或到达截止时间时中止。 |
progress(message) | 工作期间在箭头上显示的一行短文字(≤ 200);限流为每秒 4 次。 |
你的返回值就是工具结果:
| 返回 | 智能体得到 |
|---|---|
| 字符串 | 这段文本 |
undefined | OK |
| 其他任意 JSON 值 | 格式化后的 JSON 文本 |
toolText(text) | 文本(与返回字符串相同) |
toolImage(base64, 'image/png', caption?) | 一张图片,以及作为文本的说明文字 |
toolError(message) | 一个失败结果(isError: true),模型可以读到并作出反应 |
ToolResultPayload | 原样:{ content: [{ type: 'text', text } or { type: 'image', data, mimeType }], isError? },1–16 个部分 |
抛出异常也会把错误返回给智能体,内容为异常消息(并在卡片日志中写一条警告)。结果 ≤ 1 MB。
时间
- 默认超时 30 秒,或工具的
timeoutMs(最长 120 秒)。到达截止时间时,应用告诉智能体调用超时,并向你的卡片发送tools.cancel——call.signal会中止,你之后返回的任何内容都会被丢弃。 - 在你的处理函数注册之前到达的调用(例如卡片仍在加载数据时)会等待
handle(),直到截止时间。 - 调用页面已暂停的卡片会唤醒它(最多 10 秒);如果它所在的工作区没有打开,智能体会被告知去打开包含这张卡片的工作区。
- 每次调用都会显示在箭头上:箭头标签显示工具名和你的进度文字,并记入箭头日志。
开关工具
await card.tools.setEnabled('run_tests', false) // hidden from agents (e.g. until the user signs in)
await card.tools.setEnabled('run_tests', true)这会作用于你卡片的所有副本。
配合 React
import { useTool } from '@neurosquad/card-sdk/react'
export function Scratchpad({ text }: { text: string }) {
useTool('read_scratchpad', () => text || '(empty)') // the latest `text` is always used
return <pre>{text}</pre>
}工具结果会直接进入智能体的上下文。你返回的任何来自网页、文件或用户的内容都应视为不可信:注明来源、保持简短,
并且绝不要让智能体能调用的工具悄悄做出破坏性操作——先用 card.ui.confirm 询问用户。