进阶玩法

开发自定义工具

写一个 MCP server 暴露你的工具 — 10 行代码起步。

两种打包方式

Kition 的自定义工具有两条路径:内嵌工具包(TypeScript bundle 丢进 .kition/tools/,启动时由内置运行时加载)或者独立的 MCP server(任意语言,通过 stdio / SSE 暴露)。内嵌方式适合轻量、纯函数式的工具;MCP 方式适合需要状态、长连接、或者要在多个 Vault 间复用的场景。

两种方式在 Agent 看来都是同一类调用 — 工具 schema 一致,权限模型一致,Hooks 同样可以拦截。区别只在分发:内嵌工具跟 Vault 走,MCP server 跟机器走。

从用户体验上说,内嵌工具是“写一次就能用”的最短路径;MCP server 则是当工具需要被多个 Vault、多台机器、甚至多个团队共享时的正解。

内嵌工具骨架

把下面这个文件存为 .kition/tools/word-count.ts,Kition 启动时会自动扫描、类型检查并注册。默认导出必须是 defineTool({...}),Kition 用 schema 做参数校验、用 run 做执行。

ctx 参数提供了日志、Vault 文件系统、当前 session 元信息、以及 secrets 的访问接口 — 不用自己 require 任何 Kition 内部模块。

import { defineTool } from '@kition/tools'
import { z } from 'zod'

export default defineTool({
  name: 'word_count',
  title: 'Word count',
  description: 'Count words in a string, optionally splitting on punctuation.',
  input: z.object({
    text: z.string().min(1),
    splitOnPunctuation: z.boolean().default(false),
  }),
  output: z.object({
    words: z.number(),
    chars: z.number(),
  }),
  async run({ input, ctx }) {
    const re = input.splitOnPunctuation ? /[\s\p{P}]+/u : /\s+/u
    const words = input.text.trim().split(re).filter(Boolean).length
    ctx.log.info('counted', { words })
    return { words, chars: input.text.length }
  },
})

访问 Vault 与 secrets

复杂一点的工具往往要读 Vault 的笔记、调外部 API、或者持久化点东西。ctx 上挂着对应的 API — 文件读写走 ctx.vault,secrets 走 ctx.secrets(来自 Keychain / DPAPI / libsecret),HTTP 走 ctx.fetch(自动应用 provider 路由与重试策略)。

import { defineTool } from '@kition/tools'
import { z } from 'zod'

export default defineTool({
  name: 'jira_create_issue',
  title: 'Create Jira issue',
  description: 'Create a Jira issue from the current note.',
  input: z.object({
    project: z.string(),
    summary: z.string(),
    notePath: z.string(),
  }),
  async run({ input, ctx }) {
    const body = await ctx.vault.readFile(input.notePath)
    const token = await ctx.secrets.get('jira.api_token')
    const res = await ctx.fetch('https://acme.atlassian.net/rest/api/3/issue', {
      method: 'POST',
      headers: {
        authorization: `Basic ${token}`,
        'content-type': 'application/json',
      },
      body: JSON.stringify({
        fields: {
          project: { key: input.project },
          summary: input.summary,
          description: body,
          issuetype: { name: 'Task' },
        },
      }),
    })
    if (!res.ok) throw new Error(`jira ${res.status}`)
    const json = await res.json() as { key: string }
    return { key: json.key, url: `https://acme.atlassian.net/browse/${json.key}` }
  },
})

独立 MCP server

如果工具需要外部 API、数据库连接、或者你想用 Python / Go / Rust 写,就走 MCP。任何符合 Model Context Protocol 的 server 都可以挂载。下面这个最小 TypeScript 例子展示了 tools/listtools/call 两个核心 handler。

生产环境我们建议用 stdio 传输 — 启动快、隔离好、不占端口。SSE 传输适合 server 跑在远端的场景(比如内部工具服务)。

import { Server } from '@modelcontextprotocol/sdk/server'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio'

const server = new Server({ name: 'word-count', version: '0.1.0' })

server.setRequestHandler('tools/list', () => ({
  tools: [{
    name: 'word_count',
    description: 'Count words in a string',
    inputSchema: {
      type: 'object',
      properties: { text: { type: 'string' } },
      required: ['text'],
    },
  }],
}))

server.setRequestHandler('tools/call', async (req) => {
  const text = req.params.arguments.text as string
  const words = text.trim().split(/\s+/).filter(Boolean).length
  return { content: [{ type: 'text', text: String(words) }] }
})

await server.connect(new StdioServerTransport())

注册到 Kition

内嵌工具不用配置 — 文件存在就生效。MCP server 要在 .kition/mcp.json 里加一条 stdio 配置,重启 Agent 之后工具会出现在 /tools 列表里。

想要团队成员都用同一套工具?把 .kition/mcp.json 提交到 Vault 的 git 仓库 — 拉下来即可,所有 MCP 配置都是声明式的。

{
  "servers": {
    "word-count": {
      "type": "stdio",
      "command": "node",
      "args": ["./scripts/word-count-server.js"],
      "env": { "LOG_LEVEL": "info" }
    },
    "internal-tools": {
      "type": "sse",
      "url": "https://tools.internal.acme.com/mcp",
      "headers": { "authorization": "Bearer ${env:INTERNAL_TOOLS_TOKEN}" }
    }
  }
}

权限与 Hooks

  • 工具默认是“ask” — 首次调用要用户确认,可在设置里改成 allow / deny
  • 通过 permissions.tools[name] 做精细控制,比如 network.fetch 限定域名
  • Hooks 可以在 pre_tool_use / post_tool_use 拦截,做合规、审计、脱敏
  • 失败时返回非零退出码或 throw — Agent 会看到完整 stack
  • 所有工具调用自动记录到 .kition/audit.jsonl,含输入 / 输出 / 时长

调试技巧

  • kition tools list 打印当前 Vault 已加载的工具与来源
  • kition tools call word_count --json '{"text":"hi there"}' 不走 Agent 直接调用
  • MCP server 的 stderr 会进 ~/Library/Logs/Kition/mcp-*.log
  • 类型不对 / schema 不合法时启动日志里会有清晰的 zod / ajv 错误
  • 在工具里 ctx.log.debug(...)KITION_LOG_LEVEL=debug 后即可看到

相关文档

下载 Kition

本地优先的 AI 工作空间。Markdown 文档、结构化数据表、AI Agent,全部跑在你自己的电脑上。