内容
# okf-mcp
一个符合 OKF 规范的文档查询服务器。
## 概述
`okf-mcp` 作为一个 stdio MCP 服务器,与现有的 MCP 主机一起运行。在每次工具调用时,它会递归扫描工作目录,构建一个内存索引,索引来自于它找到的每个符合规范的 markdown 文件的 YAML 前置信息,并提供六个工具(`list_tags`、`list_docs`、`get_doc`、`validate_doc`、`get_index`、`get_log`),以便代理可以查找文档,而无需自己遍历文件树。
索引在每次调用时都会重建,因此新添加或更新的文件始终会被反映。无需配置文件,无需数据库,无需文件监视器 —— 只要代码库中的文件及其前置信息。
**前置信息质量是一个功能性需求。** 缺失或模糊的 `description` 意味着返回错误的文件或根本没有文件。 将 `title`、`description` 和 `tags` 视为功能的一部分,而不是可选的元数据。
## 安装
从源代码构建:
```sh
go build ./cmd/okf-mcp
```
或直接安装:
```sh
go install github.com/nrkno/plattform-okf-mcp/cmd/okf-mcp@latest
```
## 预提交钩子
在 `.githooks/pre-commit` 中包含了一个 git 预提交钩子。在每次提交时,它会验证所有 OKF 文档,捕获前置信息和结构错误。
安装钩子:
```sh
git config core.hooksPath .githooks
```
钩子需要 `okf-mcp` 位于 `PATH` 上(参见上面的[安装](#安装))。如果未找到 `okf-mcp`,则提交将被阻止,并附有安装提示。
## 使用
`okf-mcp` 必须从代码库根目录运行 —— 工作目录是扫描根目录。它支持 stdio MCP(JSON-RPC via stdin/stdout)。
将其添加到您的 MCP 主机配置中。对于 Claude Desktop(`~/Library/Application Support/Claude/claude_desktop_config.json`):
```json
{
"mcpServers": {
"okf-mcp": {
"command": "okf-mcp",
"args": []
}
}
}
```
在启动时,`okf-mcp` 会打印到 stderr:
```
okf-mcp: serving /path/to/repo
```
该行确认正在扫描哪个目录。如果看到错误的路径,请在主机配置中调整工作目录。
## 权限
MCP 主机需要一个明确的工具调用允许列表,然后代理才能调用它们。权限字符串格式取决于主机。
### opencode
在 opencode 中,工具权限遵循模式 `mcp__<server-key>__<tool-name>`,其中服务器密钥与 `opencode.json` 的 `mcp` 块中使用的密钥匹配。使用服务器密钥 `okf-mcp`(如上文[使用](#使用)部分所示),权限字符串为:
```
mcp__okf-mcp__list_tags
mcp__okf-mcp__list_docs
mcp__okf-mcp__get_doc
```
一个完整的 `opencode.json` 片段,将服务器注册和权限允许列表连接起来:
```json
{
"mcp": {
"okf-mcp": {
"type": "local",
"command": "okf-mcp",
"args": []
}
},
"permissions": {
"allow": [
"mcp__okf-mcp__list_tags",
"mcp__okf-mcp__list_docs",
"mcp__okf-mcp__get_doc"
]
}
}
```
#### 通过指令自动注册
`okf-mcp` 使用 MCP `instructions` 字段在每次会话启动时将用法指南直接注入到代理的系统中。opencode 从服务器的 `initialize` 响应中读取此字段,并自动包含它 —— 无需显式代理指令或 AGENTS.md 条目。代理将知道首先调用 `list_tags` 和 `get_doc` 以检索内容,而无需上述服务器注册和权限之外的其他配置。
### 其他主机
Claude Desktop(在上文[使用](#使用)部分中涵盖)不使用单独的允许列表 —— 注册服务器就足够了。其他 MCP 主机(Cursor、Zed、VS Code 扩展等)各自都有自己的权限模型;请参阅它们自己的文档以获取正确的格式。
## OKF 前置信息要求
仅当文件具有有效的 YAML 前置信息且包含 `type` 字段时,文件才会被索引。
### 必需字段
| 字段 | 必需 | 备注 |
|-------|----------|-------|
| `type` | **是** | 没有此字段的文件将被静默跳过 |
| `title` | 否 | 强烈建议 —— 用于 `get_doc` 中的评分 |
| `description` | 否 | 强烈建议 —— 用于 `get_doc` 中的评分 |
| `tags` | 否 | 强烈建议 —— 启用标签过滤搜索 |
### 前置信息块示例
```yaml
---
type: Playbook
title: 身份验证设置
description: 如何为内部服务配置 OAuth2
tags:
- auth
- setup
- go
---
```
### 跳过规则
当以下任何条件适用时,文件将被跳过:
- 文件不以 `---` YAML 前置信息开头
- 前置信息缺少 `type` 字段
- 文件名是 `index.md` 或 `log.md`(OKF 保留文件 —— 始终跳过)
- 文件位于隐藏目录中(`.git`、`.opencode` 等 —— 永远不会遍历)
当 `title` 或 `description` 缺失时,会向 stderr 写入警告,但文件仍会被索引:
```
okf-mcp: WARN: docs/auth.md: missing title
okf-mcp: WARN: docs/auth.md: missing description
```
## 工具
### `list_tags`
返回所有索引文档中所有唯一标签的排序 JSON 数组。
无参数。
**示例响应:**
```json
["api", "go", "observability", "setup"]
```
---
### `list_docs`
返回每个索引文档及其元数据的 JSON 数组。不包括文件内容。
无参数。
**示例响应:**
```json
[
{
"title": "用户指南",
"description": "新用户的端到端演练",
"tags": ["api", "setup"],
"file_path": "docs/guide.md"
}
]
```
---
### `get_doc`
为主题查询找到最佳匹配文档,并返回其完整内容和元数据。
**参数:**
| 参数 | 类型 | 必需 | 默认值 | 描述 |
|-------|------|----------|---------|-------------|
| `topic` | 字符串 | 是 | — | 要搜索的主题或标题 |
| `tags` | 字符串数组 | 否 | — | 可选的标签过滤器 |
| `match` | 字符串 | 否 | `"and"` | 标签过滤模式:`"and"`(所有标签必须匹配)或 `"or"`(任何标签匹配) |
**评分:** 查询被标记并与每个文档匹配。标题匹配贡献 `3×` 每个标记,标签匹配 `2×`,描述匹配 `1×`。返回单个得分最高的文档。如果得分相同,则按 `file_path` 字母顺序排序。
**示例响应:**
```json
{
"content": "# 用户指南\n...",
"file_path": "docs/guide.md",
"tags": ["api", "setup"],
"title": "用户指南",
"description": "新用户的端到端演练"
}
```
**错误情况:**
| 情况 | 错误消息 |
|-----------|---------------|
| 在 cwd 中未找到符合 OKF 规范的文档 | `index is empty: no OKF-conformant markdown docs found in cwd` |
| 索引中有文档,但没有一个与主题/标签匹配 | `no document matched topic "<topic>" with tags [...]` |
| 无效的 `match` 值 | `invalid match value "<value>": must be "and" or "or"` |
## 开发
```sh
go test ./...
go test -race ./...
go vet ./...
go build ./cmd/okf-mcp
```
```sh
# 静态二进制文件(推荐用于容器和可移植分发):
CGO_ENABLED=0 go build ./cmd/okf-mcp
```