用Common Lisp构建MCP服务器:从协议解析到AI工具集成的实践
2026/5/27 7:09:28 网站建设 项目流程

1. 项目概述:为什么用Common Lisp构建MCP服务器?

如果你和我一样,是个对Lisp方言有特殊偏好的开发者,同时又经常在AI辅助编程的生态里折腾,那你肯定听说过Model Context Protocol。简单说,MCP就像一条标准化的数据管道,让像Claude Code、Cursor这类AI助手能安全、可控地访问你本地的项目上下文、数据库、API,甚至是终端。它解决了AI工具“信息孤岛”的问题,让AI真正成为你工作流的一部分,而不是一个只能聊天的玩具。

但翻看官方文档和社区案例,清一色的TypeScript、Python,偶尔冒出来个Go或Rust。用Common Lisp来写?听起来就像开着老爷车去跑F1。但这恰恰是乐趣所在,也是价值所在。Common Lisp的交互式开发、强大的宏系统、以及那种“代码即数据”的哲学,与MCP这种需要灵活定义协议、快速迭代服务器逻辑的场景,有着惊人的契合度。这不仅仅是“能用”,而是探索一种更优雅、更符合Lisp精神的集成方式。这篇内容,就是记录我如何用Common Lisp,从零开始搓出一个功能完整、稳定可用的MCP服务器,并分享其中踩过的坑和收获的惊喜。

2. 核心架构与设计思路拆解

2.1 MCP协议核心与Common Lisp的映射关系

MCP协议的核心抽象并不复杂,主要围绕几种资源展开:Tools(工具,AI可以调用的函数)、Resources(资源,AI可以读取的数据源)、Prompts(提示词模板)以及底层的stdin/stdoutJSON-RPC通信。我们的任务,就是在Common Lisp中为这些抽象找到合适的表达方式。

首先,协议通信层。MCP服务器与客户端(如Claude Desktop)通过标准输入输出交换JSON-RPC消息。在Common Lisp中,我们有几个选择:直接用uiop:run-program处理子进程IO,或者使用更专门的库如cl-json-rpc。我选择了后者,因为它封装了JSON-RPC的请求/响应循环、ID管理和错误处理,让我们能更专注于业务逻辑。关键在于,我们需要定义一个*standard-input**standard-output*的监听循环,并正确解析initializetools/listresources/list等标准请求。

其次,数据模型层。MCP的ToolResource都有严格的JSON Schema定义。在动态类型的Common Lisp里,我们可以用CLOS(Common Lisp Object System)来优雅地建模。例如,定义一个mcp-tool类,包含namedescriptioninput-schema等槽位。input-schema本身可以用一个PLIST(属性列表)或一个哈希表来表示,在需要对外输出时,通过一个通用的to-json函数序列化成符合MCP规范的JSON。这里的一个设计重点是:保持内部表示的灵活性,同时确保对外接口的严格合规。

最后,服务器生命周期与状态管理。一个MCP服务器需要管理注册的工具、资源列表,处理并发请求(尽管MCP协议通常是顺序的,但我们要为未来的扩展留有余地),并优雅地处理关闭信号。我采用了一个全局的注册表(例如一个哈希表),来存储所有已注册的组件。同时,利用Common Lisp的条件系统来定义和处理MCP特定的错误,如tool-not-found-error,从而在协议层返回结构化的错误信息。

2.2 技术栈选型:为什么是这些库?

工欲善其事,必先利其器。Common Lisp的库生态虽然不像Python那样“应有尽有”,但对于构建一个网络服务来说,核心组件都非常成熟可靠。以下是我的选择及其理由:

  1. JSON处理:cl-jsonjonathan

    • 选择:我最终用了jonathan
    • 理由jonathan的性能通常优于cl-json,特别是在处理嵌套结构时。更重要的是,它的API非常简洁,通过定义jonathan:%to-jsonjonathan:%from-json的泛型函数,可以轻松地为自定义CLOS对象实现序列化和反序列化。这对于将我们内部的mcp-tool对象直接转换成协议要求的JSON格式至关重要,代码会非常干净。
  2. JSON-RPC通信:cl-json-rpc

    • 选择cl-json-rpc
    • 理由:如前所述,它省去了我们手动实现JSON-RPC消息分发的麻烦。我们需要做的是继承它的json-rpc:server类,并为我们支持的MCP方法(如tools/call)定义对应的泛型函数。它负责处理连接、消息读取、方法路由和响应回写。
  3. 命令行参数解析:unix-opts

    • 选择unix-opts
    • 理由:轻量、符合Unix风格。我们的服务器可能需要接受一些启动参数,比如指定配置文件路径、启用调试模式、设置监听端口(如果未来支持网络传输层)等。unix-opts能很好地完成这个任务。
  4. 日志记录:log4cl

    • 选择log4cl
    • 理由:一个功能完整的日志库,支持不同级别(DEBUG, INFO, WARN, ERROR)、输出到不同目标(控制台、文件)。在调试服务器与客户端的通信、追踪工具调用过程时,详尽的日志是救命稻草。我们可以轻松地配置在开发时输出DEBUG信息,在生产环境只输出ERROR。
  5. 测试框架:rove

    • 选择rove
    • 理由:语法直观,类似Python的pytest或Rust的assert_eq!。我们可以为每个工具函数、每个资源获取逻辑编写单元测试,并为整个JSON-RPC的请求-响应流程编写集成测试。保证在Lisp交互环境中快速迭代的同时,核心行为是正确的。

注意:库的选型并非一成不变。例如,如果你更熟悉yason来处理JSON,或者想用clack来构建一个HTTP版本的MCP服务器(MCP也支持SSE传输),完全可以根据项目需求调整。这里的选择是基于构建一个标准stdio传输MCP服务器的最简、最稳定路径。

2.3 项目结构规划

一个清晰的项目结构有助于长期维护。我采用了类似现代Common Lisp项目的典型布局:

mcp-server-cl/ ├── README.md ├── mcp-server-cl.asd ; 系统定义文件 ├── package.lisp ; 包定义 ├── src/ │ ├── protocol.lisp ; MCP协议常量、基础CLOS定义 │ ├── json-serialization.lisp ; 自定义对象的JSON编码/解码 │ ├── server.lisp ; JSON-RPC服务器主循环、请求分发 │ ├── tools/ ; 工具实现目录 │ │ ├── package.lisp │ │ ├── core.lisp ; 工具基类、注册逻辑 │ │ └── implementations/ ; 具体工具实现 │ │ ├── filesystem.lisp │ │ └── system.lisp │ ├── resources/ ; 资源实现目录(结构类似tools) │ └── utils.lisp ; 通用辅助函数 ├── tests/ │ └── suite.lisp ; 测试套件 └── bin/ └── run-server.lisp ; 可执行的入口脚本

这种结构将协议、服务器核心、业务逻辑(工具/资源)分离,符合单一职责原则。tools/implementations/resources/implementations/目录允许我们轻松地添加新的功能模块,只需实现特定的协议并注册即可,扩展性非常好。

3. 核心实现细节与实操要点

3.1 定义MCP协议的数据模型

一切从数据模型开始。在protocol.lisp中,我们首先定义MCP的核心概念。

;; protocol.lisp (defpackage :mcp-server/protocol (:use :cl) (:export #:mcp-tool #:mcp-resource #:tool-name #:tool-description #:tool-input-schema #:make-tool #:resource-uri #:resource-mime-type ...)) (in-package :mcp-server/protocol) (defclass mcp-tool () ((name :initarg :name :reader tool-name :type string :documentation "工具的唯一标识符,如 'list_files'") (description :initarg :description :reader tool-description :type string) (input-schema :initarg :input-schema :reader tool-input-schema :type list ; 用PLIST表示JSON Schema :documentation "工具参数的JSON Schema定义")) (:documentation "表示一个MCP工具。")) (defclass mcp-resource () ((uri :initarg :uri :reader resource-uri :type string :documentation "资源的URI,如 'file:///path/to/file'") (mime-type :initarg :mime-type :reader resource-mime-type :type string :documentation "资源的MIME类型,如 'text/plain'") (contents :initarg :contents :accessor resource-contents :type list ; 通常是文本或结构化数据 :documentation "资源的具体内容")) (:documentation "表示一个MCP资源。")) ;; 辅助构造函数 (defun make-tool (name description &key input-schema) (make-instance 'mcp-tool :name name :description description :input-schema (or input-schema '((:type . "object") (:properties . #())))))

这里的关键是input-schema。在MCP协议中,它必须是一个有效的JSON Schema对象。我们在Lisp内部用PLIST(例如(:type \"object\" :properties ...))或一个哈希表来存储它,这样在序列化成JSON时,jonathan可以轻松处理。

3.2 实现JSON序列化与协议适配

接下来,我们需要让我们的CLOS对象能被jonathan正确编码。在json-serialization.lisp中:

;; json-serialization.lisp (in-package :mcp-server/protocol) ;; 为jonathan定义to-json方法 (defmethod jonathan:%to-json ((tool mcp-tool)) (jonathan:with-object (jonathan:write-key-value "name" (tool-name tool)) (jonathan:write-key-value "description" (tool-description tool)) (jonathan:write-key-value "inputSchema" (tool-input-schema tool)))) ;; 同样为mcp-resource实现 (defmethod jonathan:%to-json ((resource mcp-resource)) (jonathan:with-object (jonathan:write-key-value "uri" (resource-uri resource)) (jonathan:write-key-value "mimeType" (resource-mime-type resource)) (jonathan:write-key-value "contents" (resource-contents resource)))) ;; 一个通用的工具列表响应生成函数 (defun make-tools-list-response (tools) `((:tools . ,(mapcar #'jonathan:%to-json tools))))

注意inputSchema的键名,必须严格遵守MCP协议规范(驼峰命名)。jonathan:with-object宏使得构建JSON对象变得非常直观。

实操心得:在实现序列化时,最容易出错的地方是JSON键名的格式和嵌套结构的准确性。一个很好的调试方法是,单独写一个小脚本,创建一个工具对象,调用jonathan:to-json打印出来,然后与MCP官方协议文档中的示例进行逐字段对比。确保你的输出能被标准的MCP客户端正确解析。

3.3 构建JSON-RPC服务器骨架

现在是核心的服务器逻辑。在server.lisp中,我们继承并定制json-rpc:server

;; server.lisp (defpackage :mcp-server/server (:use :cl :mcp-server/protocol) (:import-from :json-rpc #:define-json-rpc-method #:invoke-json-rpc-server) (:import-from :log4cl #:log-info #:log-debug #:log-error) (:export #:start-mcp-server)) (in-package :mcp-server/server) ;; 全局注册表 (defvar *tool-registry* (make-hash-table :test 'equal)) (defvar *resource-registry* (make-hash-table :test 'equal)) (defun register-tool (tool) (setf (gethash (tool-name tool) *tool-registry*) tool) (log-info "Tool registered: ~a" (tool-name tool))) (defun list-all-tools () (loop for tool being the hash-values of *tool-registry* collect tool)) ;; 定义MCP标准方法 (define-json-rpc-method initialize (params) (log-debug "Received initialize request: ~a" params) ;; 返回服务器能力信息,如支持的协议版本、工具/资源列表 `((:protocolVersion . "2024-11-05") (:capabilities . ((:tools . ((:listChanged . :false))) (:resources . ((:listChanged . :false))))))) (define-json-rpc-method tools/list (params) (declare (ignore params)) ; 此方法通常不需要参数 (let ((tools (list-all-tools))) (log-debug "Listing ~d tools" (length tools)) (make-tools-list-response tools))) (define-json-rpc-method tools/call (params) (destructuring-bind (&key name arguments) params (log-info "Calling tool: ~a with args ~a" name arguments) (let ((tool (gethash name *tool-registry*))) (if tool ;; 这里需要根据工具名分派到具体的实现函数 (invoke-tool tool arguments) (error 'json-rpc:json-rpc-error :code -32601 :message (format nil "Tool not found: ~a" name)))))) ;; 主服务器启动函数 (defun start-mcp-server (&key (input-stream *standard-input*) (output-stream *standard-output*)) (log-info "Starting MCP Server (Common Lisp)") (invoke-json-rpc-server :input-stream input-stream :output-stream output-stream :server-name "MCP-Server-CL"))

define-json-rpc-method宏是cl-json-rpc提供的,它自动将JSON-RPC方法名映射到Lisp函数。invoke-json-rpc-server则启动了主事件循环,持续从input-stream读取请求,分派到对应的方法,并将结果写入output-stream

4. 工具与资源的实现范例

有了服务器骨架,我们来填充一些有血有肉的工具和资源。

4.1 实现一个文件系统工具

一个非常实用的工具是让AI能读取目录列表。我们在tools/implementations/filesystem.lisp中实现。

;; tools/implementations/filesystem.lisp (in-package :mcp-server/tools) (defclass list-directory-tool (mcp-tool) ()) (defmethod initialize-instance :after ((tool list-directory-tool) &key) (call-next-method) (setf (slot-value tool 'name) "list_directory") (setf (slot-value tool 'description) "List files and directories in a given path.") (setf (slot-value tool 'input-schema) '((:type . "object") (:properties . ((:path . ((:type . "string") (:description . "The directory path to list."))))) (:required . ["path"])))) (defun invoke-list-directory (arguments) (destructuring-bind (&key path) arguments (let ((full-path (uiop:ensure-directory-pathname path))) (unless (uiop:directory-exists-p full-path) (error 'json-rpc:json-rpc-error :code -32000 :message (format nil "Directory does not exist: ~a" path))) (let ((entries (uiop:directory-files full-path))) `((:entries . ,(mapcar #'namestring entries))))))) ;; 注册这个工具 (register-tool (make-instance 'list-directory-tool))

这里我们创建了一个list-directory-tool类,并在初始化时设置了固定的namedescriptioninput-schemainvoke-list-directory是实际执行逻辑的函数,它使用Common Lisp标准库uiop(Universal Input/Output Portability)来安全地检查目录和列出文件。返回的结果是一个包含entries列表的ALIST。

我们需要修改server.lisp中的tools/call方法,使其能根据工具名调用对应的invoke-*函数。这可以通过一个分发表或另一个注册表来实现。

4.2 实现一个系统信息资源

资源(Resources)通常用于提供只读数据。例如,我们可以提供一个显示当前系统负载的资源。

;; resources/implementations/system.lisp (in-package :mcp-server/resources) (defclass system-load-resource (mcp-resource) ()) (defmethod initialize-instance :after ((resource system-load-resource) &key) (call-next-method) (setf (slot-value resource 'uri) "resource://system/load") (setf (slot-value resource 'mime-type) "application/json") ;; 资源的内容可以是一个函数,在请求时动态生成 (setf (slot-value resource 'contents) (lambda () ;; 模拟获取系统负载,实际中可能需要调用系统命令 (let ((load (format nil "~,2f" (random 2.0)))) ; 示例用随机数 `((:load . ,load) (:timestamp . ,(get-universal-time))))))) (defun get-system-load-resource () (let ((resource (make-instance 'system-load-resource))) ;; 调用contents函数获取最新数据 (setf (resource-contents resource) (funcall (slot-value resource 'contents))) resource)) ;; 注册资源URI到全局注册表 (setf (gethash "resource://system/load" *resource-registry*) #'get-system-load-resource)

这里有个技巧:我们将contents槽位设为一个函数(lambda)。这样,每次客户端请求这个资源时,我们都可以执行这个函数来获取最新的系统负载数据,而不是静态内容。在resources/listresources/read方法中,需要识别这种“动态资源”并执行函数来获取内容。

5. 集成、测试与调试全流程

5.1 组装系统并创建入口点

mcp-server-cl.asd中定义系统依赖:

(asdf:defsystem #:mcp-server-cl :description "An MCP server implemented in Common Lisp." :version "0.1.0" :author "Your Name" :license "MIT" :depends-on (#:jonathan #:cl-json-rpc #:log4cl #:unix-opts #:rove) :serial t :components ((:file "package") (:file "src/protocol") (:file "src/json-serialization") (:file "src/utils") (:module "tools" :components ((:file "package") (:file "core") (:file "implementations/filesystem"))) (:module "resources" :components ((:file "package") (:file "core") (:file "implementations/system"))) (:file "src/server") (:file "bin/run-server")))

创建一个简单的入口脚本bin/run-server.lisp

#!/usr/bin/env sbcl --script (require :asdf) (asdf:load-system :mcp-server-cl) (in-package :mcp-server/server) ;; 初始化日志 (log4cl:start-simple-logger) ;; 注册所有工具和资源(可以通过扫描目录自动完成,这里手动注册) (load "tools/implementations/filesystem.lisp") (load "resources/implementations/system.lisp") ;; 启动服务器 (start-mcp-server)

5.2 编写单元与集成测试

使用rove编写测试。在tests/suite.lisp中:

(deftest tool-registration (testing "Tool can be registered and retrieved" (let ((tool (make-instance 'mcp-tool :name "test_tool" :description "A test"))) (register-tool tool) (ok (eq tool (gethash "test_tool" *tool-registry*))) (clear-registry)))) ; 假设有一个清理函数 (deftest list-directory-tool-schema (testing "List directory tool has correct JSON schema" (let ((tool (make-instance 'list-directory-tool))) (ok (string= "list_directory" (tool-name tool))) (let ((schema (tool-input-schema tool))) (ok (getf schema :type)) (ok (getf schema :properties))))) (deftest json-serialization (testing "MCP tool serializes to correct JSON" (let ((tool (make-instance 'mcp-tool :name "test" :description "desc" :input-schema '((:type . "object"))))) (let ((json (jonathan:to-json tool))) (ok (search "\"name\":\"test\"" json)) (ok (search "\"inputSchema\":" json))))))

运行测试:(asdf:test-system :mcp-server-cl)

5.3 与真实MCP客户端(Claude Desktop)联调

这是最具挑战也最有成就感的一步。

  1. 配置Claude Desktop:在Claude Desktop的配置目录(如~/Library/Application Support/Claude/claude_desktop_config.json)中添加你的服务器配置。

    { "mcpServers": { "my-lisp-server": { "command": "/path/to/your/sbcl", "args": ["--script", "/absolute/path/to/bin/run-server.lisp"], "env": { "LANG": "en_US.UTF-8" } } } }

    踩坑实录commandargs必须使用绝对路径。相对路径在Claude Desktop的上下文中可能无法解析。环境变量LANG设置可以避免某些Lisp实现输出非UTF-8字符时出现问题。

  2. 启动与日志:重启Claude Desktop。查看Claude Desktop的日志(位置因系统而异)和你服务器进程的输出(如果你将日志重定向到了文件)。你应该能看到initialize握手成功的消息。

  3. 测试工具调用:在Claude聊天框中,尝试输入“请使用list_directory工具查看我的home目录”。Claude应该能识别到你注册的工具,并生成一个调用请求。观察服务器日志,看是否收到tools/call请求,参数是否正确,以及返回的JSON是否被Claude正确解析并展示结果。

  4. 调试通信:如果握手失败或调用无响应,问题通常出在:

    • JSON格式错误:服务器返回的JSON不符合MCP协议规范。使用jq命令或在线JSON校验器仔细检查你的序列化输出。
    • 标准IO流问题:确保服务器是从标准输入读取,并向标准输出写入。不要在代码中意外关闭了这些流或向标准错误输出调试信息(除非客户端能处理)。
    • 协议版本不匹配:在initialize响应中返回的protocolVersion必须与客户端兼容。

6. 性能优化、错误处理与进阶技巧

6.1 性能考量与优化点

虽然MCP服务器的请求频率通常不高,但良好的性能设计总是有益的。

  1. 懒加载与缓存:对于resources/read请求,如果资源内容计算成本高(如查询数据库),可以考虑缓存结果。但要注意MCP协议可能支持contents中的pollingInterval提示客户端缓存时间,服务器端缓存应与之协调。
  2. 连接管理:当前基于stdio的服务器,生命周期与客户端进程绑定。如果未来支持SSE/HTTP,需要考虑连接池和状态隔离。
  3. JSON处理性能jonathan已经很快。确保在序列化大型资源(如一个包含成千上万行代码的文件列表)时,使用流式输出或分页,避免一次性构建巨大的内存中的Lisp列表再转换。

6.2 全面的错误处理

健壮的错误处理能提升开发体验和服务器稳定性。

  1. 定义MCP错误类型
    (define-condition mcp-error (error) ((code :initarg :code :reader error-code) (message :initarg :message :reader error-message)) (:report (lambda (condition stream) (format stream "MCP Error ~a: ~a" (error-code condition) (error-message condition))))) (define-condition tool-execution-error (mcp-error) ()) (define-condition resource-not-found-error (mcp-error) ())
  2. 在工具调用中捕获错误:修改invoke-tool函数,用handler-case包裹具体工具逻辑,将Lisp错误转换为结构化的MCP错误响应。
    (defun invoke-tool (tool arguments) (handler-case (funcall (tool-implementation tool) arguments) ; 假设工具关联了一个实现函数 (unix:unix-error (c) (error 'json-rpc:json-rpc-error :code -32000 :message (format nil "System error: ~a" (unix:get-error-message c)))) (error (c) (error 'json-rpc:json-rpc-error :code -32603 :message (format nil "Internal error: ~a" c)))))
  3. 输入验证:在调用工具实现前,依据input-schema验证arguments。可以集成cl-json-schema之类的库,或者自己编写简单的验证逻辑。

6.3 进阶扩展思路

当基础服务器运行稳定后,可以考虑以下扩展:

  1. 动态工具注册:允许在服务器运行时,通过一个特殊的工具或信号,动态加载新的工具实现(.lisp文件),而无需重启服务器。这充分利用了Common Lisp的动态性。
  2. 配置化:将工具和资源的配置(如文件系统访问的根目录、允许执行的命令列表)外置到一个配置文件(如config.lispconfig.edn)中,提升安全性。
  3. 支持SSE传输:实现MCP的SSE(Server-Sent Events)传输层。这需要启动一个HTTP服务器(如用Hunchentoot),并处理/sse/messages端点。这能让服务器独立于客户端进程运行。
  4. 构建GUI配置工具:用ltkcl-cffi-gtk写一个简单的图形界面,用来启用/禁用工具、配置参数,并生成给Claude Desktop的配置片段。

7. 常见问题与排查技巧实录

在开发和调试过程中,我遇到了不少典型问题。这里汇总成表,方便你快速定位:

问题现象可能原因排查步骤与解决方案
Claude Desktop 提示“无法连接服务器”或“服务器意外退出”。1. 启动命令路径错误。
2. Lisp 镜像依赖缺失。
3. 服务器在初始化时因未处理错误而崩溃。
1.检查配置:确认commandargs中的路径是绝对路径且可执行。在终端手动运行该命令,看是否能启动并等待输入。
2.检查依赖:确保所有ASDF系统已正确安装。可以在启动脚本开头添加(require :asdf)(asdf:load-system :your-system)
3.查看日志:在服务器启动脚本中,将*standard-output**standard-error*重定向到文件,查看崩溃前的最后输出。
握手(initialize)成功,但工具列表为空。1. 工具注册逻辑未执行。
2.tools/list方法返回的JSON格式不正确。
1.确认注册:在start-mcp-server函数前添加日志,确认register-tool被调用。确保工具注册代码在服务器启动前被执行。
2.检查JSON:在tools/list方法中,将准备返回的ALIST用jonathan:to-json打印到日志文件,检查结构是否为{"tools": [...]},且每个工具对象格式正确。
工具调用无反应,或Claude提示调用失败。1.tools/call方法未正确定义或路由。
2. 工具实现函数抛出未捕获的异常。
3. 返回的JSON格式不符合MCP规范。
1.日志追踪:在tools/call方法开始处添加日志,确认请求到达。检查name参数是否正确匹配注册的工具名。
2.错误处理:用handler-case包裹工具调用,将内部错误转换为JSON-RPC错误响应。
3.验证输出:将invoke-tool的返回值序列化后记录到日志,与 MCP工具调用结果规范 对比。确保顶层是{"content": [...]}结构。
服务器进程僵死,不响应任何请求。1. 某个工具调用陷入死循环或长时间阻塞。
2. JSON-RPC消息循环因异常中断。
1.超时机制:为工具调用实现超时。可以使用bt:with-timeout(如果Lisp实现支持)来限制执行时间。
2.全局异常捕获:在invoke-json-rpc-server外层包裹一个ignore-errors或记录日志的异常处理器,确保即使单个请求处理崩溃,服务器循环也能继续。
返回的中文或特殊字符显示乱码。字符编码问题。服务器输出非UTF-8编码的JSON。1.设置流编码:在启动服务器时,设置*standard-output*的编码为utf-8(setf (stream-external-format *standard-output*) :utf-8)
2.检查库依赖:确保jonathan等库在序列化字符串时能正确处理Unicode。

最后,我个人最深刻的体会是,用Common Lisp构建MCP服务器的过程,与其说是在实现一个协议,不如说是在用Lisp的方式重新诠释“工具”与“资源”这两个概念。那种在REPL中动态定义一个新工具、热加载进正在运行的服务器、并立刻在Claude中测试的流畅体验,是静态语言难以比拟的。它让AI助手不再是外部的、笨重的集成,而是变成了开发环境里一个可以随时对话、随时扩展能力的伙伴。如果你也在用Common Lisp,不妨试试从这个项目开始,为你自己的 workflow 打造几个专属的MCP工具,那种“人机合一”的效率提升感,绝对值得你投入时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询