Agda MCP 服务器
A. 模型上下文协议(MCP) 提供交互式Agda开发能力的服务器,支持如Claude Code等AI助手。这使得通过标准化协议实现AI辅助的证明开发、交互式定理证明以及Agda代码的探索成为可能。
特点/特性
- 持久化的REPL会话在命令之间保持Agda交互状态,保留目标、上下文和类型检查结果
- 24个交互式命令全面涵盖Agda交互操作,包括证明搜索、案例拆分和模块探索
- 自动文件持久化修改代码的命令(如给定、细化、分支情况、自动)会自动将更改持久化到磁盘
- HTTP传输符合标准的MCP服务器,支持HTTP/JSON-RPC传输
- 向后兼容补丁包含用于使mcp-server与Claude Code的HTTP传输兼容的补丁
- 类型安全集成利用Haskell的类型系统构建,以实现稳健的MCP协议处理
- 智能响应格式化默认情况下提供简洁且易于阅读的输出(大小减少约90%),并可选择启用完整的JSON模式
建筑
服务器在后台线程中运行一个持久的Agda读取-求值-打印循环(REPL),并通过通道进行通信:
- 命令是通过一个(接口/渠道)发送的
Chan CommandWithResponse - 响应通过(指定的)路径返回
MVar Response - 每个MCP工具调用都被转换为一个
IOTCM在持久化的REPL中输入并执行命令 - 目标、上下文和交互状态在命令之间保持持久
文件 编辑 持久化
当命令修改Agda代码时,更改会自动持久化到磁盘:
- 响应捕获REPL回调函数拦截了输入的(内容/命令)
Response值(例如。,Resp_GiveAction,Resp_MakeCase) - 智能提取在Agda的原生API中,编辑操作是在TCM单子上下文中提取的
- 针对特定类型的策略不同操作对应不同的编辑类型:
- ReplaceHole原地孔填充(给予,细化,自动) - ReplaceLine带有行插入(情况分拆)的结构编辑 - BatchEdits多个操作按逆序位置应用(自动全部)
- 位置感知的编辑操作保留文件结构并正确处理位置无效化
这种架构确保像“加载文件”→“获取目标”→“提供解决方案”这样的操作不仅能更新REPL(读取-求值-打印-循环)状态,还能将更改持久化到源文件中。
先决条件
- Nix(发音类似“尼克”) 启用碎片(或鳞片)功能
- Agda (由Nix环境自动提供)
- 克劳德·科德 或者另一个MCP客户端
安装
- 克隆仓库:
git clone https://github.com/faezs/agda-mcp.git
cd agda-mcp- 进入 Nix 开发 shell:
nix develop- 构建服务器:
cabal build- 运行服务器:
cabal run agda-mcp服务器在……启动 http://localhost:3000/mcp 默认情况下。
配置
克劳德·科德
添加到您的Claude Code MCP配置文件中(~/.config/claude-code/mcp.json):
{
"mcpServers": {
"agda-mcp": {
"transport": "http",
"url": "http://localhost:3000/mcp",
"command": "cabal",
"args": ["run", "agda-mcp"],
"cwd": "/path/to/agda-mcp"
}
}
}其他MCP客户端
服务器实现了MCP协议2025-06-18版本,并使用HTTP进行传输。请将您的客户端配置为:
- 连接到:
http://localhost:3000/mcp - 使用HTTP POST传输JSON-RPC 2.0
- 包括
Content-Type: application/json头球 - 可选地包含
MCP-Protocol-Version: 2025-06-18头球
可用工具
所有工具均遵循MCP命名约定(snake_case)。参数以JSON对象的形式提供。
1. agda_load
加载并检查Agda文件的类型。
参数:
file(字符串):指向Agda文件的绝对路径或相对路径
示例:
{
"name": "agda_load",
"arguments": {
"file": "/path/to/Example.agda"
}
}用例: 在执行任何其他操作之前,必须首先调用此操作。它会使用指定的文件初始化Agda交互会话。
______________________________________________________________________
2. agda_get_goals
列出当前加载文件中的所有目标/漏洞。
参数: 无
示例:
{
"name": "agda_get_goals",
"arguments": {}
}返回值: 文件中目标列表,包含其ID、类型和位置。
用例: 加载文件后,使用此功能查看需要证明或填写的内容。
______________________________________________________________________
3. agda_get_goal_type
获取特定目标所需的类型。
参数:
goalId(整数):目标的数字ID(从0开始)
示例:
{
"name": "agda_get_goal_type",
"arguments": {
"goalId": 0
}
}返回值: 预期应填充此目标的类型。
用例: 理解完成证明所需的表达式类型。
______________________________________________________________________
4. agda_get_context
获取特定目标处的上下文(可用变量及其类型)。
参数:
goalId(整数):目标的数字ID
示例:
{
"name": "agda_get_context",
"arguments": {
"goalId": 0
}
}返回值: 此目标作用域内的变量列表及其类型。
用例: 查看在证明中可用的变量和假设。
______________________________________________________________________
5. agda_give
用完整的表达填满一个目标/空白。
参数:
goalId(整数):要填充的目标的数字IDexpression(字符串):要使用的Agda表达式(例如,“zero”,“suc n”,“refl”)format(字符串,可选):“简洁”(默认)或“完整”
示例:
{
"name": "agda_give",
"arguments": {
"goalId": 0,
"expression": "refl"
}
}效果:
- 在REPL中实现目标
- 自动更新文件替换
{! !}带着表情 - 向大型语言模型(LLM)返回成功/失败状态
用例: 当你有了完整的解决方案时,就完成一个目标。文件会自动编辑。
______________________________________________________________________
6. agda_refine
使用构造函数或函数细化目标,引入新的子目标。
参数:
goalId(整数):要细化的目标的数字IDexpression(字符串):构造函数或函数名(例如,“suc”,“zero”,“_+_")format(字符串,可选):“简洁”(默认)或“完整”
示例:
{
"name": "agda_refine",
"arguments": {
"goalId": 0,
"expression": "suc"
}
}效果:
- 将构造函数/函数应用于目标
- 自动更新文件替换
{! !}使用应用构造器并新增孔洞 - 返回新的目标结构
用例: 通过应用一个构造器来为实现目标取得进展,该构造器会为其参数创建新的子目标。文件会自动根据细化内容进行编辑。
______________________________________________________________________
7. agda_case_split
通过变量的模式匹配来拆分目标。
参数:
goalId(整数):目标的数字IDvariable(字符串):要进行模式匹配的变量名称format(字符串,可选):“简洁”(默认)或“完整”
示例:
{
"name": "agda_case_split",
"arguments": {
"goalId": 0,
"variable": "n"
}
}效果:
- 为所有构造函数生成模式匹配子句
- 自动更新文件删除含孔的线条,插入多个子句
- 保持缩进
- 注在案例分支后应重新加载文件以查看新目标
用例: 对一个变量进行案例分析(例如,将一个自然数拆分为零和后继情况)。整行代码被替换为多个模式匹配子句。
______________________________________________________________________
8. agda_compute
在目标的上下文中规范化并显示一个表达式。
参数:
goalId(整数):目标的数字ID(用于上下文)expression(字符串):要规范化的表达式
示例:
{
"name": "agda_compute",
"arguments": {
"goalId": 0,
"expression": "2 + 2"
}
}返回值: 该表达式的规范化形式。
用例: 评估表达式以查看其简化形式。
______________________________________________________________________
9. agda_infer_type
在目标上下文中推断表达式的类型。
参数:
goalId(整数):目标的数字ID(用于上下文)expression(字符串):用于类型检查的表达式
示例:
{
"name": "agda_infer_type",
"arguments": {
"goalId": 0,
"expression": "suc zero"
}
}返回: 表达式的推断类型。
用例: 在使用表达式之前,先检查其类型。
______________________________________________________________________
10. agda_intro
使用intro策略引入变量。
参数:
goalId(整数):目标的数字ID
示例:
{
"name": "agda_intro",
"arguments": {
"goalId": 0
}
}用例: 自动引入Agda建议的lambda绑定变量或模式变量。
______________________________________________________________________
11. agda_why_in_scope
查找某个名称的文档和范围信息。
参数:
name(字符串):要查找的名称
示例:
{
"name": "agda_why_in_scope",
"arguments": {
"name": "suc"
}
}返回值: 关于名称定义位置及其为何处于作用域内的信息。
用例: 理解函数、类型或构造函数的起源和定义。
______________________________________________________________________
可用资源
资源将Agda信息作为可读的数据源进行暴露。它们使用基于URI的路由。
1. Goals
URI 模式: resource://goals/{file}
列出指定Agda文件中的所有目标。
2. GoalInfo
URI 模式: resource://goal_info/{file}/{id}
关于特定目标的详细信息,包括其类型、上下文和位置。
3. FileContext
URI 模式: resource://file_context/{file}
Agda 文件的整体上下文和范围信息。
示例工作流程
# 1. Start the server
cabal run agda-mcp
# 2. In Claude Code, load an Agda file
/mcp agda-mcp agda_load file="/path/to/Example.agda"
# 3. Get all goals in the file
/mcp agda-mcp agda_get_goals
# Output: 5 goals: ?0:Nat(10:12) ?1:Nat(11:12) ?2:Nat(15:12) ?3:Nat(16:12) ?4:A(20:18)
# 4. Check the type of goal 0
/mcp agda-mcp agda_get_goal_type goalId=0
# Output: ?0 : Nat
# 5. See available variables
/mcp agda-mcp agda_get_context goalId=0
# Output: Context:
# n : Nat
# 6. Fill the goal - FILE IS AUTOMATICALLY EDITED
/mcp agda-mcp agda_give goalId=0 expression="n"
# ✓ Filled ?0 with 'n'
# File now shows: zero + n = n
# 7. Case split on a variable - FILE IS AUTOMATICALLY EDITED
/mcp agda-mcp agda_case_split goalId=1 variable="m"
# Split ?1 into 2 clauses:
# suc zero + n = {! !}
# suc (suc m) + n = {! !}
# File now has 2 lines instead of 1
# 8. Reload to see new goals after case split
/mcp agda-mcp agda_load file="/path/to/Example.agda"
/mcp agda-mcp agda_get_goals
# Now shows updated goal structure文件持久化的实践应用
当你使用像这样的命令时 agda_give, agda_refine,或者 agda_case_split:
- ✅ 命令在REPL中执行
- ✅ 响应在JSON编码之前被捕获
- ✅ 文件编辑内容会被自动提取并应用
- ✅ JSON响应返回给大型语言模型(LLM)
- ✅ 更改立即持久化到磁盘
无需手动编辑文件! MCP服务器处理所有源代码的修改。
发展
项目结构
agda-mcp/
├── src/
│ ├── AgdaMCP/
│ │ ├── Types.hs # MCP tool and resource definitions
│ │ ├── Server.hs # MCP handlers, REPL integration, file edit extraction
│ │ ├── FileEdit.hs # File editing strategies (ReplaceHole, ReplaceLine, BatchEdits)
│ │ ├── Format.hs # Response formatting (Concise/Full modes)
│ │ └── Repl.hs # Persistent REPL adapter
│ └── Main.hs # Entry point
├── patches/
│ └── mcp-server-header-optional.patch # Compatibility patch
├── test/
│ ├── Example.agda # Sample Agda file for testing
│ ├── SearchTest.agda # Integration test with 1Lab library
│ └── PostulateTest.agda # Postulate detection tests
├── agda-mcp.cabal # Cabal package definition
├── flake.nix # Nix flake for reproducible builds
└── README.md # This file从源代码构建
# Enter development environment
nix develop
# Build
cabal build
# Run tests (TODO)
cabal test
# Install locally
cabal install在开发模式下运行
# Rebuild and run
cabal run agda-mcp
# With verbose output (TODO: add verbose flag)
cabal run agda-mcp -- --verbose故障排除
服务器无法启动
问题: 端口3000已被使用
bind: address already in use (Address already in use)解决方案: 终止现有进程或更改端口(当前硬编码为3000)。
来自Claude代码的连接被拒绝
问题: Failed to reconnect to agda-mcp
可能的原因:
- 服务器未运行:请使用以下命令启动
cabal run agda-mcp - 配置错误:请检查MCP配置文件
- 网络问题:检查localhost:3000是否可访问
目标未能持续
问题: 加载文件后,目标消失
解决方案: 在持久化的REPL架构中,这种情况不应该发生。如果发生了:
- 检查服务器日志中的错误
- 验证文件是否成功加载
agda_load - 检查REPL线程是否正在运行(在日志中查找“Starting persistent Agda REPL thread...”)
类型检查错误
问题: Agda 报告类型错误或缺少模块
解决方案:
- 确保所有依赖项都已安装
- 检查你的Agda库路径是否配置正确
- 首先加载文件,使用
agda_load在其他操作之前
mcp-server 补丁
这个项目包含一个针对(某部分)的补丁 mcp-server Haskell库(版本0.1.0.15)以提高与不发送(特定信息/数据)的MCP客户端的兼容性 MCP-Protocol-Version HTTP 头。
补丁的作用是:
- 使……(或“制作成”)
MCP-Protocol-Version: 2025-06-18HTTP头部可选 - 如果在(此过程中)回退到协议版本协商
initialize握手 - 当缺少头部信息时记录警告,但继续处理
为何需要它: Claude Code的HTTP传输(自2.0.13版本起)在传输中发送协议版本 initialize JSON-RPC 消息,但不是作为 HTTP 头部。根据 MCP 规范 2025-06-18,mcp-server 0.1.0.15 严格要求必须包含该头部,否则会导致连接失败。
补丁位置: patches/mcp-server-header-optional.patch
这个补丁会在Nix构建过程中自动应用,通过 pkgs.applyPatches 在 flake.nix。
技术细节
文件 编辑 实施
文件持久性功能是通过结构感知来实现的:
三种编辑类型:
- 替换孔 (给予,精炼,自动):原地替换
{! expr !}带有新文本
- 根据(情况)决定是取下还是保留牙套 GiveResult 类型 - 保留周围代码结构
- 替换行 (案例分割):结构线条替换
- 删除包含目标的那一行 - 插入N条新条款,保持缩进 - 处理函数和扩展的lambda表达式情况
- 批量编辑 (SolveAll): 多孔填充
- 应用编辑更改于 反转位置顺序 (自下而上) - 防止因早期编辑而导致的位置失效 - 从混凝土中提取的每一种解决方案 Expr
位置安全:
- 使用Agda的
Range输入精确位置(从1开始计数的行/列) - 杠杆作用;利用
getInteractionRange :: InteractionId -> TCM Range - 按位置排序批量编辑以避免坐标偏移
中医语境提取:
- 与类型化工作(或:适用于类型化系统)
ResponseJSON 编码前的值 - 无需JSON解析 - 使用原生Agda类型
- 在与Agda交互的相同TCM单子中运行
做出贡献
欢迎贡献!改进方向包括:
- \[x\] ~~添加全面的测试套件~~(✓ 已实现15+项测试)
- \[x\] ~~持久文件编辑~~(✓ 已实现并具备结构感知能力)
- \[x\] ~~响应格式化~~(✓ 简洁/完整模式,尺寸减小90%)
- \[ \] 除了HTTP外,支持stdio传输
- \[ \] 可配置的端口和主机
- \[ \] 对目标信息查询的资源URI路径解析
- \[ \] 使用 --verbose 标志启用详细/调试日志记录模式
- \[ \] CI/CD 管道
- \[ \] 扩展了对lambda表达式分支的支持
许可证
BSD-3条款(与Agda相同)
致谢
- 构建于 mcp-server 翻译成中文是“MCP服务器” Haskell 库
- 由……提供支持/驱动 Agda
- 受……的启发 agda-mode(在中文语境下,可直接保留原名,或根据具体使用场景稍作调整,但通常无需翻译,因其是一个特定的软件或插件名称) 适用于VSCode
______________________________________________________________________
注: 这是一个实验性项目。MCP协议和Claude Code的实现正在不断发展,因此此服务器可能需要更新以兼容未来的版本。
