Skip to contents
library(mcpr)
#> 
#> Attaching package: 'mcpr'
#> The following object is masked from 'package:methods':
#> 
#>     initialize
#> The following object is masked from 'package:base':
#> 
#>     write

Introduction

The Model Context Protocol (MCP) enables AI models to interact with your R code. This vignette showcases several practical examples of MCP tools that leverage R’s statistical and data manipulation capabilities.

Example 1: Statistical Analysis Tool

This example creates an MCP tool that performs basic statistical analysis on numeric data:

# Create a statistical analysis MCP server
stats_server <- new_server(
  name = "r-statistics",
  description = "Statistical analysis tools using R",
  version = "1.0.0"
)

# Create a summary statistics tool
summary_stats <- new_tool(
  name = "summary_statistics",
  description = "Calculate summary statistics for a numeric vector",
  input_schema = schema(
    properties = properties(
      data = property_array(
        "Data", 
        "Numeric vector to analyze",
        items = property_number("Value", "A numeric value"),
        required = TRUE
      ),
      include_quantiles = property_boolean(
        "Include Quantiles", 
        "Whether to include quantiles in the results",
        default = FALSE
      )
    )
  ),
  handler = function(input) {
    # Convert input to numeric vector
    data <- unlist(input$data)
    
    # Calculate basic statistics
    stats <- list(
      n = length(data),
      mean = mean(data, na.rm = TRUE),
      median = median(data, na.rm = TRUE),
      sd = sd(data, na.rm = TRUE),
      min = min(data, na.rm = TRUE),
      max = max(data, na.rm = TRUE)
    )
    
    # Add quantiles if requested
    if (input$include_quantiles) {
      stats$quantiles <- as.list(quantile(data, 
                                         probs = c(0.25, 0.5, 0.75), 
                                         na.rm = TRUE))
    }
    
    # Format the results as text
    result_text <- paste0(
      "Summary Statistics:\n",
      "- n: ", stats$n, "\n",
      "- Mean: ", round(stats$mean, 4), "\n",
      "- Median: ", round(stats$median, 4), "\n",
      "- Standard Deviation: ", round(stats$sd, 4), "\n",
      "- Min: ", round(stats$min, 4), "\n",
      "- Max: ", round(stats$max, 4)
    )
    
    if (input$include_quantiles) {
      result_text <- paste0(
        result_text, "\n",
        "- 25th Percentile: ", round(stats$quantiles[[1]], 4), "\n",
        "- 50th Percentile: ", round(stats$quantiles[[2]], 4), "\n",
        "- 75th Percentile: ", round(stats$quantiles[[3]], 4)
      )
    }
    
    response_text(result_text)
  }
)

# Add the tool to the server
stats_server <- add_capability(stats_server, summary_stats)

Example 2: Data Visualization Resource

This example creates an MCP resource that generates visualizations from data:

# Load required packages
library(ggplot2)

# Create visualization server
viz_server <- new_server(
  name = "r-visualizations",
  description = "Data visualization tools using R",
  version = "1.0.0"
)

# Create a scatter plot tool
scatter_plot <- new_tool(
  name = "scatter_plot",
  description = "Create a scatter plot from x and y coordinates",
  input_schema = schema(
    properties = properties(
      x = property_array(
        "X values", 
        "X-axis coordinates",
        items = property_number("X value", "A numeric value"),
        required = TRUE
      ),
      y = property_array(
        "Y values", 
        "Y-axis coordinates",
        items = property_number("Y value", "A numeric value"),
        required = TRUE
      ),
      title = property_string(
        "Plot title",
        "Title for the plot",
        default = "Scatter Plot"
      ),
      x_label = property_string(
        "X-axis label",
        "Label for the x-axis",
        default = "X"
      ),
      y_label = property_string(
        "Y-axis label",
        "Label for the y-axis",
        default = "Y"
      )
    )
  ),
  handler = function(input) {
    # Check that x and y have the same length
    x <- unlist(input$x)
    y <- unlist(input$y)
    
    if (length(x) != length(y)) {
      return(response_error("X and Y arrays must have the same length"))
    }
    
    # Create a data frame for ggplot
    plot_data <- data.frame(x = x, y = y)
    
    # Create a temporary file for the plot
    # Note: In a production environment, consider file cleanup strategies
    # that don't risk deleting files before clients can access them
    temp_file <- tempfile(fileext = ".png")
    
    # Create the plot using ggplot2
    p <- ggplot(plot_data, aes(x = x, y = y)) +
      geom_point(color = "steelblue", size = 3) +
      labs(
        title = input$title,
        x = input$x_label,
        y = input$y_label
      ) +
      theme_minimal()
    
    # Save the plot to the temporary file
    ggsave(temp_file, p, width = 8, height = 6, dpi = 100)
    
    # Return the image
    # The application should handle cleanup of temporary files
    # based on its specific file management strategy
    response_image(temp_file)
  }
)

# Add the tool to the server
viz_server <- add_capability(viz_server, scatter_plot)

Example 3: Natural Language Processing

This example demonstrates how to create an MCP tool for text analysis:

# Create an NLP server
nlp_server <- new_server(
  name = "r-text-analysis",
  description = "Text analysis tools using R",
  version = "1.0.0"
)

# Create a text summary tool
text_analyzer <- new_tool(
  name = "text_analyzer",
  description = "Analyze text to extract basic metrics",
  input_schema = schema(
    properties = properties(
      text = property_string(
        "Text", 
        "Text content to analyze",
        required = TRUE
      )
    )
  ),
  handler = function(input) {
    # Extract text from input
    text <- input$text
    
    # Calculate basic text metrics
    char_count <- nchar(text)
    word_count <- length(unlist(strsplit(text, "\\s+")))
    sentence_count <- length(unlist(strsplit(text, "[.!?]\\s*")))
    
    # Calculate word frequencies
    words <- tolower(unlist(strsplit(text, "\\W+")))
    words <- words[words != ""]
    word_freq <- sort(table(words), decreasing = TRUE)
    
    # Get top 5 words
    top_words <- head(word_freq, 5)
    top_words_text <- paste(names(top_words), "(", top_words, ")", 
                           collapse = ", ")
    
    # Format the results
    result <- paste0(
      "Text Analysis:\n",
      "- Character count: ", char_count, "\n",
      "- Word count: ", word_count, "\n",
      "- Sentence count: ", sentence_count, "\n",
      "- Unique words: ", length(word_freq), "\n",
      "- Top 5 words: ", top_words_text
    )
    
    response_text(result)
  }
)

# Add the tool to the server
nlp_server <- add_capability(nlp_server, text_analyzer)

Example 4: Time Series Forecasting

This example creates an MCP tool for simple time series forecasting:

# Create a forecasting server
forecast_server <- new_server(
  name = "r-forecasting",
  description = "Time series forecasting tools using R",
  version = "1.0.0"
)

# Create a simple forecasting tool
simple_forecast <- new_tool(
  name = "simple_forecast",
  description = "Forecast future values based on historical time series data",
  input_schema = schema(
    properties = properties(
      values = property_array(
        "Historical values", 
        "Historical time series values",
        items = property_number("Value", "A numeric value"),
        required = TRUE
      ),
      periods = property_number(
        "Forecast periods",
        "Number of periods to forecast",
        default = 5,
        minimum = 1,
        maximum = 50
      ),
      method = property_enum(
        "Forecast method",
        "Method to use for forecasting",
        enum = c("mean", "naive", "drift", "exponential"),
        default = "exponential"
      )
    )
  ),
  handler = function(input) {
    # Extract inputs
    values <- unlist(input$values)
    periods <- input$periods
    method <- input$method
    
    # Apply the selected forecasting method
    forecast_values <- switch(
      method,
      "mean" = {
        rep(mean(values), periods)
      },
      "naive" = {
        rep(tail(values, 1), periods)
      },
      "drift" = {
        last_value <- tail(values, 1)
        avg_change <- (last_value - values[1]) / (length(values) - 1)
        last_value + (1:periods) * avg_change
      },
      "exponential" = {
        # Simple exponential smoothing
        alpha <- 0.3  # smoothing parameter
        level <- values[1]
        for (i in 2:length(values)) {
          level <- alpha * values[i] + (1 - alpha) * level
        }
        rep(level, periods)
      }
    )
    
    # Format the results
    forecast_text <- paste(
      "Forecast for next", periods, "periods using", method, "method:",
      paste(round(forecast_values, 2), collapse = ", ")
    )
    
    # Create a plot of historical + forecast values using ggplot2
    # Note: In a production environment, consider file cleanup strategies
    # that don't risk deleting files before clients can access them
    temp_file <- tempfile(fileext = ".png")
    
    # Prepare data for ggplot
    # Create a data frame with historical and forecast data
    n_hist <- length(values)
    n_forecast <- length(forecast_values)
    
    plot_data <- data.frame(
      time = 1:(n_hist + n_forecast),
      value = c(values, forecast_values),
      type = c(rep("Historical", n_hist), rep("Forecast", n_forecast))
    )
    
    # Create the plot using ggplot2
    p <- ggplot(plot_data, aes(x = time, y = value, color = type, linetype = type)) +
      geom_line(size = 1) +
      scale_color_manual(values = c("Historical" = "black", "Forecast" = "blue")) +
      scale_linetype_manual(values = c("Historical" = "solid", "Forecast" = "dashed")) +
      labs(
        title = paste("Time Series Forecast (", method, ")"),
        x = "Time Period",
        y = "Value",
        color = "Data Type",
        linetype = "Data Type"
      ) +
      theme_minimal() +
      theme(legend.position = "top")
    
    # Save the plot to the temporary file
    ggsave(temp_file, p, width = 8, height = 6, dpi = 100)
    
    # Return both text and image
    # The application should handle cleanup of temporary files
    # based on its specific file management strategy
    response(list(
      response_text(forecast_text),
      response_image(temp_file)
    ))
  }
)

# Add the tool to the server
forecast_server <- add_capability(forecast_server, simple_forecast)

Example 5: Machine Learning Classification

This example demonstrates a simple machine learning classification tool:

# Create an ML server
ml_server <- new_server(
  name = "r-machine-learning",
  description = "Machine learning tools using R",
  version = "1.0.0"
)

# Create a simple classifier tool
simple_classifier <- new_tool(
  name = "simple_classifier",
  description = "Train a simple classifier and make predictions",
  input_schema = schema(
    properties = properties(
      features = property_array(
        "Training features", 
        "Features for training (list of feature vectors)",
        items = property_array(
          "Feature vector",
          "Vector of features for a single instance",
          items = property_number("Feature", "Feature value")
        ),
        required = TRUE
      ),
      labels = property_array(
        "Training labels",
        "Labels for training data (0 or 1)",
        items = property_number(
          "Label",
          "Class label (0 or 1)"
        ),
        required = TRUE
      ),
      test_features = property_array(
        "Test features",
        "Features for prediction",
        items = property_array(
          "Feature vector",
          "Vector of features for a single instance",
          items = property_number("Feature", "Feature value")
        ),
        required = TRUE
      ),
      method = property_enum(
        "Classification method",
        "Method to use for classification",
        enum = c("logistic", "lda"),
        default = "logistic"
      )
    )
  ),
  handler = function(input) {
    # Process input data
    features <- lapply(input$features, unlist)
    labels <- unlist(input$labels)
    test_features <- lapply(input$test_features, unlist)
    method <- input$method
    
    # Check that all feature vectors have the same length
    feature_lengths <- sapply(features, length)
    if (length(unique(feature_lengths)) != 1) {
      return(response_error("All feature vectors must have the same length"))
    }
    
    test_feature_lengths <- sapply(test_features, length)
    if (any(test_feature_lengths != feature_lengths[1])) {
      return(response_error("Test features must have the same dimensions as training features"))
    }
    
    # Create a training data frame
    train_df <- as.data.frame(do.call(rbind, features))
    colnames(train_df) <- paste0("X", 1:ncol(train_df))
    train_df$y <- as.factor(labels)
    
    # Create a test data frame
    test_df <- as.data.frame(do.call(rbind, test_features))
    colnames(test_df) <- paste0("X", 1:ncol(test_df))
    
    # Train a model based on the selected method
    if (method == "logistic") {
      formula <- as.formula(paste("y ~", paste(colnames(train_df)[colnames(train_df) != "y"], collapse = " + ")))
      model <- glm(formula, data = train_df, family = "binomial")
      
      # Make predictions
      pred_probs <- predict(model, test_df, type = "response")
      predictions <- ifelse(pred_probs > 0.5, 1, 0)
      
    } else if (method == "lda") {
      # Use simple implementation to avoid additional dependencies
      # Calculate means for each class
      means_class0 <- colMeans(train_df[train_df$y == 0, colnames(train_df) != "y", drop = FALSE])
      means_class1 <- colMeans(train_df[train_df$y == 1, colnames(train_df) != "y", drop = FALSE])
      
      # Calculate pooled covariance matrix
      n0 <- sum(train_df$y == 0)
      n1 <- sum(train_df$y == 1)
      
      # Make predictions using distance to means
      predictions <- numeric(nrow(test_df))
      for (i in 1:nrow(test_df)) {
        dist0 <- sum((as.numeric(test_df[i,]) - means_class0)^2)
        dist1 <- sum((as.numeric(test_df[i,]) - means_class1)^2)
        predictions[i] <- ifelse(dist0 < dist1, 0, 1)
      }
    }
    
    # Format results
    result_text <- paste(
      "Classification results using", method, "method:\n",
      "Predictions:", paste(predictions, collapse = ", ")
    )
    
    response_text(result_text)
  }
)

# Add the tool to the server
ml_server <- add_capability(ml_server, simple_classifier)

Running These Examples

To run any of these examples, save the code to an R script and add the appropriate serve_io() or serve_http() call at the end:

# For CLI-based tools (Claude Code, Cursor, etc.)
serve_io(your_server)

# For HTTP-based tools (OpenAI, LangChain, etc.)
serve_http(your_server, port = 8080)

Then follow the client integration instructions from the “Integrating mcpr with MCP Clients” vignette.

Conclusion

These examples demonstrate how R’s powerful statistical, visualization, and machine learning capabilities can be exposed to AI systems through the Model Context Protocol. By creating specialized MCP tools, you can enhance AI applications with R’s unique strengths.

For more advanced usage, consider:

  1. Combining multiple tools in a single server
  2. Adding error handling and input validation
  3. Creating more complex responses with multiple content types
  4. Leveraging R packages for specialized domains

See the package documentation for more details on these advanced features.