03. 工具、权限、Skills 与 MCP
这一章会把 Hadamard SDK 里“真正干活”的能力讲清楚:工具怎么接、权限怎么管、skills 怎么用、MCP 又处在什么位置。
1. 先分清:工具和 Skill 不是一回事
- 工具:负责直接执行动作,比如读写文件、搜索、委派任务、操作浏览器或桌面。
- Skill:更像一套预设工作方式,用来组织模型如何思考、如何执行、什么时候调用工具。
你可以把它理解成:
- 工具决定“能做什么”
- Skill 决定“怎么做这件事更稳、更像一个固定工作流”
2. Hadamard SDK 里有哪些工具
Hadamard SDK 当前可以组合这些工具来源:
- 你自己用
tool(...)定义的本地工具 createHadamardFileTools(...)生成的文件工具createHadamardComputerUseToolkit(...)生成的 computer-use 工具- 注册 named agents 后自动出现的
Task委派工具 - 通过 MCP 挂进来的外部工具
最常见的内置 clean 工具面包括:
ReadWriteEditGlobGrepTaskcomputer_*一组桌面/浏览器替代工具
3. 如何查看当前有哪些工具
const tools = await sdk.tools.listMetadata();
const catalog = await sdk.tools.getCatalog();
console.log(tools);
console.log(catalog.byCategory.file);
console.log(catalog.byCategory.computer);每个工具元数据会告诉你:
namedescriptionprovidercategoryreadOnlymutating
仓库示例:
4. 自定义本地工具
import { z } from 'zod';
import { createAgentSdk, tool } from 'actoviq-agent-sdk';
const addNumbers = tool(
{
name: 'add_numbers',
description: 'Add two numbers together.',
inputSchema: z.object({
a: z.number(),
b: z.number(),
}),
},
async ({ a, b }) => ({ sum: a + b }),
);
const sdk = await createAgentSdk({
tools: [addNumbers],
});5. Skills:Hadamard SDK 现在已经可以直接用
当前 Hadamard SDK 已经支持:
- bundled skills
- 自定义 skills
- 从
~/.hadamard/skills、.hadamard/skills自动加载 skills inline/fork两种运行模式
常用入口:
console.log(sdk.skills.listMetadata());
const debugResult = await sdk.runSkill(
'debug',
'请分析这个仓库在发布前最应该优先验证哪些内容。',
);
const session = await sdk.createSession({ title: 'Skill Demo' });
const rememberResult = await session.runSkill(
'remember',
'记住:发版前必须等待 CI 和 npm pack --dry-run 都通过。',
);注册自定义 skill:
import { createAgentSdk, skill } from 'actoviq-agent-sdk';
const sdk = await createAgentSdk({
skills: [
skill({
name: 'release-check',
description: '检查发布准备情况并总结阻塞项。',
prompt: 'You are executing the /release-check skill.\\n\\nTask:\\n$ARGUMENTS',
}),
],
});仓库示例:
6. Dream:长期记忆整合
Hadamard SDK 现在也已经有独立的 dream 能力,用来对最近若干会话做一次记忆整合。
常用入口:
const state = await sdk.dreamState();
console.log(state);
const session = await sdk.createSession({ title: 'Dream Demo' });
const dreamResult = await session.dream({
extraContext: '把最近关于发布流程、工具使用方式和稳定约束整理成长期记忆。',
});
console.log(dreamResult.result?.text);自动 dream 入口:
await sdk.memory.updateSettings({ autoDreamEnabled: true });
await sdk.maybeAutoDream({
currentSessionId: session.id,
background: true,
});仓库示例:
7. 权限、classifier、approver
如果你不希望 agent 任意调用工具,可以配权限层。
直接给规则
const sdk = await createAgentSdk({
permissions: [
{ toolName: 'Read', behavior: 'allow' },
{ toolName: 'Write', behavior: 'ask' },
],
});用 classifier 做自动判断
const sdk = await createAgentSdk({
classifier: ({ publicName, input }) => {
if (publicName === 'Write') {
return {
behavior: 'ask',
reason: `Write needs manual review: ${JSON.stringify(input)}`,
};
}
},
});用 approver 接管 ask
const sdk = await createAgentSdk({
approver: ({ publicName }) => {
if (publicName === 'Write') {
return { behavior: 'allow', reason: 'Approved for this run.' };
}
return { behavior: 'deny', reason: 'Unexpected tool.' };
},
});会话级权限模式和规则会持久化,并在恢复会话时继续生效:
await session.setPermissionContext({
mode: 'default',
permissions: [{ toolName: 'Bash', behavior: 'ask' }],
approver,
});
const restored = await sdk.resumeSession(session.id);
console.log(restored.permissionContext);只有可序列化的模式和规则会写入会话;classifier 与 approver 回调需要由当前进程重新绑定。bypassPermissions 仍会执行硬安全检查,acceptEdits 只自动允许文件编辑工具,不会自动允许任意 shell 命令;plan 会阻止未被更高优先级规则或 classifier 明确允许的变更型工具。
8. MCP 是干嘛的
MCP 的作用是把“外部工具服务器”接进 SDK。
例如:
import {
createAgentSdk,
stdioMcpServer,
} from 'actoviq-agent-sdk';
const sdk = await createAgentSdk({
mcpServers: [
stdioMcpServer({
name: 'filesystem',
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-filesystem', '.'],
}),
],
});9. clean 命令式 helper
Hadamard SDK 也提供命令式 helper:
console.log(sdk.slashCommands.listMetadata());
const contextResult = await sdk.slashCommands.run('context');
const memoryResult = await sdk.slashCommands.run('memory', {
sessionId: 'your-session-id',
});
const dreamResult = await sdk.slashCommands.run('dream', {
sessionId: 'your-session-id',
args: '把最近稳定的项目约束整理进长期记忆。',
});当前可用的 clean 命令替代包括:
contextcompactmemorydreamtoolsskillsagents
10. Plan 模式与 Goal 模式不是一回事
Plan 模式控制“这一阶段能不能产生修改”:
/plan进入后,Agent 可以调查代码、读取配置并写计划,但变更型工具受 Plan 权限边界约束。退出时展示计划并等待确认;没有确认,不应把计划当成实施授权。GUI、TUI 和 CLI 使用同一个命令定义与权限语义。
Goal 模式控制“多轮执行要持续追求什么目标”:
/goal 完成发布前检查并修复所有回归
/goal
/goal pause
/goal resume
/goal clearGoal 随 Session 保存。runtime 每轮只注入短目标上下文,并记录 token、turn、tool 使用和进度证据。为了避免模型在工作未完成时自行宣布成功,complete 必须由 UpdateGoal 携带运行证据;普通 UI 命令只能查看、创建、暂停、恢复或清除目标。
可以先用 Plan 模式形成获批方案,再用 Goal 约束长时间实施。Plan 结束不表示 Goal 完成,Goal 存在也不会自动授予写权限。
11. Managed Policy、OS Sandbox 与审计
权限决策和 OS 隔离是两层:
- permission policy 决定工具调用是
allow、ask还是deny。 - sandbox 在执行层限制读写根、网络和子进程;平台能力不足时会明确标记 degraded。
项目策略放在 .hadamard/policy.json,用户和主机策略分别放在 ~/.hadamard/policy/user.json、~/.hadamard/policy/host.json。高权威策略可以锁定设置:
{
"version": 1,
"revision": 1,
"scope": "project",
"settings": {
"model": "medium",
"sandbox": { "network": { "mode": "deny" } },
"plugins": {
"allowedPublishers": ["example.org"],
"allowedCapabilities": ["tools", "skills"]
}
},
"rules": [
{ "id": "no-shell", "effect": "deny", "tool": "Bash" }
],
"lockedSettings": ["model", "sandbox"],
"updatedAt": "2026-07-30T00:00:00.000Z"
}托管 deny 会在 bypassPermissions 之前生效。插件在安装、启用或信任前也会检查 registry、publisher 和 capability allowlist。Settings 会显示有效策略来源和锁定字段;决策审计写入独立的脱敏 append-only 日志。
12. Typed Hooks 与插件包
Typed Hooks 覆盖 Session、Turn、Model、Tool、Permission、Compact、Stop、Worktree 等生命周期事件,handler 可以是 command、prompt 或 HTTP。Hook 配置先做 schema 校验,再按稳定顺序执行;旧式三类 shell hook 仍通过兼容 adapter 工作。
插件命令在 GUI/TUI/CLI 使用同一语义:
/plugin search <query>
/plugin install <local-package-directory>
/plugin pin <plugin-id> <version>
/plugin enable <plugin-id>
/plugin trust <plugin-id>包 manifest 使用稳定 ID、SemVer、package-local entry、integrity、publisher、signature、capabilities 和 permissions。安装版本不可变,更新会安装新版本;符号链接、特殊文件、路径穿越和 integrity 不匹配会被拒绝。
13. 模块化 Runtime 的 ToolRegistry 与 ToolPolicy
tool(...)、permissions、MCP 和 clean helpers 属于 createAgentSdk() 的高层组合接口。模块化 Runtime 使用更小的 RuntimeTool contract:
import {
AgentRuntime,
ToolRegistry,
type RuntimeTool,
type ToolPolicy,
} from 'actoviq-agent-sdk/runtime';
const addNumbers: RuntimeTool<unknown, { a: number; b: number }, number> = {
descriptor: {
name: 'add_numbers',
description: 'Add two numbers.',
input: {
parse(value) {
const input = value as { a?: unknown; b?: unknown };
if (typeof input.a !== 'number' || typeof input.b !== 'number') {
throw new TypeError('a and b must be numbers');
}
return { a: input.a, b: input.b };
},
jsonSchema: {
type: 'object',
properties: {
a: { type: 'number' },
b: { type: 'number' },
},
required: ['a', 'b'],
additionalProperties: false,
},
},
behavior: {
effect: 'read',
},
},
execute: (_context, input) => input.a + input.b,
};
const policy: ToolPolicy = {
authorize({ tool }) {
return tool.behavior?.effect === 'read'
? { type: 'allow' }
: { type: 'deny', reason: 'Only read tools are allowed.' };
},
};
const tools = new ToolRegistry([addNumbers]);
const runtime = new AgentRuntime({
models,
tools,
toolPolicy: policy,
});其中 models 是前一章构造的 ModelRegistry。实际项目还应注意:
- 未声明
behavior.effect的工具按side-effect处理,不会默认当成只读。 effect只描述副作用语义;是否允许由ToolPolicy决定。- 需要人工确认时返回
interrupt,并为 runtime 配置 checkpoint store,之后使用 interruption decision 恢复。 - Runtime v2 不会自动扫描 MCP 或 skills。要继续使用自动发现和交互式权限 UI,选择
createAgentSdk()入口。
下一章: