返回市场
管道工2MCP

管道工2MCP

作者:armish5 星标更新:2025-11-22

项目介绍

plumber2mcp

<!-- badges: start -->

R-CMD-check Codecov test coverage

<!-- badges: end -->

通过一个函数调用,为您的Plumber API添加模型上下文协议(MCP)支持。

什么是MCP?

模型上下文协议(MCP)是一种标准协议,使AI助手(如Claude、ChatGPT等)能够与外部工具和服务进行交互。通过在您的Plumber API中添加MCP支持,您可以使您的R函数可用作:

  • 工具:AI助手可以直接调用您的API端点
  • 资源:AI助手可以读取文档、数据和分析结果
  • 提示:AI助手可以使用预定义的模板来引导交互

安装

# 从GitHub安装
remotes::install_github("armish/plumber2mcp")

依赖项

此包需要:

  • R (>= 4.0.0)
  • plumber (>= 1.0.0)
  • jsonlite
  • httr

快速开始

HTTP传输(默认)

library(plumber)
library(plumber2mcp)

# 创建并运行具有MCP支持的Plumber API,通过HTTP
pr("plumber.R") %>%
  pr_mcp(transport = "http") %>%
  pr_run(port = 8000)

您的API现在有:

  • http://localhost:8000/的常规HTTP端点
  • http://localhost:8000/mcp的MCP服务器

标准IO传输(原生MCP)

library(plumber)
library(plumber2mcp)

# 创建并运行具有原生标准IO传输的Plumber API
pr("plumber.R") %>%
  pr_mcp(transport = "stdio")

与mcp-cli或其他MCP客户端一起使用:

  1. 创建一个server_config.json文件:
{
  "mcpServers": {
    "plumber2mcp": {
      "command": "Rscript",
      "args": ["-e", "plumber::pr('api.R') %>% plumber2mcp::pr_mcp(transport='stdio')"],
      "cwd": "."
    }
  }
}
  1. 测试连接:
mcp-cli servers  # 应该显示您的服务器为“就绪”
mcp-cli tools    # 列出可用工具
mcp-cli cmd --tool GET__echo --tool-args '{"msg": "Hello!"}'  # 调用一个工具

工作原理

pr_mcp()函数会自动:

  1. 发现您的端点:扫描您Plumber API中的所有端点
  2. 创建MCP工具:将每个端点转换成带有适当模式的MCP工具
  3. 添加MCP端点:添加必要的MCP协议端点
  4. 处理JSON-RPC:管理所有MCP通信
  5. 支持资源:允许AI助手从您的R环境中读取文档和数据
  6. 支持提示:暴露可重复使用的提示模板以指导AI交互
  7. 生成丰富的模式:根据您的roxygen注释创建详细的输入/输出模式

增强的文档及模式生成

plumber2mcp通过分析您的roxygen注释和函数签名,自动为您的API端点生成丰富的JSON模式和详细文档。这一功能借鉴了FastAPI-MCP,使得您的R API对AI助手更加易用。

丰富的工具描述

当您使用roxygen注释记录您的端点时,plumber2mcp会创建全面的工具描述:

#* 计算数值数据的统计操作
#* 
#* 此端点执行向量数字的各种统计计算。
#* 支持多种操作,并处理缺失值。
#* 
#* @param numbers 数值向量,用于计算统计数据
#* @param operation 要执行的统计操作:"mean"、"median"、"sum"、"sd"(默认:"mean")
#* @param na_rm:bool 是否移除NA值的逻辑值(默认:TRUE)
#* @param digits:int 结果的小数位数(默认:2)
#* @return 包含计算结果和元数据的列表
#* @post /calculate
function(numbers, operation = "mean", na_rm = TRUE, digits = 2) {
  # 将输入转换为数值
  if (is.character(numbers)) {
    numbers <- as.numeric(strsplit(numbers, ",")[[1]])
  } else {
    numbers <- as.numeric(numbers)
  }
  
  result <- switch(operation,
    "mean" = mean(numbers, na.rm = na_rm),
    "median" = median(numbers, na.rm = na_rm),
    "sum" = sum(numbers, na.rm = na_rm),
    "sd" = sd(numbers, na.rm = na_rm),
    stop("未知操作:", operation)
  )
  
  list(
    result = round(result, digits),
    operation = operation,
    count = length(numbers[!is.na(numbers)]),
    input_length = length(numbers)
  )
}

这会创建一个MCP工具,具有:

增强的描述:

计算数值数据的统计操作

此端点执行向量数字的各种统计计算。
支持多种操作,并处理缺失值。

参数:
- numbers (字符串):数值向量,用于计算统计数据
- operation (字符串) [默认:mean]:要执行的统计操作:"mean"、"median"、"sum"、"sd"
- na_rm (布尔值) [默认:TRUE]:是否移除NA值的逻辑值
- digits (整数) [默认:2]:结果的小数位数

HTTP方法:POST
路径:/calculate

输入模式:

{
  "type": "object",
  "properties": {
    "numbers": {
      "type": "string",
      "description": "数值向量,用于计算统计数据"
    },
    "operation": {
      "type": "string",
      "description": "要执行的统计操作:\"mean\"、\"median\"、\"sum\"、\"sd\"",
      "default": "mean"
    },
    "na_rm": {
      "type": "boolean",
      "description": "是否移除NA值的逻辑值",
      "default": true
    },
    "digits": {
      "type": "integer",
      "description": "结果的小数位数",
      "default": 2
    }
  },
  "required": ["numbers"]
}

输出模式:

{
  "type": "object",
  "description": "结构化的响应对象",
  "properties": {
    "result": {
      "type": "number",
      "description": "响应字段:result"
    },
    "operation": {
      "type": "string",
      "description": "响应字段:operation"
    },
    "count": {
      "type": "number",
      "description": "响应字段:count"
    },
    "input_length": {
      "type": "number",
      "description": "响应字段:input_length"
    }
  }
}

类型检测和模式特性

  1. 智能类型映射:自动将R类型映射到JSON Schema类型:

    • logicalboolean
    • integerinteger
    • numeric/doublenumber
    • characterstring
    • 并支持plumber类型注解如:bool:int:array
  2. 默认值检测:从函数签名中提取默认值并包含在模式中

  3. 必需参数 vs 可选参数:自动确定哪些参数是必需的,基于它们是否有默认值

  4. 输出模式生成:分析您的函数返回语句以自动生成响应模式

  5. 丰富的参数文档:从roxygen @param标签中提取参数描述

为什么这对AI助手很重要

有了增强的模式,AI助手可以:

  1. 更好地理解您的API:丰富的描述帮助AI助手理解每个端点的作用
  2. 提供更好的建议:详细的参数信息帮助AI助手建议适当的值
  3. 生成更好的代码:输出模式帮助AI助手了解从您的API预期什么
  4. 减少错误:类型信息和必需/可选参数检测减少了API调用错误
  5. 自我文档化:您的API对于人类和AI来说都变得自我文档化

示例AI助手交互

没有增强的模式:

用户:"你能帮我使用这个API计算一些数字的平均值吗?"
AI:"我能看到有一个calculate端点,但我不确定它需要哪些参数……"

有了增强的模式:

用户:"你能帮我使用这个API计算一些数字的平均值吗?"
AI:"我能看到您有一个calculate端点,它可以执行统计操作!
     它需要一个'numbers'参数(必需),以及可选的'operation'(默认为'mean')、
     'na_rm'(默认为TRUE)和'digits'(默认为2)。
     
     让我为您调用它:
     POST /calculate with {"numbers": "1,2,3,4,5"}
     
     这将返回一个包含结果、所用操作、有效数字数量和输入长度的对象。"

文档的最佳实践

为了充分利用增强的模式生成:

  1. 使用描述性的roxygen注释:编写清晰的标题和描述
  2. 记录所有参数:使用带有清晰描述的@param标签
  3. 在有帮助时指定类型:使用:bool:int:array注解以提高清晰度
  4. 提供有意义的默认值:默认值出现在模式中
  5. 使用一致的返回结构:返回带有命名元素的列表以获得更好的输出模式
  6. 在描述中包括示例:帮助AI助手理解预期格式

MCP端点

一旦应用了pr_mcp(),您的API会公开:

  • GET /mcp - 服务器信息和能力
  • POST /mcp/messages - JSON-RPC消息处理器,用于MCP协议

示例

给定一个简单的Plumber API:

#* 回显输入
#* 
#* 简单的端点,回显发送给它的任何消息
#* 
#* @param msg:string 要回显的消息(默认:"Hello World")
#* @get /echo
function(msg = "Hello World") {
  list(message = paste("Echo:", msg))
}

#* 将两个数字相加
#* 
#* 对两个数值执行算术加法
#* 
#* @param a:number 要相加的第一个数字
#* @param b:number 要相加的第二个数字  
#* @param precision:int 结果的小数位数(默认:2)
#* @post /add
function(a, b, precision = 2) {
  result <- as.numeric(a) + as.numeric(b)
  list(
    result = round(result, precision),
    operation = "addition",
    inputs = c(a, b)
  )
}

这些端点成为具有完整模式的丰富MCP工具:

  • GET__echo - 智能默认处理的回显消息
  • POST__add - 具有可配置精度和结构化输出的加法

增强的文档为AI助手提供了关于参数类型、默认值、描述和预期响应格式的详细信息。

提示支持

提示是AI助手可以发现并使用的可重用模板。它们为常见任务和工作流程提供结构化指导。

什么是提示?

MCP中的提示是预定义的消息模板,它们:

  • 帮助AI助手有效地使用您的API
  • 为复杂的工作流程提供结构化指导
  • 可接受参数以自定义提示内容
  • 支持多轮对话

添加提示

# 不带参数的简单提示
pr %>%
  pr_mcp(transport = "stdio") %>%
  pr_mcp_prompt(
    name = "r-help",
    description = "获取R编程的帮助",
    func = function() {
      paste(
        "我需要R编程的帮助。",
        "请提供最佳实践和常见模式的指导。",
        sep = "\n"
      )
    }
  )

# 带参数的提示
pr %>%
  pr_mcp(transport = "stdio") %>%
  pr_mcp_prompt(
    name = "analyze-dataset",
    description = "为R数据集生成综合分析计划",
    arguments = list(
      list(
        name = "dataset",
        description = "要分析的R数据集名称",
        required = TRUE
      ),
      list(
        name = "focus",
        description = "要关注的具体方面",
        required = FALSE
      )
    ),
    func = function(dataset, focus = "general") {
      sprintf(
        paste(
          "请分析R中的%s数据集。",
          "关注:%s",
          "",
          "提供:",
          "1. 总结统计",
          "2. 数据质量评估",
          "3. 关键见解",
          sep = "\n"
        ),
        dataset, focus
      )
    }
  )

# 多轮对话提示
pr %>%
  pr_mcp(transport = "stdio") %>%
  pr_mcp_prompt(
    name = "code-review",
    description = "审查R代码的质量和最佳实践",
    arguments = list(
      list(name = "code", description = "要审查的R代码", required = TRUE)
    ),
    func = function(code) {
      list(
        list(
          role = "user",
          content = list(
            type = "text",
            text = paste("请审查这段R代码:", code, sep = "\n\n")
          )
        ),
        list(
          role = "assistant",
          content = list(
            type = "text",
            text = "我会检查您的代码的正确性、性能和风格。"
          )
        ),
        list(
          role = "user",
          content = list(
            type = "text",
            text = "请提供具体的改进建议。"
          )
        )
      )
    }
  )

提示消息格式

您的提示函数可以返回几种格式的消息:

  1. 简单字符串 - 自动转换为用户消息:
func = function() "Hello World"
  1. 结构化消息 - 对角色和内容有完全控制:
func = function() {
  list(
    role = "user",
    content = list(type = "text", text = "您的消息")
  )
}
  1. 多个消息 - 用于多轮对话:
func = function() {
  list(
    list(role = "user", content = list(type = "text", text = "第一条消息")),
    list(role = "assistant", content = list(type = "text", text = "第二条消息"))
  )
}

提示的用例

工作流程指导

pr_mcp_prompt(
  pr,
  name = "data-pipeline",
  description = "构建数据处理管道的指南",
  arguments = list(
    list(name = "data_type", description = "要处理的数据类型", required = TRUE)
  ),
  func = function(data_type) {
    sprintf("为%s数据创建数据处理管道……", data_type)
  }
)

代码生成模板

pr_mcp_prompt(
  pr,
  name = "create-endpoint",
  description = "创建新Plumber端点的模板",
  func = function() {
    paste(
      "生成具有以下内容的Plumber端点:",
      "1. 正确的roxygen文档",
      "2. 输入验证",
      "3. 错误处理",
      "4. 示例用法",
      sep = "\n"
    )
  }
)

领域特定协助

pr_mcp_prompt(
  pr,
  name = "statistical-test",
  description = "选择并实现适当的统计测试",
  arguments = list(
    list(name = "research_question", description = "研究问题", required = TRUE)
  ),
  func = function(research_question) {
    sprintf(
      paste(
        "研究问题:%s",
        "",
        "请帮助我:",
        "1. 选择适当的统计测试",
        "2. 检查假设",
        "3. 在R中实现",
        "4. 解释结果",
        sep = "\n"
      ),
      research_question
    )
  }
)

示例:带有提示的完整设置

library(plumber)
library(plumber2mcp)

pr("api.R") %>%
  pr_mcp(transport = "stdio") %>%

  # 添加分析提示
  pr_mcp_prompt(
    name = "analyze",
    description = "分析来自API的数据",
    func = function() {
      "引导我通过分析此API提供的数据。"
    }
  ) %>%

  # 添加故障排除提示
  pr_mcp_prompt(
    name = "troubleshoot",
    description = "帮助解决API问题",
    arguments = list(
      list(name = "issue", description = "问题描述", required = TRUE)
    ),
    func = function(issue) {
      sprintf("我在API中遇到这个问题:%s\n\n如何解决它?", issue)
    }
  )

资源支持

资源允许AI助手从您的R环境中读取内容,例如文档、数据描述或分析结果。

添加自定义资源

# 创建具有资源的Plumber API
pr(...) %>%
  pr_mcp(transport = "stdio") %>%
  
  # 添加提供数据集信息的资源
  pr_mcp_resource(
    uri = "/data/iris-summary",
    func = function() {
      paste(
        "数据集:iris",
        paste("维度:", paste(dim(iris), collapse = " x ")),
        "",
        capture.output(summary(iris)),
        sep = "\n"
      )
    },
    name = "I