Agda交互式开发服务器

基于Model Context Protocol(MCP)的Agda交互式开发服务器,支持AI辅助证明开发、交互式定理证明和Agda代码探索。

  • by faezs
  • Haskell
  • 本地部署
  • 12 星标
  • 更新于
在 GitHub 查看 faezs/agda-mcp

使用说明

Agda MCP服务器

A. 模型上下文协议(MCP) 提供交互式Agda开发功能的服务器。这使得人工智能辅助的证明开发、交互式定理证明以及通过标准化协议探索Agda代码成为可能。

特性

  • 持久REPL会话:维护跨命令的Agda交互状态,保留目标、上下文和类型检查结果
  • 24个交互式命令:全面覆盖Agda交互操作,包括证据搜索、案例分割和模块探索
  • 自动文件持久化:修改代码的命令(give、refinement、case split、auto)会自动将更改持久化到磁盘
  • HTTP传输:符合标准的MCP服务器,具有HTTP/JSON-RPC传输
  • 类型安全集成:使用Haskell的类型系统构建,用于健壮的MCP协议处理
  • 智能响应格式:默认情况下,简洁的人类可读输出(约90%的大小减少),具有可选的完整JSON模式

建筑学

服务器在后台线程中运行一个持久的Agda REPL,通过通道进行通信:

  • 命令通过 Chan CommandWithResponse
  • 响应通过路由返回 MVar Response
  • 每个MCP工具调用都转换为 IOTCM 命令并在持久REPL中执行
  • 目标、上下文和交互状态在命令之间持续存在

文件编辑持久性

当命令修改Agda代码时,更改会自动持久化到磁盘:

  • 响应捕获:键入的REPL回调拦截 Response 值(例如。, Resp_GiveAction, Resp_MakeCase)
  • 一元的:编辑操作在TCM monad上下文中使用Agda的原生API
  • 特定类型的策略:针对不同操作的不同编辑类型:
    • ReplaceHole:就地补孔(给予、精炼、自动)
    • ReplaceLine:带线条插入的结构编辑(案例分割)
    • BatchEdits:以相反的位置顺序应用多个操作(auto_all)
  • 位置感知:编辑保留文件结构并正确处理位置无效

这种架构确保了像“加载文件”这样的操作→ “获得目标”→ “give solution”不仅更新REPL状态,还将更改持久化到源文件。

先决条件

安装

  1. 克隆存储库:
git clone https://github.com/faezs/agda-mcp.git
cd agda-mcp
  1. 进入Nix开发shell:
nix develop
  1. 构建服务器:
cabal build
  1. 运行服务器:
cabal run agda-mcp

服务器启动于 http://localhost:3000/mcp 默认情况下。

配置

Claude 代码

添加到您的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客户端

服务器使用HTTP传输实现MCP协议版本2025-06-18。将您的客户端配置为:

  • 连接到: 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 (string):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 (integer):目标的数字ID

例子:

{
  "name": "agda_get_context",
  "arguments": {
    "goalId": 0
  }
}

退货: 此目标范围内的变量及其类型列表。

用例: 看看在证明中可以使用哪些变量和假设。


5. agda_give

用完整的表情填补一个目标/洞。

论据:

  • goalId (integer):要填充的目标的数字ID
  • expression (string):要使用的Agda表达式(例如,“zero”、“suc n”、“refl”)
  • format (字符串,可选):“简明”(默认)或“完整”

例子:

{
  "name": "agda_give",
  "arguments": {
    "goalId": 0,
    "expression": "refl"
  }
}

效果:

  • 在REPL中填写目标
  • 自动更新文件:替换 {! !} 与表达式
  • 将成功/失败返回给LLM

用例: 当你有完整的解决方案时,完成一个目标。文件会自动编辑。


6. agda_refine

使用构造函数或函数细化目标,引入新的子目标。

论据:

  • goalId (整数):要细化的目标的数字ID
  • expression (string):构造函数或函数名(例如,“suc”、“zero”、“+")
  • format (字符串,可选):“简明”(默认)或“完整”

例子:

{
  "name": "agda_refine",
  "arguments": {
    "goalId": 0,
    "expression": "suc"
  }
}

效果:

  • 将构造函数/函数应用于目标
  • 自动更新文件:替换 {! !} 使用应用的构造函数和新孔
  • 返回新的目标结构

用例: 通过应用构造函数,为构造函数的参数创建新的子目标,从而在目标上取得进展。该文件会自动根据细化进行编辑。


7. agda_case_split

通过变量的模式匹配来分割目标。

论据:

  • goalId (integer):目标的数字ID
  • variable (string):要进行模式匹配的变量的名称
  • format (字符串,可选):“简明”(默认)或“完整”

例子:

{
  "name": "agda_case_split",
  "arguments": {
    "goalId": 0,
    "variable": "n"
  }
}

效果:

  • 为所有构造函数生成模式匹配子句
  • 自动更新文件:删除带孔的线,插入多个子句
  • 保留压痕
  • 备注:案例拆分后应重新加载文件以查看新目标

用例: 对变量进行案例分析(例如,将自然数拆分为零和后继案例)。整行被多个模式匹配子句替换。


8. agda_compute

在目标的上下文中规范和显示表达式。

论据:

  • goalId (整数):目标的数字ID(用于上下文)
  • expression (string):要规范化的表达式

例子:

{
  "name": "agda_compute",
  "arguments": {
    "goalId": 0,
    "expression": "2 + 2"
  }
}

退货: 表达式的标准化形式。

用例: 计算表达式以查看其简化形式。


9. agda_infer_type

推断目标上下文中表达式的类型。

论据:

  • goalId (整数):目标的数字ID(用于上下文)
  • expression (string):用于类型检查的表达式

例子:

{
  "name": "agda_infer_type",
  "arguments": {
    "goalId": 0,
    "expression": "suc zero"
  }
}

退货: 表达式的推断类型。

用例: 在使用表达式之前,请先检查它的类型。


10. agda_intro

使用介绍策略引入变量。

论据:

  • goalId (integer):目标的数字ID

例子:

{
  "name": "agda_intro",
  "arguments": {
    "goalId": 0
  }
}

用例: 自动引入lambda绑定变量或Agda建议的模式变量。


11. agda_why_in_scope

查找文档和范围信息以获取名称。

论据:

  • name (string):要查找的名称

例子:

{
  "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:

  1. ✅ 该命令在REPL中执行
  2. ✅ 在JSON编码之前捕获响应
  3. ✅ 文件编辑被自动提取和应用
  4. ✅ JSON响应返回给LLM
  5. ✅ 更改会立即持久化到磁盘

无需手动编辑文件! 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)。

克劳德代码拒绝连接

问题: Failed to reconnect to agda-mcp

可能的原因:

  1. 服务器未运行:从开始 cabal run agda-mcp
  2. 配置错误:验证MCP配置文件
  3. 网络问题:检查localhost:3000是否可访问

目标不坚持

问题: 加载文件后目标消失

解决方案: 持久REPL架构不应该发生这种情况。如果是这样:

  1. 检查服务器日志是否有错误
  2. 验证文件是否已成功加载 agda_load
  3. 检查REPL线程是否正在运行(在日志中查找“正在启动持久Agda REPL线程…”)

类型检查错误

问题: Agda报告类型错误或缺少模块

解决方案:

  1. 确保安装了所有依赖项
  2. 检查您的Agda库路径是否配置正确
  3. 首先加载文件 agda_load 在其他操作之前

mcp服务器补丁

此项目包括一个补丁 mcp-server Haskell库(版本0.1.0.15),以提高与不发送 MCP-Protocol-Version HTTP标头。

补丁的作用:

  • 使 MCP-Protocol-Version: 2025-06-18 HTTP标头可选
  • 在 initialize 握手
  • 当标头丢失时记录警告,但继续处理

为什么需要: Claude Code的HTTP传输(自2.0.13版本起)将协议版本发送到 initialize JSON-RPC消息,但不是HTTP标头。mcp服务器0.1.0.15严格要求mcp规范2025-06-18的标头,导致连接失败。

补丁位置: patches/mcp-server-header-optional.patch

此补丁在Nix构建过程中通过以下方式自动应用 pkgs.applyPatches 在 flake.nix.

技术细节

文件编辑实现

文件持久性功能是通过结构感知实现的:

三种编辑类型:

  1. 更换孔 (给予、精炼、自动):就地替换 {! expr !} 带有新文本

    • 根据具体情况删除或保留牙套 GiveResult 类型
    • 保留周围的代码结构
  2. 替换线 (案例拆分):结构线替换

    • 删除包含目标的行
    • 插入N个保留缩进的新子句
    • 处理函数和扩展lambda情况
  3. 批次编辑 (SolveAll):多个孔填充

    • 在中应用编辑 反向位置顺序 (从下到上)
    • 防止早期编辑导致职位失效
    • 从混凝土中提取的每种溶液 Expr

位置安全:

  • 使用Agda的 Range 精确位置类型(1-索引线/列)
  • 杠杆作用 getInteractionRange :: InteractionId -> TCM Range
  • 按位置对批量编辑进行排序,以避免坐标偏移

TCM上下文提取:

  • 使用打字 Response JSON编码前的值
  • 无需JSON解析-使用本机Agda类型
  • 与Agda交互在相同的TCM单子中运行

贡献

欢迎投稿!需要改进的地方:

  • [x] 添加全面的测试套件(✓已实施15+个测试)
  • [x] 持续的文件编辑(✓以结构意识实现)
  • [x] 响应格式(✓简洁/完整模式,尺寸缩小90%)
  • []除了HTTP之外,还支持stdio传输
  • []可配置的端口和主机
  • []goal_info查询的资源URI路径解析
  • []带有--Verbose标志的详细/调试日志记录模式
  • []CI/CD管道
  • []扩展的lambda案例拆分支持

许可证

BSD-3条款(与Agda相同)

致谢


注: 这是一个实验项目。MCP协议和Claude Code的实现正在不断发展,此服务器可能需要更新以与未来版本兼容。