内容
# MCP — 模型上下文协议服务器框架,用于 SWI-Prolog
本软件包提供了一个用于编写模型上下文协议(Model Context Protocol)服务器的框架,适用于 SWI-Prolog,以及一组现成的服务器模块,供 LLM 编码助手使用。这些模块可用于内省 Prolog 代码,驱动一个真正的 SWI 顶层作为“目标”,以及从配置的目录中读取源代码/ git 历史记录。
该框架建立在 `library(json_rpc_server)` 和 `library(json_schema)` 之上,这两个库是 SWI-Prolog 自带的。每个工具的参数验证都通过 JSON Schema 层进行处理。
## 状态
实验性。实现了 **2024-11-05** 版本的 MCP 规范。
传输方式:
- **stdio** — `library(mcp/stdio)`;基于 stdin/stdout 的换行符分隔的 JSON-RPC,这是官方 `mcp` CLI 和 Claude Code 的本地服务器配置所使用的格式。
- **HTTP + SSE** — `library(mcp/http)`;用于服务器发起通知的长寿命 SSE 流,以及用于客户端请求的 POST。会话通过 `Mcp-Session-Id` HTTP 头进行标识。适用于在 `www.swi-prolog.org` 上托管(在反向代理后面处理 TLS 和身份验证)。
## MCP 功能
- 工具(`tools/list`、`tools/call`、`notifications/tools/list_changed`)
- 资源(`resources/list`、`resources/read`、`resources/templates/list`)
- 提示(`prompts/list`、`prompts/get`)
进度通知(`notifications/progress` 和请求中的 `params._meta.progressToken`)尚未实现。工具调用回复单个缓冲的 JSON-RPC 响应;没有调用内的流式传输。在 HTTP 传输中,`mcp_notify/2` 路径仅到达持有 `GET /sse` 流的客户端 — 流式 HTTP `POST /mcp` 回复作为 `application/json` 发送,而不是作为 SSE 事件流,因此工具无法在其自己的响应中交错进度事件。
## 编写服务器
一个最小的 _hello_ 服务器:
```prolog
:- module(hello_server, []).
:- use_module(library(mcp/server)).
:- use_module(library(mcp/stdio)).
:- mcp_server_info(#{name:"hello", version:"0.1.0"}).
:- mcp_tool(echo,
"Echo the input message",
#{ properties: #{ msg: #{type: string} },
required: [msg]
}).
echo(#{msg: M}, M).
:- initialization(mcp_stdio_run, main).
```
通过 `swipl hello_server.pl` 运行它。服务器通过 stdin/stdout 进行 MCP 通信。
`mcp_tool/3` 是唯一的注册基元:一个名称,一行描述给 LLM 显示,以及参数的 JSON Schema。匹配的 Prolog 谓词是 `Name(+Args, -Result)`,其中 `Args` 是验证的输入字典,`Result` 是字符串(自动包装为文本内容项)、字典(单个内容项)或内容项列表。
## 包含的服务器模块
每个模块都可以通过 `library(mcp/servers/<name>)` 使用 `:- use_module/1` 加载。它们都通过相同的多文件 `tool/4` 谓词注册工具,因此单个服务器可以组合多个模块,并将它们呈现为一个面向客户端的表面。
### `library(mcp/servers/prolog_help)`
SWI-Prolog 内省,用于 Prolog 代码的 LLM 编码助手。工具:
| 工具 | 返回内容 |
| ----------------------------- | ------------------------------------------------------------------------------ |
| `predicate_help` | `Name/Arity` 的手册条目作为纯文本。 |
| `apropos` | 按谓词名称或摘要搜索手册;评分,限制。 |
| `predicate_listing` | 已定义谓词的子句(反编译或原始源)。 |
| `predicate_info` | 谓词的原始 `predicate_property/2` 快照。 |
| `predicate_property_summary` | 相同的精选分组视图 — 模块 / 位置 / 类型 / 修饰符。 |
| `pldoc_signature` | PlDoc 模式声明和确定性(`is det`/`semidet`/…)+ `:- meta_predicate`。 |
| `file_xref` | `.pl` 文件的交叉引用摘要(定义 / 导出 / 调用 / 使用)。 |
| `file_check` | 交叉引用文件并报告警告 / 错误和未定义调用站点。 |
| `module_exports` | 导出模块,附带每项导出的单行摘要。 |
| `file_autoload` | 计算文件需要的 `:- autoload/2`(或 `use_module/2`)指令块。 |
| `predicate_callers` | 遍历源根目录,找到谓词的所有调用站点。 |
| `pack_info` | 已安装软件包的元数据。 |
| `locate_source` | 谓词的源位置(s)— Prolog 子句 * 和 C 内置函数。 |
| `explain` | 术语的 `library(explain)` 快照:类型,定义,引用。 |
| `manual_section` | 通过锚点(`sec:<id>`)获取手册章节的正文。 |
还注册了一个 `prolog://help/{name}/{arity}` 资源模板,以便客户端更喜欢通过资源而不是工具获取帮助。
### `library(mcp/servers/source)`
从命名根目录读取源代码。每个工具都需要一个 `root` 名称(`source_roots` 工具列出已配置的根目录);相对路径与根目录连接并验证,以确保在其中(无 `..` 转义)。根目录通过设置 `mcp_source:swipl_devel_root` (内置的 `"swipl-devel"` 根目录)或通过多文件 `mcp_server_source:source_root/2` 子句添加。
| 工具 | 返回内容 |
| ---------------- | --------------------------------------------------------------------- |
| `source_roots` | 配置的根目录,附带绝对路径和(git)标签。 |
| `source_list` | 根目录的子目录中的文件,附带可选通配符和文件大小。 |
| `source_read` | 带行号的文件(或行范围),字节数限制。 |
| `source_search` | 根目录下的递归 POSIX-ERE grep。 |
### `library(mcp/servers/git)`
对作为 git 工作树的源根目录进行只读 git 查询。重用上述源根目录配置。所有工具都使用 `git -C <root> <args>` 调用 `git` 二进制文件;工作树从未写入。
| 工具 | 返回内容 |
| ------------ | ------------------------------------------------------------ |
| `git_log` | 最近的提交,可选限制为路径或 `--since`。 |
| `git_blame` | 文件范围的逐行作者信息。 |
| `git_show` | 提交(消息 + 差异)或提交时的文件。 |
| `git_diff` | 两个引用之间的差异,可选路径。 |
| `git_grep` | 跟踪文件的 grep,可选在引用或递归子模块中。 |
### `library(mcp/servers/swipl_kernel)`
用于处理 SWI-Prolog C 内核的专用工具。内核根目录是设置 `mcp_swipl_kernel:c_source_root` (默认为 `/home/jan/src/swipl-devel/src`;在启动时使用 `set_setting/2` 覆盖)。
| 工具 | 返回内容 |
| ------------------- | -------------------------------------------------------------- |
| `c_grep` | 递归 POSIX-ERE grep,仅限于 `*.c` 和 `*.h`。 |
| `c_function_defs` | C 文件中的函数定义,包括 `PRED_IMPL(...)`。 |
### `library(mcp/servers/target_toplevel)`
驱动一个子 `swipl` 进程作为 LLM 编码循环的“目标”:向其中加载文件,评估目标,对其运行分析器/覆盖率工具,而不污染 MCP 服务器进程。代理使用 `prolog/mcp/target_toplevel/mcp_target.pl` 中的小型仪器文件启动子进程,该文件用魔术标记包装顶层输出,以便父进程可以解复用来自纯 stdout 的事件。
目标通过字符串名称标识;可以同时存在多个目标。
| 工具 | 功能 |
| ------------------- | --------------------------------------------------------------------- |
| `target_start` | 启动一个子 `swipl`,可选地预加载文件。 |
| `target_stop` | 终止命名的目标。 |
| `target_status` | 列出正在运行的目标。 |
| `target_send` | 发送 Prolog 目标(无尾句点),返回直到下一个提示的输出。 |
| `target_load` | 目标中的 `make/0`,或 `consult(File)`。 |
| `target_check` | 运行 `library(check)` 一致性检查 — 默认 `check/0`,或选定的子集。 |
| `target_input` | 向等待输入的目标发送原始文本。 |
| `target_profile` | 在目标中运行 `profile/1`;深入到一个谓词的调用树中。 |
| `target_coverage` | 在目标中运行 `coverage/1`;深入到一个文件的子句中。 |
### `library(mcp/servers/dev_reload)`
单个工具,`mcp_reload_self`:在 MCP 服务器进程中运行 `make/0` 以拾取框架/工具模块的源代码编辑,而无需重启服务器。在此之后发送 `notifications/tools/list_changed`,以便新的 `mcp_tool/3` 声明在无需重新连接 `/mcp` 的情况下变得可见。
适用于开发人员的编辑测试循环;请勿在公共部署中启用(它授予 LLM 重载任意 Prolog 源代码的权限,该代码在服务器进程中运行)。
## 示例
`examples/` 下的可运行脚本:
- `hello.pl` — 基于 stdio 的最小可行单工具服务器。
- `prolog_help.pl` — 完整的辅助套件(`prolog_help` + `swipl_kernel` + `target_toplevel` + `source` + `git` + `dev_reload`)基于 stdio。
- `prolog_help_http.pl` — 相同的套件在 `localhost:8080` 上使用 HTTP + SSE。
- `target_toplevel.pl` — 仅限目标驱动工具,有用的作为“代理循环”伴侣。
## 测试
PLUnit 烟雾测试位于 `tests/` 下。运行整个矩阵:
make check
测试文件匹配 `tests/test_*.pl`,会自动拾取。矩阵是并行安全的(`make -j N check`);每个套件都在自己的 `swipl` 进程中运行。
一些套件需要一个真实的源树来查询(`test_source`、`test_git`)。根路径取自 Prolog 标志 `mcp_test_swipl_devel_root`,默认为 `~/src/swipl-devel`。在命令行上使用以下命令覆盖:
make check SWIPL_FLAGS=-Dmcp_test_swipl_devel_root=/path/to/swipl-devel
根目录缺失(或不是 git 工作树,对于 `test_git`)的套件会干净地跳过。
## 许可证
BSD-2-Clause。