diff --git a/DESCRIPTION b/DESCRIPTION index a92d407..7b21976 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -19,7 +19,7 @@ Description: Create, run, and connect to Model Context Protocol (MCP) servers, a License: GPL (>= 2) Depends: R (>= 4.1.0) Encoding: UTF-8 -Roxygen: list(markdown = TRUE) +Roxygen: list(markdown = TRUE, roclets = c("collate", "rd", "namespace", "mcpr::mcp_roclet")) RoxygenNote: 7.3.2 Imports: yyjsonr @@ -29,6 +29,7 @@ Suggests: knitr, rmarkdown, httr2, - ellmer + ellmer, + roxygen2 URL: https://mcpr.opifex.org/, https://github.com/devOpifex/mcpr VignetteBuilder: knitr diff --git a/NAMESPACE b/NAMESPACE index ba3ebf7..e1e030a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -17,6 +17,12 @@ S3method(resources_list,client) S3method(resources_list,server) S3method(resources_read,client) S3method(resources_read,server) +S3method(roclet_output,roclet_mcp) +S3method(roclet_process,roclet_mcp) +S3method(roxy_tag_parse,roxy_tag_mcp) +S3method(roxy_tag_parse,roxy_tag_type) +S3method(roxy_tag_rd,roxy_tag_mcp) +S3method(roxy_tag_rd,roxy_tag_type) S3method(send,jsonrpc_error) S3method(send,jsonrpc_response) S3method(tools_call,client) @@ -29,6 +35,7 @@ export(add_capability) export(ellmer_to_mcpr_tool) export(get_name) export(initialize) +export(mcp_roclet) export(new_client_io) export(new_mcp) export(new_prompt) @@ -63,3 +70,11 @@ export(serve_io) export(tools_call) export(tools_list) export(write) +importFrom(roxygen2,block_get_tag_value) +importFrom(roxygen2,block_get_tags) +importFrom(roxygen2,roclet) +importFrom(roxygen2,roclet_output) +importFrom(roxygen2,roclet_process) +importFrom(roxygen2,roxy_tag_parse) +importFrom(roxygen2,roxy_tag_rd) +importFrom(roxygen2,roxy_tag_warning) diff --git a/NEWS.md b/NEWS.md index c107000..af1bfb7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,12 @@ # mcpr 0.0.2 - Added support for `ellmer::tool` +- Added MCP roclet for automatic server generation: + - New `@mcp` tag for marking functions as MCP tools + - New `@type` tag for specifying parameter types (string, number, boolean, array, object, enum) + - `mcp_roclet()` function integrates with roxygen2 to generate complete MCP servers + - Automatically creates `inst/mcp_server.R` with tool definitions and handlers + - Supports all standard JSON Schema types and enum values # mcpr 0.0.1 diff --git a/R/input-schema.R b/R/input-schema.R index 9754161..a308811 100644 --- a/R/input-schema.R +++ b/R/input-schema.R @@ -1,10 +1,14 @@ #' Create a new input schema #' -#' @param properties List of property definitions -#' @param type Type of the schema -#' @param additional_properties Logical indicating if additional properties are allowed +#' @param properties List of property definitions created with properties() +#' @type properties object +#' @param type Type of the schema (default: "object") +#' @type type string +#' @param additional_properties Whether additional properties are allowed +#' @type additional_properties boolean #' #' @return A list representing a JSON Schema object +#' @mcp create_json_schema Create a JSON Schema object for MCP tool inputs with property validation #' @export #' #' @examples @@ -93,15 +97,24 @@ new_property <- function(type, title, description, required = FALSE, ...) { #' Create a string property definition #' #' @param title Short title for the property +#' @type title string #' @param description Longer description of the property +#' @type description string #' @param required Whether the property is required +#' @type required boolean #' @param enum Optional character vector of allowed values +#' @type enum array #' @param pattern Optional regex pattern the string must match +#' @type pattern string #' @param min_length Optional minimum length +#' @type min_length number #' @param max_length Optional maximum length -#' @param format Optional format (e.g., "date-time", "email", "uri") +#' @type max_length number +#' @param format Optional format constraint +#' @type format enum:date-time,email,uri,hostname,ipv4,ipv6 #' #' @return A string property object +#' @mcp create_string_property Create a string property with validation constraints for MCP schemas #' @export #' #' @examples @@ -147,16 +160,26 @@ property_string <- function( #' Create a number property definition #' #' @param title Short title for the property +#' @type title string #' @param description Longer description of the property +#' @type description string #' @param required Whether the property is required +#' @type required boolean #' @param minimum Optional minimum value +#' @type minimum number #' @param maximum Optional maximum value -#' @param exclusive_minimum Optional logical for exclusive minimum -#' @param exclusive_maximum Optional logical for exclusive maximum +#' @type maximum number +#' @param exclusive_minimum Whether minimum is exclusive +#' @type exclusive_minimum boolean +#' @param exclusive_maximum Whether maximum is exclusive +#' @type exclusive_maximum boolean #' @param multiple_of Optional value the number must be a multiple of -#' @param integer Logical indicating if the number should be an integer +#' @type multiple_of number +#' @param integer Whether the number should be an integer +#' @type integer boolean #' #' @return A number property object +#' @mcp create_number_property Create a number property with range and type constraints for MCP schemas #' @export #' #' @examples @@ -205,10 +228,14 @@ property_number <- function( #' Create a boolean property definition #' #' @param title Short title for the property +#' @type title string #' @param description Longer description of the property +#' @type description string #' @param required Whether the property is required +#' @type required boolean #' #' @return A boolean property object +#' @mcp create_boolean_property Create a boolean property for MCP schemas #' @export #' #' @examples @@ -316,11 +343,16 @@ property_object <- function( #' Create an enum property with predefined values #' #' @param title Short title for the property +#' @type title string #' @param description Longer description of the property +#' @type description string #' @param values Character vector of allowed values +#' @type values array #' @param required Whether the property is required +#' @type required boolean #' #' @return An enum property object +#' @mcp create_enum_property Create an enum property with predefined values for MCP schemas #' @export #' #' @examples diff --git a/R/io.R b/R/io.R index e34d329..ef30461 100644 --- a/R/io.R +++ b/R/io.R @@ -1,8 +1,10 @@ #' Serve an MCP server using stdin/stdout #' -#' @param mcp An MCP server object +#' @param mcp An MCP server object created with new_server() +#' @type mcp object #' #' @return Nothing, runs indefinitely in normal mode, or the response in test mode +#' @mcp start_mcp_server_io Start an MCP server using stdin/stdout transport #' @export serve_io <- function( mcp diff --git a/R/mcp.R b/R/mcp.R index 2352847..0ee5ea4 100644 --- a/R/mcp.R +++ b/R/mcp.R @@ -1,14 +1,21 @@ #' Create a new MCP object #' -#' @param name Name of the MCP -#' @param description Description of the MCP -#' @param version Version of the MCP -#' @param tools List of tools -#' @param resources List of resources -#' @param prompts List of prompts +#' @param name Name of the MCP server +#' @type name string +#' @param description Description of the MCP server +#' @type description string +#' @param version Version of the MCP server +#' @type version string +#' @param tools List of tools (optional) +#' @type tools array +#' @param resources List of resources (optional) +#' @type resources array +#' @param prompts List of prompts (optional) +#' @type prompts array #' @param ... Forwarded to [new_server()] #' #' @return A new MCP object +#' @mcp create_mcp_server Create a new MCP server with specified name, description, and version #' @export #' #' @examples @@ -94,11 +101,16 @@ new_mcp <- function(...) { #' Create a new tool #' #' @param name Name of the tool -#' @param description Description of the tool -#' @param input_schema Input schema for the tool -#' @param handler Function to handle the tool +#' @type name string +#' @param description Description of the tool +#' @type description string +#' @param input_schema Input schema for the tool (must be a schema object) +#' @type input_schema object +#' @param handler Function to handle the tool execution +#' @type handler object #' #' @return A new tool capability +#' @mcp create_mcp_tool Create a new MCP tool with input schema and handler function #' @export #' #' @examples @@ -152,12 +164,18 @@ new_tool <- function( #' Create a new resource #' #' @param name Name of the resource +#' @type name string #' @param description Description of the resource +#' @type description string #' @param uri URI of the resource -#' @param mime_type Mime type of the resource -#' @param handler Function to handle the resource +#' @type uri string +#' @param mime_type MIME type of the resource (optional) +#' @type mime_type string +#' @param handler Function to handle the resource request +#' @type handler object #' #' @return A new resource capability +#' @mcp create_mcp_resource Create a new MCP resource with URI and MIME type #' @export #' #' @examples @@ -199,11 +217,16 @@ new_resource <- function(name, description, uri, mime_type = NULL, handler) { #' Create a new prompt #' #' @param name Name of the prompt +#' @type name string #' @param description Description of the prompt +#' @type description string #' @param arguments List of arguments for the prompt -#' @param handler Function to handle the prompt +#' @type arguments array +#' @param handler Function to handle the prompt execution +#' @type handler object #' #' @return A new prompt capability +#' @mcp create_mcp_prompt Create a new MCP prompt with arguments and handler function #' @export #' #' @examples @@ -269,10 +292,13 @@ new_capability <- function( #' Add a capability to an MCP object #' -#' @param capability A capability object -#' @param mcp An MCP object +#' @param mcp An MCP server object +#' @type mcp object +#' @param capability A tool, resource, or prompt capability object +#' @type capability object #' #' @return The MCP object with the capability added +#' @mcp add_mcp_capability Add a tool, resource, or prompt capability to an existing MCP server #' @export add_capability <- function(mcp, capability) { UseMethod("add_capability", capability) diff --git a/R/response.R b/R/response.R index 7e729ae..6ed9b9b 100644 --- a/R/response.R +++ b/R/response.R @@ -1,6 +1,7 @@ #' Create a response object #' -#' @param text Text content +#' @param text Text content for the response +#' @type text string #' @param image Image content #' @param audio Audio content #' @param video Video content @@ -25,6 +26,7 @@ #' #' @name response #' @return A response object +#' @mcp create_text_response Create a text response for MCP tool handlers #' @export response_text <- function(text) { if (missing(text)) { diff --git a/R/roclet.R b/R/roclet.R new file mode 100644 index 0000000..52a881e --- /dev/null +++ b/R/roclet.R @@ -0,0 +1,509 @@ +#' MCP Roclet for Generating MCP Servers +#' +#' This roclet automatically generates MCP (Model Context Protocol) servers +#' from R functions annotated with @mcp tags. +#' +#' @importFrom roxygen2 roclet roclet_process roclet_output roxy_tag_parse roxy_tag_rd block_get_tags roxy_tag_warning block_get_tag_value +#' @export +#' @examples +#' \dontrun{ +#' # Use the roclet in roxygenise +#' roxygen2::roxygenise(roclets = c("rd", "mcpr::mcp_roclet")) +#' } +mcp_roclet <- function() { + roxygen2::roclet("mcp") +} + +# Custom tags are automatically recognized by having roxy_tag_parse methods + +#' Parse @mcp tag +#' +#' Parses @mcp tags to extract tool name and description. +#' +#' @param x A roxy_tag object +#' @method roxy_tag_parse roxy_tag_mcp +#' @export +roxy_tag_parse.roxy_tag_mcp <- function(x) { + # Parse the raw content + raw <- trimws(x$raw) + + if (raw == "") { + # If no content, use defaults from function + x$val <- list( + name = NULL, # Will be filled from function name + description = NULL # Will be filled from title + ) + } else { + # Split by first whitespace to separate name and description + # Use regexpr to find first space + first_space <- regexpr("\\s", raw) + + if (first_space == -1) { + # Only name provided (no spaces) + x$val <- list( + name = raw, + description = NULL + ) + } else { + # Both name and description provided + name_part <- substr(raw, 1, first_space - 1) + desc_part <- trimws(substr(raw, first_space + 1, nchar(raw))) + + x$val <- list( + name = name_part, + description = desc_part + ) + } + } + + x +} + +#' Parse @type tag +#' +#' Parses @type tags to extract parameter type information. +#' Format: @type param_name type enum_values +#' +#' @param x A roxy_tag object +#' @method roxy_tag_parse roxy_tag_type +#' @export +roxy_tag_parse.roxy_tag_type <- function(x) { + # Parse the raw content + raw <- trimws(x$raw) + + if (raw == "") { + roxygen2::roxy_tag_warning(x, "Empty @type tag") + return(x) + } + + # Split by whitespace + parts <- strsplit(raw, "\\s+")[[1]] + + if (length(parts) < 2) { + roxygen2::roxy_tag_warning( + x, + "Invalid @type format. Expected: param_name type [enum_values]" + ) + return(x) + } + + param_name <- parts[1] + param_type <- parts[2] + + # Validate type + valid_types <- c( + "string", + "number", + "integer", + "boolean", + "array", + "object", + "enum" + ) + + # Handle enum type specially + if (grepl("^enum:", param_type)) { + param_type_base <- "enum" + enum_string <- gsub("^enum:", "", param_type) + enum_values <- trimws(strsplit(enum_string, ",")[[1]]) + } else { + param_type_base <- param_type + enum_values <- NULL + } + + if (!param_type_base %in% valid_types) { + roxygen2::roxy_tag_warning( + x, + paste( + "Invalid parameter type:", + param_type_base, + ". Valid types:", + paste(valid_types, collapse = ", ") + ) + ) + return(x) + } + + x$val <- list( + param_name = param_name, + type = param_type_base, + enum_values = enum_values + ) + + x +} + +#' Roxygen2 tag for @mcp +#' This function is called by Roxygen2 to generate documentation for the @mcp tag +#' @param x Roxygen2 tag object +#' @param base_path Base path for the package +#' @param env Environment +#' @return NULL (invisible) +#' @method roxy_tag_rd roxy_tag_mcp +#' @export +roxy_tag_rd.roxy_tag_mcp <- function(x, base_path, env) { + # @mcp tags are not for .Rd generation, only for MCP roclet + NULL +} + +#' Roxygen2 tag handler for @type +#' This function is called by Roxygen2 to generate documentation for the @type +#' @param x Roxygen2 tag object +#' @param base_path Base path for the package +#' @param env Environment +#' @return NULL (invisible) +#' @method roxy_tag_rd roxy_tag_type +#' @export +roxy_tag_rd.roxy_tag_type <- function(x, base_path, env) { + # @type tags are not for .Rd generation, only for MCP roclet + NULL +} + +#' Process blocks for MCP roclet +#' +#' @param x MCP roclet object +#' @param blocks List of roxy_block objects +#' @param env Environment +#' @param base_path Base path +#' @return List of processed MCP tools +#' @method roclet_process roclet_mcp +#' @export +roclet_process.roclet_mcp <- function(x, blocks, env, base_path) { + tools <- list() + + for (block in blocks) { + # Check if block has @mcp tag + mcp_tags <- roxygen2::block_get_tags(block, "mcp") + + if (length(mcp_tags) > 0) { + # Process this block as an MCP tool + tool_info <- process_mcp_block(block) + if (!is.null(tool_info)) { + tools[[length(tools) + 1]] <- tool_info + } + } + } + + tools +} + +#' Generate MCP server output +#' +#' @param x MCP roclet object +#' @param results List of processed tools +#' @param base_path Base path +#' @param ... Additional arguments +#' @return NULL (invisible) +#' @method roclet_output roclet_mcp +#' @export +roclet_output.roclet_mcp <- function(x, results, base_path, ...) { + if (length(results) == 0) { + message("No @mcp tags found. No MCP server generated.") + return(invisible(NULL)) + } + + # Generate MCP server file + server_content <- generate_mcp_server(results, base_path) + + # Write to file + output_file <- file.path(base_path, "inst", "mcp_server.R") + writeLines(server_content, output_file) + + message("Generated MCP server: ", output_file) + message("Found ", length(results), " MCP tools:") + for (tool in results) { + message(" - ", tool$name, ": ", tool$description) + } + + invisible(NULL) +} + +#' Process a single block with @mcp tag +#' +#' @param block A roxy_block object +#' @return A list with tool information or NULL +#' @keywords internal +process_mcp_block <- function(block) { + # Get @mcp tag + mcp_tags <- roxygen2::block_get_tags(block, "mcp") + if (length(mcp_tags) == 0) { + return(NULL) + } + + mcp_tag <- mcp_tags[[1]] # Use first @mcp tag + + # Get function name from block object + func_name <- if (!is.null(block$object) && !is.null(block$object$alias)) { + block$object$alias + } else { + "unknown_function" + } + + # Get tool name and description + tool_name <- mcp_tag$val$name %||% paste0(func_name, "_tool") + tool_description <- mcp_tag$val$description %||% + roxygen2::block_get_tag_value(block, "title") %||% + paste("Tool for", func_name) + + # Get parameters and types + param_tags <- roxygen2::block_get_tags(block, "param") + type_tags <- roxygen2::block_get_tags(block, "type") + + # Build parameter information + params <- build_param_info(param_tags, type_tags) + + list( + name = tool_name, + description = tool_description, + function_name = func_name, + params = params, + file = block$file, + line = block$line + ) +} + +#' Build parameter information from @param and @type tags +#' +#' @param param_tags List of @param tags +#' @param type_tags List of @type tags +#' @return List of parameter information +#' @keywords internal +build_param_info <- function(param_tags, type_tags = list()) { + params <- list() + + # Create lookup table for parameter types + type_lookup <- list() + for (type_tag in type_tags) { + if (!is.null(type_tag$val$param_name)) { + type_lookup[[type_tag$val$param_name]] <- type_tag$val + } + } + + # Process each @param tag + for (param_tag in param_tags) { + # Parse parameter name(s) and description + raw <- trimws(param_tag$raw) + if (raw == "") { + next + } + + # Split by first whitespace to separate names and description + first_space <- regexpr("\\s", raw) + if (first_space == -1) { + next + } # No space found + + param_names <- substr(raw, 1, first_space - 1) + param_desc <- trimws(substr(raw, first_space + 1, nchar(raw))) + + # Handle multiple parameter names separated by comma + names <- trimws(strsplit(param_names, ",")[[1]]) + + for (name in names) { + # Get type information from @type tags + type_info <- type_lookup[[name]] + param_type <- if (!is.null(type_info)) type_info$type else "string" + enum_values <- if (!is.null(type_info)) type_info$enum_values else NULL + + params[[name]] <- list( + name = name, + description = param_desc, + type = param_type, + enum_values = enum_values, + required = TRUE # Default to required, could be enhanced later + ) + } + } + + params +} + + +#' Generate MCP server R code +#' +#' @param tools List of tool information +#' @param base_path Base path for the package +#' @return Character vector of R code lines +#' @keywords internal +generate_mcp_server <- function(tools, base_path) { + lines <- c( + "# Auto-generated MCP Server", + "# Generated by mcpr::mcp_roclet", + "", + "library(mcpr)", + "" + ) + + # Generate tool definitions + for (i in seq_along(tools)) { + tool <- tools[[i]] + lines <- c(lines, generate_tool_code(tool, i)) + } + + # Generate server creation + lines <- c( + lines, + "", + "# Create MCP server", + "mcp_server <- new_server(", + " name = \"Auto-generated MCP Server\",", + " description = \"MCP server generated from R functions with @mcp tags\",", + " version = \"1.0.0\"", + ")", + "" + ) + + # Add tools to server + for (i in seq_along(tools)) { + tool <- tools[[i]] + lines <- c( + lines, + paste0("mcp_server <- add_capability(mcp_server, ", tool$name, "_tool)") + ) + } + + # Add server deployment + lines <- c( + lines, + "", + "# Start the server", + "serve_io(mcp_server)" + ) + + lines +} + +#' Generate R code for a single tool +#' +#' @param tool Tool information list +#' @param index Tool index for naming +#' @return Character vector of R code lines +#' @keywords internal +generate_tool_code <- function(tool, index) { + lines <- c( + paste0("# Tool: ", tool$name), + paste0(tool$name, "_tool <- new_tool("), + paste0(" name = \"", tool$name, "\","), + paste0(" description = \"", tool$description, "\","), + " input_schema = schema(" + ) + + # Generate properties + if (length(tool$params) > 0) { + lines <- c(lines, " properties = properties(") + + prop_lines <- c() + for (param_name in names(tool$params)) { + param <- tool$params[[param_name]] + prop_line <- generate_property_code(param) + prop_lines <- c(prop_lines, paste0(" ", prop_line)) + } + + # Join properties with commas + if (length(prop_lines) > 0) { + prop_lines[length(prop_lines)] <- prop_lines[length(prop_lines)] # No comma on last + for (i in seq_len(length(prop_lines) - 1)) { + prop_lines[i] <- paste0(prop_lines[i], ",") + } + } + + lines <- c(lines, prop_lines, " )") + } else { + lines <- c(lines, " properties = properties()") + } + + lines <- c( + lines, + " ),", + " handler = function(params) {", + paste0(" # Call the original function: ", tool$function_name), + generate_handler_code(tool), + " }", + ")", + "" + ) + + lines +} + +#' Generate property code for a parameter +#' +#' @param param Parameter information +#' @return Character string with property code +#' @keywords internal +generate_property_code <- function(param) { + type <- param$type + name <- param$name + desc <- param$description + required <- param$required + + if (type == "enum" && !is.null(param$enum_values)) { + values_str <- paste0( + "c(", + paste0("\"", param$enum_values, "\"", collapse = ", "), + ")" + ) + return(paste0( + name, + " = property_enum(\"", + name, + "\", \"", + desc, + "\", values = ", + values_str, + ", required = ", + required, + ")" + )) + } + + func_name <- switch( + type, + "string" = "property_string", + "number" = "property_number", + "integer" = "property_number", # Use property_number for integers + "boolean" = "property_boolean", + "array" = "property_array", + "object" = "property_object", + "property_string" # default + ) + + paste0( + name, + " = ", + func_name, + "(\"", + name, + "\", \"", + gsub("\"", "'", desc), + "\", required = ", + required, + ")" + ) +} + +#' Generate handler code that calls the original function +#' +#' @param tool Tool information +#' @return Character vector of handler code lines +#' @keywords internal +generate_handler_code <- function(tool) { + if (length(tool$params) == 0) { + return(c( + paste0(" result <- ", tool$function_name, "()"), + " response_text(result)" + )) + } + + # Generate parameter extraction and function call + param_names <- names(tool$params) + args_str <- paste0("params$", param_names, collapse = ", ") + + c( + paste0(" result <- ", tool$function_name, "(", args_str, ")"), + " response_text(result)" + ) +} + +# Helper function for null coalescing +`%||%` <- function(x, y) if (is.null(x)) y else x diff --git a/README.md b/README.md index 1a4971b..37bcbd2 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,79 @@ serve_http(mcp, port = 3000) See the [Get Started](https://mcpr.opifex.org/articles/get-started) guide for more information. +## MCP Roclet for Automatic Server Generation + +mcpr includes a roxygen2 roclet that can automatically generate MCP servers from your R functions using special documentation tags. This provides a convenient way to expose existing R functions as MCP tools. + +### Usage + +1. Add `@mcp` and `@type` tags to your function documentation: + +```r +#' Add two numbers +#' @param x First number +#' @param y Second number +#' @type x number +#' @type y number +#' @mcp add_numbers Add two numbers together +add_numbers <- function(x, y) { + x + y +} +``` + +2. Generate the MCP server using roxygen2: + +```r +# Generate documentation and MCP server +roxygen2::roxygenise(roclets = c("rd", "mcpr::mcp_roclet")) +``` + +This will create an MCP server file at `inst/mcp_server.R` that includes: +- Tool definitions for all functions with `@mcp` tags +- Proper input schemas based on `@type` tags +- Handler functions that call your original R functions +- A complete, runnable MCP server + +### Supported Types + +The `@type` tag supports these parameter types: +- `string` - Text values +- `number` - Numeric values (integers and decimals) +- `integer` - Integer values +- `boolean` - True/false values +- `array` - Lists/vectors +- `object` - Complex R objects +- `enum:value1,value2,value3` - Enumerated values + +### Example Generated Server + +```r +# Generated MCP server code +add_numbers_tool <- new_tool( + name = "add_numbers", + description = "Add two numbers together", + input_schema = schema( + properties = properties( + x = property_number("x", "First number", required = TRUE), + y = property_number("y", "Second number", required = TRUE) + ) + ), + handler = function(params) { + result <- add_numbers(params$x, params$y) + response_text(result) + } +) + +mcp_server <- new_server( + name = "Auto-generated MCP Server", + description = "MCP server generated from R functions with @mcp tags", + version = "1.0.0" +) + +mcp_server <- add_capability(mcp_server, add_numbers_tool) +serve_io(mcp_server) +``` + ### Client Here's a simple example of using the client to interact with an MCP server: diff --git a/_pkgdown.yml b/_pkgdown.yml index c503d9d..d8aba47 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -39,6 +39,7 @@ reference: contents: - serve_io - serve_http + - get_name - title: "Client Functions" desc: > @@ -63,6 +64,18 @@ reference: - response - matches("^response_") +- title: "Roxygen2 Extension" + desc: > + Functions for extending roxygen2 with MCP server generation + contents: + - mcp_roclet + - roxy_tag_parse.roxy_tag_mcp + - roxy_tag_parse.roxy_tag_type + - roclet_process.roclet_mcp + - roxy_tag_rd.roxy_tag_mcp + - roxy_tag_rd.roxy_tag_type + - roclet_output.roclet_mcp + - title: "Ellmer Integration" desc: > Functions for integrating with the Ellmer package diff --git a/docs/404.html b/docs/404.html index c75d43b..45e2767 100644 --- a/docs/404.html +++ b/docs/404.html @@ -27,7 +27,7 @@ mcpr - 0.0.1.9000 + 0.0.2.9000 + + + + + +
+
+
+ +
+

Build parameter information from @param and @type tags

+
+ +
+

Usage

+
build_param_info(param_tags, type_tags = list())
+
+ +
+

Arguments

+ + +
param_tags
+

List of @param tags

+ + +
type_tags
+

List of @type tags

+ +
+
+

Value

+

List of parameter information

+
+ +
+ + +
+ + + +
+ + + + + + + diff --git a/docs/reference/client.html b/docs/reference/client.html index f75d8d3..d034afc 100644 --- a/docs/reference/client.html +++ b/docs/reference/client.html @@ -7,7 +7,7 @@ mcpr - 0.0.1.9000 + 0.0.2.9000 + + + + + +
+
+
+ +
+

Convert a single ellmer type to an mcpr property

+
+ +
+

Usage

+
convert_ellmer_type_to_mcpr_property(ellmer_type, prop_name)
+
+ +
+

Arguments

+ + +
ellmer_type
+

An ellmer Type object (TypeBasic, TypeArray, etc.)

+ + +
prop_name
+

The name of the property (for better error messages)

+ +
+
+

Value

+

An mcpr property object

+
+ +
+ + +
+ + + +
+ + + + + + + diff --git a/docs/reference/convert_ellmer_types_to_mcpr.html b/docs/reference/convert_ellmer_types_to_mcpr.html new file mode 100644 index 0000000..3f4063d --- /dev/null +++ b/docs/reference/convert_ellmer_types_to_mcpr.html @@ -0,0 +1,83 @@ + +Convert ellmer TypeObject to mcpr schema — convert_ellmer_types_to_mcpr • mcpr + Skip to contents + + +
+
+
+ +
+

Convert ellmer TypeObject to mcpr schema

+
+ +
+

Usage

+
convert_ellmer_types_to_mcpr(type_object)
+
+ +
+

Arguments

+ + +
type_object
+

An ellmer TypeObject containing the function arguments

+ +
+
+

Value

+

An mcpr schema object

+
+ +
+ + +
+ + + +
+ + + + + + + diff --git a/docs/reference/create_ellmer_handler.html b/docs/reference/create_ellmer_handler.html index 6a85ae4..58a98f6 100644 --- a/docs/reference/create_ellmer_handler.html +++ b/docs/reference/create_ellmer_handler.html @@ -7,7 +7,7 @@ mcpr - 0.0.1.9000 + 0.0.2.9000 + + + + + +
+
+
+ +
+

This function converts tools from the ellmer package to a format compatible +with the mcpr package. It takes an ellmer ToolDef object and creates an +mcpr tool object with the appropriate input schema and handler function.

+
+ +
+

Usage

+
ellmer_to_mcpr_tool(ellmer_tool)
+
+ +
+

Arguments

+ + +
ellmer_tool
+

An ellmer ToolDef object created with ellmer::tool()

+ +
+
+

Value

+

An mcpr tool object compatible with new_tool()

+
+ + +
+

Examples

+
if (FALSE) { # \dontrun{
+# Create an ellmer tool
+ellmer_rnorm <- ellmer::tool(
+  rnorm,
+  "Generate random normal numbers",
+  n = ellmer::type_integer("Number of observations"),
+  mean = ellmer::type_number("Mean value", required = FALSE),
+  sd = ellmer::type_number("Standard deviation", required = FALSE)
+)
+
+# Convert to mcpr format
+mcpr_tool <- ellmer_to_mcpr_tool(ellmer_rnorm)
+
+# Add to an mcpr server
+server <- new_server("MyServer", "Test server", "1.0.0")
+add_capability(server, mcpr_tool)
+} # }
+
+
+
+
+ + +
+ + + +
+ + + + + + + diff --git a/docs/reference/extract_mcp_result.html b/docs/reference/extract_mcp_result.html index ccd5f43..cb93ba4 100644 --- a/docs/reference/extract_mcp_result.html +++ b/docs/reference/extract_mcp_result.html @@ -7,7 +7,7 @@ mcpr - 0.0.1.9000 + 0.0.2.9000 + + + + + +
+
+
+ +
+

Generate handler code that calls the original function

+
+ +
+

Usage

+
generate_handler_code(tool)
+
+ +
+

Arguments

+ + +
tool
+

Tool information

+ +
+
+

Value

+

Character vector of handler code lines

+
+ +
+ + +
+ + + +
+ + + + + + + diff --git a/docs/reference/generate_mcp_server.html b/docs/reference/generate_mcp_server.html new file mode 100644 index 0000000..031c279 --- /dev/null +++ b/docs/reference/generate_mcp_server.html @@ -0,0 +1,87 @@ + +Generate MCP server R code — generate_mcp_server • mcpr + Skip to contents + + +
+
+
+ +
+

Generate MCP server R code

+
+ +
+

Usage

+
generate_mcp_server(tools, base_path)
+
+ +
+

Arguments

+ + +
tools
+

List of tool information

+ + +
base_path
+

Base path for the package

+ +
+
+

Value

+

Character vector of R code lines

+
+ +
+ + +
+ + + +
+ + + + + + + diff --git a/docs/reference/generate_property_code.html b/docs/reference/generate_property_code.html new file mode 100644 index 0000000..d45aed2 --- /dev/null +++ b/docs/reference/generate_property_code.html @@ -0,0 +1,83 @@ + +Generate property code for a parameter — generate_property_code • mcpr + Skip to contents + + +
+
+
+ +
+

Generate property code for a parameter

+
+ +
+

Usage

+
generate_property_code(param)
+
+ +
+

Arguments

+ + +
param
+

Parameter information

+ +
+
+

Value

+

Character string with property code

+
+ +
+ + +
+ + + +
+ + + + + + + diff --git a/docs/reference/generate_tool_code.html b/docs/reference/generate_tool_code.html new file mode 100644 index 0000000..842f733 --- /dev/null +++ b/docs/reference/generate_tool_code.html @@ -0,0 +1,87 @@ + +Generate R code for a single tool — generate_tool_code • mcpr + Skip to contents + + +
+
+
+ +
+

Generate R code for a single tool

+
+ +
+

Usage

+
generate_tool_code(tool, index)
+
+ +
+

Arguments

+ + +
tool
+

Tool information list

+ + +
index
+

Tool index for naming

+ +
+
+

Value

+

Character vector of R code lines

+
+ +
+ + +
+ + + +
+ + + + + + + diff --git a/docs/reference/get_name.html b/docs/reference/get_name.html new file mode 100644 index 0000000..a3b3d7c --- /dev/null +++ b/docs/reference/get_name.html @@ -0,0 +1,83 @@ + +Get the name of a client — get_name • mcpr + Skip to contents + + +
+
+
+ +
+

Get the name of a client

+
+ +
+

Usage

+
get_name(x)
+
+ +
+

Arguments

+ + +
x
+

A client object

+ +
+
+

Value

+

The name of the client

+
+ +
+ + +
+ + + +
+ + + + + + + diff --git a/docs/reference/index.html b/docs/reference/index.html index 5f767d2..798fcf4 100644 --- a/docs/reference/index.html +++ b/docs/reference/index.html @@ -7,7 +7,7 @@ mcpr - 0.0.1.9000 + 0.0.2.9000
+ + + + + +
+
+
+ +
+

This roclet automatically generates MCP (Model Context Protocol) servers +from R functions annotated with @mcp tags.

+
+ +
+

Usage

+
mcp_roclet()
+
+ + +
+

Examples

+
if (FALSE) { # \dontrun{
+# Use the roclet in roxygenise
+roxygen2::roxygenise(roclets = c("rd", "mcpr::mcp_roclet"))
+} # }
+
+
+
+ + +
+ + + + + + + diff --git a/docs/reference/mcpr_clients_to_ellmer_tools.html b/docs/reference/mcpr_clients_to_ellmer_tools.html index 45c643c..d247d05 100644 --- a/docs/reference/mcpr_clients_to_ellmer_tools.html +++ b/docs/reference/mcpr_clients_to_ellmer_tools.html @@ -11,7 +11,7 @@ mcpr - 0.0.1.9000 + 0.0.2.9000 + + + + + +
+
+
+ +
+

Process a single block with @mcp tag

+
+ +
+

Usage

+
process_mcp_block(block)
+
+ +
+

Arguments

+ + +
block
+

A roxy_block object

+ +
+
+

Value

+

A list with tool information or NULL

+
+ +
+ + +
+ + + + + + + diff --git a/docs/reference/process_request.html b/docs/reference/process_request.html index 12120a8..7093646 100644 --- a/docs/reference/process_request.html +++ b/docs/reference/process_request.html @@ -7,7 +7,7 @@ mcpr - 0.0.1.9000 + 0.0.2.9000 + + + + + +
+
+
+ +
+

Generate MCP server output

+
+ +
+

Usage

+
# S3 method for class 'roclet_mcp'
+roclet_output(x, results, base_path, ...)
+
+ +
+

Arguments

+ + +
x
+

MCP roclet object

+ + +
results
+

List of processed tools

+ + +
base_path
+

Base path

+ + +
...
+

Additional arguments

+ +
+
+

Value

+

NULL (invisible)

+
+ +
+ + +
+ + + + + + + diff --git a/docs/reference/roclet_process.roclet_mcp.html b/docs/reference/roclet_process.roclet_mcp.html new file mode 100644 index 0000000..07d5a0b --- /dev/null +++ b/docs/reference/roclet_process.roclet_mcp.html @@ -0,0 +1,96 @@ + +Process blocks for MCP roclet — roclet_process.roclet_mcp • mcpr + Skip to contents + + +
+
+
+ +
+

Process blocks for MCP roclet

+
+ +
+

Usage

+
# S3 method for class 'roclet_mcp'
+roclet_process(x, blocks, env, base_path)
+
+ +
+

Arguments

+ + +
x
+

MCP roclet object

+ + +
blocks
+

List of roxy_block objects

+ + +
env
+

Environment

+ + +
base_path
+

Base path

+ +
+
+

Value

+

List of processed MCP tools

+
+ +
+ + +
+ + + + + + + diff --git a/docs/reference/roxy_tag_parse.roxy_tag_mcp.html b/docs/reference/roxy_tag_parse.roxy_tag_mcp.html new file mode 100644 index 0000000..a434a30 --- /dev/null +++ b/docs/reference/roxy_tag_parse.roxy_tag_mcp.html @@ -0,0 +1,80 @@ + +Parse @mcp tag — roxy_tag_parse.roxy_tag_mcp • mcpr + Skip to contents + + +
+
+
+ +
+

Parses @mcp tags to extract tool name and description.

+
+ +
+

Usage

+
# S3 method for class 'roxy_tag_mcp'
+roxy_tag_parse(x)
+
+ +
+

Arguments

+ + +
x
+

A roxy_tag object

+ +
+ +
+ + +
+ + + + + + + diff --git a/docs/reference/roxy_tag_parse.roxy_tag_type.html b/docs/reference/roxy_tag_parse.roxy_tag_type.html new file mode 100644 index 0000000..98402c7 --- /dev/null +++ b/docs/reference/roxy_tag_parse.roxy_tag_type.html @@ -0,0 +1,83 @@ + +Parse @type tag — roxy_tag_parse.roxy_tag_type • mcpr + Skip to contents + + +
+
+
+ +
+

Parses @type tags to extract parameter type information. +Format: @type param_name type enum_values

+
+ +
+

Usage

+
# S3 method for class 'roxy_tag_type'
+roxy_tag_parse(x)
+
+ +
+

Arguments

+ + +
x
+

A roxy_tag object

+ +
+ +
+ + +
+ + + + + + + diff --git a/docs/reference/roxy_tag_rd.roxy_tag_mcp.html b/docs/reference/roxy_tag_rd.roxy_tag_mcp.html new file mode 100644 index 0000000..ced7ce0 --- /dev/null +++ b/docs/reference/roxy_tag_rd.roxy_tag_mcp.html @@ -0,0 +1,95 @@ + +Roxygen2 tag for @mcp This function is called by Roxygen2 to generate documentation for the @mcp tag — roxy_tag_rd.roxy_tag_mcp • mcpr + Skip to contents + + +
+
+
+ +
+

Roxygen2 tag for @mcp +This function is called by Roxygen2 to generate documentation for the @mcp tag

+
+ +
+

Usage

+
# S3 method for class 'roxy_tag_mcp'
+roxy_tag_rd(x, base_path, env)
+
+ +
+

Arguments

+ + +
x
+

Roxygen2 tag object

+ + +
base_path
+

Base path for the package

+ + +
env
+

Environment

+ +
+
+

Value

+

NULL (invisible)

+
+ +
+ + +
+ + + + + + + diff --git a/docs/reference/roxy_tag_rd.roxy_tag_type.html b/docs/reference/roxy_tag_rd.roxy_tag_type.html new file mode 100644 index 0000000..b70ccc1 --- /dev/null +++ b/docs/reference/roxy_tag_rd.roxy_tag_type.html @@ -0,0 +1,95 @@ + +Roxygen2 tag handler for @type This function is called by Roxygen2 to generate documentation for the @type — roxy_tag_rd.roxy_tag_type • mcpr + Skip to contents + + +
+
+
+ +
+

Roxygen2 tag handler for @type +This function is called by Roxygen2 to generate documentation for the @type

+
+ +
+

Usage

+
# S3 method for class 'roxy_tag_type'
+roxy_tag_rd(x, base_path, env)
+
+ +
+

Arguments

+ + +
x
+

Roxygen2 tag object

+ + +
base_path
+

Base path for the package

+ + +
env
+

Environment

+ +
+
+

Value

+

NULL (invisible)

+
+ +
+ + +
+ + + + + + + diff --git a/docs/reference/schema.html b/docs/reference/schema.html index 1a0c172..783db40 100644 --- a/docs/reference/schema.html +++ b/docs/reference/schema.html @@ -7,7 +7,7 @@ mcpr - 0.0.1.9000 + 0.0.2.9000