Qilin MCP服务器框架

Qilin 是一个使用 Go 语言实现的 Model Context Protocol (MCP) 服务器框架。它提供了一套简洁的 API,帮助开发者快速构建符合 MCP 标准的应用后端,从而 enabling 语言模型 (LLM) 客户端与之交互,获取上下文信息和调用外部功能。

主要功能点

  • MCP协议支持: 完全遵循 Model Context Protocol (MCP) 的规范,处理标准的 JSON-RPC 请求和响应。
  • 工具注册与调用: 允许开发者定义、注册具有特定名称、描述和输入schema的工具。LLM客户端可以通过 'tools/call' 方法调用这些工具,并获取结构化或非结构化的结果。
  • 资源托管与访问: 支持注册具有URI、名称、描述和MIME类型的资源。客户端可以通过 'resources/read' 方法读取资源内容。支持URI模板和路径参数。
  • 资源列表与模板: 提供 'resources/list' 方法列出可访问的资源,以及 'resources/templates/list' 方法列出URI模板,帮助客户端理解可用的资源结构。
  • 资源变更通知: 支持 'resources/subscribe' 和 'resources/unsubscribe' 方法,允许客户端订阅特定资源的更新通知 ('notifications/resources/updated')。
  • 资源列表变更通知: 支持在资源列表发生变化时向客户端发送通知 ('notifications/resources/list_changed')。
  • 中间件支持: 提供工具和资源的中间件机制,方便在请求处理前后添加自定义逻辑(如认证、日志记录等)。
  • 多种内容类型: 工具和资源handler支持返回文本、JSON、图片、音频以及嵌入式资源等多种格式的内容。
  • JSON-RPC over Stdio: 默认使用标准输入/输出作为传输通道,符合某些LLM客户端(如一些本地运行的客户端或开发工具)的连接方式。框架设计上也考虑了未来支持其他传输协议。
  • 上下文管理: 提供丰富的上下文对象(如 'ToolContext', 'ResourceContext'),方便handler访问请求信息、路径参数,并向客户端发送响应。

安装步骤

Qilin 是一个 Go 语言库。要使用 Qilin 构建您的 MCP 服务器,首先需要安装 Go 环境。然后,可以通过 Go Modules 将 Qilin 库添加到您的项目依赖中:

go get github.com/miyamo2/qilin

接下来,您就可以在您的 Go 项目代码中导入 'github.com/miyamo2/qilin' 包来构建您的 MCP 服务器应用。

服务器配置 (供 MCP 客户端使用)

MCP 客户端(如一个大型语言模型应用)需要配置如何启动和连接到您的 MCP 服务器。典型的配置会包含服务器的名称、启动服务器可执行文件的命令及其参数。请注意,以下是一个通用的配置示例结构,具体的 'command' 和 'args' 取决于您使用 Qilin 框架编写并编译出的服务器应用程序。

{
  // MCP 服务器的唯一名称,客户端用于识别和管理该服务器实例。
  "name": "您的自定义服务器名称",

  // 启动您的 MCP 服务器应用程序的命令行命令。
  // 这通常是您编译好的 Go 可执行文件的路径。
  "command": "/path/to/your/compiled/qilin_server_executable",

  // 传递给您的服务器可执行文件的可选命令行参数列表。
  // 根据您的服务器应用程序需要配置。
  "args": ["--config", "/etc/your_server/config.yaml"],

  // 其他可能的MCP客户端特有配置...
  "protocol": "stdio" // 例如,指定传输协议为标准IO
}

MCP 客户端会使用上述配置来启动您的 MCP 服务器进程,并通过标准输入/输出进行 JSON-RPC 通信。

基本使用方法 (构建 MCP 服务器)

以下是使用 Qilin 框架构建一个简单 MCP 服务器的基本步骤:

  1. 创建 Qilin 实例:

    package main
    
    import "github.com/miyamo2/qilin"
    
    func main() {
        // 创建一个新的 Qilin 服务器实例,指定服务器名称
        q := qilin.New("my_awesome_mcp_server")
    
        // ... 注册工具、资源等 ...
    
        // 启动服务器
        if err := q.Start(); err != nil {
            // 处理启动错误
            panic(err)
        }
    }
  2. 注册工具 (Tool): 定义工具的输入结构体和处理函数。

    // 定义工具的输入参数结构体,使用 jsonschema 标签描述字段
    type AddParams struct {
        A float64 'json:"a" jsonschema:"title=First Number"'
        B float64 'json:"b" jsonschema:"title=Second Number"'
    }
    
    // 定义工具的处理函数
    func AddToolHandler(c qilin.ToolContext) error {
        var params AddParams
        // 绑定客户端传入的参数到结构体
        if err := c.Bind(&params); err != nil {
            return err
        }
        // 执行工具逻辑
        result := params.A + params.B
        // 返回结果给客户端 (JSON 格式)
        return c.JSON(map[string]float64{"sum": result})
    }
    
    func init() {
        // 在 Qilin 实例上注册工具
        q.Tool("add", (*AddParams)(nil), AddToolHandler,
            qilin.ToolWithDescription("Add two numbers together")) // 可选添加描述
    }
  3. 注册资源 (Resource): 定义资源的URI模式和处理函数。

    // 定义资源内容结构体
    type UserInfo struct {
        ID   string 'json:"id"'
        Name string 'json:"name"'
    }
    
    // 定义资源的处理函数
    func GetUserInfoHandler(c qilin.ResourceContext) error {
        // 从URI路径中获取参数
        userID := c.Param("userId")
        if userID == "" {
            return fmt.Errorf("userId is required")
        }
        // 模拟数据查询
        userInfo := UserInfo{
            ID:   userID,
            Name: fmt.Sprintf("User %s", userID),
        }
        // 返回资源内容给客户端 (JSON 格式)
        return c.JSON(userInfo)
    }
    
    func init() {
         // 在 Qilin 实例上注册资源,支持路径参数 {userId}
        q.Resource("User Profile", "myapp://users/{userId}", GetUserInfoHandler,
            qilin.ResourceWithDescription("Get user information by ID"), // 可选添加描述
            qilin.ResourceWithMimeType("application/json"))           // 可选指定MIME类型
    }
  4. 注册资源列表处理器 (可选): 如果您希望自定义 'resources/list' 的行为,可以注册资源列表处理器。

    func CustomResourceListHandler(c qilin.ResourceListContext) error {
        // c.Resources() 包含所有注册的资源(包括模板)
        // 您可以在这里动态生成或过滤资源列表
        dynamicResourceURI, _ := url.Parse("myapp://dynamic/resource")
        c.SetResource(dynamicResourceURI.String(), qilin.Resource{
            URI: (*qilin.ResourceURI)(dynamicResourceURI),
            Name: "Dynamic Resource",
            Description: "This resource is generated dynamically",
            MimeType: "text/plain",
        })
        // 不要清除 c.Resources() 中框架已注册的静态资源,除非您想完全替换列表
        return nil
    }
    
    func init() {
        q.ResourceList(CustomResourceListHandler)
    }
  5. 注册资源/列表变更观察者 (可选): 注册函数以便在资源或资源列表发生变化时通知客户端。这些函数会在服务器启动后在新goroutine中运行。

    func UserStatusChangeObserver(c qilin.ResourceChangeContext) {
        // 这是一个运行很长时间的观察者函数
        // 监听用户状态变化,并在变化时调用 c.Publish
        for {
            select {
            case <-c.Context().Done(): // 服务器关闭时退出
                return
            case <-time.After(5 * time.Minute): // 假设每5分钟检查一次
                 // 模拟某个用户状态改变,并发布通知
                 changedUserURI, _ := url.Parse("myapp://users/user123")
                 c.Publish(changedUserURI, time.Now())
            }
        }
    }
    
    func init() {
        // 为URI模式 myapp://users/{userId} 注册观察者
        q.ResourceChangeObserver("myapp://users/{userId}", UserStatusChangeObserver)
    }
  6. 编译并运行: 将您的 Go 代码编译成可执行文件,然后按照服务器配置中的 'command' 和 'args' 启动它。MCP 客户端即可连接并与之交互。

进阶用法

  • 中间件: 使用 'q.UseInTools()' 和 'q.UseInResources()' 添加全局中间件。
  • 自定义 JSON 库: 使用 'qilin.WithJSONMarshalFunc()' 和 'qilin.WithJSONUnmarshalFunc()' 选项替换默认的 JSON 库。
  • 错误处理: 在 Tool 或 Resource handler 中返回 'error' 会自动转换为 JSON-RPC 错误响应。

通过上述步骤,您可以利用 Qilin 框架快速构建符合 MCP 规范的应用后端,为您的 LLM 应用提供强大的上下文感知和外部功能调用能力。

信息

分类

AI与计算