Plumber API MCP支持工具

为Plumber API添加模型上下文协议(MCP)支持,使AI助手能够直接调用R函数端点并获取文档资源。

  • by armish
  • R
  • 本地部署
  • 8 星标
  • 更新于
在 GitHub 查看 armish/plumber2mcp

使用说明

水管2mcp

通过单个函数调用将模型上下文协议(MCP)支持添加到Plumber API中。

什么是MCP?

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

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

安装

# Install from GitHub
remotes::install_github("armish/plumber2mcp")

依赖项

此软件包要求:

  • R(>=4.0.0)
  • 水管工(>=1.0.0)
  • Jsonlite
  • 高温工程试验堆

快速开始

HTTP传输(默认)

library(plumber)
library(plumber2mcp)

# Create and run a Plumber API with MCP support via HTTP
pr("plumber.R") %>%
  pr_mcp(transport = "http") %>%
  pr_run(port = 8000)

您的API现在具有:

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

标准传输(本地MCP)

library(plumber)
library(plumber2mcp)

# Create and run a Plumber API with native stdio transport
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  # Should show your server as "Ready"
mcp-cli tools    # List available tools
mcp-cli cmd --tool GET__echo --tool-args '{"msg": "Hello!"}'  # Call a tool

运作原理

这 pr_mcp() 自动功能:

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

增强的文档和模式生成

plumber2mcp通过分析您的roxygen注释和函数签名,自动为您的API端点生成丰富的JSON模式和详细的文档。此功能受FastAPI MCP的启发,使您的R API更容易被AI助手使用。

丰富的工具描述

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

#* Calculate statistical operations on numeric data
#* 
#* This endpoint performs various statistical calculations on a vector of numbers.
#* It supports multiple operations and handles missing values.
#* 
#* @param numbers Numeric vector of values to calculate statistics for
#* @param operation Statistical operation to perform: "mean", "median", "sum", "sd" (default: "mean")
#* @param na_rm:bool Logical value indicating whether to remove NA values (default: TRUE)
#* @param digits:int Number of decimal places to round the result (default: 2)
#* @return List containing the calculated result and metadata
#* @post /calculate
function(numbers, operation = "mean", na_rm = TRUE, digits = 2) {
  # Convert input to numeric
  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("Unknown operation: ", operation)
  )
  
  list(
    result = round(result, digits),
    operation = operation,
    count = length(numbers[!is.na(numbers)]),
    input_length = length(numbers)
  )
}

这将创建一个MCP工具,其中包含:

增强描述:

Calculate statistical operations on numeric data

This endpoint performs various statistical calculations on a vector of numbers.
It supports multiple operations and handles missing values.

Parameters:
- numbers (string): Numeric vector of values to calculate statistics for
- operation (string) [default: mean]: Statistical operation to perform: "mean", "median", "sum", "sd"
- na_rm (boolean) [default: TRUE]: Logical value indicating whether to remove NA values
- digits (integer) [default: 2]: Number of decimal places to round the result

HTTP Method: POST
Path: /calculate

输入架构:

{
  "type": "object",
  "properties": {
    "numbers": {
      "type": "string",
      "description": "Numeric vector of values to calculate statistics for"
    },
    "operation": {
      "type": "string",
      "description": "Statistical operation to perform: \"mean\", \"median\", \"sum\", \"sd\"",
      "default": "mean"
    },
    "na_rm": {
      "type": "boolean",
      "description": "Logical value indicating whether to remove NA values",
      "default": true
    },
    "digits": {
      "type": "integer",
      "description": "Number of decimal places to round the result",
      "default": 2
    }
  },
  "required": ["numbers"]
}

输出架构:

{
  "type": "object",
  "description": "Structured response object",
  "properties": {
    "result": {
      "type": "number",
      "description": "Response field: result"
    },
    "operation": {
      "type": "string",
      "description": "Response field: operation"
    },
    "count": {
      "type": "number",
      "description": "Response field: count"
    },
    "input_length": {
      "type": "number",
      "description": "Response field: input_length"
    }
  }
}

类型检测和模式特征

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

    • logical → boolean
    • integer → integer
    • numeric/double → number
    • character → string
    • 并支持水管工类型的注释,如 :bool, :int, :array
  2. 默认值检测:从函数签名中提取默认值并将其包含在架构中

  3. 必需参数与可选参数:根据参数是否具有默认值自动确定需要哪些参数

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

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

为什么这对人工智能助理很重要

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

  1. 更好地理解你的API:丰富的描述有助于AI助手理解每个端点的作用
  2. 提供更好的建议:详细的参数信息有助于AI助手建议适当的值
  3. 生成更好的代码:输出模式帮助人工智能助手了解API的预期
  4. 减少错误:类型信息和所需/可选参数检测减少了API调用错误
  5. 自我文档:您的API成为人类和人工智能的自主文档

AI助手交互示例

如果没有增强的模式:

User: "Can you help me calculate the mean of some numbers using this API?"
AI: "I can see there's a calculate endpoint, but I'm not sure what parameters it needs..."

使用增强的模式:

User: "Can you help me calculate the mean of some numbers using this API?"
AI: "I can see you have a calculate endpoint that performs statistical operations! 
     It needs a 'numbers' parameter (required) and optionally 'operation' (defaults to 'mean'), 
     'na_rm' (defaults to TRUE), and 'digits' (defaults to 2). 
     
     Let me call it for you:
     POST /calculate with {"numbers": "1,2,3,4,5"}
     
     This will return an object with the result, operation used, count of valid numbers, 
     and input length."

文档的最佳实践

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

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

MCP端点

曾经 pr_mcp() 应用时,您的API将公开:

  • GET /mcp -服务器信息和功能
  • POST /mcp/messages -MCP协议的JSON-RPC消息处理程序

示例

给定一个简单的管道工API:

#* Echo back the input
#* 
#* Simple endpoint that echoes back whatever message you send it
#* 
#* @param msg:string The message to echo back (default: "Hello World")
#* @get /echo
function(msg = "Hello World") {
  list(message = paste("Echo:", msg))
}

#* Add two numbers together
#* 
#* Performs arithmetic addition on two numeric values
#* 
#* @param a:number First number to add
#* @param b:number Second number to add  
#* @param precision:int Number of decimal places to round result (default: 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助手提供了有关参数类型、默认值、描述和预期响应格式的详细信息。

提示支持

提示是可重复使用的模板,人工智能助手可以发现并用于与您的API交互。它们为常见任务和工作流程提供结构化的指导。

什么是提示?

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

  • 帮助人工智能助手了解如何有效使用API
  • 为复杂的工作流程提供结构化的指导
  • 可以接受参数来自定义提示内容
  • 支持多回合对话

添加提示

# Simple prompt with no arguments
pr %>%
  pr_mcp(transport = "stdio") %>%
  pr_mcp_prompt(
    name = "r-help",
    description = "Get help with R programming",
    func = function() {
      paste(
        "I need help with R programming.",
        "Please provide guidance on best practices and common patterns.",
        sep = "\n"
      )
    }
  )

# Prompt with arguments
pr %>%
  pr_mcp(transport = "stdio") %>%
  pr_mcp_prompt(
    name = "analyze-dataset",
    description = "Generate a comprehensive analysis plan for an R dataset",
    arguments = list(
      list(
        name = "dataset",
        description = "Name of the R dataset to analyze",
        required = TRUE
      ),
      list(
        name = "focus",
        description = "Specific aspect to focus on",
        required = FALSE
      )
    ),
    func = function(dataset, focus = "general") {
      sprintf(
        paste(
          "Please analyze the %s dataset in R.",
          "Focus: %s",
          "",
          "Provide:",
          "1. Summary statistics",
          "2. Data quality assessment",
          "3. Key insights",
          sep = "\n"
        ),
        dataset, focus
      )
    }
  )

# Multi-turn conversation prompt
pr %>%
  pr_mcp(transport = "stdio") %>%
  pr_mcp_prompt(
    name = "code-review",
    description = "Review R code for quality and best practices",
    arguments = list(
      list(name = "code", description = "The R code to review", required = TRUE)
    ),
    func = function(code) {
      list(
        list(
          role = "user",
          content = list(
            type = "text",
            text = paste("Please review this R code:", code, sep = "\n\n")
          )
        ),
        list(
          role = "assistant",
          content = list(
            type = "text",
            text = "I'll review your code for correctness, performance, and style."
          )
        ),
        list(
          role = "user",
          content = list(
            type = "text",
            text = "Please provide specific suggestions for improvement."
          )
        )
      )
    }
  )

提示消息格式

提示函数可以以多种格式返回消息:

  1. 简单字符串 -自动转换为用户消息:
func = function() "Hello World"
  1. 结构化消息 -完全控制角色和内容:
func = function() {
  list(
    role = "user",
    content = list(type = "text", text = "Your message")
  )
}
  1. 多条消息 -对于多回合对话:
func = function() {
  list(
    list(role = "user", content = list(type = "text", text = "First message")),
    list(role = "assistant", content = list(type = "text", text = "Second message"))
  )
}

提示用例

工作流程指导

pr_mcp_prompt(
  pr,
  name = "data-pipeline",
  description = "Guide for building data processing pipelines",
  arguments = list(
    list(name = "data_type", description = "Type of data to process", required = TRUE)
  ),
  func = function(data_type) {
    sprintf("Create a data processing pipeline for %s data...", data_type)
  }
)

代码生成模板

pr_mcp_prompt(
  pr,
  name = "create-endpoint",
  description = "Template for creating new Plumber endpoints",
  func = function() {
    paste(
      "Generate a Plumber endpoint with:",
      "1. Proper roxygen documentation",
      "2. Input validation",
      "3. Error handling",
      "4. Example usage",
      sep = "\n"
    )
  }
)

领域特定协助

pr_mcp_prompt(
  pr,
  name = "statistical-test",
  description = "Choose and implement appropriate statistical tests",
  arguments = list(
    list(name = "research_question", description = "Research question", required = TRUE)
  ),
  func = function(research_question) {
    sprintf(
      paste(
        "Research Question: %s",
        "",
        "Help me:",
        "1. Choose the appropriate statistical test",
        "2. Check assumptions",
        "3. Implement in R",
        "4. Interpret results",
        sep = "\n"
      ),
      research_question
    )
  }
)

示例:使用提示完成设置

library(plumber)
library(plumber2mcp)

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

  # Add analysis prompt
  pr_mcp_prompt(
    name = "analyze",
    description = "Analyze data from the API",
    func = function() {
      "Guide me through analyzing the data available from this API."
    }
  ) %>%

  # Add troubleshooting prompt
  pr_mcp_prompt(
    name = "troubleshoot",
    description = "Help troubleshoot API issues",
    arguments = list(
      list(name = "issue", description = "Description of the issue", required = TRUE)
    ),
    func = function(issue) {
      sprintf("I'm experiencing this issue with the API: %s\n\nHow can I resolve it?", issue)
    }
  )

资源支持

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

添加自定义资源

# Create a Plumber API with resources
pr(...) %>%
  pr_mcp(transport = "stdio") %>%
  
  # Add a resource that provides dataset information
  pr_mcp_resource(
    uri = "/data/iris-summary",
    func = function() {
      paste(
        "Dataset: iris",
        paste("Dimensions:", paste(dim(iris), collapse = " x ")),
        "",
        capture.output(summary(iris)),
        sep = "\n"
      )
    },
    name = "Iris Dataset Summary",
    description = "Statistical summary and structure of the iris dataset"
  ) %>%
  
  # Add a resource that shows current memory usage
  pr_mcp_resource(
    uri = "/system/memory",
    func = function() {
      mem <- gc()
      paste(
        "R Memory Usage:",
        paste("Used (Mb):", round(sum(mem[,2]), 2)),
        paste("Gc Trigger (Mb):", round(sum(mem[,6]), 2)),
        "",
        "Object sizes in workspace:",
        capture.output(print(object.size(ls.str()), units = "Mb")),
        sep = "\n"
      )
    },
    name = "R Memory Usage",
    description = "Current R session memory statistics"
  ) %>%
  
  # Add a resource with model diagnostics
  pr_mcp_resource(
    uri = "/models/latest-lm",
    func = function() {
      # Example: fit a model and return diagnostics
      model <- lm(mpg ~ wt + hp, data = mtcars)
      paste(
        "Linear Model: mpg ~ wt + hp",
        "",
        capture.output(summary(model)),
        "",
        "Diagnostic plots saved to: /tmp/lm_diagnostics.png",
        sep = "\n"
      )
    },
    name = "Latest Linear Model",
    description = "Diagnostics for the most recent linear model",
    mimeType = "text/plain"
  )

内置R帮助资源

pr(...) %>% 
  pr_mcp(transport = "stdio") %>%
  pr_mcp_help_resources()  # Adds help for common R functions

这会自动为以下内容添加资源:

  • R帮助主题(/help/mean, /help/lm等等)
  • R会话信息(/r/session-info)
  • 已安装的软件包(/r/packages)

带参数的动态资源

虽然当前的实现不支持URI模板,但您可以创建根据运行时条件进行调整的资源:

# Create resources based on available data files
data_files <- list.files("data/", pattern = "\\.csv$")

my_pr <- pr(...)
for (file in data_files) {
  my_pr <- my_pr %>%
    pr_mcp_resource(
      uri = paste0("/data/", tools::file_path_sans_ext(file)),
      func = local({
        current_file <- file  # Capture the file name
        function() {
          data <- read.csv(file.path("data", current_file))
          paste(
            paste("File:", current_file),
            paste("Rows:", nrow(data)),
            paste("Columns:", paste(names(data), collapse = ", ")),
            "",
            "First 5 rows:",
            capture.output(print(head(data, 5))),
            sep = "\n"
          )
        }
      }),
      name = paste("Dataset:", tools::file_path_sans_ext(file)),
      description = paste("Contents of", file)
    )
}

资源使用示例

一旦您的MCP服务器运行了资源,AI助手就可以:

  1. 浏览可用资源 -AI可以列出所有资源,以查看可用的信息
  2. 阅读特定资源 -AI可以读取资源内容以了解您的数据和环境
  3. 根据上下文使用资源 -AI在回答问题或编写代码时可以参考资源信息

与AI助手的交互示例:

You: "What datasets are available in this R session?"
AI: *Lists resources and reads relevant ones*
    "I can see you have the iris dataset available. Based on the 
    /data/iris-summary resource, it has 150 observations of 5 variables..."

You: "Can you help me analyze the relationship between Sepal.Length and Petal.Length?"
AI: *Reads the iris summary resource for context*
    "Based on the iris dataset summary, I can see that Sepal.Length ranges 
    from 4.3 to 7.9 and Petal.Length from 1.0 to 6.9. Let me create a 
    linear model to analyze their relationship..."

高级用法

自定义MCP路径

my_pr %>% pr_mcp(transport = "http", path = "/my-mcp-server")

筛选端点

# Include only specific endpoints
my_pr %>% pr_mcp(transport = "http", include_endpoints = c("GET__echo", "POST__add"))

# Exclude specific endpoints
my_pr %>% pr_mcp(transport = "stdio", exclude_endpoints = c("POST__internal"))

自定义服务器信息

my_pr %>% pr_mcp(
  transport = "http",
  server_name = "my-api-mcp",
  server_version = "1.0.0"
)

完整示例

以下是创建启用MCP的API的分步示例:

  1. 创建水管工API文件(my_api.R):
#* @apiTitle My MCP-Enabled API
#* @apiDescription API with MCP support for AI assistants

#* Get current time
#* @get /time
function() {
  list(time = Sys.time())
}

#* Calculate factorial
#* @param n Integer to calculate factorial
#* @post /factorial
function(n) {
  n <- as.integer(n)
  if (n < 0) stop("n must be non-negative")
  list(result = factorial(n))
}
  1. 创建并运行服务器:
library(plumber)
library(plumber2mcp)

pr("my_api.R") %>%
  pr_mcp(transport = "http") %>%
  pr_run(port = 8000)
  1. 您的API现在可以访问:
    • HTTP API: http://localhost:8000/
    • MCP端点: http://localhost:8000/mcp
    • API文件: http://localhost:8000/__docs__/

测试

运行示例服务器:

source(system.file("examples/run_mcp_server.R", package = "plumber2mcp"))

使用MCP客户端进行测试:

source(system.file("examples/test_mcp_client.R", package = "plumber2mcp"))

使用AI助手

MCP服务器运行后,您可以配置AI助手来使用它:

克劳德桌面

添加到您的Claude配置文件中:

{
  "mcpServers": {
    "my-r-api": {
      "url": "http://localhost:8000/mcp"
    }
  }
}

其他人工智能助理

查看AI助手的文档,了解MCP配置说明。

MCP协议详细信息

此包实现了 模型上下文协议 规范。MCP端点处理:

  • 工具发现:将所有可用的Plumber端点作为MCP工具列出
  • 工具执行:将MCP工具调用转换为Plumber端点请求
  • 错误处理:正确格式化MCP响应格式中的错误

MCP检验员测试

MCP检查员 是用于测试和调试MCP服务器的工具。

在HTTP传输中使用MCP检查器

  1. 使用HTTP传输启动水管工API:
library(plumber)
library(plumber2mcp)

pr("api.R") %>%
  pr_mcp(transport = "http") %>%
  pr_run(port = 8000)
  1. 在新终端中,导航到示例目录:
cd /path/to/plumber2mcp/inst/examples
mcp-inspector --config http_wrapper_config.json --server plumber2mcp

这 stdio-wrapper.py 该脚本将MCP Inspector的stdio接口连接到您的HTTP服务器。

使用带标准传输的MCP检查器

直接使用stdio配置:

cd /path/to/plumber2mcp/inst/examples
mcp-inspector --config stdio_config.json --server plumber2mcp

故障排除

常见问题

  1. 端口已在使用中:更改中的端口号 pr_run(port = 8001)
  2. 未找到MCP端点:确保你打过电话 pr_mcp() 之前 pr_run()
  3. 工具未显示:检查Plumber端点是否具有正确的注释
  4. MCP检查器连接错误:
    • 对于HTTP:在启动MCP检查器之前,确保服务器在端口8000上运行
    • 检查一下 cwd 配置文件中的路径指向正确的目录

调试模式

启用详细日志记录:

# For stdio transport
my_pr %>% pr_mcp(transport = "stdio", debug = TRUE)

# For HTTP transport (debug not available)
my_pr %>% pr_mcp(transport = "http") %>% pr_run(port = 8000)

贡献

欢迎投稿!请在GitHub上提交问题和拉取请求。

许可证

麻省理工学院