clojure-mcp 文档

Table of Contents

1. 工具配置

1.1. 概览

.clojure-mcp/config.edn 文件中的 :tools-config 键允许你为特定工具提供配置. 这对于使用 AI 模型或具有其他可配置行为的工具尤其有用.

1.2. 配置结构

{:tools-config {<tool-id> {<config-key> <config-value>}}}

1.3. 工具的模型配置

许多工具可以配置为使用特定的 AI 模型. 配置系统提供了一个辅助函数 get-tool-model 来简化这种模式.

1.3.1. 示例配置

{:tools-config {:dispatch_agent {:model :openai/my-o3}
                :architect {:model :anthropic/my-claude-3}
                :code_critique {:model :openai/my-gpt-4o}
                :bash {:default-timeout-ms 60000
                       :working-dir "/opt/project"
                       :bash-over-nrepl false}}

 ;; 定义上面引用的模型
 :models {:openai/my-o3 {:model-name "o3-mini"
                      :temperature 0.2
                      :api-key [:env "OPENAI_API_KEY"]}
          :openai/my-gpt-4o {:model-name "gpt-4o"
                          :temperature 0.3
                          :api-key [:env "OPENAI_API_KEY"]}
          :anthropic/my-claude-3 {:model-name "claude-3-haiku-20240307"
                                  :api-key [:env "ANTHROPIC_API_KEY"]}}}

1.4. API 函数

1.4.1. get-tools-config

返回整个工具配置映射.

(config/get-tools-config nrepl-client-map)
;; => {:dispatch_agent {:model :openai/o3}, :architect {...}}

1.4.2. get-tool-config

返回特定工具的配置.

(config/get-tool-config nrepl-client-map :dispatch_agent)
;; => {:model :openai/o3}

1.4.3. get-tool-model

根据工具的配置创建一个模型. 此函数位于 clojure-mcp.agent.langchain.model 命名空间中. 它是一个便捷函数, 可以:

  1. 查找工具的配置
  2. 提取模型键 (默认为 :model)
  3. 使用模型配置创建模型
  4. 优雅地处理错误
(require '[clojure-mcp.agent.langchain.model :as model])

;; 使用默认的 :model 键
(model/get-tool-model nrepl-client-map :dispatch_agent)

;; 使用自定义配置键
(model/get-tool-model nrepl-client-map :code_critique :primary-model)
(model/get-tool-model nrepl-client-map :code_critique :fallback-model)

1.5. 实现工具配置

要向工具添加配置支持:

(ns my-tool.tool
  (:require [clojure-mcp.config :as config]
            [clojure-mcp.agent.langchain.model :as model]))

(defn create-my-tool
  ([nrepl-client-atom]
   (create-my-tool nrepl-client-atom nil))
  ([nrepl-client-atom model]
   (let [;; 使用显式提供的模型或从配置中获取
         final-model (or model
                        (model/get-tool-model @nrepl-client-atom :my_tool))]
     {:tool-type :my-tool
      :nrepl-client-atom nrepl-client-atom
      :model final-model})))

1.6. 当前支持的工具

1.6.1. AI 驱动的工具

  • dispatchagent: 支持 :model 配置以使用自定义 AI 模型
  • architect: 支持 :model 配置以使用自定义 AI 模型
  • codecritique: 支持 :model 配置以使用自定义 AI 模型

1.6.2. 其他可配置的工具

  • bash: 命令执行配置
    • :default-timeout-ms - 默认超时时间 (毫秒) (默认为 180000, 即 3 分钟)
    • :working-dir - 命令的默认工作目录 (如果未设置, 则使用 nrepl-user-dir)
    • :bash-over-nrepl - 覆盖全局 bash-over-nrepl 设置 (true/false)

2. 使用 AI 生成你的自定义 MCP 服务器

欢迎来到 MCP 服务器配置的新时代! 你现在可以使用大型语言模型 (LLMs) 生成一个完全定制的 Clojure MCP 服务器, 以满足你的确切需求. 只需将此目录中的文档作为上下文提供给你喜欢的 LLM, 描述你想要什么, 然后让它创建你的个性化服务器.

2.1. 工作原理

  1. 提供上下文: 将此目录中的相关文档文件提供给 LLM
  2. 描述你的需求: 告诉它你想要什么工具, 提示和资源
  3. 生成: LLM 将创建一个完整的自定义 MCP 服务器实现
  4. 部署: 保存生成的代码并开始使用你的自定义服务器

2.2. 包含为上下文的基本文档

在提示 LLM 时, 请包含以下关键文档:

  1. custom-mcp-server.md - 创建自定义服务器的综合指南
  2. creating-tools-multimethod.mdcreating-tools-without-clojuremcp.md - 取决于你的工具需求
  3. creating-prompts.md - 如果你需要自定义提示
  4. creating-resources.md - 如果你需要自定义资源

2.3. 示例提示和想法

2.3.1. 1. 最小化的只读服务器

使用附加的 ClojureMCP 文档, 创建一个最小化的自定义 MCP 服务器, 包含:
- 仅只读工具: read_file, glob_files, fs_grep, 和 directory_tree
- 无写入功能
- 一个强调代码探索和分析的自定义提示
- 在端口 3000 上启动

2.3.2. 2. Clojure 学习环境

根据提供的 ClojureMCP 文档, 为学习 Clojure 生成一个自定义 MCP 服务器:
- 仅包含: clojure_eval, read_file, 和一个自定义的 "explain_code" 工具
- 添加一个以学习为中心的提示, 鼓励分步解释
- 将 "resources/clojure-tutorials/" 中的所有文件作为资源包含进来
- 添加一个包含常见 Clojure 习语和模式的自定义资源

2.3.3. 3. 项目文档服务器

使用 ClojureMCP 文档, 创建一个专注于文档的 MCP 服务器:
- 包含工具: read_file, glob_files, fs_grep, 和 file_write (仅限 .md 文件)
- 添加一个自定义工具 "generate_docs", 用于从 Clojure 代码创建文档
- 将 "docs/" 目录中的所有 markdown 文件作为资源包含进来
- 添加一个专门用于技术写作和文档的提示
- 将文件操作限制为仅 markdown 和文本文件

2.3.4. 4. 代码审查助手

使用附加的文档, 为代码审查创建一个自定义的 ClojureMCP 服务器:
- 工具: read_file, fs_grep, glob_files, code_critique, 和一个自定义的 "suggest_refactoring" 工具
- 添加专注于 Clojure 最佳实践和代码质量的提示
- 将 "resources/style-guides/" 中的样式指南作为资源包含进来
- 创建一个检查常见 Clojure 反模式的自定义工具

2.3.5. 5. 测试驱动开发服务器

根据提供的文档, 为 TDD 工作流生成一个 ClojureMCP 服务器:
- 包含: clojure_eval, read_file, file_write, 和自定义的 "run_tests" 和 "generate_test" 工具
- 添加一个以 TDD 为中心的提示, 鼓励先编写测试
- 创建一个从函数定义生成测试桩的自定义工具
- 将测试模板作为资源包含进来

2.3.6. 6. 数据分析服务器

使用 ClojureMCP 文档, 创建一个用于数据分析的服务器:
- 工具: clojure_eval, read_file, 和用于 CSV/JSON 处理的自定义工具
- 添加一个自定义的 "visualize_data" 工具, 用于生成 ASCII 图表
- 将数据分析库的文档作为资源包含进来
- 创建专注于数据探索和洞察的提示

2.3.7. 7. 自定义领域特定服务器

根据 ClojureMCP 文档, 为 [你的领域] 创建一个自定义服务器:
- 仅包含与 [你的领域] 相关的工具
- 添加包装 [你的领域] 操作的自定义工具
- 将 "[你的领域]/templates/" 中的所有文件作为提示包含进来
- 从 "[你的领域]/documentation/" 添加资源
- 创建理解 [你的领域] 术语的领域特定提示

2.4. 高级定制示例

2.4.1. 从现有代码添加自定义工具

使用 ClojureMCP 文档, 创建一个将我现有的 Clojure 函数作为工具的服务器:
- 将这些函数转换为 MCP 工具: [在此粘贴你的函数]
- 对复杂工具使用 multimethod 方法
- 对简单工具使用简单的 map 方法
- 包含适当的验证和错误处理

2.4.2. 集成外部服务

生成一个与外部服务集成的 ClojureMCP 服务器:
- 创建一个调用我的 REST API 的自定义工具, URL 为 [URL]
- 添加一个与我的数据库交互的工具 (只读)
- 包含身份验证处理
- 添加适当的错误处理和重试机制

2.4.3. 注重安全的配置

使用文档创建一个高度受限的 ClojureMCP 服务器:
- 仅将特定目录列入文件操作的白名单
- 禁用所有写操作, 除了在 "/tmp/mcp-workspace/" 中
- 为所有用户输入添加自定义验证
- 为所有操作包含审计日志
- 将 clojure_eval 限制为安全的函数子集

2.5. 获得最佳结果的技巧

  1. 具体化: 你的要求越详细, 生成的服务器就越好
  2. 提供示例: 如果你有具体的使用案例, 请在提示中包含它们
  3. 迭代: 首先生成一个基本版本, 然后要求进行优化
  4. 增量测试: 从一个最小化的服务器开始, 逐步添加功能
  5. 包含约束: 指定任何安全性, 性能或兼容性要求

2.6. 常见的请求模式

2.6.1. 工具选择模式

  • "只读探索服务器"
  • "完整的开发环境"
  • "仅文档服务器"
  • "测试和验证服务器"
  • "学习和教程服务器"

2.6.2. 资源组织模式

  • "将目录 X 中的所有 .md 文件作为资源包含"
  • "将目录 Y 中的所有示例代码作为提示添加"
  • "创建一个与我的项目结构匹配的资源层次结构"

2.6.3. 自定义工具创意

  • 文件格式转换器 (EDN ↔ JSON 等)
  • 项目脚手架工具
  • 代码生成工具
  • 与外部服务集成
  • 领域特定操作

2.7. 示例: 自定义服务器的完整提示

我需要一个用于我的 Web 应用程序的自定义 ClojureMCP 服务器. 使用附加的
ClojureMCP 文档 (custom-mcp-server.md, creating-tools-multimethod.md,
creating-prompts.md, creating-resources.md), 请生成一个完整的自定义服务器, 包含:

工具:
- 所有只读工具 (read_file, glob_files, fs_grep, directory_tree)
- 用于 REPL 交互的 clojure_eval
- file_write 仅限于: .clj, .cljs, .edn, .json 文件
- 一个自定义的 "validate_api_endpoint" 工具, 检查 Ring 处理程序是否遵循我们的约定
- 一个自定义的 "generate_handler" 工具, 为新的 API 端点创建样板代码

提示:
- 一个理解我们 Web 应用架构 (Ring, Reitit, next.jdbc) 的主提示
- 一个专门针对 API 开发最佳实践的提示
- 一个用于数据库查询优化的提示

资源:
- 将 "doc/api-examples/" 中的所有文件作为资源包含
- 添加我们来自 "standards/api.md" 的 API 约定指南
- 包含常见的中间件配置

配置:
- 将文件操作限制在我们的项目目录和 /tmp
- 在端口 4000 上启动
- 包含有关可用工具的有用启动消息

请生成完整的 main.clj 文件, 包含所有必要的导入和服务器
实现. 请包含解释关键决策的注释.

2.8. 开始使用

  1. 收集此目录中的文档文件
  2. 选择你的 LLM (Claude, GPT-4 等)
  3. 根据上面的示例制作你的提示
  4. 审查和测试生成的服务器
  5. 根据需要进行迭代和优化

可能性是无穷的! 你的自定义 MCP 服务器只需一个精心制作的提示即可.

2.9. 记住

  • 生成的服务器是一个起点 - 你可以随时手动优化它
  • 在添加每个工具时进行测试, 以确保其按预期工作
  • 牢记安全 - 适当地限制文件操作
  • 为你的自定义工具编写文档以供将来参考
  • 与社区分享有趣的配置!

生成快乐! 🚀

3. 在 Clojure MCP 中配置自定义模型

Clojure MCP 系统通过 .clojure-mcp/config.edn 文件支持用户定义的模型配置. 这允许你定义可重用的模型配置, 可以在整个项目中通过名称引用.

3.1. 概览

自定义模型在你的项目配置文件的 :models 键下定义. 每个模型由一个带命名空间的关键字标识, 并包含一个带有模型参数的配置映射.

3.2. 配置位置

your-project/
├── .clojure-mcp/
│   └── config.edn    # 你的配置文件
├── src/
└── deps.edn

3.3. 基本配置

向你的 .clojure-mcp/config.edn 添加一个 :models 映射:

{;; 其他配置选项...
 :allowed-directories ["."]

 ;; 自定义模型配置
 :models {:openai/my-fast-gpt {:model-name "gpt-4o"
                                :temperature 0.3
                                :max-tokens 2048}

          :anthropic/my-claude {:model-name "claude-3-5-sonnet-20241022"
                                :temperature 0.7
                                :max-tokens 4096}}}

3.4. 使用 Open Router 访问其他模型与 OpenAi 客户端

{:models {:openai/glm {:model-name "z-ai/glm-4.5"
                       :api-key [:env "OPENROUTER_API_KEY"]
                       :base-url "https://openrouter.ai/api/v1/chat/completions"
                       :temperature 1}}}

在这种情况下, 关键字命名空间 :openai/ 用于配置提供商 API.

你也可以像这样配置提供商:

{:models {:glm-4-5 {:provider :openai
                    :model-name "z-ai/glm-4.5"
                    :api-key [:env "OPENROUTER_API_KEY"]
                    :base-url "https://openrouter.ai/api/v1/chat/completions"
                    :temperature 1}}}

3.5. 模型配置示例

3.5.1. 基本模型

:models {;; 一个快速, 专注的 GPT-4 配置
         :openai/my-fast-gpt {:model-name "gpt-4o"
                              :temperature 0.3
                              :max-tokens 2048}

         ;; 一个创意写作配置
         :openai/my-creative-gpt {:model-name "gpt-4o"
                                  :temperature 1.5
                                  :max-tokens 8192}

         ;; 一个平衡的 Claude 配置
         :anthropic/my-claude {:model-name "claude-3-5-sonnet-20241022"
                               :temperature 0.7
                               :max-tokens 4096}}

3.5.2. 推理模型

对于支持推理/思考能力的模型:

:models {;; OpenAI 推理模型
         :openai/my-o3 {:model-name "o3"
                        :max-tokens 8192
                        :thinking {:effort :high}}

         ;; Anthropic 推理模型
         :anthropic/my-reasoning-claude {:model-name "claude-3-5-sonnet-20241022"
                                         :max-tokens 8192
                                         :thinking {:enabled true
                                                    :return true
                                                    :send true
                                                    :budget-tokens 4096}}

         ;; Google Gemini 推理模型
         :google/my-gemini-reasoning {:model-name "gemini-2.5-pro"
                                      :max-tokens 8192
                                      :thinking {:enabled true
                                                 :effort :medium
                                                 :budget-tokens 8192}}}

3.5.3. 特定于提供商的功能

:models {;; 带有代码执行的 Google Gemini
         :google/my-gemini-coder {:model-name "gemini-2.5-flash"
                                  :temperature 0.5
                                  :max-tokens 4096
                                  :google {:allow-code-execution true
                                           :include-code-execution-output true}}

         ;; 带有缓存的 Anthropic
         :anthropic/my-cached-claude {:model-name "claude-3-5-sonnet-20241022"
                                      :max-tokens 4096
                                      :anthropic {:cache-system-messages true
                                                  :cache-tools true}}

         ;; 带有组织设置的 OpenAI
         :openai/my-org-gpt {:model-name "gpt-4o"
                             :max-tokens 4096
                             :openai {:organization-id "org-123"
                                      :project-id "proj-456"
                                      :strict-tools true}}}

3.6. 使用自定义模型

3.6.1. 从代码中

使用 create-model-from-config 函数与 nREPL 客户端映射:

(require '[clojure-mcp.agent.langchain.model :as model])

;; 直接创建一个模型
(let [model (model/create-model-from-config nrepl-client-map :openai/my-fast-gpt)]
  ;; 使用模型...
  )

;; 在运行时覆盖参数
(let [model (model/create-model-from-config
             nrepl-client-map
             :openai/my-fast-gpt
             {:temperature 0.5})] ; 覆盖 temperature
  ;; 使用模型...
  )

;; 获取一个构建器以进行进一步定制
(let [builder (model/create-model-builder-from-config
               nrepl-client-map
               :anthropic/my-claude)
      model (.build builder)]
  ;; 使用模型...
  )

3.6.2. 回退行为

如果在你的自定义配置中未找到模型键, 系统会自动回退到内置的默认值 (如果可用). 例如:

;; 如果 :openai/gpt-4o 不在你的配置中, 它将使用内置的默认值
(let [model (model/create-model-from-config nrepl-client-map :openai/gpt-4o)]
  ;; 使用内置配置
  )

3.7. 可用参数

3.7.1. 通用参数 (所有提供商)

  • :model-name - 模型标识符 (字符串或枚举)
  • :api-key - API 密钥 (可选, 如果未提供则使用环境变量)
  • :temperature - 控制随机性 (0.0-2.0)
  • :max-tokens - 生成的最大令牌数
  • :top-p - Nucleus 采样参数 (0.0-1.0)
  • :stop-sequences - 停止序列列表
  • :max-retries - 重试次数
  • :timeout - 请求超时 (毫秒)
  • :log-requests - 记录 API 请求 (布尔值)
  • :log-responses - 记录 API 响应 (布尔值)

3.7.2. Anthropic 特定参数

  • :top-k - Top-k 采样参数
  • :thinking - 思考配置映射:
    • :enabled - 启用思考模式
    • :return - 在响应中返回思考过程
    • :send - 将思考过程发送到 API
    • :budget-tokens - 思考的令牌预算
  • :anthropic - 提供商特定选项:
    • :version - API 版本
    • :beta - Beta 功能
    • :cache-system-messages - 缓存系统消息
    • :cache-tools - 缓存工具定义

3.7.3. Google 特定参数

  • :seed - 用于可复现性的随机种子
  • :frequency-penalty - 频率惩罚 (-2.0 到 2.0)
  • :presence-penalty - 存在惩罚 (-2.0 到 2.0)
  • :thinking - 思考配置映射:
    • :enabled - 启用思考模式
    • :effort - 努力程度 (:low, :medium, :high)
    • :budget-tokens - 令牌预算 (或根据努力程度自动计算)
  • :google - 提供商特定选项:
    • :allow-code-execution - 允许代码执行
    • :include-code-execution-output - 包括执行输出
    • :response-logprobs - 包括对数概率
    • :logprobs - 返回的对数概率数

3.7.4. OpenAI 特定参数

  • :seed - 用于可复现性的随机种子
  • :frequency-penalty - 频率惩罚 (-2.0 到 2.0)
  • :presence-penalty - 存在惩罚 (-2.0 到 2.0)
  • :thinking - 对于推理模型:
    • :effort - 推理努力程度 (:low, :medium, :high)
    • :return - 在响应中返回思考过程
  • :openai - 提供商特定选项:
    • :organization-id - 组织标识符
    • :project-id - 项目标识符
    • :max-completion-tokens - 最大完成令牌数
    • :strict-tools - 严格工具模式
    • :parallel-tool-calls - 允许并行工具调用
    • :user - 用于跟踪的用户标识符

3.8. 内置模型

系统包括 21 个预配置模型, 无需配置即可使用:

3.8.1. OpenAI

  • :openai/gpt-4o
  • :openai/gpt-4-1
  • :openai/gpt-4-1-mini
  • :openai/gpt-4-1-nano
  • :openai/o1, :openai/o1-mini
  • :openai/o3, :openai/o3-mini, :openai/o3-pro
  • :openai/o4-mini, :openai/o4-mini-reasoning

3.8.2. Google

  • :google/gemini-2-5-flash-lite
  • :google/gemini-2-5-pro
  • :google/gemini-2-5-flash
  • :google/gemini-2-5-flash-reasoning
  • :google/gemini-2-5-pro-reasoning

3.8.3. Anthropic

  • :anthropic/claude-opus-4
  • :anthropic/claude-opus-4-reasoning
  • :anthropic/claude-3-5-haiku
  • :anthropic/claude-sonnet-4
  • :anthropic/claude-sonnet-4-reasoning

3.9. API 密钥

API 密钥可以通过三种方式提供 (按优先级顺序):

  1. 在模型配置中: :api-key "your-key"
  2. 在创建模型时作为运行时覆盖
  3. 从环境变量中:
    • OpenAI: OPENAI_API_KEY
    • Google: GEMINI_API_KEY
    • Anthropic: ANTHROPIC_API_KEY

3.10. 验证

默认情况下, 所有配置都会根据 Clojure specs 进行验证. 要禁用验证:

(model/create-model-from-config
  nrepl-client-map
  :openai/my-model
  {}
  {:validate? false})

3.11. 完整示例

这是一个包含各种模型配置的完整 .clojure-mcp/config.edn 文件:

{;; 文件访问权限
 :allowed-directories ["."]

 ;; 其他配置选项
 :cljfmt true
 :bash-over-nrepl true

 ;; 模型配置
 :models {;; 特定任务的 OpenAI 模型
          :openai/code-reviewer {:model-name "gpt-4o"
                                 :temperature 0.2
                                 :max-tokens 4096}

          :openai/creative-writer {:model-name "gpt-4o"
                                   :temperature 1.2
                                   :max-tokens 8192
                                   :top-p 0.95}

          :openai/reasoner {:model-name "o3"
                           :max-tokens 16384
                           :thinking {:effort :high}}

          ;; 用于不同用例的 Anthropic 模型
          :anthropic/analyzer {:model-name "claude-3-5-sonnet-20241022"
                               :temperature 0.3
                               :max-tokens 4096}

          :anthropic/deep-thinker {:model-name "claude-3-5-sonnet-20241022"
                                   :max-tokens 8192
                                   :thinking {:enabled true
                                             :return true
                                             :send true
                                             :budget-tokens 8192}
                                   :anthropic {:cache-system-messages true}}

          ;; 用于代码执行任务的 Google Gemini
          :google/code-runner {:model-name "gemini-2.5-flash"
                              :temperature 0.1
                              :max-tokens 4096
                              :google {:allow-code-execution true
                                      :include-code-execution-output true}}}}

4. Clojure MCP Prompt CLI

一个用于与 AI agent 交互的命令行界面, 该 agent 可以访问所有 Clojure MCP 工具.

4.1. 先决条件

  • 正在运行的 nREPL 服务器 (默认端口 7888, 可配置)
  • 为所选模型 (Anthropic, OpenAI 等) 配置的 API 密钥

4.2. 用法

启动你的 nREPL 服务器:

clojure -M:nrepl

在另一个终端中, 运行 CLI:

clojure -M:prompt-cli -p "Your prompt here"

4.3. 选项

  • -p, --prompt PROMPT - 发送给 agent 的提示 (必需)
  • -m, --model MODEL - 覆盖默认模型 (例如, :openai/gpt-4, :anthropic/claude-3-5-sonnet)
  • -c, --config CONFIG - 自定义 agent 配置文件的路径 (可选)
  • -d, --dir DIRECTORY - 工作目录 (默认为 REPL 的工作目录)
  • -P, --port PORT - nREPL 服务器端口 (默认: 7888)
  • -h, --help - 显示帮助信息

4.4. 示例

使用默认模型的基本用法:

clojure -M:prompt-cli -p "What namespaces are available?"

使用特定模型:

clojure -M:prompt-cli -p "Evaluate (+ 1 2)" -m :openai/gpt-4

创建代码:

clojure -M:prompt-cli -p "Create a fibonacci function"

使用自定义 agent 配置:

clojure -M:prompt-cli -p "Analyze this project" -c my-custom-agent.edn

连接到不同的 nREPL 端口:

clojure -M:prompt-cli -p "Run tests" -P 8888

指定工作目录:

clojure -M:prompt-cli -p "List files" -d /path/to/project

4.5. 配置

CLI 会正确初始化 nREPL 连接, 包括:

  • 从 REPL 自动检测工作目录
  • 从工作目录加载 .clojure-mcp/config.edn
  • 环境检测和初始化 (Clojure, ClojureScript 等)
  • 加载 REPL 辅助函数

4.6. 默认 Agent 配置

默认情况下, CLI 使用 parent-agent-config, 其中包括:

  • Clojure REPL 系统提示
  • 访问所有可用工具
  • 项目上下文 (代码索引和摘要)
  • 无状态内存 (每次调用都是独立的)

4.7. 自定义 Agent 配置

你可以创建一个 EDN 格式的自定义 agent 配置文件:

{:id :my-agent
 :name "my_agent"
 :description "My custom agent"
 :system-message "Your system prompt here..."
 :context true  ; 包含项目上下文
 :enable-tools [:read_file :clojure_eval :grep]  ; 特定工具或 [:all]
 :memory-size 100  ; 或 false 表示无状态
 :model :anthropic/claude-3-5-sonnet-20241022}

4.8. 环境变量

设置 DEBUG=1 以在出错时查看堆栈跟踪:

DEBUG=1 clojure -M:prompt-cli -p "Your prompt"

4.9. 模型配置

模型可以在 .clojure-mcp/config.edn 中配置:

{:models {:openai/my-gpt4 {:model-name "gpt-4"
                            :temperature 0.3
                            :api-key [:env "OPENAI_API_KEY"]}}}

然后使用:

clojure -M:prompt-cli -p "Your prompt" -m :openai/my-gpt4

4.10. 工具配置

默认情况下, agent 可以访问所有工具, 这些工具会根据项目的 .clojure-mcp/config.edn 设置进行过滤:

  • enable-toolsdisable-tools 设置会被遵守
  • 来自 tools-config 的工具特定配置会被应用

5. 创建你自己的自定义 MCP 服务器

🎉 欢迎来到有趣的部分!* 创建你自己的自定义 MCP 服务器不仅简单——它还能赋予你力量并带来愉悦. 你可以打造一个个性化的 AI 助手, 它能理解你的工作流程, 你的项目结构, 以及你的开发风格. 在 clojure-mcp 的 alpha 阶段, 创建自己的主入口点是配置服务器的主要方式, 让你完全控制你的开发体验.

把它想象成构建你自己的个性化 AI 开发伴侣. 想要只读工具以进行更安全的探索? 没问题. 需要专门的 ClojureScript 工具? 添加它们. 有符合团队编码标准的自定义提示? 完美. 这是你的服务器, 为你的需求量身定制.

💡 专业提示: 始终参考 src/clojure_mcp/main.clj 来查看当前优化的工具, 资源和提示集. 这个文件代表了精心策划的默认配置, 是理解哪些工具能很好地协同工作以及它们是如何组织的最佳参考.

5.1. 为什么要创建自定义服务器?

  • 个性化: 只包含你实际使用的工具
  • 自定义工作流: 添加特定于你的项目或团队的提示
  • 专用资源: 暴露你项目独特的文档
  • 工具集成: 为你的特定技术栈 (Shadow-cljs, Figwheel 等) 添加工具
  • 安全控制: 在只读探索或完全编辑能力之间进行选择
  • 性能: 更小的工具集意味着更快的启动速度和更少的认知开销

5.2. 新的简化模式 (自 v0.5.0 起)

重构后的 clojure-mcp 的美妙之处在于, 创建自定义服务器现在变得非常简单. 新模式使用工厂函数来创建工具, 提示和资源. 以下是它的工作原理:

  1. 为工具, 提示和/或资源**定义工厂函数**
  2. 用你的工厂**调用** core/build-and-start-mcp-server
  3. 就这样! 核心会处理所有复杂的设置

5.3. 最小化自定义服务器示例

让我们从最简单的自定义服务器开始, 它重用了 main 中的所有内容:

(ns my-company.mcp-server
  (:require [clojure-mcp.core :as core]
            [clojure-mcp.main :as main]))

(defn start-mcp-server [opts]
  (core/build-and-start-mcp-server
   opts
   {:make-tools-fn main/make-tools
    :make-prompts-fn main/make-prompts
    :make-resources-fn main/make-resources}))

就这样! 你现在拥有一个使用所有标准组件的功能齐全的 MCP 服务器.

5.4. 自定义资源

想要添加你自己的文档? 创建你自己的 make-resources 函数:

(ns my-company.mcp-server
  (:require [clojure-mcp.core :as core]
            [clojure-mcp.main :as main]
            [clojure-mcp.resources :as resources]
            [clojure.java.io :as io]))

(defn make-resources [nrepl-client-atom working-dir]
  ;; 从默认资源开始
  (concat
   (main/make-resources nrepl-client-atom working-dir)
   ;; 添加你的自定义资源
   [(resources/create-file-resource
     "custom://architecture"
     "ARCHITECTURE.md"
     "我们的系统架构文档"
     "text/markdown"
     (.getCanonicalPath (io/file working-dir "docs/ARCHITECTURE.md")))

    (resources/create-string-resource
     "custom://team-standards"
     "团队编码标准"
     "我们团队的 Clojure 编码标准"
     "text/markdown"
     "# 团队编码标准\n\n- 始终使用 kebab-case\n- 优先使用线程宏\n- 先编写测试")]))

(defn start-mcp-server [opts]
  (core/build-and-start-mcp-server
   opts
   {:make-tools-fn main/make-tools
    :make-prompts-fn main/make-prompts
    :make-resources-fn make-resources}))  ; 使用我们的自定义资源

5.5. 自定义工具

5.5.1. 选择性工具加载

也许你想要一个只读服务器以进行更安全的探索:

(ns my-company.read-only-server
  (:require [clojure-mcp.core :as core]
            [clojure-mcp.main :as main]
            ;; 只导入你需要的工具
            [clojure-mcp.tools.directory-tree.tool :as directory-tree-tool]
            [clojure-mcp.tools.unified-read-file.tool :as unified-read-file-tool]
            [clojure-mcp.tools.grep.tool :as new-grep-tool]
            [clojure-mcp.tools.glob-files.tool :as glob-files-tool]
            [clojure-mcp.tools.think.tool :as think-tool]
            [clojure-mcp.tools.eval.tool :as eval-tool]
            [clojure-mcp.tools.project.tool :as project-tool]))

(defn make-read-only-tools [nrepl-client-atom working-directory]
  ;; 只包含只读和评估工具
  [(directory-tree-tool/directory-tree-tool nrepl-client-atom)
   (unified-read-file-tool/unified-read-file-tool nrepl-client-atom)
   (new-grep-tool/grep-tool nrepl-client-atom)
   (glob-files-tool/glob-files-tool nrepl-client-atom)
   (think-tool/think-tool nrepl-client-atom)
   (eval-tool/eval-code nrepl-client-atom)
   (project-tool/inspect-project-tool nrepl-client-atom)])

(defn start-mcp-server [opts]
  (core/build-and-start-mcp-server
   opts
   {:make-tools-fn make-read-only-tools
    :make-prompts-fn main/make-prompts
    :make-resources-fn main/make-resources}))

5.5.2. 添加自定义工具

有一个自定义工具? 把它加进来:

(ns my-company.mcp-server
  (:require [clojure-mcp.core :as core]
            [clojure-mcp.main :as main]
            [my-company.database-tool :as db-tool]))

(defn make-tools [nrepl-client-atom working-directory]
  ;; 从 main 工具开始, 并添加你自己的
  (conj (main/make-tools nrepl-client-atom working-directory)
        (db-tool/database-query-tool nrepl-client-atom)))

(defn start-mcp-server [opts]
  (core/build-and-start-mcp-server
   opts
   {:make-tools-fn make-tools
    :make-prompts-fn main/make-prompts
    :make-resources-fn main/make-resources}))

5.6. 自定义提示

添加特定于项目的提示来指导你的 AI 助手:

(defn make-prompts [nrepl-client-atom working-dir]
  ;; 从默认提示开始
  (concat
   (main/make-prompts nrepl-client-atom working-dir)
   ;; 添加自定义提示
   [{:name "database-migration"
     :description "生成数据库迁移代码"
     :arguments [{:name "table-name"
                  :description "要迁移的表名"
                  :required? true}
                 {:name "operation"
                  :description "add-column, remove-column, create-table 等"
                  :required? true}]
     :prompt-fn (fn [_ args callback]
                  (callback
                   {:description "数据库迁移助手"
                    :messages [{:role :user
                                :content (str "为以下内容生成数据库迁移: "
                                            (get args "table-name")
                                            " 操作: " (get args "operation")
                                            "\n使用我们的标准迁移格式.")}]}))}

    {:name "test-generator"
     :description "为命名空间生成测试用例"
     :arguments [{:name "namespace"
                  :description "要测试的命名空间"
                  :required? true}]
     :prompt-fn (fn [_ args callback]
                  (let [ns-name (get args "namespace")]
                    (callback
                     {:description "测试生成"
                      :messages [{:role :user
                                  :content (str "请为以下内容生成全面的测试: " ns-name
                                              "\n\n包括:"
                                              "\n- 每个公共函数的单元测试"
                                              "\n- 适当时使用基于属性的测试"
                                              "\n- 边缘情况和错误条件"
                                              "\n- 使用我们团队的测试命名约定")}]})))}]))

5.7. 修改现有的工具, 资源和提示

有时你需要修改现有的组件而不是创建新的. 常见原因包括:

  • 解决工具之间的名称冲突
  • 更改描述以影响 AI 助手如何使用它们
  • 为你的特定工作流定制行为

5.7.1. 修改工具名称和描述

工具只是 map, 所以你可以在你的工厂函数中修改它们:

(defn make-tools [nrepl-client-atom working-directory]
  (let [standard-tools (main/make-tools nrepl-client-atom working-directory)]
    ;; 查找并修改特定工具
    (map (fn [tool]
           (case (:name tool)
             ;; 将 bash 重命名为更具体的名称
             "bash" (assoc tool
                          :name "shell_command"
                          :description "在项目目录中执行 shell 命令. 用于: git 操作, 运行测试, 文件系统操作.")

             ;; 使文件读取更加突出
             "read_file" (assoc tool
                               :description "先读文件! 编辑前务必使用此工具. 智能读取器, 支持 Clojure 文件的模式匹配.")

             ;; 不鼓励使用 file_edit, 提倡使用 clojure_edit
             "file_edit" (assoc tool
                               :description "简单的文本替换 - 避免用于 Clojure 文件! 请改用 clojure_edit.")

             ;; 返回未修改的
             tool))
         standard-tools)))

5.7.2. 添加前缀以避免冲突

如果你正在组合来自多个来源的工具:

(defn prefix-tool-names [prefix tools]
  (map #(update % :name (fn [n] (str prefix "_" n))) tools))

(defn make-tools [nrepl-client-atom working-directory]
  (concat
   ;; 带前缀的标准工具
   (prefix-tool-names "core" (main/make-tools nrepl-client-atom working-directory))

   ;; 带有不同前缀的你的自定义工具
   (prefix-tool-names "custom"
                      [(my-special-tool/special-tool nrepl-client-atom)])))

5.7.3. 完整示例: 定制所有内容

以下是如何选择性地修改组件同时保留你想要的内容:

(ns my-company.custom-mcp-server
  (:require [clojure-mcp.core :as core]
            [clojure-mcp.main :as main]
            [clojure-mcp.resources :as resources]
            [clojure.string :as str]))

(defn customize-for-safety [tool]
  ;; 让所有编辑工具都警告安全问题
  (if (str/includes? (:name tool) "edit")
    (update tool :description
            #(str "⚠️ 小心: 这会修改文件! " %))
    tool))

(defn make-tools [nrepl-client-atom working-directory]
  (->> (main/make-tools nrepl-client-atom working-directory)
       ;; 移除我们不想要的工具
       (remove #(= (:name %) "bash"))  ; 太危险了
       ;; 修改剩余的工具
       (map customize-for-safety)
       ;; 重命名潜在的冲突
       (map (fn [tool]
              (case (:name tool)
                "think" (assoc tool :name "reflect")  ; 避免与其他系统冲突
                tool)))))

(defn make-resources [nrepl-client-atom working-dir]
  (let [standard-resources (main/make-resources nrepl-client-atom working-dir)]
    (concat
     ;; 修改现有资源
     (map (fn [resource]
            (case (:name resource)
              ;; 让项目摘要更突出
              "PROJECT_SUMMARY.md"
              (assoc resource
                     :name "MAIN_PROJECT_CONTEXT"
                     :description "关键: 主要项目文档 - 务必先加载此项!")

              ;; 其他保持原样
              resource))
          standard-resources)

     ;; 添加你自己的
     [(resources/create-file-resource
       "custom://runbook"
       "RUNBOOK.md"
       "应急程序和操作手册"
       "text/markdown"
       (str working-dir "/docs/RUNBOOK.md"))])))

(defn start-mcp-server [opts]
  (core/build-and-start-mcp-server
   opts
   {:make-tools-fn make-tools
    :make-prompts-fn main/make-prompts
    :make-resources-fn make-resources}))

5.8. 真实世界示例: Shadow-cljs 服务器

以下是 Shadow-cljs 示例如何扩展主服务器:

(ns my-company.shadow-mcp
  (:require [clojure-mcp.core :as core]
            [clojure-mcp.main :as main]
            [clojure-mcp.tools.eval.tool :as eval-tool]
            [clojure-mcp.nrepl :as nrepl]
            [clojure.tools.logging :as log]))

;; ... shadow-specific tool implementation ...

(defn make-tools [nrepl-client-atom working-directory & [{:keys [port shadow-port shadow-build shadow-watch] :as config}]]
  (if (and port shadow-port (not= port shadow-port))
    (conj (main/make-tools nrepl-client-atom working-directory)
          (shadow-eval-tool-secondary-connection-tool nrepl-client-atom config))
    (conj (main/make-tools nrepl-client-atom working-directory)
          (shadow-eval-tool nrepl-client-atom config))))

(defn start-mcp-server [opts]
  (core/build-and-start-mcp-server
   opts
   {:make-tools-fn (fn [nrepl-client-atom working-directory]
                     (make-tools nrepl-client-atom working-directory opts))
    :make-prompts-fn main/make-prompts
    :make-resources-fn main/make-resources}))

5.9. 完整的自定义服务器模板

这是一个你可以用作起点的完整模板:

(ns my-company.custom-mcp-server
  "为我们团队的 Clojure 开发量身定制的自定义 MCP 服务器"
  (:require [clojure-mcp.core :as core]
            [clojure-mcp.main :as main]
            [clojure-mcp.resources :as resources]
            [clojure-mcp.prompts :as prompts]
            ;; 根据需要添加特定的工具 requires
            [clojure-mcp.tools.eval.tool :as eval-tool]
            [clojure-mcp.tools.unified-read-file.tool :as read-tool]
            [clojure.java.io :as io]))

(defn make-resources
  "包含我们团队文档的自定义资源"
  [nrepl-client-atom working-dir]
  (concat
   ;; 包含一些默认值
   [(first (main/make-resources nrepl-client-atom working-dir))] ; PROJECT_SUMMARY
   ;; 添加我们的自定义资源
   [(resources/create-file-resource
     "custom://style-guide"
     "STYLE_GUIDE.md"
     "我们全面的 Clojure 风格指南"
     "text/markdown"
     (.getCanonicalPath (io/file working-dir "docs/STYLE_GUIDE.md")))]))

(defn make-prompts
  "我们工作流的自定义提示"
  [nrepl-client-atom working-dir]
  [{:name "pr-review"
    :description "审查拉取请求的代码更改"
    :arguments []
    :prompt-fn (prompts/simple-content-prompt-fn
                "PR 审查指南"
                "请审查最近的更改, 重点关注:
                 1. 我们团队的风格指南合规性
                 2. 测试覆盖率
                 3. 性能影响
                 4. API 兼容性")}])

(defn make-tools
  "为我们团队精选的工具"
  [nrepl-client-atom working-directory]
  ;; 从 main 工具中混合搭配或添加你自己的
  [(eval-tool/eval-code nrepl-client-atom)
   (read-tool/unified-read-file-tool nrepl-client-atom)
   ;; ... 根据需要添加更多工具
   ])

(defn start-mcp-server
  "启动我们的自定义 MCP 服务器"
  [opts]
  (core/build-and-start-mcp-server
   opts
   {:make-tools-fn make-tools
    :make-prompts-fn make-prompts
    :make-resources-fn make-resources}))

5.10. 配置 deps.edn

将你的 deps.edn 指向你的自定义服务器:

{:aliases
  {:my-mcp
    {:deps {org.slf4j/slf4j-nop {:mvn/version "2.0.16"}
            com.bhauman/clojure-mcp {:local/root "~/workspace/clojure-mcp"}}
     :extra-paths ["src"] ; 你的自定义服务器所在的位置
     :exec-fn my-company.custom-mcp-server/start-mcp-server
     :exec-args {:port 7888}}}}

5.11. 成功秘诀

  1. 从简单开始: 首先重用 main 的工厂函数, 然后逐步定制
  2. 增量测试: 一次添加一个定制并进行测试
  3. 记录你的选择: 注释你为什么包含/排除特定的工具
  4. 版本控制: 将你的自定义服务器保存在版本控制中
  5. 团队共享: 与你的团队共享你的服务器配置
  6. 工厂函数签名: 始终为你的工厂函数使用 [nrepl-client-atom working-directory]

5.12. 常见n模式

5.12.1. 开发服务器 vs 生产服务器

(defn make-dev-tools [nrepl-client-atom working-directory]
  ;; 所有工具, 包括编辑
  (main/make-tools nrepl-client-atom working-directory))

(defn make-prod-tools [nrepl-client-atom working-directory]
  ;; 用于生产调试的只读工具
  [(read-tool/unified-read-file-tool nrepl-client-atom)
   (eval-tool/eval-code nrepl-client-atom)])

(defn start-mcp-server [{:keys [env] :as opts}]
  (let [tools-fn (if (= env "production") make-prod-tools make-dev-tools)]
    (core/build-and-start-mcp-server
     opts
     {:make-tools-fn tools-fn
      :make-prompts-fn main/make-prompts
      :make-resources-fn main/make-resources})))

5.12.2. 特定项目类型的服务器

(defn make-web-app-tools [nrepl-client-atom working-directory]
  ;; Web 开发工具
  (concat
   (main/make-tools nrepl-client-atom working-directory)
   [(http-tool/http-client-tool nrepl-client-atom)]))

(defn make-library-tools [nrepl-client-atom working-directory]
  ;; 库开发工具 - 专注于文档和 API 设计
  (concat
   [(doc-tool/documentation-tool nrepl-client-atom)]
   (main/make-tools nrepl-client-atom working-directory)))

5.12.3. 使用替代传输方式

新模式也支持不同的传输机制. 例如, 使用 SSE (Server-Sent Events):

(ns my-company.sse-server
  (:require [clojure-mcp.main :as main]
            [clojure-mcp.sse-core :as sse-core]))

(defn start-sse-mcp-server [opts]
  ;; 使用 SSE 传输代替 stdio
  (sse-core/build-and-start-mcp-server
   opts
   {:make-tools-fn main/make-tools
    :make-prompts-fn main/make-prompts
    :make-resources-fn main/make-resources}))

5.13. 结论

使用新模式创建你自己的自定义 MCP 服务器比以往任何时候都简单. 工厂函数方法意味着你可以:

  1. 从一个重用所有内容的单函数服务器开始
  2. 通过替换单个工厂函数逐步定制
  3. 混合和匹配来自不同来源的组件
  4. 轻松维护和共享你的配置

记住: 在 alpha 阶段, 这就是配置 clojure-mcp 的方式. 新模式使其变得如此简单, 没有理由不创建你完美的开发环境!

定制快乐! 🚀

6. 创建不依赖 ClojureMCP 的工具

本指南解释了如何使用简单的 Clojure map 为 ClojureMCP 创建工具, 而无需将 ClojureMCP 作为库依赖. 这种方法允许你创建可以注册到任何 ClojureMCP 服务器的独立工具.

6.1. 工具注册 Map Schema

工具被定义为一个具有以下结构的 Clojure map:

{:name        "tool-name"           ; String: 工具的唯一标识符
 :description "Tool description"    ; String: 人类可读的描述
 :schema      {...}                 ; Map: 定义输入参数的 JSON Schema
 :tool-fn     (fn [exchange params callback] ...)} ; Function: 工具实现

6.1.1. Schema 字段

:schema 字段应该是一个表示 JSON Schema 的 Clojure map. 这定义了工具输入参数的结构和验证规则:

{:type "object"
 :properties {"param1" {:type "string"
                        :description "Description of param1"}
              "param2" {:type "integer"
                        :description "Description of param2"}}
 :required ["param1"]}  ; 必需参数名称的列表

6.1.2. 工具函数签名

:tool-fn 具有以下签名:

(fn [exchange params callback]
  ;; 工具实现
  )

其中:

  • exchange - 对于简单工具通常被忽略 (可用于高级功能)
  • params - 一个包含工具输入参数的 Clojure map, 键为字符串
  • callback - 一个用结果调用的函数

6.1.3. 回调函数

回调函数需要两个参数:

(callback result-vector error-boolean)

其中:

  • result-vector - 一个包含工具输出的字符串向量
  • error-boolean - true 如果发生错误, false 否则

6.2. 完整示例: 天气工具

这是一个不依赖 ClojureMCP 的天气工具的完整示例:

(ns weather-tool.core
  (:require [clojure.string :as str]))

(defn fetch-weather
  "模拟获取天气数据的函数. 在实际实现中,
   这将调用天气 API."
  [location]
  ;; 模拟天气数据
  (case (str/lower-case location)
    "london"     {:temp 15 :condition "Partly cloudy" :humidity 65}
    "new york"   {:temp 22 :condition "Sunny" :humidity 45}
    "tokyo"      {:temp 18 :condition "Rainy" :humidity 80}
    "sydney"     {:temp 25 :condition "Clear" :humidity 55}
    ;; 未知地点的默认值
    {:temp 20 :condition "Unknown" :humidity 50}))

(defn format-weather-report
  "将天气数据格式化为可读字符串."
  [{:keys [temp condition humidity]} location]
  (format "Weather in %s: %d°C, %s, Humidity: %d%%"
          location temp condition humidity))

(def weather-tool
  {:name "get_weather"

   :description "获取指定地点的当前天气"

   :schema {:type "object"
            :properties {"location" {:type "string"
                                    :description "要获取天气的城市或地点"}}
            :required ["location"]}

   :tool-fn (fn [_exchange params callback]
              (try
                ;; 从 params 中提取 location (字符串键)
                (let [location (get params "location")]
                  (if location
                    ;; 获取并格式化天气数据
                    (let [weather-data (fetch-weather location)
                          report (format-weather-report weather-data location)]
                      ;; 成功: 返回结果向量, 错误标志为 false
                      (callback [report] false))
                    ;; 错误: 缺少必需参数
                    (callback ["Error: Location parameter is required"] true)))
                ;; 处理任何异常
                (catch Exception e
                  (callback [(str "Error fetching weather: " (.getMessage e))] true))))})

;; 注册工具到 ClojureMCP 服务器的示例
;; (这应该由服务器完成, 而不是工具库)
#_
(defn register-weather-tool [mcp-server]
  (clojure-mcp.core/add-tool mcp-server weather-tool))

6.3. 高级示例: 计算器工具

这是一个更复杂的示例, 包含多个参数和验证:

(ns calculator-tool.core)

(defn safe-divide [a b]
  (if (zero? b)
    (throw (ex-info "Division by zero" {:a a :b b}))
    (/ a b)))

(def calculator-tool
  {:name "calculator"

   :description "执行基本的算术运算"

   :schema {:type "object"
            :properties {"operation" {:type "string"
                                     :enum ["add" "subtract" "multiply" "divide"]
                                     :description "要执行的算术运算"}
                         "a" {:type "number"
                              :description "第一个操作数"}
                         "b" {:type "number"
                              :description "第二个操作数"}}
            :required ["operation" "a" "b"]}

   :tool-fn (fn [_exchange params callback]
              (try
                ;; 提取参数
                (let [operation (get params "operation")
                      a (get params "a")
                      b (get params "b")]
                  ;; 验证所有参数都存在
                  (cond
                    (nil? operation)
                    (callback ["Error: operation parameter is required"] true)

                    (nil? a)
                    (callback ["Error: parameter 'a' is required"] true)

                    (nil? b)
                    (callback ["Error: parameter 'b' is required"] true)

                    :else
                    ;; 执行计算
                    (let [result (case operation
                                   "add"      (+ a b)
                                   "subtract" (- a b)
                                   "multiply" (* a b)
                                   "divide"   (safe-divide a b)
                                   (throw (ex-info "Invalid operation"
                                                   {:operation operation})))]
                      (callback [(str "Result: " result)] false))))

                ;; 处理异常
                (catch Exception e
                  (callback [(str "Calculation error: " (.getMessage e))] true))))})

6.4. 最佳实践

  1. 参数验证: 在处理之前, 始终验证必需的参数是否存在.
  2. 错误处理: 将你的工具逻辑包装在 try-catch 块中, 并适当地使用错误标志.
  3. 结果格式: 始终将结果作为字符串向量返回, 即使是单行输出.
  4. 字符串键: 记住 params 使用字符串键, 而不是关键字.
  5. 纯函数: 将你的工具逻辑保留在纯函数中, 这些函数可以独立于 MCP 框架进行测试.
  6. 文档: 为工具及其参数提供清晰的描述.

6.5. 测试你的工具

你可以独立地测试你的工具函数:

(defn test-weather-tool []
  ;; 创建一个打印结果的测试回调
  (let [test-callback (fn [result error?]
                        (println "Result:" result)
                        (println "Error?" error?))]
    ;; 使用有效输入进行测试
    ((:tool-fn weather-tool) nil {"location" "London"} test-callback)

    ;; 使用缺失输入进行测试
    ((:tool-fn weather-tool) nil {} test-callback)))

6.6. 与 ClojureMCP 集成

要将你的工具与 ClojureMCP 服务器一起使用, 服务器管理员只需将你的工具 map 添加到他们的服务器配置中:

;; 在服务器的代码中 (不是你的工具库)
(require '[clojure-mcp.core :as mcp])
(require '[weather-tool.core :as weather])

(defn setup-server [mcp-server]
  ;; 注册你的工具
  (mcp/add-tool mcp-server weather/weather-tool))

这种分离允许工具作者创建和分发工具, 而无需任何对 ClojureMCP 的依赖, 而服务器操作员可以轻松集成任何兼容的工具.

7. 使用 ClojureMCP 的 Multimethod 系统创建工具

本指南演示了如何使用 ClojureMCP 基于 multimethod 的工具系统创建自定义 MCP 工具. 这种方法在 ClojureMCP 生态系统内构建工具时, 提供了结构, 验证和集成方面的好处.

📝 注意: 本指南涵盖了创建工具的 multimethod 方法. 有关其他方法, 请参阅:

  • [不依赖 ClojureMCP 创建工具](creating-tools-without-clojuremcp.md) - 用于基于简单 map 的工具
  • [创建你自己的自定义 MCP 服务器](custom-mcp-server.md) - 用于将工具集成到你的服务器中

7.1. 目录

  • [何时使用此方法](#何时使用此方法)
  • [架构概览](#架构概览)
  • [工具系统 Multimethods](#工具系统-multimethods)
  • [简单工具示例](#简单工具示例)
  • [复杂工具示例](#复杂工具示例)
  • [核心与工具分离](#核心与工具分离)
  • [测试你的工具](#测试你的工具)
  • [与你的自定义服务器集成](#与你的自定义服务器集成)
  • [最佳实践](#最佳实践)

7.2. 何时使用此方法

在以下情况下使用 multimethod 系统:

  • 你正在为 ClojureMCP 专门构建工具
  • 你想要结构化的验证和错误处理
  • 你需要利用 ClojureMCP 的基础设施 (nREPL 客户端, 路径验证等)
  • 你正在为 ClojureMCP 项目贡献工具
  • 你更喜欢 multimethod 模式的结构和指导

在以下情况下使用简单的 map (参见 [不依赖 ClojureMCP 创建工具](creating-tools-without-clojuremcp.md)):

  • 你想要最大的可移植性
  • 你正在创建独立的工具库
  • 你需要与非 ClojureMCP 服务器共享工具
  • 你更喜欢最小的依赖

7.3. 架构概览

ClojureMCP 工具系统使用 Clojure 的 multimethod 调度来为工具创建一个灵活, 可扩展的架构. 每个工具都通过一组定义其行为的 multimethod 来实现:

工具定义
├── 工厂函数 (创建工具配置)
├── Multimethod 实现
│   ├── tool-name
│   ├── tool-description
│   ├── tool-schema
│   ├── validate-inputs
│   ├── execute-tool
│   └── format-results
└── 注册函数 (返回 MCP 注册 map)

7.4. 工具系统 Multimethods

工具系统定义了每个工具必须实现的六个核心 multimethods:

7.4.1. 必需的 Multimethods

  1. tool-name - 返回工具名称 (字符串)
  2. tool-description - 返回给 AI 助手的描述
  3. tool-schema - 定义输入参数 schema
  4. validate-inputs - 验证和规范化输入参数
  5. execute-tool - 执行实际的工具操作
  6. format-results - 为 MCP 协议格式化结果

7.4.2. Multimethod 调度

所有 multimethods 都根据工具配置中的 :tool-type 键进行调度:

(defmethod tool-system/tool-name :my-custom-tool [_]
  "my_custom_tool")

7.5. 简单工具示例

让我们创建一个简单的 "echo" 工具来演示基本模式:

7.5.1. 步骤 1: 创建工具命名空间

(ns my-project.tools.echo.tool
  "返回输入消息的简单 echo 工具."
  (:require
   [clojure-mcp.tool-system :as tool-system]))

;; 创建工具配置的工厂函数
(defn create-echo-tool
  "创建 echo 工具配置."
  []
  {:tool-type :echo})

7.5.2. 步骤 2: 实现 Multimethods

;; 工具名称 (在 AI 看来)
(defmethod tool-system/tool-name :echo [_]
  "echo")

;; 给 AI 助手的描述
(defmethod tool-system/tool-description :echo [_]
  "返回带可选前缀的输入消息的 Echo 工具.

   参数:
   - message: 要回显的消息 (必需)
   - prefix: 要添加到消息的可选前缀")

;; 输入 schema 验证
(defmethod tool-system/tool-schema :echo [_]
  {:type :object
   :properties {:message {:type :string
                          :description "要回显的消息"}
                :prefix {:type :string
                         :description "消息的可选前缀"}}
   :required [:message]})

;; 输入验证和规范化
(defmethod tool-system/validate-inputs :echo [_ inputs]
  (let [{:keys [message prefix]} inputs]
    (when-not message
      (throw (ex-info "Missing required parameter: message" {:inputs inputs})))

    (when (not (string? message))
      (throw (ex-info "Message must be a string" {:inputs inputs})))

    {:message message
     :prefix (or prefix "")}))

;; 执行工具操作
(defmethod tool-system/execute-tool :echo [_ inputs]
  (let [{:keys [message prefix]} inputs]
    {:echo-result (str prefix message)
     :original-message message
     :had-prefix (not (empty? prefix))}))

;; 为 MCP 协议格式化结果
(defmethod tool-system/format-results :echo [_ result]
  {:result [(str "Echo: " (:echo-result result))]
   :error false})

7.5.3. 步骤 3: 创建注册函数

;; 注册函数
(defn echo-tool
  "返回 echo 工具的注册 map."
  []
  (tool-system/registration-map (create-echo-tool)))

7.6. 复杂工具示例

让我们看一个与 nREPL 客户端交互的更复杂的工具:

7.6.1. 文件计数器工具

此工具计算文件中的行数, 并具有过滤功能:

(ns my-project.tools.file-counter.tool
  "用于计算文件中带过滤行数的工具."
  (:require
   [clojure-mcp.tool-system :as tool-system]
   [clojure-mcp.utils.valid-paths :as valid-paths]
   [clojure.java.io :as io]
   [clojure.string :as str]))

;; 带有依赖的工厂函数
(defn create-file-counter-tool
  "创建带有 nREPL 客户端依赖的文件计数器工具."
  [nrepl-client-atom]
  {:tool-type :file-counter
   :nrepl-client-atom nrepl-client-atom})

(defmethod tool-system/tool-name :file-counter [_]
  "file_counter")

(defmethod tool-system/tool-description :file-counter [_]
  "计算文件中带可选过滤的行数.

   参数:
   - path: 文件或目录的路径
   - pattern: 可选的用于匹配行的正则表达式模式
   - include_empty: 是否包含空行 (默认: true)")

(defmethod tool-system/tool-schema :file-counter [_]
  {:type :object
   :properties {:path {:type :string :description "文件或目录的路径"}
                :pattern {:type :string :description "用于匹配行的正则表达式"}
                :include_empty {:type :boolean :description "包含空行"}}
   :required [:path]})

(defmethod tool-system/validate-inputs :file-counter [{:keys [nrepl-client-atom]} inputs]
  (let [{:keys [path pattern include_empty]} inputs
        nrepl-client @nrepl-client-atom]

    ;; 验证路径存在且可访问
    (let [validated-path (valid-paths/validate-path-with-client path nrepl-client)
          file (io/file validated-path)]

      (when-not (.exists file)
        (throw (ex-info "File does not exist" {:path validated-path})))

      ;; 如果提供了正则表达式模式, 则进行验证
      (when pattern
        (try (re-pattern pattern)
             (catch Exception e
               (throw (ex-info "Invalid regex pattern"
                             {:pattern pattern :error (.getMessage e)})))))

      {:path validated-path
       :pattern pattern
       :include-empty (if (nil? include_empty) true include_empty)})))

(defmethod tool-system/execute-tool :file-counter [_ inputs]
  (let [{:keys [path pattern include-empty]} inputs
        file (io/file path)
        regex (when pattern (re-pattern pattern))]

    (if (.isDirectory file)
      ;; 处理目录
      (let [files (->> (file-seq file)
                       (filter #(.isFile %))
                       (filter #(str/ends-with? (.getName %) ".clj")))
            results (for [f files]
                      (let [lines (line-seq (io/reader f))
                            filtered-lines (cond->> lines
                                             (not include-empty) (remove str/blank?)
                                             regex (filter #(re-find regex %)))]
                        {:file (.getPath f)
                         :line-count (count filtered-lines)}))]
        {:type :directory
         :path path
         :total-files (count results)
         :total-lines (reduce + (map :line-count results))
         :file-results results})

      ;; 处理单个文件
      (let [lines (line-seq (io/reader file))
            filtered-lines (cond->> lines
                             (not include-empty) (remove str/blank?)
                             regex (filter #(re-find regex %)))]
        {:type :file
         :path path
         :line-count (count filtered-lines)
         :total-lines (count (line-seq (io/reader file)))}))))

(defmethod tool-system/format-results :file-counter [_ result]
  (case (:type result)
    :file
    {:result [(str "File: " (:path result))
              (str "Matching lines: " (:line-count result))
              (str "Total lines: " (:total-lines result))]
     :error false}

    :directory
    {:result (concat [(str "Directory: " (:path result))
                      (str "Files processed: " (:total-files result))
                      (str "Total matching lines: " (:total-lines result))
                      ""]
                     (map #(str "  " (:file %) ": " (:line-count %) " lines")
                          (:file-results result)))
     :error false}))

(defn file-counter-tool
  "返回文件计数器工具的注册 map."
  [nrepl-client-atom]
  (tool-system/registration-map (create-file-counter-tool nrepl-client-atom)))

7.7. 核心与工具分离

对于复杂的工具, 将核心功能与 MCP 集成部分分开:

7.7.1. 核心功能 (core.clj)

(ns my-project.tools.file-counter.core
  "核心文件计数功能."
  (:require
   [clojure.java.io :as io]
   [clojure.string :as str]))

(defn count-lines-in-file
  "在单个文件中计算行数, 带可选过滤."
  [file-path & {:keys [pattern include-empty]
                :or {include-empty true}}]
  (let [file (io/file file-path)
        lines (line-seq (io/reader file))
        regex (when pattern (re-pattern pattern))]

    (when-not (.exists file)
      (throw (ex-info "File not found" {:path file-path})))

    (let [filtered-lines (cond->> lines
                           (not include-empty) (remove str/blank?)
                           regex (filter #(re-find regex %)))]
      {:path file-path
       :line-count (count filtered-lines)
       :total-lines (count lines)})))

(defn count-lines-in-directory
  "计算目录中所有文件的行数."
  [dir-path & opts]
  (let [dir (io/file dir-path)
        files (->> (file-seq dir)
                   (filter #(.isFile %)))]

    (when-not (.exists dir)
      (throw (ex-info "Directory not found" {:path dir-path})))

    {:directory dir-path
     :files (map #(apply count-lines-in-file (.getPath %) opts) files)}))

7.7.2. 工具集成 (tool.clj)

(ns my-project.tools.file-counter.tool
  "文件计数器的 MCP 工具集成."
  (:require
   [clojure-mcp.tool-system :as tool-system]
   [clojure-mcp.utils.valid-paths :as valid-paths]
   [my-project.tools.file-counter.core :as core]
   [clojure.java.io :as io]))

;; 实现使用 execute-tool 中的核心函数
(defmethod tool-system/execute-tool :file-counter [_ inputs]
  (let [{:keys [path pattern include-empty]} inputs
        file (io/file path)]
    (if (.isDirectory file)
      (core/count-lines-in-directory path
                                     :pattern pattern
                                     :include-empty include-empty)
      (core/count-lines-in-file path
                                :pattern pattern
                                :include-empty include-empty))))

7.8. 测试你的工具

7.8.1. 单元测试核心功能

(ns my-project.tools.file-counter.core-test
  (:require
   [clojure.test :refer :all]
   [my-project.tools.file-counter.core :as core]
   [clojure.java.io :as io]))

(deftest test-count-lines
  (let [temp-file (io/file "test-file.txt")]
    (try
      (spit temp-file "line 1\nline 2\n\nline 4")
      (let [result (core/count-lines-in-file (.getPath temp-file))]
        (is (= 4 (:total-lines result)))
        (is (= 4 (:line-count result))))

      (let [result (core/count-lines-in-file (.getPath temp-file)
                                             :include-empty false)]
        (is (= 3 (:line-count result))))

      (finally
        (.delete temp-file)))))

7.8.2. 使用工具系统进行集成测试

(ns my-project.tools.file-counter.tool-test
  (:require
   [clojure.test :refer :all]
   [clojure-mcp.tool-system :as tool-system]
   [clojure-mcp.config :as config]
   [my-project.tools.file-counter.tool :as tool]
   [clojure.java.io :as io]))

(deftest test-tool-integration
  (let [test-dir (System/getProperty "user.dir")
        mock-client (atom {})
        _ (config/set-config! mock-client :nrepl-user-dir test-dir)
        _ (config/set-config! mock-client :allowed-directories [test-dir])
        tool-config (tool/create-file-counter-tool mock-client)

        ;; 测试验证
        inputs {:path "README.md"}
        validated (tool-system/validate-inputs tool-config inputs)

        ;; 测试执行
        result (tool-system/execute-tool tool-config validated)

        ;; 测试格式化
        formatted (tool-system/format-results tool-config result)]

    (is (string? (:path validated)))
    (is (map? result))
    (is (vector? (:result formatted)))
    (is (false? (:error formatted)))))

7.8.3. 手动 REPL 测试

(comment
  ;; 在 REPL 中测试
  (require '[my-project.tools.file-counter.tool :as tool])
  (require '[clojure-mcp.tool-system :as tool-system])
  (require '[clojure-mcp.config :as config])

  ;; 创建带有正确配置设置的工具
  (def test-dir (System/getProperty "user.dir"))
  (def mock-client (atom {}))
  (config/set-config! mock-client :nrepl-user-dir test-dir)
  (config/set-config! mock-client :allowed-directories [test-dir])
  (def tool-instance (tool/file-counter-tool mock-client))

  ;; 直接测试工具函数
  (def tool-fn (:tool-fn tool-instance))
  (tool-fn nil {"path" "README.md"}
           (fn [result error]
             (println "Result:" result)
             (println "Error:" error))))

7.9. 与你的自定义服务器集成

要将你基于 multimethod 的工具添加到你的自定义 MCP 服务器, 请参阅 [创建你自己的自定义 MCP 服务器](custom-mcp-server.md).

这是一个快速示例:

(ns my-company.custom-mcp-server
  (:require
   [clojure-mcp.core :as core]
   [clojure-mcp.main :as main]
   ;; 你的自定义工具
   [my-project.tools.file-counter.tool :as file-counter-tool]
   [my-project.tools.echo.tool :as echo-tool]))

(defn my-tools [nrepl-client-atom]
  ;; 将 main 工具与你的自定义工具结合
  (concat
   (main/my-tools nrepl-client-atom)
   [(echo-tool/echo-tool)
    (file-counter-tool/file-counter-tool nrepl-client-atom)]))

> 💡 提示: 始终检查 src/clojure_mcp/main.clj 以了解标准工具是如何集成和组织的.

7.10. 最佳实践

7.10.1. 1. 错误处理

始终提供带有上下文的详细错误消息:

(defmethod tool-system/validate-inputs :my-tool [_ inputs]
  (when-not (:required-param inputs)
    (throw (ex-info "Missing required parameter: required-param"
                    {:inputs inputs
                     :error-details "This parameter is needed for processing"}))))

7.10.2. 2. 输入验证

彻底验证所有输入:

(defmethod tool-system/validate-inputs :my-tool [_ inputs]
  (let [{:keys [file-path timeout]} inputs]
    ;; 检查必需参数
    (when-not file-path
      (throw (ex-info "Missing file path" {:inputs inputs})))

    ;; 验证类型
    (when (and timeout (not (pos-int? timeout)))
      (throw (ex-info "Timeout must be a positive integer" {:inputs inputs})))

    ;; 规范化并返回
    {:file-path (str file-path)
     :timeout (or timeout 5000)}))

7.10.3. 3. 结果格式化

始终将结果作为字符串向量返回:

(defmethod tool-system/format-results :my-tool [_ result]
  {:result [(str "Processed: " (:file result))
            (str "Lines: " (:line-count result))]
   :error false})

7.10.4. 4. 配置和依赖

使用工厂函数模式进行依赖注入:

(defn create-my-tool
  "创建带有可配置选项的工具."
  [nrepl-client-atom & {:keys [timeout max-files]
                        :or {timeout 30000 max-files 100}}]
  {:tool-type :my-tool
   :nrepl-client-atom nrepl-client-atom
   :timeout timeout
   :max-files max-files})

7.10.5. 5. 文档

为 AI 助手提供全面的描述:

(defmethod tool-system/tool-description :my-tool [_]
  "工具功能的详细描述.

   参数:
   - param1: 第一个参数的描述
   - param2: 第二个参数的描述

   示例:
   - 基本用法: tool({param1: 'value'})
   - 高级用法: tool({param1: 'value', param2: 'option'})

   注意: 任何重要的使用说明或限制.")

7.10.6. 6. 关注点分离

  • 将核心逻辑保留在 core.clj 文件中
  • tool.clj 仅用于 MCP 集成
  • 为核心功能编写单元测试
  • 为工具行为编写集成测试

这种架构使你的工具:

  • 可独立测试
  • 可在 MCP 上下文之外重用
  • 更易于维护和调试
  • 更加模块化和可组合

7.11. 选择你的方法

multimethod 系统功能强大, 但并非总是必需. 考虑:

  • 当你需要它们提供的结构和验证时, 使用 multimethods
  • 当你需要可移植性或简单性时, 使用简单的 map
  • 混合方法 - 你可以在同一个服务器中同时使用两者!

无论你选择哪种方法, 关键是创建能增强你开发工作流的工具. multimethod 系统为复杂工具提供了坚实的基础, 同时保持了整个 ClojureMCP 生态系统的一致性.

8. 创建资源

本指南解释了如何使用简单的 Clojure map 为 ClojureMCP 创建资源. 这是创建资源的标准方法, 无论你是在 ClojureMCP 内部使用它们还是作为独立组件.

8.1. 什么是资源?

MCP 中的资源是客户端可以访问的只读内容. 与工具 (执行操作) 和提示 (生成对话) 不同, 资源提供静态或动态内容, 如文档, 配置文件或项目信息.

8.2. 资源注册 Map Schema

资源被定义为一个具有以下结构的 Clojure map:

{:url         "custom://resource-id"    ; String: 资源的唯一 URI
 :name        "Resource Name"           ; String: 人类可读的名称
 :description "Resource description"    ; String: 此资源提供的内容
 :mime-type   "text/plain"             ; String: 内容的 MIME 类型
 :resource-fn (fn [exchange request callback] ...)} ; Function: 资源实现

8.2.1. URL 字段

:url 字段应该是一个唯一的 URI, 用于标识你的资源. 常见模式:

  • custom://my-resource - 你的资源的自定义方案
  • file://path/to/file - 基于文件的资源
  • project://component - 特定于项目的资源

8.2.2. MIME 类型字段

资源的常见 MIME 类型:

  • "text/plain" - 纯文本文件
  • "text/markdown" - Markdown 文档
  • "application/json" - JSON 数据
  • "text/html" - HTML 内容
  • "text/csv" - CSV 数据

8.2.3. 资源函数签名

:resource-fn 具有以下签名:

(fn [exchange request callback]
  ;; 资源实现
  )

其中:

  • exchange - 对于简单资源通常被忽略 (MCP exchange 对象)
  • request - 请求对象 (对于简单资源通常被忽略)
  • callback - 一个用内容调用的函数

8.2.4. 回调函数

回调函数需要一个参数 - 一个字符串向量:

(callback ["content line 1" "content line 2" ...])

对于单字符串内容, 将其包装在向量中:

(callback ["This is my resource content"])

8.3. 简单示例: 静态文本资源

这是一个静态资源的简单示例:

(ns documentation-resources.core)

(def coding-guidelines-resource
  {:url "custom://coding-guidelines"

   :name "Coding Guidelines"

   :description "项目编码标准和最佳实践"

   :mime-type "text/markdown"

   :resource-fn (fn [_exchange _request callback]
                  (callback
                   ["# 编码指南

## 命名约定
- 函数名使用 kebab-case
- 协议和记录使用 PascalCase
- 私有函数以 ~-~ 为前缀

## 代码结构
- 保持函数在 20 行以下
- 每个函数一个概念
- 清晰, 描述性的名称

## 测试
- 为所有公共函数编写测试
- 在适当的地方使用生成测试
- 目标代码覆盖率 80%"]))})

8.4. 示例: 基于文件的资源

这是一个从文件中读取内容的示例:

(ns file-resources.core
  (:require [clojure.java.io :as io]
            [clojure.string :as str]))

(defn read-file-safely
  "安全地读取文件并将其内容作为字符串向量返回."
  [file-path]
  (try
    (with-open [reader (io/reader file-path)]
      [(slurp reader)])
    (catch Exception e
      [(str "Error reading file: " (.getMessage e))])))

(def readme-resource
  {:url "custom://project-readme"

   :name "README.md"

   :description "包含设置说明的项目 README 文件"

   :mime-type "text/markdown"

   :resource-fn (fn [_exchange _request callback]
                  (let [content (read-file-safely "README.md")]
                    (callback content)))})

8.5. 示例: 带有当前数据的动态资源

资源可以提供随时间变化的动态内容:

(ns dynamic-resources.core
  (:require [clojure.data.json :as json])
  (:import [java.time LocalDateTime]
           [java.time.format DateTimeFormatter]))

(defn get-system-info []
  {:timestamp (.format (LocalDateTime/now)
                       (DateTimeFormatter/ISO_LOCAL_DATE_TIME))
   :java-version (System/getProperty "java.version")
   :os-name (System/getProperty "os.name")
   :user-dir (System/getProperty "user.dir")
   :available-processors (.availableProcessors (Runtime/getRuntime))
   :free-memory (.freeMemory (Runtime/getRuntime))
   :total-memory (.totalMemory (Runtime/getRuntime))})

(def system-info-resource
  {:url "custom://system-info"

   :name "System Information"

   :description "当前系统和 JVM 信息"

   :mime-type "application/json"

   :resource-fn (fn [_exchange _request callback]
                  (try
                    (let [info (get-system-info)
                          json-str (json/write-str info {:indent true})]
                      (callback [json-str]))
                    (catch Exception e
                      (callback [(str "Error generating system info: "
                                     (.getMessage e))]))))})

8.6. 高级示例: 多部分资源

资源可以在向量中返回多个字符串以用于多部分内容:

(ns report-resources.core
  (:require [clojure.string :as str]))

(defn generate-project-report [project-data]
  ;; 返回报告部分的向量
  [(str "# 项目报告\n\n生成于: " (java.util.Date.))
   "\n## 概述\n"
   (str "项目: " (:name project-data))
   (str "版本: " (:version project-data))
   "\n## 依赖\n"
   (str/join "\n" (map #(str "- " %) (:dependencies project-data)))
   "\n## 统计\n"
   (str "文件数: " (:file-count project-data))
   (str "代码行数: " (:loc project-data))])

(def project-report-resource
  {:url "custom://project-report"

   :name "Project Report"

   :description "全面的项目分析报告"

   :mime-type "text/markdown"

   :resource-fn (fn [_exchange _request callback]
                  (let [project-data {:name "my-project"
                                     :version "1.0.0"
                                     :dependencies ["org.clojure/clojure"
                                                   "ring/ring-core"
                                                   "compojure"]
                                     :file-count 42
                                     :loc 3500}
                        report-sections (generate-project-report project-data)]
                    (callback report-sections)))})

8.7. 最佳实践

  1. 唯一的 URL: 确保每个资源都有一个唯一的 URL 以避免冲突.
  2. 错误处理: 始终优雅地处理错误并在回调中返回错误消息.
  3. 内容格式: 将内容作为字符串向量返回, 即使是单字符串内容.
  4. MIME 类型: 使用适当的 MIME 类型来帮助客户端正确处理内容.
  5. 性能: 如果资源被频繁访问, 则缓存昂贵的计算.
  6. 文档: 为每个资源包含的内容提供清晰的描述.

8.8. 资源如何被访问

与工具和提示不同, 资源通常在没有参数的情况下被访问. 客户端只需通过其 URL 请求资源.

8.8.1. 客户端 (MCP 客户端)

客户端发送类似以下的请求:

{
  "method": "resources/read",
  "params": {
    "uri": "custom://coding-guidelines"
  }
}

8.8.2. 服务器端 (你的资源)

MCP 服务器调用你的资源函数:

(resource-fn exchange request callback)

对于简单资源, exchangerequest 参数通常被忽略, 因为资源由其 URL 标识, 并且通常不需要额外的参数.

8.8.3. 高级: 使用请求对象

虽然大多数资源忽略请求参数, 但高级用例可能会检查它:

(def advanced-resource
  {:url "custom://versioned-doc"
   :name "Versioned Documentation"
   :description "可以显示不同版本的文档"
   :mime-type "text/markdown"
   :resource-fn (fn [exchange request callback]
                  ;; 实际上, 请求对象的结构取决于 MCP
                  ;; 实现, 但如果支持, 你可以提取版本信息
                  (let [version "latest" ; 默认版本
                        content (case version
                                  "v1" "# Version 1 Documentation..."
                                  "v2" "# Version 2 Documentation..."
                                  "latest" "# Latest Documentation...")]
                    (callback [content])))})

8.9. 测试你的资源

你可以独立地测试你的资源函数:

(defn test-resource []
  ;; 创建一个打印结果的测试回调
  (let [test-callback (fn [result]
                        (println "Resource content:")
                        (doseq [line result]
                          (println line)))]
    ;; 测试资源
    ((:resource-fn coding-guidelines-resource) nil nil test-callback)))

8.10. 与 ClojureMCP 集成

要将你的资源与 ClojureMCP 服务器一起使用:

;; 在服务器的代码中
(require '[clojure-mcp.core :as mcp])
(require '[documentation-resources.core :as docs])

(defn setup-server [mcp-server]
  ;; 注册你的资源
  (mcp/add-resource mcp-server docs/coding-guidelines-resource))

8.11. 常见资源模式

8.11.1. 1. 配置资源

提供配置或设置信息:

(def config-resource
  {:url "custom://app-config"
   :name "Application Configuration"
   :description "当前应用程序设置"
   :mime-type "application/json"
   :resource-fn (fn [_ _ callback]
                  (callback [(slurp "config.json")]))})

8.11.2. 2. 生成的文档

动态创建文档:

(defn generate-api-docs [api-endpoints]
  (str "# API Documentation\n\n"
       (str/join "\n\n"
                 (map (fn [{:keys [method path description]}]
                        (format "## %s %s\n%s" method path description))
                      api-endpoints))))

(def api-docs-resource
  {:url "custom://api-docs"
   :name "API Documentation"
   :description "自动生成的 API 文档"
   :mime-type "text/markdown"
   :resource-fn (fn [_ _ callback]
                  (let [endpoints [{:method "GET" :path "/users"
                                   :description "列出所有用户"}
                                  {:method "POST" :path "/users"
                                   :description "创建一个新用户"}]
                        docs (generate-api-docs endpoints)]
                    (callback [docs])))})

8.11.3. 3. 聚合资源

将多个来源合并为一个资源:

(defn read-files [file-paths]
  (map #(try (slurp %)
             (catch Exception e (str "Error reading " % ": " (.getMessage e))))
       file-paths))

(def combined-docs-resource
  {:url "custom://all-docs"
   :name "All Documentation"
   :description "来自多个文件的合并文档"
   :mime-type "text/plain"
   :resource-fn (fn [_ _ callback]
                  (let [files ["README.md" "CONTRIBUTING.md" "LICENSE"]
                        contents (read-files files)
                        combined (str/join "\n\n---\n\n" contents)]
                    (callback [combined])))})

8.12. 资源发现

当客户端连接到你的 MCP 服务器时, 它可以发现可用的资源. 你的资源将出现在资源列表中, 带有它们的 URL, 名称和描述, 使其易于用户查找和访问他们需要的内容.

无论你是在 ClojureMCP 内部使用资源还是作为独立组件, 结构和实现都保持不变, 使它们具有高度的可移植性和可重用性.

9. 创建提示

本指南解释了如何使用简单的 Clojure map 为 ClojureMCP 创建提示. 这是创建提示的标准方法, 无论你是在 ClojureMCP 内部使用它们还是作为独立组件.

9.1. 什么是提示?

MCP 中的提示生成对话上下文, 帮助 AI 助手理解特定任务或工作流. 它们可以提供指令, 设置场景, 或引导助手完成复杂操作.

9.2. 提示注册 Map Schema

提示被定义为一个具有以下结构的 Clojure map:

{:name        "prompt-name"         ; String: 提示的唯一标识符
 :description "Prompt description"  ; String: 人类可读的描述
 :arguments   [...]                 ; Vector: 参数定义 (可以为空)
 :prompt-fn   (fn [exchange request-args callback] ...)} ; Function: 提示实现

9.2.1. 参数字段

:arguments 字段是一个 map 的向量, 每个 map 定义一个参数:

{:name        "arg-name"           ; String: 参数标识符
 :description "Argument description" ; String: 人类可读的描述
 :required?   true}                 ; Boolean: 参数是否必需

9.2.2. 提示函数签名

:prompt-fn 具有以下签名:

(fn [exchange request-args callback]
  ;; 提示实现
  )

其中:

  • exchange - 对于简单提示通常被忽略 (MCP exchange 对象)
  • request-args - 一个包含提示参数的 Clojure map, 键为字符串
  • callback - 一个用结果调用的函数

9.2.3. 回调函数

回调函数需要一个参数 - 一个具有以下结构的 map:

(callback {:description "Description of the result"
           :messages    [{:role :user :content "Message content"}
                         {:role :assistant :content "Response content"}]})

其中:

  • :description - 一个描述提示产生内容的字符串
  • :messages - 一个消息 map 的向量, 每个 map 包含:
    • :role - :user, :assistant, 或 :system
    • :content - 消息的文本内容

9.3. 简单示例: 静态提示

这是一个没有参数的提示的简单示例:

(ns coding-prompts.core)

(def code-review-prompt
  {:name "code_review_guide"

   :description "提供进行彻底代码审查的指南"

   :arguments [] ; 不需要参数

   :prompt-fn (fn [_exchange _request-args callback]
                (callback
                 {:description "代码审查指南"
                  :messages [{:role :assistant
                              :content "审查代码时, 请关注:

1. **正确性**: 代码是否做了它应该做的事情?
2. **清晰性**: 代码是否易于理解和维护?
3. **性能**: 是否有明显的性能问题?
4. **安全性**: 是否有任何安全漏洞?
5. **风格**: 代码是否遵循项目约定?

对于发现的每个问题:
- 解释为什么它是一个问题
- 建议一个具体的改进
- 在有帮助时提供代码示例"}]}))})

9.4. 带参数的示例: 命名空间工作流

这是一个使用参数来自定义提示的示例:

(ns workflow-prompts.core
  (:require [clojure.string :as str]))

(defn create-namespace-sync-content [namespace-name]
  (format "请将 REPL 与命名空间同步: %s

遵循以下步骤:
1. 使用 glob 或 grep 定位命名空间文件
2. 使用以下命令加载它: (require '[%s] :reload)
3. 使用以下命令切换到它: (in-ns '%s)
4. 验证命名空间是否已正确加载
5. 使用 (dir %s) 列出可用的函数

这确保 REPL 拥有该命名空间的最新版本."
          namespace-name namespace-name namespace-name namespace-name))

(def namespace-sync-prompt
  {:name "sync_namespace"

   :description "生成用于将 REPL 与特定命名空间同步的指令"

   :arguments [{:name "namespace"
                :description "完全限定的 Clojure 命名空间 (例如, 'myapp.core')"
                :required? true}]

   :prompt-fn (fn [_exchange request-args callback]
                (let [namespace-name (get request-args "namespace")]
                  (if namespace-name
                    (callback
                     {:description (str "将 REPL 与命名空间同步: " namespace-name)
                      :messages [{:role :user
                                  :content (create-namespace-sync-content namespace-name)}]})
                    ;; 处理缺失的参数
                    (callback
                     {:description "错误: 缺少命名空间参数"
                      :messages [{:role :assistant
                                  :content "错误: 'namespace' 参数是必需的但未提供."}]}))))})

9.5. 高级示例: 多消息对话

提示可以返回多个消息来设置对话上下文:

(ns teaching-prompts.core)

(def refactoring-lesson-prompt
  {:name "teach_refactoring"

   :description "关于重构 Clojure 代码的交互式课程"

   :arguments [{:name "code_snippet"
                :description "要重构的代码"
                :required? true}
               {:name "focus_area"
                :description "要关注的具体方面 (例如, 'performance', 'readability')"
                :required? false}]

   :prompt-fn (fn [_exchange request-args callback]
                (let [code (get request-args "code_snippet")
                      focus (get request-args "focus_area" "general improvement")]
                  (if code
                    (callback
                     {:description "交互式重构课程"
                      :messages [{:role :user
                                  :content (str "我有一段需要重构的代码:\n\n~~~clojure\n"
                                               code "\n~~~\n\n"
                                               "关注领域: " focus)}
                                 {:role :assistant
                                  :content "我将帮助你重构这段代码. 让我先分析一下..."}
                                 {:role :user
                                  :content "请逐步解释你的分析并建议改进."}]})
                    (callback
                     {:description "错误: 缺少代码"
                      :messages [{:role :assistant
                                  :content "请提供要重构的代码片段."}]}))))})

9.6. 实用函数

你可以创建辅助函数来简化提示的创建:

(defn simple-prompt
  "创建一个没有参数且只有一条消息的简单提示."
  [name description content]
  {:name name
   :description description
   :arguments []
   :prompt-fn (fn [_ _ callback]
                (callback
                 {:description description
                  :messages [{:role :assistant :content content}]}))})

;; 用法示例
(def style-guide
  (simple-prompt "clojure_style"
                 "Clojure 风格指南"
                 "遵循以下 Clojure 风格指南:
                  - 函数名使用 kebab-case
                  - 优先使用线程宏以提高可读性
                  - 保持函数小而专注"))

9.7. 最佳实践

  1. 清晰的描述: 为提示及其参数提供清晰的描述.
  2. 参数验证: 在使用必需参数之前, 始终对其进行验证.
  3. 有意义的消息: 结构化消息, 为 AI 助手提供清晰的上下文.
  4. 错误处理: 优雅地处理缺失或无效的参数.
  5. 消息角色: 使用适当的角色 (:user, :assistant, :system) 来结构化对话.
  6. 字符串键: 记住 request-args 使用字符串键, 而不是关键字.

9.8. 测试你的提示

你可以独立地测试你的提示函数:

(defn test-namespace-prompt []
  ;; 创建一个打印结果的测试回调
  (let [test-callback (fn [result]
                        (println "Description:" (:description result))
                        (println "Messages:")
                        (doseq [msg (:messages result)]
                          (println (str "  " (:role msg) ": " (:content msg)))))]
    ;; 使用有效输入进行测试
    ((:prompt-fn namespace-sync-prompt) nil {"namespace" "myapp.core"} test-callback)

    ;; 使用缺失输入进行测试
    ((:prompt-fn namespace-sync-prompt) nil {} test-callback)))

9.9. 参数如何传递

当客户端 (如 Claude Desktop) 调用提示时, 它会将参数作为 map 传递. 以下是流程的工作方式:

9.9.1. 客户端 (MCP 客户端)

客户端发送一个带参数的请求, 如下所示:

{
  "method": "prompts/get",
  "params": {
    "name": "sync_namespace",
    "arguments": {
      "namespace": "myapp.core"
    }
  }
}

9.9.2. 服务器端 (你的提示)

MCP 服务器将其转换为 Clojure map 并传递给你的提示函数:

;; 你的 prompt-fn 接收到:
;; request-args = {"namespace" "myapp.core"}

9.9.3. 示例用法流程

  1. 客户端列出可用提示 - 发现 "syncnamespace" 及其参数
  2. 客户端使用特定的参数值调用提示:

    Prompt: sync_namespace
    Arguments: namespace="myapp.utils"
    
  3. 服务器调用你的 prompt-fn:

    (prompt-fn exchange {"namespace" "myapp.utils"} callback)
    
  4. 你的提示根据参数生成消息
  5. 客户端接收生成的消息 并在对话中使用它们

9.9.4. 测试参数传递

这是一个更完整的测试示例, 显示了不同的参数场景:

(defn test-prompt-parameters []
  (let [test-callback (fn [result]
                        (println "\nResult:")
                        (println result))]

    ;; 测试 1: 提供了所有必需的参数
    (println "Test 1: Valid parameters")
    ((:prompt-fn refactoring-lesson-prompt)
     nil
     {"code_snippet" "(defn add [a b] (+ a b))"
      "focus_area" "performance"}
     test-callback)

    ;; 测试 2: 省略了可选参数 (使用默认值)
    (println "\nTest 2: Optional parameter omitted")
    ((:prompt-fn refactoring-lesson-prompt)
     nil
     {"code_snippet" "(defn add [a b] (+ a b))"}
     test-callback)

    ;; 测试 3: 缺少必需参数 (错误情况)
    (println "\nTest 3: Missing required parameter")
    ((:prompt-fn refactoring-lesson-prompt)
     nil
     {"focus_area" "readability"}
     test-callback)))

9.10. 与 ClojureMCP 集成

要将你的提示与 ClojureMCP 服务器一起使用:

;; 在服务器的代码中
(require '[clojure-mcp.core :as mcp])
(require '[workflow-prompts.core :as workflow])

(defn setup-server [mcp-server]
  ;; 注册你的提示
  (mcp/add-prompt mcp-server workflow/namespace-sync-prompt))

9.11. 常见提示模式

9.11.1. 1. 系统指令

提供一致指令或上下文的提示:

(def coding-standards
  {:name "coding_standards"
   :description "特定于项目的编码标准"
   :arguments []
   :prompt-fn (fn [_ _ callback]
                (callback
                 {:description "编码标准"
                  :messages [{:role :system
                              :content "将这些编码标准应用于所有代码..."}]}))})

9.11.2. 2. 基于模板的提示

用参数填充模板的提示:

(defn template-prompt [template-string]
  (fn [_ request-args callback]
    (let [filled (reduce (fn [s [k v]]
                          (str/replace s (str "{{" k "}}") (str v)))
                        template-string
                        request-args)]
      (callback
       {:description "已填充的模板"
        :messages [{:role :user :content filled}]}))))

(def bug-report-prompt
  {:name "bug_report"
   :description "生成一个 bug 报告"
   :arguments [{:name "component" :description "受影响的组件" :required? true}
               {:name "error" :description "错误消息" :required? true}]
   :prompt-fn (template-prompt "Bug in {{component}}: {{error}}\nPlease investigate.")})

9.11.3. 3. 条件提示

根据参数变化的提示:

(def adaptive-helper
  {:name "adaptive_helper"
   :description "根据用户专业水平提供帮助"
   :arguments [{:name "topic" :description "要解释的主题" :required? true}
               {:name "level" :description "专业水平: beginner, intermediate, expert" :required? false}]
   :prompt-fn (fn [_ request-args callback]
                (let [topic (get request-args "topic")
                      level (get request-args "level" "intermediate")
                      intro (case level
                              "beginner" "让我用简单的术语解释一下:"
                              "expert" "这是一个详细的技术解释:"
                              "让我提供一个全面的概述:")]
                  (callback
                   {:description (str "关于 " topic " 的帮助")
                    :messages [{:role :user
                                :content (str intro "\n解释 " topic)}]})))})

无论你是在 ClojureMCP 内部使用提示还是作为独立组件, 结构和实现都保持不变, 使它们具有高度的可移植性和可重用性.

10. 在 ClojureMCP 中配置资源

ClojureMCP 中的资源提供了一种通过模型上下文协议向 LLM 暴露文件和内容的方式. 它们允许 LLM 访问项目文档, 配置文件和其他重要资源.

10.1. 配置结构

资源在你的 .clojure-mcp/config.edn 文件的 :resources 键下配置. 每个资源都是一个 map 条目, 资源名称作为键.

:resources {"resource-name" {:description "此资源提供的内容"
                             :file-path "path/to/file.md"}}

10.2. 资源字段

10.2.1. 必填字段

  • :description - 对资源包含内容的清晰描述. 这有助于 LLM 理解何时访问该资源.
  • :file-path - 作为资源提供的文件的路径

10.2.2. 可选字段

  • :url - 资源的自定义 URL (默认为 custom://kebab-case-name)
  • :mime-type - 资源的 MIME 类型 (如果未指定, 则自动检测)

10.3. 自动检测功能

10.3.1. MIME 类型检测

如果未指定 :mime-type, ClojureMCP 会使用 Apache Tika 根据文件扩展名自动检测:

  • .mdtext/markdown
  • .clj, .cljs, .cljctext/x-clojure
  • .ednapplication/edn
  • .jsonapplication/json
  • .txttext/plain
  • 以及更多…

10.3.2. URL 生成

如果未指定 :url, 它会根据资源名称自动生成:

  • 转换为小写
  • 将非字母数字字符替换为连字符
  • 示例: "My Resource.txt""custom://my-resource-txt"

10.4. 示例

10.4.1. 1. 简单资源

:resources {"deps.edn" {:description "项目依赖配置"
                        :file-path "deps.edn"}}

10.4.2. 2. 多个资源

:resources {"deps.edn" {:description "项目依赖"
                        :file-path "deps.edn"}

            "build.clj" {:description "构建配置脚本"
                         :file-path "build.clj"}

            "CHANGELOG.md" {:description "项目变更日志和版本历史"
                            :file-path "CHANGELOG.md"}

            "API.md" {:description "API 文档"
                      :file-path "doc/API.md"}}

10.4.3. 3. 自定义 URL 和 MIME 类型

:resources {"custom-data" {:description "自定义数据文件"
                           :file-path "data/custom.dat"
                           :url "special://my-custom-data"
                           :mime-type "application/octet-stream"}}

10.4.4. 4. 覆盖默认资源

你可以通过使用相同的名称来覆盖内置资源:

:resources {"README.md" {:description "增强的项目文档"
                         :file-path "doc/README-extended.md"}

            "PROJECT_SUMMARY.md" {:description "详细的项目摘要"
                                  :file-path ".clojure-mcp/PROJECT_SUMMARY.md"}}

10.5. 文件路径

10.5.1. 相对路径

路径相对于 nrepl-user-dir (通常是你的项目根目录) 进行解析:

:file-path "doc/guide.md"        ; 解析为 <project-root>/doc/guide.md
:file-path ".clojure-mcp/info.md" ; 解析为 <project-root>/.clojure-mcp/info.md

10.5.2. 绝对路径

绝对路径按原样使用:

:file-path "/usr/local/share/doc/myapp/manual.md"

10.6. 默认资源

ClojureMCP 包括几个自动可用的内置资源:

  1. PROJECT_SUMMARY.md - 用于 LLM 上下文的项目摘要文档
    • 路径: PROJECT_SUMMARY.md
    • 描述: "托管 REPL 的项目的 Clojure 项目摘要文档"
  2. README.md - 项目 README
    • 路径: README.md
    • 描述: "当前 Clojure 项目的 README 文档"
  3. CLAUDE.md - Claude 特定说明
    • 路径: CLAUDE.md
    • 描述: "当前项目的 Claude 说明文档"
  4. LLM_CODE_STYLE.md - 代码风格指南
    • 路径: LLM_CODE_STYLE.md
    • 描述: "为当前项目编写 Clojure 代码的指南"
  5. Clojure Project Info - 动态资源
    • 从项目分析中生成
    • 描述: "有关当前 Clojure 项目结构, REPL 环境和依赖项的信息"

10.7. 过滤资源

使用启用/禁用列表控制哪些资源可用:

;; 仅启用特定资源
:enable-resources ["README.md" "deps.edn" "API.md"]

;; 或禁用特定资源
:disable-resources ["LLM_CODE_STYLE.md" "CLAUDE.md"]

注意: 过滤是在 core.clj 中的 MCP 服务器级别应用的, 而不是在资源创建时.

10.8. 资源行为

10.8.1. 缺失文件

指向不存在文件的资源在创建期间会自动被过滤掉. 它们不会出现在可用资源列表中.

10.8.2. 文件更改

资源在被访问时动态读取文件内容, 因此对文件的更改会立即反映出来, 无需重新启动服务器.

10.8.3. 访问控制

资源遵守 allowed-directories 配置 - 允许目录之外的文件不能作为资源提供.

10.9. 最佳实践

  1. 描述性名称 - 使用清晰, 描述性的资源名称, 以表明内容类型
  2. 有用的描述 - 编写有助于 LLM 理解何时访问每个资源的描述
  3. 项目文档 - 将关键项目文档作为资源包括:
    • 架构图
    • API 文档
    • 配置指南
    • 开发工作流
  4. 保持文件更新 - 由于资源动态读取文件, 请保持资源文件为最新状态
  5. 逻辑组织 - 将相关资源分组并使用一致的命名:

    "api/rest.md" {:description "REST API 文档" ...}
    "api/graphql.md" {:description "GraphQL schema 文档" ...}
    "api/examples.md" {:description "API 使用示例" ...}
    
  6. 谨慎覆盖 - 覆盖默认值时, 确保你的替换提供等效或更好的信息

10.10. 用例

10.10.1. 项目文档

:resources {"ARCHITECTURE.md" {:description "系统架构概述"
                               :file-path "doc/ARCHITECTURE.md"}
            "DATABASE.md" {:description "数据库 schema 和迁移"
                          :file-path "doc/DATABASE.md"}}

10.10.2. 配置示例

:resources {"config-dev.edn" {:description "开发配置示例"
                              :file-path "config/dev.edn"}
            "config-prod.edn" {:description "生产配置模板"
                               :file-path "config/prod.edn"}}

10.10.3. 测试数据

:resources {"test-fixtures" {:description "测试 fixture 数据"
                             :file-path "test/fixtures/data.edn"}
            "test-schema" {:description "测试数据库 schema"
                          :file-path "test/resources/schema.sql"}}

10.11. 故障排除

10.11.1. 资源未出现

  • 检查文件是否存在于指定路径
  • 验证路径是否在 allowed-directories
  • 检查启用/禁用资源过滤器

10.11.2. 错误的 MIME 类型

  • 在配置中明确指定 :mime-type
  • 检查文件扩展名是否被 Apache Tika 识别

10.11.3. 路径解析问题

  • 对项目外的文件使用绝对路径
  • 记住相对路径是从 nrepl-user-dir 开始的
  • 使用 (System/getProperty "user.dir") 检查工作目录

11. 在 ClojureMCP 中配置提示

ClojureMCP 中的提示允许你定义可重用的模板, 以指导 LLM 的交互. 它们支持 Mustache 模板以实现动态内容, 并可通过 .clojure-mcp/config.edn 文件进行配置.

11.1. 配置结构

提示在你的配置文件中的 :prompts 键下进行配置. 每个提示都是一个 map 条目, 提示名称作为键.

:prompts {"prompt-name" {:description "此提示的功能"
                         :args [{:name "param1"
                                :description "param1 的描述"
                                :required? true}]
                         :content "包含 {{param1}} 的模板内容"}}

11.2. 提示字段

11.2.1. 必填字段

  • :description - 对提示功能的清晰描述. 在列出可用提示时会显示给 LLM.

11.2.2. 可选字段

  • :args - 参数定义的向量. 每个参数都有:
    • :name - 参数名称 (在模板中使用)
    • :description - 此参数的用途
    • :required? - 布尔值, 指示是否必需 (默认为 false)
  • :content - 内联模板内容 (使用此项或 :file-path)
  • :file-path - 模板文件的路径 (使用此项或 :content)

11.3. 模板语法

提示通过 Pogonos 库使用 Mustache 模板:

11.3.1. 基本变量替换

Hello {{name}}, welcome to {{project}}!

11.3.2. 条件部分

{{#error}}
Error occurred: {{error}}
{{/error}}

{{^error}}
No errors!
{{/error}}

11.3.3. 列表/集合

Files to review:
{{#files}}
- {{.}}
{{/files}}

11.4. 示例

11.4.1. 1. 简单的内联提示

:prompts {"greeting" {:description "生成个性化问候"
                     :args [{:name "name" :description "用户姓名" :required? true}]
                     :content "Hello {{name}}! How can I help you today?"}}

11.4.2. 2. 代码审查提示

:prompts {"code-review" {:description "为指定文件生成代码审查"
                         :args [{:name "file" :description "要审查的文件路径" :required? true}
                                {:name "focus" :description "审查重点领域" :required? false}]
                         :content "请对以下文件进行彻底的代码审查: {{file}}

{{#focus}}
重点领域: {{focus}}
{{/focus}}

分析:
1. 代码风格和习惯用法
2. 性能
3. 错误处理
4. 测试需求"}}

11.4.3. 3. 基于文件的提示

.clojure-mcp/prompts/debug-help.md 创建一个文件:

我需要帮助调试 {{namespace}} 中的一个问题.

{{#error}}
错误消息是:
```
{{error}}
```
{{/error}}

{{#context}}
附加信息:
{{context}}
{{/context}}

请帮助我:
1. 理解根本原因
2. 建议一个修复方案
3. 验证解决方案

然后在配置中引用它:

:prompts {"debug-help" {:description "帮助调试 Clojure 命名空间中的问题"
                        :args [{:name "namespace" :description "有问题的命名空间" :required? true}
                               {:name "error" :description "错误消息" :required? false}
                               {:name "context" :description "附加信息" :required? false}]
                        :file-path ".clojure-mcp/prompts/debug-help.md"}}

11.5. 模板行为

11.5.1. 缺失变量

如果模板引用的变量未提供, 它将渲染为空字符串:

  • 模板: "Hello {{name}}, age: {{age}}"
  • 仅提供 {:name "Alice"}"Hello Alice, age: "

11.5.2. 多余参数

提供的参数未在模板中使用会被简单忽略 - 不会发生错误.

11.6. 覆盖默认提示

你可以通过在配置中使用相同的名称来覆盖内置提示:

:prompts {"clojure_repl_system_prompt" {:description "自定义系统提示"
                                        :args []
                                        :content "我的自定义系统指令..."}}

11.7. 过滤提示

使用启用/禁用列表控制哪些提示可用:

;; 仅启用特定提示
:enable-prompts ["code-review" "debug-help"]

;; 或禁用特定提示
:disable-prompts ["chat-session-summarize" "plan-and-execute"]

11.8. 文件路径

  • 相对路径nrepl-user-dir (通常是你的项目根目录) 解析
  • 绝对路径 按原样使用
  • 文件路径可以引用允许目录中的任何可读文件

11.9. 最佳实践

  1. 清晰的描述 - 编写描述, 帮助用户理解何时使用每个提示
  2. 可选参数 - 对那些增强功能但非必需的参数使用 required? false
  3. 条件部分 - 使用 {{#param}}...{{/param}} 来优雅地处理可选参数
  4. 对复杂模板使用文件 - 对长或复杂的模板使用单独的文件, 以保持配置整洁
  5. 有意义的名称 - 使用描述性的提示名称, 以表明其用途
  6. 模板中的文档 - 在模板中包含指令以指导 LLM

11.10. 默认提示

ClojureMCP 包括几个内置提示:

  • clojure_repl_system_prompt - 用于 Clojure 开发的系统指令
  • create-update-project-summary - 生成项目文档
  • chat-session-summarize - 总结对话以上下文
  • chat-session-resume - 从上一个会话恢复
  • plan-and-execute - 使用草稿板进行规划
  • ACT/add-dir - 将目录添加到允许的路径
  • ACT/scratch_pad_load - 从文件加载草稿板
  • ACT/scratch_pad_save_as - 将草稿板保存到文件

这些都可以通过配置被覆盖或禁用.

12. 配置自定义 Agent

Clojure MCP 服务器支持通过配置定义自定义 AI agent. 每个配置的 agent 在 MCP 界面中都成为其自己的工具, 让你无需编写代码即可创建专门的助手.

12.1. 概览

agent 工具构建器根据你 .clojure-mcp/config.edn 文件中定义的 agent 配置动态创建 MCP 工具. 每个 agent 可以拥有:

  • 自己的系统提示和个性
  • 对特定工具的访问权限 (读, 写, 执行或无)
  • 自定义上下文 (文件, 项目信息)
  • 不同的 AI 模型
  • 唯一的名称和描述

12.2. ⚠️ 重要: 工具访问权限变更

默认情况下, Agent 没有任何工具. 你必须通过 :enable-tools 明确指定 agent 可以使用的工具. 这是一项安全功能, 以防止意外访问强大的功能.

Agent 现在可以访问所有可用工具, 包括:

  • 只读工具 (grep, readfile 等)
  • 文件编辑工具 (filewrite, clojureedit 等)
  • 代码执行工具 (clojureeval, bash)
  • Agent 工具 (dispatchagent, architect)

在授予工具访问权限时, 请务必考虑安全隐患.

12.3. 基本配置

:agents 键添加到你的 .clojure-mcp/config.edn 中:

{:agents [{:id :my-agent
           :name "my_agent"
           :description "用于特定任务的自定义 agent"
           :system-message "你是一个乐于助人的助手, 专门从事..."
           :context true
           :enable-tools [:read_file :grep]  ; 必须明确列出工具
           :disable-tools nil}]}

12.4. 配置选项

每个 agent 配置支持以下键:

12.4.1. 必填字段

  • :id - 唯一的关键字标识符
  • :name - 在 MCP 中作为工具名称出现的字符串名称
  • :description - 在 MCP 界面中显示的字符串描述
  • :system-message - 定义 agent 行为的系统提示

12.4.2. 可选字段

  • :model - 使用的 AI 模型 (关键字引用或模型对象)
    • 引用在 :models 配置中定义的模型
    • 如果未指定, 则回退到默认值
  • :context - 提供给 agent 的上下文:
    • true - 包括 PROJECTSUMMARY.md 和代码索引
    • falsenil - 无上下文
    • ["file1.md", "file2.clj"] - 特定文件路径
  • :enable-tools - 控制 agent 可以使用的工具:
    • nil - 无工具 (如果省略, 则为默认值)
    • [:all] - 所有可用工具
    • [:tool1 :tool2] - 仅限特定工具
  • :disable-tools - 要禁用的工具 ID 列表 (在 enable-tools 之后应用)
  • :memory-size - 控制对话内存行为:
    • nil, false, 或 < 10 - 无状态 (默认): 每次聊天时清除内存
    • >= 10 - 持久化: 累积消息直到达到限制, 然后完全重置
  • :track-file-changes - 是否跟踪并显示文件差异 (默认: true)
    • true - 显示 agent 所做的文件修改的差异
    • false - 禁用文件更改跟踪

12.5. Agent 可用的工具

Agent 可能会访问所有 MCP 工具:

12.5.1. 只读工具

Tool ID Description
:LS 目录树视图
:read_file 读取带模式匹配的文件内容
:grep 搜索文件内容
:glob_files 按模式查找文件
:think 推理工具
:clojure_inspect_project 项目结构分析

12.5.2. 评估工具

Tool ID Description
:clojure_eval 在 REPL 中执行 Clojure 代码
:bash 执行 shell 命令

12.5.3. 文件编辑工具

Tool ID Description
:file_write 创建或覆盖文件
:file_edit 通过文本替换编辑文件
:clojure_edit 结构感知的 Clojure 编辑
:clojure_edit_replace_sexp S-表达式替换

12.5.4. Agent 工具

Tool ID Description
:dispatch_agent 启动子 agent
:architect 技术规划助手
:scratch_pad 持久化数据存储
:code_critique 代码审查反馈

12.6. 工具访问模式

12.6.1. 无工具 (默认)

{:id :minimal-agent
 :name "minimal_agent"
 :description "没有工具的 agent"
 :system-message "你只根据你的训练提供建议"
 ;; :enable-tools nil  ; 可以省略 - nil 是默认值
}

12.6.2. 只读访问

{:id :research-agent
 :name "research_agent"
 :description "可以读取但不能修改"
 :system-message "你研究和分析代码"
 :enable-tools [:read_file :grep :glob_files :clojure_inspect_project]}

12.6.3. 写访问

{:id :code-writer
 :name "code_writer"
 :description "可以创建和修改文件"
 :system-message "你编写和重构代码. 在写入前总是进行测试."
 :enable-tools [:read_file :grep :clojure_eval :file_write :clojure_edit]}

12.6.4. 完全访问

{:id :full-access-agent
 :name "full_access"
 :description "可以访问所有工具 - 请谨慎使用"
 :system-message "你拥有完全的系统访问权限. 请确认破坏性操作."
 :enable-tools [:all]
 :disable-tools [:dispatch_agent]  ; 仍然可以排除特定工具
}

12.7. 完整示例

这是一个具有不同能力级别 agent 的综合配置:

{:allowed-directories ["." "src" "test" "resources"]

 ;; 定义自定义模型 (可选)
 :models {:anthropic/fast
          {:model-name "claude-3-haiku-20240307"
           :api-key [:env "ANTHROPIC_API_KEY"]
           :temperature 0.3
           :max-tokens 2048}

          :openai/smart
          {:model-name "gpt-4-turbo-preview"
           :api-key [:env "OPENAI_API_KEY"]
           :temperature 0.2
           :max-tokens 4096}}

 ;; Agent 定义 - 从最低能力到最高能力
 :agents [;; 最小 agent - 无工具
          {:id :advisor
           :name "advisor"
           :description "提供建议, 无任何工具访问权限"
           :system-message "你是一位技术顾问. 根据你的知识提供指导,
                           不访问文件或运行代码."
           :context false
           ;; 没有 :enable-tools 意味着没有工具
           ;; 没有 :memory-size 意味着无状态 (默认)
          }

          ;; 只读研究 agent - 无状态
          {:id :research-agent
           :name "research_agent"
           :description "研究代码模式并查找示例"
           :system-message "你是一名研究专家. 查找模式, 示例,
                           并分析结构. 要彻底并提供具体位置."
           :model :anthropic/fast
           :context true
           :memory-size false  ; 显式无状态
           :enable-tools [:grep :glob_files :read_file :clojure_inspect_project]
           :disable-tools nil}

          ;; 文档专家 - 无状态
          {:id :doc-reader
           :name "doc_reader"
           :description "阅读并总结文档"
           :system-message "你是一名文档专家. 清楚地总结
                           并专注于实际用法."
           :context ["README.md" "doc/"]
           :memory-size nil  ; 无状态 (与省略相同)
           :enable-tools [:read_file :glob_files]}

          ;; 测试运行器 - 可以执行但不能修改
          {:id :test-runner
           :name "test_runner"
           :description "运行测试并分析结果"
           :system-message "你运行测试并分析结果. 你可以执行代码
                           但不能修改文件."
           :context ["test/"]
           :memory-size 5  ; < 10 = 无状态
           :enable-tools [:read_file :grep :glob_files :clojure_eval :bash]
           :disable-tools [:file_write :file_edit :clojure_edit]}

          ;; 代码编写器 - 用于多步重构的持久内存
          {:id :code-writer
           :name "code_writer"
           :description "编写和修改代码文件"
           :system-message "你是一个代码编写助手. 负责任地创建和编辑文件.
                           在写入文件前总是测试代码.
                           清楚地解释更改."
           :model :openai/smart
           :context true
           :memory-size 50  ; 持久化 - 记住最近的编辑
           :enable-tools [:read_file :grep :glob_files
                          :clojure_eval :bash
                          :file_write :file_edit
                          :clojure_edit :clojure_edit_replace_sexp]}

          ;; 具有大内存的完全访问 agent
          {:id :admin-agent
           :name "admin_agent"
           :description "完全系统访问 - 请极其谨慎使用"
           :system-message "你拥有完整的系统访问权限. 总是确认破坏性
                           操作. 在采取行动前解释风险. 未经明确确认
                           绝不删除或覆盖."
           :context true
           :memory-size 200  ; 用于复杂操作的大型持久内存
           :enable-tools [:all]  ; 访问所有内容
           :disable-tools nil}]}

12.8. 使用已配置的 Agent

配置完成后, 你的 agent 在你的 MCP 客户端 (如 Claude Desktop) 中会显示为单独的工具:

  1. 每个 agent 以其配置的名称显示 (例如, research_agent, code_writer)
  2. 为你的任务选择合适的 agent 工具
  3. 将你的提示发送给 agent
  4. agent 根据其专门的配置和工具访问权限进行响应

12.8.1. 示例用法

User: research_agent("Find all uses of multimethods in this project")
Research Agent: 我将搜索整个项目中的 multimethod 用法...
[仅使用只读工具进行搜索和分析]

User: code_writer("Add a new test for the filter-tools function")
Code Writer: 我将为 filter-tools 创建一个测试. 让我先检查一下现有的测试...
[可以读取, 评估和写入文件]

User: advisor("What's the best way to structure a Clojure web app?")
Advisor: 根据常见的模式和最佳实践...
[提供建议, 不访问任何工具]

12.9. 安全考虑

12.9.1. 最小权限原则

始终为 agent 提供其任务所需的最小工具:

  1. 对于一般建议/咨询, 无工具
  2. 对于分析和研究, 只读工具
  3. 仅当需要测试时, 执行工具
  4. 仅当需要文件修改时, 写入工具
  5. 仅在受控环境和受信任的用户下, 完全访问

12.9.2. 风险级别

Access Level Risk Use Cases
No tools None 一般建议, 解释
Read-only Low 代码审查, 研究, 分析
Read + Execute Medium 测试, 调试, 验证
Read + Write High 代码生成, 重构
All tools Very High 系统管理, 复杂自动化

12.9.3. 最佳实践

  1. 默认为无工具 - 只添加需要的内容
  2. 使用 :disable-tools:all 中移除危险工具
  3. 清晰的系统消息 - 指导 agent 负责任地使用工具
  4. 审计配置 - 定期审查 agent 的能力
  5. 在沙盒中测试 - 首先在安全的环境中尝试新配置

12.10. 上下文配置

:context 字段控制 agent 可以访问哪些信息:

12.10.1. 默认上下文 (:context true)

包括:

  • PROJECT_SUMMARY.md (如果存在)
  • 来自 clojure-inspect-project 的当前项目结构
  • .clojure-mcp/code_index.txt (如果存在)

12.10.2. 自定义文件上下文

:context ["README.md"           ; 单个文件
          "doc/"                ; 目录中的所有文件
          "src/my_app/core.clj" ; 特定源文件
          "../other-project/summary.md"] ; 相对路径有效

12.10.3. 无上下文 (:context false:context nil)

Agent 仅以其系统消息开始, 适用于通用 agent.

12.11. 模型配置

Agent 可以使用在 :models 配置中定义的自定义模型:

{:models {:openai/o3-mini
          {:model-name "o3-mini"
           :api-key [:env "OPENAI_API_KEY"]
           :temperature 0.1}}

 :agents [{:id :reasoning-agent
           :name "reasoning_agent"
           :model :openai/o3-mini  ; 引用模型
           ;; ... 其他配置
           }]}

12.12. 内存配置

:memory-size 字段控制 agent 如何在调用之间处理对话内存. 与连续丢弃旧消息的滑动窗口不同, 持久化 agent 会累积消息直到接近其限制, 然后完全重置.

12.12.1. 内存模式

Configuration Mode Behavior
nil (default) Stateless 每次聊天前清除内存, 重新开始
false Stateless 显式无状态, 与 nil 相同
< 10 Stateless 小于 10 的数字被视为无状态
>= 10 Persistent 累积消息直到达到限制, 然后重置

12.12.2. 无状态模式 (默认)

无状态 agent 在每次聊天开始时清除其内存, 但在对话期间会维持一个 100 条消息的缓冲区:

{:id :task-agent
 :name "task_agent"
 ;; 未指定 memory-size = 无状态 (默认)
 :description "独立执行任务"
 :system-message "你独立完成任务..."}

{:id :analyzer
 :name "analyzer"
 :memory-size false  ; 显式无状态
 :description "无对话历史地分析代码"
 :system-message "你分析代码..."}

无状态的优点:

  • 可预测的行为 - 每次调用都重新开始
  • 不会因之前的任务而污染上下文
  • 是单用途操作的理想选择
  • 仍然支持复杂的多轮对话 (100 条消息缓冲区)

12.12.3. 持久化模式

持久化 agent 在多次调用之间保持对话历史, 累积消息直到接近配置的限制:

{:id :assistant
 :name "assistant"
 :memory-size 50  ; 累积约 35 条消息后重置
 :description "具有记忆的对话助手"
 :system-message "你是一个乐于助人的助手..."}

{:id :tutor
 :name "tutor"
 :memory-size 100  ; 累积约 85 条消息后重置
 :description "记住之前课程的教育导师"
 :system-message "你是一个有耐心的导师..."}

持久化内存行为:

  • 在多次调用之间累积对话历史
  • 当接近内存限制时 (大小 - 15 条消息), 完全重置
  • 重置后重新添加原始上下文以保持连贯性
  • 不是滑动窗口 - 它是一个在接近满时重置的缓冲区

12.12.4. 特殊情况: Dispatch Agent

dispatch_agent 工具默认为具有 100 条消息窗口的持久化模式. 这可以在 :tools-config 中被覆盖:

{:tools-config {:dispatch_agent {:memory-size 200}}  ; 增加 dispatch agent 内存
 ;; 
 :tools-config {:dispatch_agent {:memory-size false}}} ; 使 dispatch agent 无状态

12.12.5. 内存大小示例

{:agents [;; 无状态示例
          {:id :code-reviewer
           :name "code_reviewer"
           ;; 默认: 无 memory-size = 无状态
           :description "每次独立审查代码"
           :system-message "审查提供的代码..."}

          {:id :test-runner
           :name "test_runner"
           :memory-size 5  ; < 10 = 无状态
           :description "无历史地运行测试"
           :system-message "运行并分析测试结果..."}

          ;; 持久化示例
          {:id :chat-bot
           :name "chat_bot"
           :memory-size 30  ; 小缓冲区 - 约 15 条消息后重置
           :description "具有短期记忆的简单聊天机器人"
           :system-message "你是一个友好的聊天机器人..."}

          {:id :project-assistant
           :name "project_assistant"
           :memory-size 100  ; 标准缓冲区 - 约 85 条消息后重置
           :description "具有对话记忆的项目助手"
           :system-message "你帮助处理正在进行的项目任务..."}

          {:id :long-context-agent
           :name "long_context"
           :memory-size 300  ; 大缓冲区 - 约 285 条消息后重置
           :description "用于复杂, 长时间对话的 agent"
           :system-message "你处理复杂的多部分任务..."}]}

12.12.6. 选择正确的内存配置

使用无状态 (默认) 当:

  • 每个任务都是独立的
  • 你想要可预测, 清新的行为
  • 处理不应持久化的敏感数据
  • 构建单用途工具
  • 避免上下文污染很重要

使用持久化当:

  • 构建对话式界面
  • 任务建立在之前的交互之上
  • 在多个查询之间保持上下文
  • 创建辅导或教练 agent
  • 实现多步工作流

12.12.7. 内存管理细节

无状态 agent:

  • 在每次 chat 调用开始时清除内存
  • 使用配置的上下文重新初始化
  • 在对话期间维持 100 条消息的缓冲区
  • 非常适合具有许多工具调用的复杂单个任务

持久化 agent:

  • 在调用之间保留对话历史
  • 累积消息直到接近限制 (大小 - 15)
  • 当接近限制时完全重置内存 (不是滑动窗口)
  • 重置后重新添加原始上下文
  • 通过避免部分内存丢失来确保对话保持连贯

12.13. 工具过滤逻辑

  • 如果 :enable-toolsnil 或省略: 无工具启用
  • 如果 :enable-tools[:all]: 所有可用工具都启用
  • 如果 :enable-tools[...]: 仅列出的工具启用
  • :disable-tools:enable-tools 之后应用以移除特定工具

示例:

;; 无工具 (默认)
{:enable-tools nil}  ; 或完全省略

;; 仅特定工具
{:enable-tools [:read_file :grep]}

;; 除少数外所有工具
{:enable-tools [:all]
 :disable-tools [:bash :file_write]}

12.14. 缓存和性能

  • Agent 在首次创建后被缓存以提高性能
  • 同一个 agent 实例在多次调用中被重用
  • 缓存键基于 agent 的 :id
  • 内存行为取决于 :memory-size 配置:
    • 无状态 agent: 每次调用时内存清除 (重新开始)
    • 持久化 agent: 内存跨会话中的调用持续存在
  • Agent 缓存持续到 MCP 服务器会话结束

12.15. 故障排除

12.15.1. Agent 未出现

  • 检查 config.edn:agents 的格式是否正确
  • 确保所有必填字段 (:id, :name, :description, :system-message) 都存在
  • 配置更改后重启 MCP 服务器

12.15.2. 无可用工具

  • 记住: agent 默认没有工具
  • :enable-tools 中明确列出工具
  • 检查工具 ID 是否完全匹配 (使用下划线: :read_file 而不是 :read-file)

12.15.3. 未找到模型

  • 验证模型是否在 :models 配置中定义
  • 检查 API 密钥的环境变量
  • 确保模型名称对于提供商是正确的

12.15.4. 工具不工作

  • 验证 :enable-tools 中的工具 ID 是否与可用工具匹配
  • 检查工具是否未在 :disable-tools 中被禁用
  • 确保 agent 具有必要的工具组合 (例如, 在 :clojure_edit 之前需要 :read_file)

12.15.5. 内存问题

  • Agent 不记得之前的对话: 检查 :memory-size 是否 >= 10 以实现持久化内存
  • Agent 记住了不需要的上下文: 将 :memory-size 设置为 nil, false, 或 < 10 以实现无状态行为
  • 对话变得不连贯: 内存可能太小; 增加 :memory-size 以进行更长的对话
  • Agent 在对话中重置: 当内存接近限制 (大小 - 15) 时会发生这种情况; 增加 :memory-size
  • 重要: 持久化内存不是滑动窗口 - 它会累积消息然后完全重置

12.16. 从先前版本迁移

如果你有现有的 agent 配置:

12.16.1. 旧行为 (更新前)

  • Agent 默认拥有所有只读工具
  • 无法访问写入或执行工具

12.16.2. 新行为

  • Agent 默认没有工具
  • 明确启用后可以访问任何工具

12.16.3. 迁移步骤

  1. 审查现有的 agent 配置
  2. 添加明确的 :enable-tools 列表
  3. 对于只读 agent, 添加: :enable-tools [:read_file :grep :glob_files ...]
  4. 在部署前进行彻底测试

12.17. 与 MCP 服务器集成

agent 工具构建器自动包含在主 MCP 服务器中. 当服务器启动时:

  1. .clojure-mcp/config.edn 读取 :agents 配置
  2. 为每个配置的 agent 创建一个单独的工具
  3. 每个 agent 只获得其指定的工具
  4. 将这些工具注册到 MCP 协议
  5. 每个 agent 在你的 MCP 客户端中都显示为单独的工具

无需更改代码 - 只需将 agent 配置添加到你的 config.edn 文件并重启服务器即可.

13. 组件过滤配置

ClojureMCP 允许通过 .clojure-mcp/config.edn 中的配置选项来精细控制向 AI 助手暴露哪些工具, 提示和资源. 这对于创建只包含所需组件的专注型 MCP 服务器非常有用.

13.1. 目录

  • [概览](#概览)
  • [工具过滤](#工具过滤)
  • [提示过滤](#提示过滤)
  • [资源过滤](#资源过滤)
  • [示例](#示例)
  • [最佳实践](#最佳实践)

13.2. 概览

组件过滤使用允许/拒绝列表模式:

  • 启用列表 (enable-*) - 指定后, 只有这些项被启用
  • 禁用列表 (disable-*) - 在启用过滤后应用, 以移除特定项
  • 默认行为 - 未指定过滤时, 所有组件都被启用

过滤逻辑遵循以下顺序:

  1. 如果提供了启用列表且为空 ([]), 则不启用任何内容
  2. 如果提供了带项目的启用列表, 则只启用这些项目
  3. 如果未提供启用列表 (nil), 则所有项目开始时都处于启用状态
  4. 然后应用禁用列表, 从已启用的集合中移除项目

13.3. 工具过滤

控制 AI 助手可用的工具.

13.3.1. 配置键

{:enable-tools [:clojure-eval :read-file :file-write]  ; 仅这些工具
 :disable-tools [:dispatch-agent :architect]}          ; 移除这些工具

13.3.2. 工具标识符

工具可以使用关键字或字符串指定:

  • :clojure-eval"clojure-eval"
  • :read-file"read_file"
  • :file-write"file_write"

常见工具 ID 包括:

  • :clojure-eval - 评估 Clojure 代码
  • :read-file - 读取文件内容
  • :file-edit - 编辑文件
  • :file-write - 写入文件
  • :bash - 执行 shell 命令
  • :grep - 搜索文件内容
  • :glob-files - 按模式查找文件
  • :dispatch-agent - 启动子代理
  • :architect - 技术规划
  • :code-critique - 代码审查
  • :scratch-pad - 持久化存储

13.3.3. 示例

最小化仅 REPL 服务器:

{:enable-tools [:clojure-eval]}

只读探索服务器:

{:enable-tools [:read-file :grep :glob-files :LS :clojure-inspect-project]}

完全访问, 但排除代理:

{:disable-tools [:dispatch-agent :architect :code-critique]}

13.4. 提示过滤

控制提供给 AI 助手的系统提示.

13.4.1. 配置键

{:enable-prompts ["clojure_repl_system_prompt" "chat-session-summarize"]
 :disable-prompts ["scratch-pad-save-as"]}

13.4.2. 提示名称

提示通过其字符串名称 (非关键字) 识别:

  • "clojure_repl_system_prompt" - 主要 REPL 交互提示
  • "chat-session-summarize" - 会话摘要
  • "scratch-pad-load" - 加载草稿板数据
  • "scratch-pad-save-as" - 保存草稿板快照

13.4.3. 示例

仅基本提示:

{:enable-prompts ["clojure_repl_system_prompt"]}

禁用草稿板提示:

{:disable-prompts ["scratch-pad-load" "scratch-pad-save-as"]}

13.5. 资源过滤

控制向 AI 助手暴露的资源文件.

13.5.1. 配置键

{:enable-resources ["PROJECT_SUMMARY.md" "README.md"]
 :disable-resources ["CLAUDE.md" "LLM_CODE_STYLE.md"]}

13.5.2. 资源名称

目前, 资源通过其字符串名称 (非 URI 或路径) 识别:

  • "PROJECT_SUMMARY.md" - 项目概览
  • "README.md" - 主要文档
  • "CLAUDE.md" - Claude 特定说明
  • "LLM_CODE_STYLE.md" - 编码风格指南

13.5.3. 示例

仅项目文档:

{:enable-resources ["PROJECT_SUMMARY.md" "README.md"]}

移除 AI 特定资源:

{:disable-resources ["CLAUDE.md" "LLM_CODE_STYLE.md"]}

13.6. 另请参阅

  • [模型配置](model-configuration.md) - 配置自定义 LLM 模型
  • [工具配置](tools-configuration.md) - 配置工具特定设置
  • [创建自定义 MCP 服务器](custom-mcp-server.md) - 构建具有自定义过滤的服务器

14. ClojureMCP 文档

此目录包含使用 ClojureMCP 创建 MCP (Model Context Protocol) 组件的文档.

14.1. 文档文件

14.1.1. 配置指南

14.1.2. [组件过滤配置](component-filtering.md)

了解如何使用启用/禁用列表来控制你的 MCP 服务器所暴露的工具, 提示和资源. 非常适合创建只包含所需组件的专注, 安全或专门的 MCP 服务器.

14.1.3. [模型配置](model-configuration.md)

使用你自己的 API 密钥, 端点和参数配置自定义 LLM 模型. 通过 LangChain4j 集成, 支持 OpenAI, Anthropic, Google Gemini 等.

14.1.4. [工具配置](tools-configuration.md)

使用自定义设置配置单个工具, 包括为 AI 驱动的工具 (如 dispatchagent, architect 和 codecritique) 选择模型.

14.1.5. 创建自定义服务器

14.1.6. [创建你自己的自定义 MCP 服务器](custom-mcp-server.md)

了解如何通过自定义工具, 提示和资源来创建你自己的个性化 MCP 服务器. 这是在 alpha 阶段配置 ClojureMCP 的主要方式, 既简单又强大!

14.1.7. [使用 AI 生成你的自定义 MCP 服务器](gen-your-mcp-server.md)

欢迎来到 MCP 服务器配置的新时代! 了解如何通过提供文档作为上下文并描述你的需求来使用大型语言模型生成完全定制的 Clojure MCP 服务器. 包括大量示例和提示模板.

14.1.8. [使用 ClojureMCP 的 Multimethod 系统创建工具](creating-tools-multimethod.md)

了解如何使用 ClojureMCP 的结构化 multimethod 方法创建工具. 这在 ClojureMCP 生态系统中构建工具时提供了验证, 错误处理和集成方面的好处.

14.1.9. [不依赖 ClojureMCP 创建工具](creating-tools-without-clojuremcp.md)

了解如何将工具创建为简单的 Clojure map, 而不依赖于 ClojureMCP 的 multimethod 系统. 这种方法允许你创建可以轻松共享并集成到任何 MCP 服务器的独立工具.

14.1.10. [创建提示](creating-prompts.md)

在 MCP 中创建提示的标准指南. 提示生成对话上下文, 以帮助 AI 助手理解特定任务或工作流程. 无论你是使用 ClojureMCP 还是创建独立提示, 这种方法都适用.

14.1.11. [创建资源](creating-resources.md)

在 MCP 中创建资源的标准指南. 资源提供只读内容, 如文档, 配置文件或项目信息. 无论你是使用 ClojureMCP 还是创建独立资源, 这种方法都适用.

14.2. 快速开始

对于大多数用户, 请从 [创建你自己的自定义 MCP 服务器](custom-mcp-server.md) 开始, 学习如何为你的特定需求配置 ClojureMCP.

14.3. 核心概念

  • 工具: 执行操作和计算
  • 提示: 为 AI 助手生成对话上下文
  • 资源: 提供只读内容

14.4. 快速参考

Component Schema Callback Signature
Tool {:name, :description, :schema, :tool-fn} (callback result-vector error-boolean)
Prompt {:name, :description, :arguments, :prompt-fn} (callback {:description "...", :messages [...]})
Resource {:url, :name, :description, :mime-type, :resource-fn} (callback ["content..."])

14.5. 注意

  • 工具 可以使用 ClojureMCP 的 multimethod 系统或作为简单的 map 创建 (参见工具文档)
  • 提示资源 总是作为简单的 map 创建, 使其天生具有可移植性
  • 所有组件都可以在没有 MCP 服务器的情况下独立测试
  • 所有传递给组件函数的参数 map 都使用字符串键

15. Clojure MCP: AI 辅助下的 REPL 驱动开发

⚠️ Alpha 软件 - 正在开发中

Clojure MCP 将 AI 模型连接到你的 Clojure 开发 环境, 实现了由大型语言模型 (LLMs) 驱动的卓越的 REPL 驱动开发体验.

15.1. 🚀 快速概览

Clojure MCP 将 LLMs 转化为:

  1. 强大的 Clojure 编码助手.
  2. 强大的 Clojure REPL 助手: 快速评估, 调试和迭代.
  3. Clojure 感知编辑器: 语法感知编辑, 自动 linting 和括号平衡.

15.2. TLDR: 这对我意味着什么?

仅凭 Clojure MCP, 你就可以将一个 LLM 变成一个强大的 Clojure REPL 和编码助手.

LLMs 在 Clojure REPL 中表现出色: 当前的 LLMs 无疑是 出色的 Clojure REPL 助手, 它们能快速地执行评估, 其效果远超你的想象. 问问任何有过这种 经历的人, 他们都会告诉你 LLMs 在 Clojure REPL 中的表现 远比他们想象的要好. 此外, 我们必须记住, 临时代码的形式和可维护性并不重要.

如丝般顺滑的 Clojure 编辑: 使用当前的编辑工具, LLMs 仍然在括号处理上遇到困难. Clojure MCP 对 编辑有不同的看法, 显著提高了编辑接受率. Clojure MCP 会对传入的代码进行 lint, 如果可能的话修复括号, 使用 clj-rewrite 应用语法感知的补丁, 然后对 最终结果进行 lint 和格式化. 这是一个强大的编辑流程, 在编辑 Clojure 代码时表现优异.

这两个特性以及一组其他 Clojure 感知 工具共同创造了一种全新而独特的 LLM 开发体验, 你 至少应该尝试一次, 以了解它是多么具有变革性.

15.3. 目录

  • [好消息](#好消息)
  • [🚀 概览](#-概览)
  • [主要特性](#主要特性)
    • [为什么选择 AI 辅助的 REPL 驱动开发?](#为什么选择-ai-辅助的-repl-驱动开发)
  • [🧠 模型兼容性](#-模型兼容性)
  • [统一的 Clojure 工具箱](#统一的-clojure-工具箱)
    • [为什么这些工具作为一个完整的系统工作](#为什么这些工具作为一个完整的系统工作)
    • [与 Claude Code 和其他代码助手一起使用](#与-claude-code-和其他代码助手一起使用)
  • [帮助和社区资源](#帮助和社区资源)
  • [📋 安装](#-安装)
    • [先决条件](#先决条件)
  • [设置 ClojureMCP](#设置-clojuremcp)
    • [安装概览](#安装概览)
    • [步骤 1: 配置你的目标项目的 nREPL 连接](#步骤-1-配置你的目标项目的-nrepl-连接)
    • [步骤 2: 安装 Clojure MCP 服务器](#步骤-2-安装-clojure-mcp-服务器)
    • [步骤 3: 配置 Claude Desktop](#步骤-3-配置-claude-desktop)
    • [步骤 4: 测试完整设置](#步骤-4-测试完整设置)
    • [故障排除技巧](#故障排除技巧)
    • [除 Claude Desktop 外的其他客户端](#除-claude-desktop-外的其他客户端)
  • [开始新的对话](#开始新的对话)
  • [项目摘要管理](#项目摘要管理)
  • [聊天会话的总结和恢复](#聊天会话的总结和恢复)
  • [使用 ClojureScript (shadow-cljs)](#使用-clojurescript-shadow-cljs)
    • [快速开始](#快速开始)
    • [切换回 Clojure](#切换回-clojure)
    • [shadow-cljs 开发技巧](#shadow-cljs-开发技巧)
  • [LLM API 密钥](#llm-api-密钥)
  • [学习曲线](#学习曲线)
  • [🧰 可用工具](#-可用工具)
    • [只读工具](#只读工具)
    • [代码评估](#代码评估)
    • [文件编辑工具](#文件编辑工具)
    • [Agent 工具 (需要 API 密钥)](#agent-工具-需要-api-密钥)
    • [实验性工具](#实验性工具)
    • [关键工具特性](#关键工具特性)
  • [🔧 定制](#-定制)
  • [⚙️ 配置](#-配置)
    • [配置文件位置](#配置文件位置)
    • [配置选项](#配置选项)
    • [示例配置](#示例配置)
    • [配置详情](#配置详情)
    • [常见配置模式](#常见配置模式)
  • [📜 开发实践](#-开发实践)
    • [推荐工作流](#推荐工作流)
    • [最佳实践](#最佳实践)
  • [🔧 项目维护](#-项目维护)
  • [📚 理念](#-理念)
  • [📝 许可证](#-许可证)
    • [许可证摘要](#许可证摘要)

15.4. 好消息

有一个 Clojure 开发者可能已经开始相信的故事. 这个 故事说, 现代 LLMs 是在大量主流 编程语言的代码上训练的, 因此 LLMs 在 处理像 Clojure 这样的 niche 语言时表现不佳. 我在这里告诉你, 这完全不是真的.

LLMs 绝对可以读写 Clojure. 然而, 我们的秘密 武器是 REPL, 以及它如何为 LLMs 提供一个快速集中的反馈循环 来验证和优化代码.

恕我直言, Clojure 是 LLM 辅助开发的绝佳语言. 它所需要的只是一座桥梁… 这就是我试图用 ClojureMCP 创建的.

15.5. 🚀 概览

该项目实现了一个 MCP 服务器, 将 AI 模型连接到 Clojure nREPL, 以及专门的 Clojure 编辑工具, 从而实现独特的 Clojure 开发体验.

Clojure MCP 提供了 Claude Code 使用的工具的超集, 因此你可以用它来处理 Clojure 而无需任何其他工具.

我强烈建议从使用 ClojureMCP 和 Claude Desktop 开始. Claude Desktop 让你能看到完整的推理和工具 执行链, 这对于理解 LLM 如何与工具交互非常有帮助. 看到明确的推理和行动对于 学习如何与 LLMs 作为编码助手一起工作是无价的.

15.6. 主要特性

  • Clojure REPL 连接 - 它会对 eval 进行 lint 并自动平衡括号
  • Clojure 感知编辑 - 使用 clj-kondo, parinfer, cljfmt, 和 clj-rewrite
  • 为 Clojure 开发优化的工具集 - Claude Code 工具的超集

15.6.1. 为什么选择 AI 辅助的 REPL 驱动开发?

对于 Clojurists 来说, 一个 LLM 辅助的 REPL 是杀手级应用.

有了 REPL, LLMs 可以:

  • 在 REPL 中 迭代 代码, 完成后在将其添加到你的代码之前展示结果
  • 验证 并探测你的代码中的错误
  • 在 REPL 中 调试 你的代码

16. 以及更多

此外, 在某些 LLM 客户端 (包括 Claude Desktop) 中, 你可以 随时控制模型可用的工具, 因此你可以轻松移除编辑文件的能力, 将模型 限制在 REPL 工具, 并强制使用 REPL.

16.1. 🧠 模型兼容性

这些工具旨在与最新的 LLM 模型配合使用. 为了获得最佳的 sexp 编辑和 Clojure 特定工具体验, 我们推荐:

  • Anthropic Claude 3.7Claude 4.1 (sonnet 或 opus) (特别是 Claude 4.1 以获得最佳效果)
  • Gemini 2.5
  • OpenAI o4-minio3chat-gpt-5

我强烈推荐 Claude 4.1, 如果你想看到长期的自主 agent 行动链.

ClojureMCP 的结构化编辑工具需要较高的模型性能, 因此使用这些推荐的模型之一将显著改善 你的体验.

我个人几乎所有事情都使用 Claude 4.1 Opus/Sonnet, 并且我订阅了 Anthropic 的每月 100 美元的 5x Max 计划. 我从中 得到的价值远超我所支付的费用.

16.1.1. 与 Claude Code 和其他代码助手一起使用

ClojureMCP 几乎可以与任何 LLM 客户端一起使用, 如 Claude Desktop, Claude Code 等等.

我使用 ClojureMCP 和 Claude Desktop, 因为我可以更清楚地阅读工具 输出, 这有助于我理解工具的 性能如何, 以及它们是否能很好地协同工作, 使 LLM 成为 一个有效的 Clojure 编码助手.

我也将 ClojureMCP 与 Claude Code 一起使用, 效果很好, 但我确保 关闭了许多与 ClojureMCP 工具功能重复的 Claude Code 工具.

虽然你*可以*将这些工具与 Claude Code 和其他带有 自有工具的代码助手一起使用, 但我建议**首先独立尝试 Clojure MCP 工具**, 以体验其全部 功能. 一旦你对 Clojure MCP 工具集感到满意, 你可以根据你的特定工作流程需求, 做出明智的决定, 是 专门使用它还是将其与其他代码助手和开发工具集成.

16.2. 帮助和社区资源

16.3. 📋 安装

16.3.1. 先决条件

16.4. 设置 ClojureMCP

设置 ClojureMCP 可能具有挑战性, 因为它目前处于 alpha 阶段, 且未针对快速安装进行优化. 本指南将逐步引导你完成该过程.

16.4.1. 安装概览

  1. 配置 nREPL: 在你的项目中设置并验证一个在端口 7888 上的 nREPL 服务器
  2. 安装 ClojureMCP: 将 clojure-mcp 添加到你的 ~/.clojure/deps.edn
  3. 配置 MCP 客户端: 在 Claude Desktop 或其他 MCP 客户端中将 clojure-mcp 设置为 MCP 服务器
  4. 安装 Riggrep (可选): [ripgrep](https://github.com/BurntSushi/ripgrep#installation) 是一个智能, 快速的文件搜索工具, 尊重 .gitignore.

注意: 此设置验证所有组件是否协同工作. 在确认基本设置有效后, 你可以自定义特定的配置细节 (如端口号).

16.4.2. 步骤 1: 配置你的目标项目的 nREPL 连接

在你希望获得 AI 辅助的 Clojure 项目中, 你需要确保可以在端口 7888 (你可以使用任何端口) 上启动一个 nREPL 服务器.

  1. 对于 deps.edn 项目

    :nrepl 别名添加到你的项目的 deps.edn 中:

    {
      ;; ... 你的项目依赖 ...
      :aliases {
        ;; AI 连接的 nREPL 服务器
        ;; 包括你希望用于开发的所有路径
        :nrepl {:extra-paths ["test"]
                :extra-deps {nrepl/nrepl {:mvn/version "1.3.1"}}
                            ;; 这允许 nrepl 中断失控的 repl eval
                :jvm-opts ["-Djdk.attach.allowAttachSelf"]
                :main-opts ["-m" "nrepl.cmdline" "--port" "7888"]}}}
    

    **验证**配置:

    $ clojure -M:nrepl
    

    你应该会看到 nREPL 服务器在端口 7888 上启动.

  2. 对于 Leiningen 项目

    使用以下命令启动 nREPL 服务器:

    $ lein repl :headless :port 7888
    

16.4.3. 步骤 2: 安装 Clojure MCP 服务器

clojure-mcp 作为别名添加到你的 ~/.clojure/deps.edn 中:

{:aliases
  {:mcp
    {:deps {org.slf4j/slf4j-nop {:mvn/version "2.0.16"} ;; stdio 服务器需要
            com.bhauman/clojure-mcp {:git/url "https://github.com/bhauman/clojure-mcp.git"
                                     :git/tag "v0.1.11-alpha"
                                     :git/sha "7739dba"}}
     :exec-fn clojure-mcp.main/start-mcp-server
     :exec-args {:port 7888}}}}

查找最新版本: 访问 [https://github.com/bhauman/clojure-mcp/commits/main](https://github.com/bhauman/clojure-mcp/commits/main) 获取最新的 commit SHA, 或克隆仓库并运行 git log --oneline -1.

  1. 验证安装

    ⚠️ 重要: 在启动 clojure-mcp 之前, 你必须有一个在端口 7888 上运行的 nREPL 服务器.

    1. 首先, 在你的项目目录中启动你的 nREPL 服务器:

      $ clojure -M:nrepl
      # 或者对于 Leiningen:
      $ lein repl :headless :port 7888
      
    2. 然后, 在一个新的终端中, 启动 clojure-mcp:

      $ clojure -X:mcp :port 7888
      

    你应该会看到类似这样的 JSON-RPC 输出:

    {"jsonrpc":"2.0","method":"notifications/tools/list_changed"}
    {"jsonrpc":"2.0","method":"notifications/tools/list_changed"}
    {"jsonrpc":"2.0","method":"notifications/resources/list_changed"}
    {"jsonrpc":"2.0","method":"notifications/prompts/list_changed"}
    
  2. 故障排除

    连接被拒绝错误:

    Execution error (ConnectException) at sun.nio.ch.Net/connect0 (Net.java:-2).
    Connection refused
    

    这意味着 clojure-mcp 无法连接到你的 nREPL 服务器. 请确保:

    • nREPL 服务器正在运行
    • 端口号匹配 (默认: 7888)

    无关输出: 如果你看到除 JSON-RPC 消息之外的输出, 很可能是因为 clojure-mcp 被包含在一个更大的环境中. 确保 clojure-mcp 在其自己的隔离依赖项下运行.

  3. 重要说明
    • 位置独立性: MCP 服务器可以从任何目录运行——它不需要在你的项目目录中. 它使用 nREPL 连接来获取上下文.
    • 共享文件系统: 目前, nREPL 和 MCP 服务器必须在同一台机器上运行, 因为它们假定共享文件系统.
    • 依赖隔离: 不要将 clojure-mcp 包含在你的项目依赖项中. 它应该使用自己的依赖项单独运行. 始终在其别名中使用 :deps (而不是 :extra-deps).
  4. 命令行参数

    MCP 服务器通过 clojure -X:mcp 接受以下命令行参数:

    Argument Type Description Default Example
    :port integer 要连接的 nREPL 服务器端口 7888 :port 7889
    :host string nREPL 服务器主机 "localhost" :host "192.168.1.10"

16.4.4. 步骤 3: 配置 Claude Desktop

这通常是最具挑战性的部分——确保应用程序的启动环境具有正确的 PATH 和环境变量.

选择最有可能拾取你的环境配置的 shell 可执行文件:

如果你使用的是 Bash, 找到明确的 bash 可执行文件路径:

$ which bash
/opt/homebrew/bin/bash

如果你使用的是 Z Shell, 找到明确的 zsh 可执行文件路径:

$ which zsh
/bin/zsh

现在我们将使用这个明确的 shell 路径在 Claude Desktop 配置的 command 参数中, 如下所示.

创建或编辑 ~/Library/Application\ Support/Claude/claude_desktop_config.json:

{
    "mcpServers": {
        "clojure-mcp": {
            "command": "/opt/homebrew/bin/bash",
            "args": [
                "-c",
                "clojure -X:mcp :port 7888"
            ]
        }
    }
}

16.4.5. 步骤 4: 测试完整设置

  1. 在你的目标项目中 启动 nREPL:

    cd /path/to/your/project
    clojure -M:nrepl
    

    寻找: nREPL server started on port 7888...

  2. 重启 Claude Desktop (配置更改后需要)
  3. 验证连接: 在 Claude Desktop 中, 点击聊天区域的 + 按钮. 你应该在菜单中看到 "Add from clojure-mcp". 需要注意的是, 这可能需要一些时间才会显示出来.
  4. 如果出现错误, 请参阅 [故障排除技巧](#故障排除技巧). 如果连接成功, 请参阅 [开始新的对话](#开始新的对话) 部分.

16.4.6. 故障排除技巧

如果 Claude Desktop 无法运行 clojure 命令:

  1. 手动测试你的命令: 在终端中运行你配置中的确切命令
  2. 检查你的 PATH: 确保 which clojure 在新的终端中有效
  3. 启用日志记录: 检查 Claude Desktop 日志以获取错误消息
  4. 先简化: 从基本配置开始, 然后增加复杂性

如果你仍然有问题, 可以考虑向 AI 助手 (Claude, ChatGPT, Gemini) 咨询有关你系统设置的特定 PATH 配置.

  1. 首先尝试这个

    如果上述 claude_desktop_config.json 不起作用, 很 可能是 PATH 环境变量设置不正确, 无法 找到 clojurejava.

    根据你的设置, 你可以通过直接更改 PATH 环境变量来解决这个问题:

    {
        "mcpServers": {
            "clojure-mcp": {
                "command": "/opt/homebrew/bin/bash",
                "args": [
                    "-c",
                    "export PATH=/opt/homebrew/bin:$PATH; exec clojure -X:mcp :port 7888"
                ]
            }
        }
    }
    
  2. 常见的 PATH 位置
    • Homebrew (Apple Silicon): /opt/homebrew/bin
    • Homebrew (Intel Mac): /usr/local/bin
    • Nix*: /home/username/.nix-profile/bin or /nix/var/nix/profiles/default/bin
    • System Default: /usr/bin:/usr/local/bin
  3. 调试策略

    这些是一些示例, 为你提供调试失败的 ClojureMCP 启动的方法.

    检查环境:

    {
        "mcpServers": {
            "clojure-mcp": {
                "command": "/opt/homebrew/bin/bash",
                "args": [
                    "-c",
                    "echo $PATH > /Users/bruce/claude-desktop-path.txt"
                ]
            }
        }
    }
    

    捕获 ClojureMCP 输出:

    {
        "mcpServers": {
            "clojure-mcp": {
                "command": "/opt/homebrew/bin/bash",
                "args": [
                    "-c",
                    "clojure -X:mcp :port 7888 | tee /Users/bruce/clojure-mcp-stdout.log"
                ]
            }
        }
    }
    
  4. 高级配置示例

    如果你需要 source 环境变量 (如 API 密钥, 见 [LLM API 密钥](#llm-api-密钥)):

    {
        "mcpServers": {
            "clojure-mcp": {
                "command": "/bin/sh",
                "args": [
                    "-c",
                    "source ~/.my-llm-api-keys.sh && PATH=/Users/username/.nix-profile/bin:$PATH && clojure -X:mcp :port 7888"
                ]
            }
        }
    }
    

16.4.7. 除 Claude Desktop 外的其他客户端

请参阅 [Wiki](https://github.com/bhauman/clojure-mcp/wiki) 了解有关设置其他 MCP 客户端的信息.

16.5. 开始新的对话

一切设置好后, 我建议在 Claude 中开始一个新的聊天.

你要做的第一件事是在连接到 nREPL 的对话中初始化 关于 Clojure 项目的上下文.

在 Claude Desktop 中点击 + 工具, 并可选择添加

  • 资源 PROJECT_SUMMARY.md - (让 LLM 创建这个) 见下文
  • 资源 Clojure Project Info - 它会内省连接到 nREPL 的项目
  • 资源 LLM_CODE_STYLE.md - 这是你的个人编码风格说明 (将此仓库中的一个复制到你的项目根目录)
  • 提示 clojure_repl_system_prompt - 关于如何编码的说明 - 从 Clod Code 中借鉴了很多

然后开始聊天.

我建议从陈述一个问题开始, 然后与 LLM 聊天以 交互式地设计一个解决方案. 你可以要求 Claude "提出"一个 问题的解决方案.

稍微迭代一下, 然后让它:

A. 在 REPL 中编码并验证想法.

> 不要低估 LLMs 使用 REPL 的能力! 当前的 LLMs > 在使用 Clojure REPL 方面绝对是出色的.

B. 要求 LLM 对源代码进行更改, 然后在文件编辑后让它在 REPL 中验证代码.

C. 要求运行测试. D. 要求提交更改.

> 创建一个分支, 让 LLM 经常提交, 这样它就不会因为走错方向而毁掉好的工作.

16.6. 📜 开发实践

16.6.1. 推荐工作流

  1. 表达问题 - 清楚地说明你想要解决什么
  2. 在 REPL 中开发 - 逐步解决问题
  3. 逐步验证 - 在继续之前测试每个表达式
  4. 保存到文件 - 当解决方案工作时, 妥善保存
  5. 重新加载并验证 - 确保保存的代码有效

16.6.2. 最佳实践

  • 小步快跑 - 偏爱许多小的, 有效的步骤, 而不是少数大的步骤
  • 人类指导 - 提供反馈以保持开发在正轨上
  • 尽早测试 - 在 REPL 中直接验证想法, 然后再提交

16.7. 项目摘要管理

该项目包括一个用于维护 LLM 友好的 PROJECT_SUMMARY.md 的工作流程, 帮助助手快速了解代码库结构.

16.7.1. 工作原理

  1. 创建摘要: 要生成或更新 PROJECTSUMMARY.md 文件, 请在 + > clojure-mcp 菜单中使用 MCP 提示 create-update-project-summary. 此提示将:
    • 分析代码库结构
    • 记录关键文件, 依赖项和可用工具
    • 以针对 LLM 助手优化的格式生成全面的文档
  2. 使用摘要: 与助手开始新的对话时:
    • "Project Summary" 资源会自动加载 PROJECTSUMMARY.md
    • 这为助手提供了关于项目结构的即时上下文
    • 助手无需长时间探索即可提供更准确的帮助
  3. 保持更新: 在添加了新功能或组件的高效会话结束时:
    • 再次调用 create-update-project-summary 提示
    • 系统将使用新添加的功能更新 PROJECTSUMMARY.md
    • 这确保了摘要随着开发的进行而保持最新

此工作流程创造了一个良性循环, 每个会话都建立在先前会话累积的知识之上, 使助手随着项目的演变而变得越来越有效.

16.8. 聊天会话的总结和恢复

Clojure MCP 服务器提供了一对提示, 可使用 scratch_pad 工具实现跨聊天会话的对话连续性. 默认情况下, 数据仅在当前会话的**内存中**存储. 要跨服务器重启持久化摘要, 你必须使用 scratch pad 部分中描述的配置选项启用 scratch pad 持久性.

16.8.1. 工作原理

系统使用两个互补的提示:

  1. chat-session-summarize: 创建当前对话的摘要
    • 将详细摘要保存到 scratch pad
    • 捕获已完成的工作, 正在进行的工作以及接下来的工作
    • 接受一个可选的 chat_session_key 参数 (默认为 "chat_session_summary")
  2. chat-session-resume: 从先前的对话中恢复上下文
    • 读取 PROJECTSUMMARY.md 文件
    • 调用 clojure_inspect_project 获取当前项目状态
    • 从 scratch pad 检索先前的会话摘要
    • 提供一个简短的 8 行摘要, 说明上次中断的地方
    • 接受一个可选的 chat_session_key 参数 (默认为 "chat_session_summary")

16.8.2. 使用工作流

结束会话:

  1. 在高效对话结束时, 调用 chat-session-summarize 提示
  2. 助手会将全面的摘要存储在 scratch pad 中
  3. 由于 scratch pad 的全局状态, 此摘要在会话之间保持持久

开始新会话:

  1. 继续工作时, 调用 chat-session-resume 提示
  2. 助手将加载所有相关上下文并提供简短摘要
  3. 然后你可以在拥有完整上下文的情况下从上次中断的地方继续

16.8.3. 多个会话的高级用法

你可以通过使用自定义键来维护多个并行的对话上下文:

# 对于功能开发
chat-session-summarize with key "feature-auth-system"

# 对于 bug 修复
chat-session-summarize with key "debug-memory-leak"

# 恢复特定上下文
chat-session-resume with key "feature-auth-system"

这使得在不同开发上下文之间切换的同时, 能够保持每个对话线程的完整状态.

16.8.4. 好处

  • 无缝连续性: 从你上次中断的地方精确地继续
  • 上下文保留: 重要细节在会话之间不会丢失
  • 多上下文: 并行处理不同的功能/bug
  • 减少重复: 无需重新解释你正在做什么

聊天摘要功能通过捕获尚未正式化为项目文档的对话特定上下文和决策, 对 PROJECTSUMMARY.md 进行了补充.

16.9. 使用 ClojureScript (shadow-cljs)

ClojureMCP 可与 [shadow-cljs](https://github.com/thheller/shadow-cljs) 无缝协作, 用于 ClojureScript 开发. 以下是设置方法:

16.9.1. 快速开始

  1. 启动你的 shadow-cljs 服务器 并带上 nREPL 端口:

    # 启动 shadow-cljs (默认使用端口 9000, 或在 shadow-cljs.edn 中配置)
    npx shadow-cljs watch app
    
  2. 配置 Claude Desktop 或其他客户端 以连接到 shadow-cljs nREPL 端口:

    {
     "mcpServers": {
         "clojure-mcp": {
             "command": "/bin/sh",
             "args": [
                 "-c",
                 "PATH=/opt/homebrew/bin:$PATH && clojure -X:mcp :port 9000"
             ]
         }
      }
    }
    

    或者将 shadow 端口更改为 7888 (或你配置的任何端口), 并保持你的客户端配置不变.

  3. 在 Claude Desktop 中 切换到 ClojureScript REPL:

    一旦 Claude Desktop 连接上, 提示 Claude 评估:

    (shadow/repl :app)
    

    :app 替换为 shadow-cljs.edn 中的实际构建 ID.

  4. 全部设置完毕! 现在所有 clojure_eval 调用都将被路由到你的 ClojureScript REPL, 允许你:
    • 评估 ClojureScript 代码
    • 与你正在运行的应用程序交互
    • 使用所有 ClojureMCP 工具进行 ClojureScript 开发

16.9.2. 切换回 Clojure

要退出 ClojureScript REPL 并返回到 Clojure, 让 Claude 评估:

:cljs/quit

16.9.3. shadow-cljs 开发技巧

  • 构建选择: 根据你的 shadow-cljs.edn 配置使用适当的构建 ID (:app, :main, :test 等)
  • 热重载: 在使用 ClojureMCP 时, shadow-cljs 热重载继续正常工作
  • 浏览器连接: 确保你的浏览器连接到 shadow-cljs 以进行浏览器目标构建
  • Node.js 构建: 同样适用于 Node.js 目标构建

这种集成为你的 ClojureScript 项目带来了 ClojureMCP REPL 驱动开发工作流的全部功能!

16.9.4. Clojure 和 ClojureScript 双重设置

ClojureMCP 甚至支持同时连接到两个 REPL!

在你的 ~/.clojure/deps.edn 中以双重模式添加 clojure-mcp 别名, 确保根据需要设置端口 (你的 nrepl 端口), shadow 端口和 shadow 构建.

{:aliases
  {:mcp-shadow-dual
    {:deps {org.slf4j/slf4j-nop {:mvn/version "2.0.16"} ;; stdio 服务器需要
            com.bhauman/clojure-mcp {:git/url "https://github.com/bhauman/clojure-mcp.git"
                                     :git/tag "v0.1.11-alpha"
                                     :git/sha "7739dba"}}
     :exec-fn clojure-mcp.main-examples.shadow-main/start-mcp-server
     :exec-args {:port 7888 :shadow-port 7889 :shadow-build "app"}}}}

请务必更新你的 claude_desktop_config.json 以使用新的别名. 记住: 只有在需要覆盖 deps.edn 中的设置时, 你才需要向 ClojureMCP 服务器提供参数.

以下是使用双重配置的示例:

给 Claude 的提示:

> 在 clojure 中评估此表达式: (+ 1 2 3)

Claude 的回应:

> 表达式 (+ 1 2 3) 评估为 6. > 这是 Clojure 中的一个简单加法操作, 其中 + 函数将所有参数相加: 1 + 2 + 3 = 6.

现在试试 ClojureScript:

> 在 clojurescript 中评估相同的表达式, 并将结果输出到浏览器控制台.

Claude 的回应:

> 表达式 (+ 1 2 3) 在 ClojureScript 中也评估为 6, 结果已记录到浏览器控制台. > 该函数返回 nil, 因为 js/console.log 不返回值, 但如果你检查浏览器的开发者控制台, 你应该会看到 6 被打印出来.

成功!

16.10. LLM API 密钥

> 这不是使用 Clojure MCP 服务器所必需的.

> 重要: 如果你在你的 > 环境中设置了以下 API 密钥, 那么 ClojureMCP 在你使用 > dispatch_agent,~architect~ 和 code_critique 工具时会调用它们. 这些 > 调用会产生 API 费用.

提供的一些 MCP 工具本身就是代理, 它们需要 API 密钥才能工作.

要使用代理工具, 你需要一个或多个以下提供商的 API 密钥:

  1. 设置环境变量

    选项 1: 在你的 shell 中导出

    export ANTHROPIC_API_KEY="your-anthropic-api-key-here"
    export OPENAI_API_KEY="your-openai-api-key-here"
    export GEMINI_API_KEY="your-gemini-api-key-here"
    

    选项 2: 添加到你的 shell 配置文件 (.bashrc, .zshrc 等)

    # 将这些行添加到你的 shell 配置文件
    export ANTHROPIC_API_KEY="your-anthropic-api-key-here"
    export OPENAI_API_KEY="your-openai-api-key-here"
    export GEMINI_API_KEY="your-gemini-api-key-here"
    
  2. 配置 Claude Desktop

    设置 Claude Desktop 时, 通过更新你的配置, 确保它可以访问你的环境变量.

    我个人在 bash 命令中直接 source 它们:

    {
        "mcpServers": {
            "clojure-mcp": {
                "command": "/bin/sh",
                "args": [
                    "-c",
                    "source ~/.api_credentials.sh && PATH=/your/bin/path:$PATH && clojure -X:mcp"
                ]
            }
        }
    }
    

    > 注意: 代理工具可以使用任何可用的 API 密钥. 你不需要全部三个 - 只需设置你有的那些即可. 工具会自动从可用模型中选择. 目前, ANTHROPIC API 仅限于 dispatchagent.

16.11. 学习曲线

> 这个工具有一个学习曲线. 在实践中, 你可能需要提醒 > LLM 在 REPL 中进行开发. 你可能还需要提醒 LLM > 使用 clojure_edit 系列工具, 这些工具内置了 linter > 以防止括号不平衡等问题.

16.12. 🧰 可用工具

main.clj 中包含的默认工具按类别组织, 以支持不同的工作流程:

16.12.1. 只读工具

Tool Name Description Example Usage
LS 返回文件和目录的递归树状视图 探索项目结构
read_file 智能文件阅读器, 支持基于模式的 Clojure 文件探索 读取文件, 折叠视图, 模式匹配
grep 使用正则表达式进行快速内容搜索 查找包含特定模式的文件
glob_files 基于模式的文件查找 按名称模式查找文件, 如 *.clj
think 记录复杂推理和头脑风暴的想法 规划方法, 组织思路

16.12.2. 代码评估

Tool Name Description Example Usage
clojure_eval 在当前命名空间中评估 Clojure 代码 测试表达式, 如 (+ 1 2)
bash 在主机系统上执行 shell 命令 运行测试, git 命令, 文件操作

16.12.3. 文件编辑工具

Tool Name Description Example Usage
clojure_edit 结构感知的 Clojure 表单编辑 替换/插入函数, 处理 defmethod
clojure_edit_replace_sexp 修改函数内的表达式 更改特定的 s-expressions
file_edit 通过替换文本字符串来编辑文件 简单的文本替换
file_write 写入完整文件, 带安全检查 创建新文件, 带验证覆盖

16.12.4. Agent 工具 (需要 API 密钥)

Tool Name Description Example Usage
dispatch_agent 启动具有只读工具的代理以进行复杂搜索 多步文件探索和分析
architect 技术规划和实施指导 系统设计, 架构决策

16.12.5. 实验性工具

Tool Name Description Example Usage
scratch_pad 用于结构化数据存储的持久工作区 任务跟踪, 规划, 工具间通信, 可选文件持久化 (默认禁用)
code_critique 交互式代码审查和改进建议 迭代式代码质量改进

16.12.6. 关键工具特性

  1. 智能文件读取 (read_file)
    • 折叠视图: 对于大型 Clojure 文件, 仅显示函数签名
    • 模式匹配: 使用 name_pattern 按名称查找函数, content_pattern 搜索内容
    • defmethod 支持: 处理分派值, 如 "area :rectangle" 或向量分派
    • 多语言: Clojure 文件获得智能功能, 其他文件显示原始内容
  2. 结构感知编辑 (clojure_edit)
    • 基于表单的操作: 按类型和标识符定位函数, 而非文本匹配
    • 多种操作: 替换, insertbefore, insertafter
    • 语法验证: 内置 linting 防止括号不平衡
    • defmethod 处理: 适用于限定名称和分派值
  3. 代码评估 (clojure_eval)
    • REPL 集成: 在连接的 nREPL 会话中执行
    • 辅助函数: 内置命名空间和符号探索工具
    • 多个表达式: 评估和分区多个表达式
  4. Shell 命令 (bash)
    • 可配置执行: 可根据配置通过 nREPL 或本地运行
    • 会话隔离: 使用 nREPL 模式时, 在单独的会话中运行, 以防止 REPL 干扰
    • 输出截断: 一致的 8500 字符限制, 智能分配 stderr/stdout
    • 路径安全: 对照允许的目录验证文件系统路径
  5. Agent 系统 (dispatch_agent)
    • 自主搜索: 处理复杂的多步探索任务
    • 只读访问: 代理具有只读工具访问权限
    • 详细结果: 返回分析和发现
  6. Scratch Pad (scratch_pad)
    • 持久工作区: 存储结构化数据以进行规划和工具间通信
    • 默认仅内存: 数据仅存储在内存中, 会话结束时丢失 (默认行为)
    • 可选文件持久化: 启用以在会话和服务器重启之间保存数据
    • 基于路径的操作: 使用 set_path, get_path, delete_path 进行精确数据操作
    • JSON 兼容性: 存储任何与 JSON 兼容的数据 (对象, 数组, 字符串, 数字,布尔值)

    默认行为 (仅内存): 默认情况下, scratch pad 仅在内存中操作. 数据在会话期间持续存在, 但在 MCP 服务器停止时丢失.

    启用持久化:

    添加到 .clojure-mcp/config.edn:

    {:scratch-pad-load true    ; 默认 false
     :scratch-pad-file "workspace.edn"}  ; 默认为 "scratch_pad.edn"
    

    持久化详情:

    • 文件保存在你项目内的 .clojure-mcp/ 目录中
    • 启用持久化时, 更改会自动保存
    • 损坏的文件会得到优雅处理并报告错误

16.13. 🔧 定制

ClojureMCP 设计为高度可定制. 在 alpha 阶段, 创建你自己的自定义 MCP 服务器是为你的特定需求配置系统的主要方式.

你可以定制:

  • 工具 - 选择要包含的工具, 使用 multimethods 或简单的 map 创建新工具
  • 提示 - 为你的工作流程添加特定于项目的提示
  • 资源 - 暴露你的文档, 配置和项目信息
  • 工具选择 - 创建只读服务器, 开发服务器或专门配置

定制方法既简单又强大 - 你基本上是在构建自己的个性化 AI 开发伴侣.

📖 [完整定制文档](doc/README.

快速开始: [创建你自己的自定义 MCP 服务器](doc/custom-mcp-server.md) - 这是大多数用户应该开始的地方.

16.14. CLI 选项

使用 -X 调用需要 EDN 值.

  1. :port

    可选 - 要连接的 nREPL 服务器端口. 当使用 :start-nrepl-cmd 而不带 :port 时, 将从命令输出中自动发现端口.

    :port 7888

  2. :host

    可选 - nREPL 服务器主机. 如果未指定, 则默认为 localhost.

    :host "localhost":host "0.0.0.0"

  3. :start-nrepl-cmd

    可选 - 如果 nREPL 服务器尚未运行, 则自动启动一个的命令. 必须指定为字符串向量. MCP 服务器将启动此进程并管理其生命周期.

    当不带 :port 使用时, MCP 服务器将自动从命令的输出中解析端口. 当与 :port 一起使用时, 它将使用该固定端口.

    重要: 此选项要求从你的项目目录 (你的 deps.ednproject.clj 所在的位置) 启动 clojure-mcp. nREPL 服务器将在当前工作目录中启动. 这对于 Claude Code 和其他你希望自动启动 nREPL 而无需手动过程管理的命令行 LLM 客户端特别有用.

    Claude Desktop 用户注意: Claude Desktop 不会从你的项目目录启动 MCP 服务器, 因此 :start-nrepl-cmd 将不起作用, 除非你还提供一个指向你特定项目的 :project-dir 作为命令行参数. 例如: :project-dir '"/path/to/your/clojure/project"'. 此限制不影响 Claude Code 或其他你从项目目录运行的基于 CLI 的工具.

    :start-nrepl-cmd ["lein" "repl" ":headless"]:start-nrepl-cmd ["clojure" "-M:nrepl"]

  4. :config-file

    可选 - 指定配置文件的位置. 必须是现有文件的路径.

    :config-file "/path/to/config.edn"

  5. :project-dir

    可选 - 指定你的代码库的工作目录. 这会覆盖从 nREPL 连接自动内省项目目录的方式. 必须是现有目录的路径.

    :project-dir "/path/to/your/clojure/project"

  6. :nrepl-env-type

    可选 - 指定我们通过 nREPL 连接连接到的环境类型. 这会覆盖自动检测. 有效选项是:

    :nrepl-env-type :bb

16.14.1. 示例用法

# 仅使用端口的基本用法
clojure -X:mcp :port 7888

# 自动启动 nREPL 服务器和端口发现
# 非常适合 Claude Code - 从你的项目目录运行此命令
clojure -X:mcp :start-nrepl-cmd '["lein" "repl" ":headless"]'

# 对于 Claude Code 和 Clojure 项目 (从项目目录)
clojure -X:mcp :start-nrepl-cmd '["clojure" "-M:nrepl"]'

# 自动启动并指定端口 (使用固定端口, 不解析)
clojure -X:mcp :port 7888 :start-nrepl-cmd '["clojure" "-M:nrepl"]'

# 对于 Claude Desktop: 必须提供 project-dir, 因为它不是从你的项目运行的
clojure -X:mcp :start-nrepl-cmd '["lein" "repl" ":headless"]' :project-dir '"/path/to/your/clojure/project"'

# 使用自定义主机和项目目录
clojure -X:mcp :port 7888 :host '"0.0.0.0"' :project-dir '"/path/to/project"'

# 使用自定义配置文件
clojure -X:mcp :port 7888 :config-file '"/path/to/custom-config.edn"'

# 指定 Babashka 环境
clojure -X:mcp :port 7888 :nrepl-env-type :bb

注意: 使用 -X 调用时, 字符串值需要为 shell 正确引用, 因此字符串使用 '"value"' 语法.

16.15. ⚙️ 配置

Clojure MCP 服务器通过项目根目录下的 .clojure-mcp/config.edn 文件支持最小化的项目特定配置. 此配置为 MCP 服务器提供安全控制和 自定义选项.

16.15.1. 配置文件位置

在你的项目根目录下创建一个 .clojure-mcp/config.edn 文件:

your-project/
├── .clojure-mcp/
│   └── config.edn
├── src/
├── deps.edn
└── ...

16.15.2. 配置选项

配置在 [这里](doc/CONFIG.md) 有详细文档.

16.15.3. 示例配置

{:allowed-directories ["."
                       "src"
                       "test"
                       "resources"
                       "dev"
                       "/absolute/path/to/shared/code"
                       "../sibling-project"]
 :emacs-notify false
 :write-file-guard :full-read
 :cljfmt true
 :bash-over-nrepl true
 :scratch-pad-load false  ; Default: false
 :scratch-pad-file "scratch_pad.edn"}

16.15.4. 配置详情

路径解析:

  • 相对路径 (如 "src", "../other-project") 相对于你的项目根目录解析
  • 绝对路径 (如 "/home/user/shared") 按原样使用
  • 项目根目录自动包含在允许的目录中

安全:

  • 工具会根据允许的目录验证所有文件操作
  • 尝试访问允许目录之外的文件将失败并报错
  • 这可以防止意外访问敏感的系统文件
  • Bash 工具不遵守这些边界, 所以要小心

默认行为:

  • 没有配置文件时, 只有项目目录及其子目录可访问
  • nREPL 工作目录自动添加到允许的目录中

16.15.5. 常见配置模式

  1. 开发设置
    {:allowed-directories ["."
                           "src"
                           "test"
                           "dev"
                           "resources"
                           "docs"]
     :write-file-guard :full-read
     :cljfmt true
     :bash-over-nrepl true
     :scratch-pad-load false  ; 仅内存的 scratch pad
     :scratch-pad-file "scratch_pad.edn"}
    
  2. 多项目设置与持久化
    {:allowed-directories ["."
                           "../shared-utils"
                           "../common-config"
                           "/home/user/reference-code"]
     :write-file-guard :partial-read
     :cljfmt true
     :bash-over-nrepl true
     :scratch-pad-load true  ; 启用文件持久化
     :scratch-pad-file "workspace.edn"}
    
  3. 受限模式 (额外安全)
    {:allowed-directories ["src"
                           "test"]
     :write-file-guard :full-read
     :cljfmt false        ; 保留原始格式
     :bash-over-nrepl false  ; 仅使用本地执行
     :scratch-pad-load false  ; 无持久化
     :scratch-pad-file "scratch_pad.edn"}
    

    注意: 配置在 MCP 服务器启动时加载. 更改配置后请重启服务器.

16.16. 高级用法

16.16.1. 代码索引

如上所述, dispatch-agent-context 配置选项允许你在调用 dispatch_agent 之前 添加关于你的代码的上下文. 默认包含位于项目 ./.clojure-mcp/ 文件夹中的 code_index.txt 文件. 当然, 这可以自定义.

为了生成代码索引, 你需要为此目的设置一个别名, 然后从 CLI 运行 clojure-mcp.

{:aliases
  {:index
    {:deps {org.slf4j/slf4j-nop {:mvn/version "2.0.16"} ;; stdio 服务器需要
            com.bhauman/clojure-mcp {:git/url "https://github.com/bhauman/clojure-mcp.git"
                                     :git/tag "v0.1.11-alpha"
                                     :git/sha "7739dba"}}
            :exec-fn clojure-mcp.code-indexer/map-project
            :exec-args {}}}}

然后从 CLI 运行索引器:

# 使用默认设置的基本用法
clojure -X:index

# 自定义代码索引生成
clojure -X:index :dirs '["src" "lib"]' :include-tests true :out-file '"my-index.txt"'

当然, 在调用 dispatch_agent 时, 你需要指定代码索引文件的名称.

16.17. 🔧 项目维护

# 运行测试
clojure -X:test

# 运行特定测试
clojure -X:test :dirs '["test"]' :include '"repl_tools_test"'

# 运行 linter
clojure -M:lint

16.18. 📚 理念

该项目的核心理念是:

  1. 小步快跑, 丰富反馈 带来更高质量的代码
  2. REPL 驱动开发 提供最高质量的反馈循环
  3. 保持人类在环 确保洞察力和可维护的代码

16.19. 📝 许可证

Eclipse Public License - v 2.0

Copyright (c) 2025 Bruce Hauman

本程序及随附材料根据 Eclipse Public License 2.0 的条款提供, 可在 http://www.eclipse.org/legal/epl-2.0 获取

16.19.1. 许可证摘要

  • 自由使用 用于个人项目, 内部业务工具和开发
  • 修改和分发 - 欢迎改进和分叉
  • 商业用途 - 企业可以无限制地商业使用
  • 灵活许可 - 可与专有代码结合
  • 📤 分享改进 - 分发时必须提供源代码

17. Clojure MCP 项目摘要

17.1. 项目概览

Clojure MCP 是一个模型上下文协议 (MCP) 服务器, 它使 AI 助手 (如 Claude) 能够直接与 Clojure REPL 交互. 它在人类和 LLM 之间提供了一个协作的, REPL 驱动的开发工作流. 核心理念是 "小步快跑, 高质量丰富反馈", 以实现高效开发.

该项目允许 AI 助手:

  • 评估 Clojure 代码并立即看到结果
  • 通过逐步验证来增量开发解决方案
  • 导航和探索命名空间和符号
  • 使用正确的格式和结构感知的操作编辑 Clojure 文件
  • 访问文档和源代码
  • 直接在 REPL 环境中测试代码

17.2. 关键文件路径和描述

17.2.1. 核心系统文件

  • /src/clojure_mcp/core.clj: 已重构 - 提供了用于构建 MCP 服务器的可重用 API, 带有主要的 build-and-start-mcp-server 函数
  • /src/clojure_mcp/main.clj: 已重构 - 展示如何使用工厂函数消费核心 API 的示例实现
  • /src/clojure_mcp/nrepl.clj: 用于连接到 Clojure REPL 的 nREPL 客户端实现
  • /src/clojure_mcp/tool_system.clj: 定义了基于 multimethod 的工具架构
  • /src/clojure_mcp/prompts.clj: 管理 AI 助手的系统提示
  • /src/clojure_mcp/resources.clj: 管理要暴露给 AI 助手的资源
  • /src/clojure_mcp/config.clj: 已增强 - 支持 .clojure-mcp/config.edn 文件的配置系统
    • 支持 :tools-config 用于工具特定的配置
    • 提供 get-tool-configget-tools-config 辅助函数
  • /src/clojure_mcp/linting.clj: 代码质量和格式化实用程序
  • /src/clojure_mcp/sse_core.clj: Server-Sent Events 传输实现
  • /src/clojure_mcp/sse_main.clj: 使用新模式的示例 SSE 服务器

17.2.2. 工具实现

  1. 活跃工具 (在 main.clj 中使用)
    • /src/clojure_mcp/tools/eval/: 代码评估工具
    • /src/clojure_mcp/tools/unified_read_file/: 增强的文件读取功能, 具有基于模式的代码探索
      • tool.clj: 带有 MCP 集成的主要工具实现
      • pattern_core.clj: 用于 Clojure 代码分析的核心模式匹配功能
      • file_timestamps.clj: 跟踪文件读/写时间戳以确保安全
    • /src/clojure_mcp/tools/form_edit/: 结构感知的 Clojure 代码编辑
      • combined_edit_tool.clj: 统一的表单编辑工具
      • tool.clj: S-表达式替换工具
    • /src/clojure_mcp/tools/file_edit/: 基本文件编辑操作
    • /src/clojure_mcp/tools/file_write/: 文件写入操作
    • /src/clojure_mcp/tools/directory_tree/: 文件系统导航
    • /src/clojure_mcp/tools/grep/: 文件内容搜索
    • /src/clojure_mcp/tools/glob_files/: 基于模式的文件查找
    • /src/clojure_mcp/tools/project/: 项目结构分析
    • /src/clojure_mcp/tools/agent_tool_builder/: 新增 - 基于配置的代理工具系统
      • tool.clj: 从配置创建代理工具
      • core.clj: 构建和运行代理的核心功能
      • default_agents.clj: dispatchagent, architect, codecritique 和 clojureeditagent 的默认配置
      • file_changes.clj: 跟踪代理操作的文件更改
    • /src/clojure_mcp/tools/think/: AI 助手的反思性思考工具
    • /src/clojure_mcp/tools/bash/: Shell 命令执行
      • 新增: 使用独立的 nREPL 会话进行隔离
      • 每个 bash 工具实例在初始化时创建自己的会话
      • 命令在与主 REPL 隔离的环境中执行
      • 通过配置支持 nREPL 和本地执行模式
    • /src/clojure_mcp/tools/scratch_pad/: 用于工具间通信的持久性草稿板
      • core.clj: 数据存储和检索的核心功能
      • tool.clj: 带有基于路径的操作 (setpath, getpath, deletepath) 的 MCP 集成
      • config.clj: 用于持久性的配置文件管理
      • truncate.clj: 带有深度截断的漂亮打印
  2. 未使用的工具 (移至 othertools/)

    注意: 这些工具已移至 /src/clojure_mcp/other_tools/, 以明确将它们与活跃使用的工具分开. 它们仍然功能齐全并通过测试, 但未在 main.clj 中注册. 这种组织上的改变通过区分基本工具和可能非必要的工具, 有助于保持更清晰的代码库.

    • /src/clojure_mcp/other_tools/create_directory/: 创建目录的工具
    • /src/clojure_mcp/other_tools/list_directory/: 列出目录内容的工具
    • /src/clojure_mcp/other_tools/move_file/: 移动/重命名文件的工具
    • /src/clojure_mcp/other_tools/namespace/: Clojure 命名空间探索工具
      • 包括: current_namespace, clojure_list_namespaces, clojure_list_vars_in_namespace
    • /src/clojure_mcp/other_tools/symbol/: 符号信息和文档工具
      • 包括: symbol_completions, symbol_metadata, symbol_documentation, source_code, symbol_search

    所有未使用的工具都有相应的测试文件移至 /test/clojure_mcp/other_tools/, 并更新了命名空间声明.

17.2.3. 示例主文件

  • /src/clojure_mcp/main_examples/shadow_main.clj: 用于 Shadow CLJS 支持的示例自定义服务器
  • /src/clojure_mcp/main_examples/figwheel_main.clj: 用于 Figwheel Main 支持的示例自定义服务器

17.2.4. 资源目录

  • /resources/prompts/: AI 助手的系统提示
  • /resources/prompts/system/: 核心系统提示
  • /resources/agent/: 代理特定的资源
  • /resources/logback.xml: 日志配置文件

17.2.5. 文档

  • /doc/README.md: 文档概述
  • /doc/custom-mcp-server.md: 创建自定义 MCP 服务器的指南
  • /doc/model-configuration.md: 配置自定义 LLM 模型的指南
  • /doc/creating-tools-multimethod.md: 使用 multimethod 创建工具的指南
  • /doc/creating-tools-without-clojuremcp.md: 创建独立工具的指南
  • /doc/creating-prompts.md: 创建自定义提示的指南
  • /doc/creating-resources.md: 创建自定义资源的指南
  • /doc/gen-your-mcp-server.md: 生成 MCP 服务器的指南

17.3. 依赖和版本

17.3.1. 核心依赖

  • org.clojure/clojure (1.12.1): Clojure 语言
  • io.modelcontextprotocol.sdk/mcp (0.10.0): 模型上下文协议 SDK
  • nrepl/nrepl (1.3.1): Clojure 的网络 REPL 服务器
  • rewrite-clj/rewrite-clj (1.1.47): 用于解析和转换 Clojure 代码的库
  • dev.weavejester/cljfmt (0.13.1): Clojure 代码格式化
  • clj-kondo/clj-kondo (2024.03.13): Clojure 的静态分析器和 linter
  • org.clojure/tools.logging (1.3.0): Clojure 的日志抽象
  • ch.qos.logback/logback-classic (1.4.14): SLF4J 的 Logback 实现

17.3.2. AI 集成依赖

  • dev.langchain4j/langchain4j (1.0.1): 用于 LLM 集成的 Java 库
  • dev.langchain4j/langchain4j-anthropic (1.0.1-beta6): Anthropic 特定集成
  • dev.langchain4j/langchain4j-google-ai-gemini (1.0.1-beta6): Google Gemini 集成
  • dev.langchain4j/langchain4j-open-ai (1.0.1): OpenAI 集成
  • pogonos/pogonos (0.2.1): 用于提示的 Mustache 模板

17.3.3. 其他依赖

  • org.clojars.oakes/parinfer (0.4.0): Clojure 的括号推断
  • org.apache.tika/tika-core (3.2.0): 内容检测和提取
  • org.clojure/data.json (2.5.1): JSON 解析和生成
  • org.clojure/tools.cli (1.1.230): 命令行参数解析

17.4. 配置系统

该项目通过 .clojure-mcp/config.edn 文件支持项目特定的配置:

17.4.1. 配置位置

your-project/
├── .clojure-mcp/
│   └── config.edn
├── src/
└── deps.edn

17.4.2. 配置选项

  • allowed-directories: 控制 MCP 工具可以访问的目录 (安全)
  • emacs-notify: Emacs 集成的布尔标志
  • write-file-guard: 控制文件时间戳跟踪行为 (默认: :partial-read)
    • :partial-read - 完整和折叠读取都会更新时间戳 (默认)
    • :full-read - 只有完整读取才会更新时间戳 (最安全)
    • false - 完全禁用时间戳检查
  • cljfmt: 启用/禁用编辑流程中 cljfmt 格式化的布尔标志 (默认: true)
    • true - 对编辑过的文件应用 cljfmt 格式化 (默认行为)
    • false - 禁用格式化, 保留精确的空白和格式
  • bash-over-nrepl: 控制 bash 命令执行模式的布尔标志 (默认: true)
    • true - 通过 nREPL 连接执行 bash 命令 (默认行为)
    • false - 在 MCP 服务器上本地执行 bash 命令
  • scratch-pad-load: 启用/禁用草稿板持久性的布尔标志 (默认: false)
    • true - 启动时加载现有数据并保存更改到磁盘
    • false - 草稿板仅在内存中操作, 无文件持久性
  • scratch-pad-file: 草稿板持久性的文件名 (默认: "scratch_pad.edn")
    • 指定 .clojure-mcp/ 目录内的文件名
    • 仅在 scratch-pad-loadtrue 时使用
  • enable-tools: 要启用的工具 ID 列表 (默认: nil - 所有工具都启用)
    • 提供时, 仅启用此列表中的工具
    • 空列表 [] 禁用所有工具
    • 工具 ID 可以是关键字或字符串 (例如, :clojure-eval"clojure-eval")
  • disable-tools: 要禁用的工具 ID 列表 (默认: nil - 不禁用任何工具)
    • enable-tools 过滤后应用
    • 用于在启用大多数工具的同时排除特定工具
    • 工具 ID 可以是关键字或字符串
  • enable-prompts: 要启用的提示名称列表 (默认: nil - 所有提示都启用)
    • 提供时, 仅启用此列表中的提示
    • 空列表 [] 禁用所有提示
    • 提示名称必须是字符串 (例如, "chat-session-summarize""clojure_repl_system_prompt")
  • disable-prompts: 要禁用的提示名称列表 (默认: nil - 不禁用任何提示)
    • enable-prompts 过滤后应用
    • 用于在启用大多数提示的同时排除特定提示
    • 提示名称必须是字符串
  • enable-resources: 要启用的资源名称列表 (默认: nil - 所有资源都启用)
    • 提供时, 仅启用此列表中的资源
    • 空列表 [] 禁用所有资源
    • 资源名称必须是字符串 (例如, "PROJECT_SUMMARY.md""README.md")
  • disable-resources: 要禁用的资源名称列表 (默认: nil - 不禁用任何资源)
    • enable-resources 过滤后应用
    • 用于在启用大多数资源的同时排除特定资源
    • 资源名称必须是字符串
  • models: 自定义模型配置的 map (默认: {})
    • 为 LangChain4j 集成定义命名的模型配置
    • 键是带命名空间的关键字, 如 :openai/my-gpt4:anthropic/my-claude
    • 值是带有模型参数的配置 map
    • 支持环境变量引用: {:api-key [:env "OPENAI_API_KEY"]}
    • 如果未找到自定义模型, 则回退到内置默认值
  • tools-config: 工具特定配置的 map (默认: {})
    • 使用自定义设置配置单个工具
    • 键是作为关键字的工具 ID (例如, :dispatch_agent, :architect)
    • 值是每个工具特定的配置 map
    • 示例: :dispatch_agent {:model :openai/o3} 使用自定义模型

17.4.3. 示例配置

{:allowed-directories ["." "src" "test" "resources" "../sibling-project"]
 :emacs-notify false
 :write-file-guard :partial-read
 :cljfmt true
 :bash-over-nrepl true
 :scratch-pad-load false
 :scratch-pad-file "scratch_pad.edn"
 :enable-tools [:clojure-eval :read-file :file-write :grep :glob-files]
 :disable-tools [:dispatch-agent :architect]
 :enable-prompts ["clojure_repl_system_prompt" "chat-session-summarize"]
 :disable-prompts ["scratch-pad-save-as"]
 :enable-resources ["PROJECT_SUMMARY.md" "README.md"]
 :disable-resources ["CLAUDE.md" "LLM_CODE_STYLE.md"]
 :tools-config {:dispatch_agent {:model :openai/o3}
                :architect {:model :anthropic/claude-3-haiku-20240307}}
 :models {:openai/my-fast {:model-name "gpt-4o"
                           :temperature 0.3
                           :max-tokens 2048
                           ;; 引用环境变量
                           :api-key [:env "OPENAI_API_KEY"]}
          :openai/o3 {:model-name "o3-mini"
                      :temperature 0.2
                      :api-key [:env "OPENAI_API_KEY"]}
          :anthropic/my-reasoning {:model-name "claude-3-5-sonnet-20241022"
                                   :thinking {:enabled true
                                             :budget-tokens 4096}
                                   ;; 直接值 (不建议在生产中使用)
                                   :api-key "sk-ant-..."}}}

17.4.4. 路径解析和安全

  • 相对路径从项目根目录解析
  • 绝对路径按原样使用
  • 所有文件操作都根据允许的目录进行验证
  • 项目根目录自动包含在允许的目录中

17.5. 可用工具

以下工具在默认配置 (main.clj) 中可用:

17.5.1. 只读工具

Tool Name Description Example Usage
LS 返回文件和目录的递归树状视图 探索项目结构
read_file 智能文件阅读器, 具有基于模式的 Clojure 文件探索 读取文件, 折叠视图, 模式匹配
grep 快速内容搜索工具, 适用于任何代码库大小 查找包含特定模式的文件
glob_files 快速文件模式匹配工具, 适用于任何代码库大小 按名称模式查找文件, 如 *.clj
think 使用该工具思考某事 规划方法, 组织思路

17.5.2. 代码评估

Tool Name Description Example Usage
clojure_eval 接受一个 Clojure 表达式并在当前命名空间中评估它 测试表达式, REPL 驱动开发
bash 在主机系统上执行 bash shell 命令 运行测试, git 命令, 文件操作

17.5.3. 文件编辑工具

Tool Name Description Example Usage
clojure_edit 使用指定操作编辑 Clojure 文件中的顶级表单 替换/插入函数, 处理 defmethod
clojure_edit_replace_sexp 通过查找和替换特定的 s-表达式来编辑文件 更改函数内的特定 s-表达式
file_edit 通过用新字符串替换特定文本字符串来编辑文件 简单的文本替换
file_write 将文件写入本地文件系统 创建新文件, 带验证覆盖

17.5.4. 内省

Tool Name Description Example Usage
clojure_inspect_project 分析并提供有关 Clojure 项目结构的详细信息 理解项目组织, 依赖关系

17.5.5. 代理工具 (基于配置)

默认代理工具通过 agent-tool-builder 系统自动创建. 这些可以通过 :tools-config 自定义, 或通过 :agents 配置完全替换:

Tool Name Description Example Usage
dispatch_agent 启动一个可以访问只读工具的新代理 多步文件探索和分析
architect 任何技术或编码任务的首选工具 系统设计, 架构决策
code_critique 对 Clojure 代码提供建设性反馈 迭代式代码质量改进
clojure_edit_agent 高效地应用多个代码更改 多个文件的结构化编辑

17.5.6. 实验性工具

Tool Name Description Example Usage
scratch_pad 用于在工具调用之间存储结构化数据的持久草稿板 任务跟踪, 中间结果, 代理间通信

17.6. 工具示例

17.6.1. 代码评估

clojure_eval:
  Input: (+ 1 2)
  Output: => 3

17.6.2. 文件操作

read_file:
  Input: {:path "/path/to/file.clj",
          :collapsed true,
          :name_pattern "validate.*",
          :content_pattern "try|catch"}
  Output: 带有基于模式的折叠视图的文件内容

file_edit:
  Input: {:file_path "/path/to/file.clj",
          :old_string "(defn old",
          :new_string "(defn new"}
  Output: 显示所做更改的差异

17.6.3. Clojure 特定编辑

clojure_edit:
  Input: {:file_path "/path/to/file.clj",
          :form_identifier "my-func",
          :form_type "defn",
          :content "(defn my-func [x] (* x 2))",
          :operation "replace"}
  Output: 显示语法感知的函数替换差异

clojure_edit_replace_sexp:
  Input: {:file_path "/path/to/file.clj",
          :match_form "(+ x 2)",
          :new_form "(+ x 10)"}
  Output: 显示 s-表达式替换差异

17.6.4. Scratch Pad - 持久化数据存储

scratch_pad:
  op: set_path
  path: ["todos", 0]
  value: {task: "Write tests", done: false}
  explanation: 添加第一个任务
  Output: 在路径 ["todos", 0] 存储的值

scratch_pad:
  op: get_path
  path: ["todos", 0]
  explanation: 检查第一个任务
  Output: 在 ["todos", 0] 的值: {task: "Write tests", done: false}

17.7. 架构和设计模式

17.7.1. 核心架构组件

  1. MCP 服务器: 将工具暴露给 AI 助手的入口点
  2. nREPL 客户端: 连接到 Clojure REPL 进行代码评估
  3. 工具系统: 用于定义工具的可扩展的基于 multimethod 的架构
  4. 提示系统: 为 AI 助手提供上下文和指导
  5. 工厂模式: 使用工厂函数创建工具, 提示和资源的新模式

17.7.2. 关键实现模式

  1. 工厂函数模式: 重构后的架构使用工厂函数:
    • make-tools: (fn [nrepl-client-atom working-directory] ...) 返回工具序列
    • make-prompts: (fn [nrepl-client-atom working-directory] ...) 返回提示序列
    • make-resources: (fn [nrepl-client-atom working-directory] ...) 返回资源序列
    • 所有组件都通过 core/build-and-start-mcp-server 创建
  2. Multimethod 调度: 工具系统使用 multimethod 实现可扩展性:
    • tool-name: 决定工具的名称
    • tool-description: 提供人类可读的描述
    • tool-schema: 定义输入/输出 schema
    • validate-inputs: 验证工具输入
    • execute-tool: 执行实际操作
    • format-results: 为 AI 格式化结果
  3. 核心/工具分离: 每个工具遵循一个模式:
    • core.clj: 没有 MCP 依赖的纯功能
    • tool.clj: 使用工具系统的 MCP 集成层
  4. 结构化 Clojure 代码编辑: 使用 rewrite-clj 来:
    • 将 Clojure 代码解析为 zipper 结构
    • 执行结构感知的转换
    • 保持正确的格式和空白
    • 相较于通用文本编辑的关键优势:
      • 使用表单标识符进行基于模式的匹配
      • 按类型和名称而不是文本匹配来定位表单
      • 结构感知的匹配忽略空白差异
      • 为括号平衡提供早期语法验证
      • 正确处理像 defmethod 这样的特殊表单及其分派值
  5. REPL 驱动开发: 所有工具都设计为支持:
    • 增量开发
    • 立即反馈
    • 逐步验证
  6. 基于模式的代码探索: read_file 工具支持:
    • 使用 name_pattern 对函数名进行正则表达式匹配
    • 使用 content_pattern 进行基于内容的模式匹配
    • 使用折叠视图和选择性展开进行专注的代码阅读
    • 带有使用提示的 Markdown 格式输出
  7. 文件时间戳跟踪: 确保文件操作安全:
    • 跟踪文件上次读取或修改的时间
    • 防止编辑已被外部修改的文件
    • 写入操作后自动更新时间戳
    • 允许在一次读取后进行多次连续编辑
    • 一致地使用规范路径以实现可靠的文件识别
  8. 持久状态管理: scratch_pad 工具提供:
    • 所有工具调用中都可访问的基于全局 atom 的存储
    • 使用 set_path~/~get_path~/~delete_path 操作进行基于路径的数据结构操作
    • 无需解析即可直接存储 JSON 兼容值
    • 路径元素为字符串和数字的数组
    • 用于调试和检查的树形可视化
    • 漂亮打印的输出, 在深度 3 处截断以提高可读性
    • 基于配置文件: 通过 .clojure-mcp/config.edn 文件启用持久性
    • 对损坏文件的错误处理并通知用户

17.8. 开发工作流建议

  1. 设置和配置:
    • 使用 Clojure MCP 服务器配置 Claude Desktop
    • 如果需要, 设置文件系统和 Git 集成
  2. REPL 驱动开发:
    • 从小的增量步骤开始
    • 在 REPL 中评估代码以验证正确性
    • 验证后将工作代码保存到文件
  3. 工具使用最佳实践:
    • 使用 clojure_eval 测试代码片段
    • 使用 clojure_editclojure_edit_replace_sexp 进行语法感知的代码编辑
    • 如果文件可能已被外部修改, 编辑前务必使用 read_file 读取文件
    • 使用 file_write 后, 你可以立即编辑文件而无需再次读取
    • 使用 scratch_pad 用于:
      • 跨工具调用跟踪任务和待办事项
      • 存储中间计算结果
      • 增量构建复杂的数据结构
      • 在不同代理或工具调用之间共享上下文
  4. 日志系统:
    • 使用 clojure.tools.logging 和 Logback 后端
    • 日志写入 logs/clojure-mcp.log, 每日轮换
    • resources/logback.xml 中配置日志级别
    • 服务器启动/关闭和错误会自动记录
  5. 项目维护:
    • 使用 clojure -X:test 运行所有测试
    • 使用 clojure -X:test :includes '["test-file-name"]' 运行特定测试文件
      • 注意: 使用 :includes (复数) 表示特定测试模式, 而不是 :include
      • 示例: clojure -X:test :includes '["persistence-test"]'
    • 在重大更改后更新此项目摘要
  6. 测试最佳实践:
    • 使用 clojure-mcp.tools.test-utils 中提供的测试实用程序
    • 处理文件操作时始终使用规范路径 (.getCanonicalPath())
    • 在测试中尝试修改文件之前, 先用时间戳跟踪器注册文件
    • 在时间戳操作之间包含小延迟以确保时间戳不同
  7. 基于模式的代码探索:
    • 使用增强的 read_file 工具进行高效的代码库导航
    • 结合 name_patterncontent_pattern 来关注相关代码
    • 使用其分派值查找特定的 defmethod 实现
    • 示例:
      • 查找所有验证函数: {:name_pattern "validate.*"}
      • 查找错误处理: {:content_pattern "try|catch|throw"}
      • 查找特定函数的使用位置: {:content_pattern "some-important-function"}
      • 查找特定的 defmethod: {:name_pattern "area :rectangle"}

17.9. 创建自定义 MCP 服务器

重构后的架构使得创建自定义 MCP 服务器变得简单:

17.9.1. 最小示例

(ns my-company.mcp-server
  (:require [clojure-mcp.core :as core]
            [clojure-mcp.main :as main]))

(defn start-mcp-server [opts]
  (core/build-and-start-mcp-server
   opts
   {:make-tools-fn main/make-tools
    :make-prompts-fn main/make-prompts
    :make-resources-fn main/make-resources}))

17.9.2. 自定义工具示例

(defn make-tools [nrepl-client-atom working-directory]
  (concat
   (main/make-tools nrepl-client-atom working-directory)
   [(my-custom-tool/create-tool nrepl-client-atom)]))

有关创建自定义服务器的全面文档, 请参见 /doc/custom-mcp-server.md.

17.10. 扩展点

  1. 添加新工具:
    • /src/clojure_mcp/tools/ 中为活跃工具创建一个新的工具命名空间
    • 实现 tool-system 中所需的 multimethod
    • main.cljmake-tools 函数中注册该工具
    • 注意: /src/clojure_mcp/other_tools/ 中的工具不会自动注册
  2. 创建自定义服务器:
    • 为工具, 提示和资源定义工厂函数
    • 使用你的工厂调用 core/build-and-start-mcp-server
    • 参见 main_examples/ 中的示例实现
  3. 增强现有工具:
    • 大多数工具遵循一个可以添加新步骤来修改的流水线架构
    • 流水线步骤遵循带有错误短路的 thread-first 模式
  4. 重新激活未使用的工具:
    • /src/clojure_mcp/other_tools/ 中的工具可以通过以下方式重新激活:
      • 将它们移回 /src/clojure_mcp/tools/
      • 更新命名空间声明
      • 将它们添加到 main.clj 的导入和 make-tools 函数中
    • 或者, 通过核心 API 使用这些工具创建自定义 MCP 服务器
  5. 替代传输:
    • 使用 sse-core/build-and-start-mcp-server 进行 SSE 传输
    • 参见 sse-main.clj 中的示例实现

17.11. 最近的组织变更

代理工具重构: 将单个硬编码的代理工具替换为基于配置的系统:

  • 代理工具 (dispatchagent, architect, codecritique, clojureeditagent) 现在通过 agent-tool-builder 创建
  • 默认代理自动可用, 但可以通过 :tools-config 自定义
  • 用户可以通过 :agents 配置完全覆盖默认值
  • 将来自 :tools-config 的工具特定配置合并到默认代理中
  • 在保持向后兼容性的同时, 删除了 600 多行冗余代码

模型配置支持: 通过 .clojure-mcp/config.edn 添加了对用户定义模型配置的支持:

  • 用户可以在 :models 键下定义自定义的命名模型配置
  • 新的 create-model-builder-from-configcreate-model-from-config 函数使用 nrepl-client-map 访问用户配置
  • 在未找到自定义模型时回退到内置默认值
  • 支持使用 [:env "VAR_NAME"] 语法的环境变量引用, 以实现安全的 API 密钥配置
  • 支持所有现有的验证和模型参数
  • 示例: :models {:openai/my-fast {:model-name "gpt-4o" :temperature 0.3 :api-key [:env "OPENAI_API_KEY"]}}

新的工厂函数模式: 该项目已重构为使用更清晰的模式来创建自定义 MCP 服务器:

  • 具有一致签名的工厂函数 (make-tools, make-prompts, make-resources)
  • 通过 core/build-and-start-mcp-server 的单一入口点
  • 简化的自定义服务器创建
  • main_examples/ 目录中的示例实现

Scratch Pad 工具添加: 添加了一个新的 scratch_pad 工具, 用于跨工具调用的持久数据存储. 该工具能够:

  • 通过共享状态实现代理间通信
  • 使用结构化待办事项列表进行任务跟踪
  • 增量构建复杂的数据结构
  • 使用 set_path~/~get_path~/~delete_path 操作进行基于路径的数据操作
  • 直接存储 JSON 兼容值

工具重组: 为了提高代码库的可维护性, 未使用的工具已移至 /src/clojure_mcp/other_tools/. 这种分离明确了哪些工具在主 MCP 服务器 (main.clj) 中被积极使用, 哪些工具虽然仍然可用但目前并非必不可少.

此项目摘要旨在为 AI 助手提供对 Clojure MCP 项目结构和功能的快速理解, 从而在最少的附加上下文下实现更有效的辅助. 该项目不断发展, 改进重点是使其更容易创建自定义 MCP 服务器, 同时保持与各种 LLM 的兼容性.

18. LLM 代码风格偏好

18.1. Clojure 风格指南

18.1.1. 条件语句

  • 对于单个条件检查, 使用 if, 而不是 cond
  • 仅当有多个条件分支时才使用 cond
  • 优先使用 if-letwhen-let 在一步中绑定并测试一个值
  • 对于只有单个结果且没有 else 分支的条件, 考虑使用 when
  • 考虑 cond->cond->>

18.1.2. 变量绑定

  • 避免不必要的 let 绑定以减少代码点
  • 仅当一个值被多次使用或为了清晰起见时才使用 let
  • 内联只使用一次的值, 而不是将它们绑定到变量
  • 使用线程宏 (->, ->>) 来消除中间绑定

18.1.3. 参数和解构

  • 当访问多个键时, 在函数参数中使用解构
  • 示例: [{:keys [::zloc ::match-form] :as ctx}] 用于命名空间键, 而不是单独的 let 绑定
  • 示例: [{:keys [zloc match-form] :as ctx}] 用于常规关键字

18.1.4. 控制流

  • 尽可能跟踪实际值而不是布尔标志
  • 使用 when 提前返回, 而不是使用深度嵌套的条件语句
  • 对于“未找到”的条件, 返回 nil 而不是带有布尔标志的对象

18.1.5. 注释

  • 不要在生成的代码中包含注释, 除非特别要求.

18.1.6. 嵌套

  • 通过使用适当的控制流结构来最小化嵌套级别
  • 对顺序操作使用线程宏 (->, ->>)

18.1.7. 函数设计

  • 函数通常应该只做一件事
  • 纯函数优于有副作用的函数
  • 返回可被调用者使用的有用值
  • 更小的函数使编辑更快, 并减少 token 数量
  • 减少 token 让我高兴

18.1.8. 库偏好

  • 优先使用 clojure.string 函数, 而不是 Java 互操作来进行字符串操作
    • 使用 str/ends-with? 代替 .endsWith
    • 使用 str/starts-with? 代替 .startsWith
    • 使用 str/includes? 代替 .contains
    • 使用 str/blank? 代替检查 .isEmpty.trim
  • 遵循 Clojure 命名约定 (谓词以 ? 结尾)
  • 偏爱更具表现力和惯用性的内置 Clojure 函数

18.1.9. REPL 最佳实践

  • 始终使用 :reload 标志重新加载命名空间: (require '[namespace] :reload)
  • 始终切换到你正在工作的命名空间

18.1.10. 测试最佳实践

  • 在运行测试前, 始终使用 :reload 标志重新加载命名空间: (require '[namespace] :reload)
  • 测试正常执行路径和错误条件

18.1.11. 使用 Shell 命令

  • 优先使用惯用的 clojure.java.shell/sh 来执行 shell 命令
  • 始终处理 shell 命令执行可能产生的错误
  • 对相对路径使用显式工作目录: (shell/sh "cmd" :dir "/path")
  • 对于测试构建和任务, 运行 clojure -X:test 而不是零散地运行测试
  • 捕获 shell 输出时, 请记住对于非常大的输出可能会被截断
  • 考虑对具有成熟 CLI 工具的任务使用 shell 命令, 如 diffing 或 git 操作
  • 上下文维护:
    • 使用 clojure_eval:reload 来确保你正在使用最新的代码
    • 始终切换到你正在工作的 (in-ns ...) 命名空间
    • 跨命名空间边界时, 保持函数和命名空间引用完全限定

19. Clojure MCP FAQ

19.1. 目录

  • [为什么我总是看到类似 "File has been modified since last read" 的消息?](#为什么我总是看到类似-file-has-been-modified-since-last-read-的消息)
  • [如何验证文件读取跟踪是否正常工作?](#如何验证文件读取跟踪是否正常工作)
  • [如何配置文件时间戳跟踪行为?](#如何配置文件时间戳跟踪行为)
  • [为什么会有折叠读取?](#为什么会有折叠读取)

19.2. 为什么我总是看到类似 "File has been modified since last read" 的消息?

问: 为什么当 AI 尝试编辑文件时, 我会收到这个错误?

File has been modified since last read: /Users/myname/myproject/src/something.clj
Please read the WHOLE file again with collapse: false before editing.

答: 这是预期行为. Clojure MCP 实现了一个类似于 Claude Code 的文件安全系统, 用于防止编辑冲突.

19.2.1. 文件时间戳跟踪系统

该系统跟踪文件的读取和修改时间, 以确保 AI 助手不会意外覆盖外部更改. 这可以防止以下情况:

  • 在你工作时, 另一位开发人员修改了文件
  • 你的编辑器自动保存了更改
  • Git 操作更新了文件
  • 构建工具修改了生成的文件

所有编辑工具在成功编辑后都会自动更新内部时间戳:

  • clojure_edit - 结构感知的 Clojure 表单编辑
  • clojure_edit_replace_sexp - S-表达式替换
  • file_edit - 基于文本的文件编辑
  • file_write - 文件创建/覆盖

这意味着 AI 在使用任何这些工具后, 可以继续对文件进行编辑, 而无需再次读取.

19.2.2. 为什么折叠读取不算数

当 AI 使用 read_filecollapsed: true (Clojure 文件的默认设置) 时, 它只看到文件的部分视图:

  • 只显示函数签名
  • 函数体被隐藏, 除非它们与搜索模式匹配
  • 注释可能被排除

由于折叠读取不显示完整的文件内容, 它们不会更新文件的“上次读取”时间戳. 这是故意的 - 系统要求 AI 在允许编辑之前, 必须看到文件的 完整当前状态.

当你看到此错误消息时, 这仅仅意味着系统提示 AI 在进行更改之前先读取完整文件. 这种安全机制确保编辑始终基于当前的文件状态, 防止意外丢失工作, 并在你的开发工作流程中保持一致性.

19.3. 如何验证文件读取跟踪是否正常工作?

问: 我如何测试时间戳跟踪系统是否按预期工作?

答: 你可以要求 AI 运行此验证序列:

请通过执行以下测试序列来验证文件时间戳跟踪系统是否正常工作:

1. 首先, 使用 file_write 创建一个新的测试文件:
   - 路径: ./timestamp-tracking-test.clj
   - 内容: (ns test.timestamp-tracking)\n\n(defn hello []\n  (println "Hello, World!"))

2. 在不先读取文件的情况下, 尝试使用 file_edit 对其进行编辑:
   - 将 "Hello, World!" 替换为 "Hello, Universe!"
   - 这应该会失败, 并显示 "File has been modified since last read" 错误

3. 现在对文件进行一次折叠读取:
   - 使用带 collapsed: true 的 read_file
   - 然后再次尝试相同的编辑
   - 这仍然应该失败, 因为折叠读取不会更新时间戳

4. 对文件进行一次完整读取:
   - 使用带 collapsed: false 的 read_file
   - 然后再次尝试相同的编辑
   - 这应该会成功

5. 在不再次读取的情况下, 进行另一次编辑:
   - 将 "Universe" 替换为 "Clojure"
   - 这应该会成功, 因为上一次编辑更新了时间戳

6. 通过使用 bash 的 touch 命令模拟外部修改:
   - 运行: touch ./timestamp-tracking-test.clj
   - 等待 1 秒 (sleep 1)
   - 然后尝试将 "Clojure" 编辑为 "MCP"
   - 这应该会失败, 因为文件被外部修改了

7. 清理:
   - 删除测试文件

报告每一步的结果, 指出是否出现了预期的行为.

此测试序列验证了:

  • 新文件在编辑前需要读取
  • 折叠读取不满足时间戳要求
  • 完整读取会正确更新时间戳
  • 成功的编辑会自动更新时间戳
  • 外部修改能被正确检测到

如果所有步骤都产生预期的结果, 则文件时间戳跟踪系统工作正常.

19.4. 如何配置文件时间戳跟踪行为?

问: 我可以更改文件时间戳跟踪的工作方式吗?

答: 可以! :write-file-guard 配置选项允许你自定义时间戳跟踪行为. 将此添加到你的 .clojure-mcp/config.edn 文件中:

{:write-file-guard :full-read}  ; 默认行为

可用选项:

  • :full-read (默认) - 只有完整读取 (collapsed: false) 才会更新时间戳. 这是最安全的选择, 确保 AI 在编辑前看到完整的文件内容.
  • :partial-read - 完整和折叠读取都会更新时间戳. 这允许在折叠读取后进行编辑, 但安全性较低.
  • false - 完全禁用时间戳检查. 文件可以在没有任何读取要求的情况下进行编辑. 请谨慎使用!

示例配置:

;; 允许在折叠读取后进行编辑
{:write-file-guard :partial-read}

;; 禁用所有时间戳检查
{:write-file-guard false}

此配置在以下情况下很有用:

  • 你独自工作, 不太可能发生外部修改 (:partial-read)
  • 你正在进行快速原型设计, 并希望跳过安全检查 (false)
  • 你希望在团队环境中获得最大程度的安全性 (:full-read - 默认)

19.5. 为什么会有折叠读取?

问: 为什么 AI 有时会以“折叠”模式读取文件?

答: 折叠读取是一项强大的功能, 旨在帮助 AI 助手更高效地处理 Clojure 代码库.

19.5.1. 折叠读取的好处

1. 高效的代码导航

  • 大型 Clojure 文件可能包含数千行代码
  • 折叠视图仅显示函数签名, 快速提供文件结构的概览
  • AI 可以迅速了解文件中存在哪些函数, 定义和表单

2. 基于模式的探索

  • AI 可以使用 name_pattern 搜索特定函数 (例如, "validate.*" 查找所有验证函数)
  • 它可以找到包含特定逻辑的代码, 使用 content_pattern (例如, "try|catch" 查找错误处理)
  • 只有匹配的函数会被展开, 从而将焦点保持在相关代码上

3. 令牌效率

  • 只显示需要的内容可以减少 AI 需要处理的文本量
  • 这使得 AI 能够探索更多文件并在整个代码库中建立联系
  • 在处理大型项目时尤其重要

4. 迭代式发现

  • AI 可以从折叠读取开始以了解结构
  • 然后使用模式深入到特定的兴趣领域
  • 最后, 仅在需要编辑时才读取完整文件

19.5.2. 示例工作流程

你可能会看到 AI:

  1. 使用折叠读取来查看命名空间中的所有函数
  2. 使用 name_pattern: "handle-.*" 来查找所有处理函数
  3. 使用 content_pattern: "database|db" 来查找与数据库相关的代码
  4. 当需要编辑某些内容时, 使用 collapsed: false 进行读取

这种方法模仿了人类开发人员导航代码的方式 - 我们不会阅读每个文件的每一行, 而是扫描结构并放大与当前任务相关的内容.

20. Clojure-MCP 配置

如果你想调整 clojure-mcp 的操作但又不想自定义自己的服务器, 现在你可以通过配置文件控制大多数设置.

配置文件的默认位置是 .clojure-mcp/config.edn. 你可以通过 :config-file CLI 参数覆盖此路径 (参见 README “CLI 选项”).

20.1. 核心配置

这些基本设置影响 clojure-mcp 如何与你的本地系统交互.

20.1.1. :allowed-directories

为了安全起见, 控制 MCP 工具可以访问哪些目录. 路径可以是相对的 (从项目根目录解析) 或绝对的.

20.1.2. :cljfmt

布尔标志, 用于启用/禁用编辑流程中的 cljfmt 格式化 (默认为 true). 禁用时, 文件编辑会保留原始格式, 而不应用 cljfmt.

可用值:

  • true (默认) - 对所有编辑过的文件应用 cljfmt 格式化
  • false - 禁用格式化, 保留确切的空白和格式

何时使用每个设置:

  • true - 最适合在整个项目中保持一致的代码风格
  • false - 当处理具有特定格式要求的文件或希望保留手动格式时很有用

20.1.3. :write-file-guard

控制文件时间戳跟踪行为 (默认为 :partial-read). 此设置根据读取操作决定何时允许文件编辑.

可用值:

  • :partial-read (默认) - 完整和折叠读取都会更新时间戳. 允许在折叠读取后进行编辑, 提供了更多便利性, 但安全性稍低.
  • :full-read - 只有完整读取 (collapsed: false) 才会更新时间戳. 这是最安全的选择, 确保 AI 在编辑前看到完整的文件内容.
  • false - 完全禁用时间戳检查. 文件可以在没有任何读取要求的情况下进行编辑. 请谨慎使用!

何时使用每个设置:

  • :partial-read - 适合单人开发, 当你想要更快的工作流程但仍希望防止外部修改时
  • :full-read - 最适合团队环境或处理可能被外部修改的文件时
  • false - 仅用于快速原型设计或当你确定不会发生外部修改时

时间戳跟踪系统可防止当文件被外部进程 (其他开发人员, 编辑器, git 操作等) 修改时发生意外覆盖.

20.1.4. :start-nrepl-cmd

可选 - 如果 nREPL 服务器尚未运行, 则自动启动一个的命令. 必须指定为字符串向量. MCP 服务器将启动此进程并管理其生命周期.

重要: 此功能要求 MCP 服务器从你的项目目录 (你的 deps.ednproject.clj 所在的位置) 启动. nREPL 服务器将在当前工作目录中启动. 这使其非常适合与 Claude Code 和其他命令行 LLM 客户端一起使用, 当你想要自动启动 nREPL 时 - 你可以简单地在你的项目目录中启动 Claude Code, nREPL 将自动启动.

Claude Desktop 用户注意: Claude Desktop 不会从你的项目目录启动 MCP 服务器, 因此你配置文件中的 :start-nrepl-cmd 本身将不起作用. 你还必须在 Claude Desktop 的设置中配置 :project-dir 参数, 以指向你的特定项目. 此限制不影响 Claude Code 或其他你从项目目录运行的基于 CLI 的工具.

行为:

  • 当不带 :port 使用时, MCP 服务器将自动从命令的输出中解析端口
  • 当与 :port 一起使用时, 它将使用该固定端口, 而不是从输出中解析

可用值:

  • ["lein" "repl" ":headless"] - 以无头模式启动 Leiningen REPL
  • ["clojure" "-M:nrepl"] - 使用 nREPL 别名启动 Clojure
  • ["bb" "nrepl-server"] - 启动 Babashka nREPL 服务器

何时使用:

  • 与从你的项目目录启动的 Claude Code 或其他基于 CLI 的 LLM 工具一起使用
  • 当你想要自动管理 nREPL 服务器而无需单独的终端窗口时
  • 在 CI/CD 环境中, 自动启动是有益的

20.1.5. :emacs-notify

布尔标志, 用于启用 Emacs 集成通知.

Emacs notify 目前只是一个玩具… 它会将焦点切换到 正在编辑的文件, 并在发生更改时高亮显示. 可能有 更好的方法来处理这个问题, 比如使用 auto-revert 和现有的 Emacs 库.

Emacs 集成的先决条件:

  • emacsclient 必须在你的系统 PATH 中可用
  • Emacs 服务器必须正在运行 (使用 M-x server-start 启动或将 (server-start) 添加到你的初始化文件中)
  • 该集成允许 MCP 服务器与你的 Emacs 编辑器通信, 以增强开发工作流程

20.1.6. :scratch-pad-load

布尔标志, 用于在启动时自动加载草稿板 (默认为 false).

可用值:

  • false (默认) - 草稿板会保存到磁盘, 但在启动时不加载
  • true - 启动时加载现有数据

何时使用每个设置:

  • false - 最适合临时规划和仅限会话的数据
  • true - 当你希望数据在会话和服务器重启之间持久化时

20.1.7. :scratch-pad-file

草稿板持久化的文件名 (默认为 "scratch_pad.edn").

配置:

  • 指定 .clojure-mcp/ 目录内的文件名

20.1.8. :bash-over-nrepl

布尔标志, 用于控制 bash 命令执行模式 (默认为 true). 此设置决定 bash 命令是通过 nREPL 连接执行还是在 MCP 服务器上本地执行.

n可用值:

  • true (默认) - 通过带有隔离会话的 nREPL 连接执行 bash 命令
  • false - 在 Clojure MCP 服务器进程中本地执行 bash 命令

何时使用每个设置:

  • true - 最适合大多数开发场景, 因为它允许你仅沙盒化 nREPL 服务器进程
  • false - 当 nREPL 服务器不是 Clojure 进程时 (例如, ClojureScript/CLJS, Babashka, Scittle) 很有用

技术细节:

  • 当为 true 时, bash 命令在单独的 nREPL 会话中运行
  • 两种模式都应用一致的输出截断 (总共 8500 个字符, 在 stdout/stderr 之间分配)
  • 本地执行对于简单命令可能更快, 但需要 MCP 服务器安装必要的工具

20.1.9. :dispatch-agent-context

为 dispatch agent 预先提供关于你的代码的详细信息, 以帮助它更快, 更准确地找到答案.

可用值:

  • true (默认) - 将 PROJECT_SUMMARY.md (如果可用) 和 ./.clojure-mcp/code_index.txt 添加到上下文中
  • 指定发送到 dispatch_agent 的特定文件的向量

注意: 可能会消耗更多的 API token, 甚至超过 LLM 的上下文窗口

20.2. 中级定制

其他选项允许你微调, 增强甚至覆盖默认行为.

clojure-mcp 的许多行为都作为组件暴露出来. 这些包括资源, 提示, 代理, 工具和模型.

20.2.1. 资源

:resources 键下配置.

资源包括你希望与 clojure-mcp 一起使用的文件和其他内容. 资源提供只读内容, 如文档, 配置文件或项目信息. 无论你是在 ClojureMCP 内部使用还是作为独立资源, 这种方法都适用.

[配置资源](doc/configuring-resources.md)

20.2.2. 提示

:prompts 键下配置.

你可以向 clojure-mcp 添加自己的提示, 然后根据需要按名称调用它们. 提示生成对话上下文, 以帮助 AI 助手理解特定任务或工作流程. 无论你是在 ClojureMCP 内部使用还是作为独立提示, 这种方法都适用.

[创建提示](doc/creating-prompts.md)

20.2.3. 模型

:models 键下配置.

你可能希望使用与默认提供的模型不同的模型. 使用你自己的 API 密钥, 端点和参数配置自定义 LLM 模型. 通过 LangChain4j 集成, 支持 OpenAI, Anthropic, Google Gemini 等.

[模型配置](doc/model-configuration.md)

20.2.4. 代理

:agents 键下配置.

请参阅 README 的“代理工具”部分中的代理配置指南; 专门的文档即将发布.

20.2.5. 工具配置

:tools-config 键下配置.

20.2.6. 组件过滤

对于你定义的每个服务器, 你可以使用过滤器对暴露的工具, 提示和资源进行精细控制. 了解如何使用启用/禁用列表来控制你的 MCP 服务器所暴露的工具, 提示和资源. 非常适合创建只包含所需组件的专注, 安全或专门的 MCP

Author: 青岛红创翻译

Created: 2025-11-06 Thu 15:46