Skip to content

๐Ÿ”Œ Integrate moodleR with mcpR Packageย #31

@dietrichson

Description

@dietrichson

Description

Once the generic mcpR package is developed, integrate moodleR to expose its functionality via MCP for Claude Desktop and other AI assistants.

Integration Approach

Option 1: Decorator File (Recommended)

Create R/mcp_functions.R with decorated versions of key functions:

#* @mcp_tool
#* @description Analyze grades for a specific course
#* @param course_id [integer] The course to analyze
#* @param summary_type [enum: basic,detailed,visual] Type of summary
#* @returns Analysis results with optional visualization
mcp_analyze_course_grades <- function(course_id, summary_type = "basic") {
  con <- mdl_get_connection()
  grades <- mdl_grades(con) %>%
    filter(courseid == course_id) %>%
    collect()
  
  result <- list(
    course_id = course_id,
    student_count = n_distinct(grades$userid),
    grade_count = nrow(grades)
  )
  
  if (summary_type %in% c("detailed", "visual")) {
    result$summary <- summary(grades)
  }
  
  if (summary_type == "visual") {
    result$plot <- plot(grades)
  }
  
  result
}

#* @mcp_resource
#* @description Access course listing with details
#* @param limit [integer = 100] Maximum courses to return
#* @param category_id [integer] Filter by category (optional)
#* @returns Data frame of courses with category information
mcp_get_courses <- function(limit = 100, category_id = NULL) {
  con <- mdl_get_connection()
  courses <- mdl_courses(con)
  
  if (\!is.null(category_id)) {
    courses <- courses %>% filter(categoryid == category_id)
  }
  
  courses %>%
    limit(limit) %>%
    collect()
}

#* @mcp_tool
#* @description Generate forum activity word cloud
#* @param forum_id [integer] Forum to analyze (optional)
#* @param days_back [integer = 30] Number of days to include
#* @param min_words [integer = 20] Minimum words for cloud
#* @returns Base64 encoded word cloud image
mcp_forum_wordcloud <- function(forum_id = NULL, days_back = 30, min_words = 20) {
  con <- mdl_get_connection()
  posts <- mdl_forum_posts(con)
  
  if (\!is.null(forum_id)) {
    posts <- posts %>% filter(forum == forum_id)
  }
  
  cutoff_date <- Sys.Date() - days_back
  posts <- posts %>%
    filter(created >= cutoff_date) %>%
    collect()
  
  if (nrow(posts) < min_words) {
    return(list(error = "Insufficient posts for word cloud"))
  }
  
  plot(posts)  # Returns base64 image via mcpR type conversion
}

Option 2: Package-Level Registration

Add to R/zzz.R:

.onLoad <- function(libname, pkgname) {
  if (requireNamespace("mcpR", quietly = TRUE)) {
    # Auto-register specific functions
    mcpR::mcp_register_package("moodleR",
      tools = c("mdl_create_cache", "summary.mdl_*", "plot.mdl_*"),
      resources = c("mdl_courses", "mdl_users", "mdl_grades", "mdl_forum_posts", "mdl_log"),
      exclude = c("*_query", "mdl_get_connection")  # Internal functions
    )
  }
}

Option 3: Configuration File

Create inst/mcp/config.yml:

tools:
  - name: analyze_grades
    function: mdl_grades
    wrapper: |
      function(course_id = NULL) {
        grades <- mdl_grades()
        if (\!is.null(course_id)) {
          grades <- grades %>% filter(courseid == course_id)
        }
        summary(collect(grades))
      }
    description: "Analyze grade distribution"
    
resources:
  - name: courses
    function: mdl_courses
    pagination: true
    default_limit: 100
    
  - name: users  
    function: mdl_users
    filters:
      - field: "deleted"
        default: 0

Claude Desktop Configuration

Basic Setup

{
  "mcpServers": {
    "moodleR": {
      "command": "Rscript",
      "args": ["-e", "moodleR::mcp_serve()"],
      "env": {
        "R_CONFIG_ACTIVE": "default"
      }
    }
  }
}

With Custom Config

{
  "mcpServers": {
    "moodleR": {
      "command": "Rscript",
      "args": [
        "-e",
        "moodleR::mcp_serve(config = '~/.moodleR/config.yml')"
      ]
    }
  }
}

Helper Functions for moodleR

1. Connection Management

# Ensure connection is available for MCP calls
mcp_ensure_connection <- function() {
  if (\!exists(".moodle_connection", envir = .GlobalEnv)) {
    .GlobalEnv$.moodle_connection <- mdl_get_connection()
  }
  .GlobalEnv$.moodle_connection
}

2. Error Handling

# Wrap functions with moodleR-specific error handling
mcp_safe_query <- function(expr) {
  tryCatch({
    expr
  }, error = function(e) {
    if (grepl("connection", e$message)) {
      list(error = "Database connection failed", details = e$message)
    } else if (grepl("permission", e$message)) {
      list(error = "Insufficient permissions", details = e$message)
    } else {
      list(error = "Query failed", details = e$message)
    }
  })
}

3. Result Formatting

# Format results for AI consumption
mcp_format_result <- function(data, include_summary = TRUE) {
  result <- list(
    row_count = nrow(data),
    column_names = names(data)
  )
  
  if (include_summary && nrow(data) > 0) {
    result$preview <- head(data, 10)
    result$summary <- summary(data)
  }
  
  result
}

Testing Integration

Test File: tests/testthat/test-mcp.R

test_that("MCP functions are properly exposed", {
  skip_if_not_installed("mcpR")
  
  # Check function registration
  registry <- mcpR::mcp_get_registry()
  expect_true("mcp_analyze_course_grades" %in% names(registry$tools))
  expect_true("mcp_get_courses" %in% names(registry$resources))
  
  # Test function execution
  result <- mcp_analyze_course_grades(course_id = 1)
  expect_type(result, "list")
  expect_true(all(c("course_id", "student_count") %in% names(result)))
})

Documentation Updates

1. Add MCP Section to README

## Using with Claude Desktop

moodleR can be used with Claude Desktop via the Model Context Protocol:

1. Install moodleR and mcpR packages
2. Configure your Moodle database connection
3. Add to Claude Desktop config:
   ```json
   {
     "mcpServers": {
       "moodleR": {
         "command": "Rscript",
         "args": ["-e", "moodleR::mcp_serve()"]
       }
     }
   }
  1. Ask Claude to analyze your Moodle data!

### 2. Create Vignette
`vignettes/mcp-integration.Rmd`:
- Setup instructions
- Example prompts
- Best practices
- Troubleshooting

## Implementation Steps
1. [ ] Wait for mcpR package to be functional
2. [ ] Create mcp_functions.R with decorated functions
3. [ ] Add mcp_serve() wrapper function
4. [ ] Test with Claude Desktop
5. [ ] Document integration
6. [ ] Create example prompts
7. [ ] Add to package tests

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions