|  | 
| 1 | 1 | #' Model context protocol for your R session | 
| 2 |  | -#'  | 
|  | 2 | +#' | 
| 3 | 3 | #' @description | 
| 4 | 4 | #' Together, these functions implement a model context protocol server for your | 
| 5 | 5 | #' R session. | 
| 6 |  | -#'  | 
| 7 |  | -#' @section Configuration:  | 
| 8 |  | -#'  | 
|  | 6 | +#' | 
|  | 7 | +#' @section Configuration: | 
|  | 8 | +#' | 
| 9 | 9 | #' [mcp_proxy()] should be configured with the MCP clients via the `Rscript` | 
| 10 | 10 | #' command. For example, to use with Claude Desktop, paste the following in your | 
| 11 |  | -#' Claude Desktop configuration (on macOS, at  | 
|  | 11 | +#' Claude Desktop configuration (on macOS, at | 
| 12 | 12 | #' `file.edit("~/Library/Application Support/Claude/claude_desktop_config.json")`): | 
| 13 |  | -#'  | 
|  | 13 | +#' | 
| 14 | 14 | #' ```json | 
| 15 | 15 | #' { | 
| 16 | 16 | #'   "mcpServers": { | 
|  | 
| 21 | 21 | #'   } | 
| 22 | 22 | #' } | 
| 23 | 23 | #' ``` | 
| 24 |  | -#'  | 
|  | 24 | +#' | 
| 25 | 25 | #' Or, to use with Claude Code, you might type in a terminal: | 
| 26 |  | -#'  | 
|  | 26 | +#' | 
| 27 | 27 | #' ```bash | 
| 28 | 28 | #' claude mcp add -s "user" r-acquaint Rscript -e "acquaint::mcp_proxy()" | 
| 29 | 29 | #' ``` | 
| 30 |  | -#'  | 
|  | 30 | +#' | 
| 31 | 31 | #' **mcp_proxy() is not intended for interactive use.** | 
| 32 |  | -#'  | 
|  | 32 | +#' | 
| 33 | 33 | #' The proxy interfaces with the MCP client on behalf of the server hosted in | 
| 34 | 34 | #' your R session. **Use [mcp_serve()] to start the MCP server in your R session.** | 
| 35 | 35 | #' Place a call to `acquaint::mcp_serve()` in your `.Rprofile`, perhaps with | 
| 36 | 36 | #' `usethis::edit_r_profile()`, to start a server for your R session every time | 
| 37 | 37 | #' you start R. | 
| 38 |  | -#'  | 
|  | 38 | +#' | 
| 39 | 39 | #' @examples | 
| 40 | 40 | #' if (interactive()) { | 
| 41 | 41 | #' mcp_serve() | 
| 42 | 42 | #' } | 
| 43 |  | -#'  | 
|  | 43 | +#' | 
| 44 | 44 | #' @name mcp | 
| 45 | 45 | #' @export | 
| 46 | 46 | mcp_serve <- function() { | 
| 47 |  | -  # HACK: If a server is already running in one session via `.Rprofile`,  | 
| 48 |  | -  # `mcp_serve()` will be called again when the client runs the command  | 
|  | 47 | +  # HACK: If a server is already running in one session via `.Rprofile`, | 
|  | 48 | +  # `mcp_serve()` will be called again when the client runs the command | 
| 49 | 49 |   # Rscript -e "acquaint::mcp_serve()" and the existing server will be wiped. | 
| 50 | 50 |   # Returning early in this case allows for the desired R session server to be | 
| 51 | 51 |   # running already before the client initiates the proxy. | 
| @@ -99,11 +99,12 @@ handle_message_from_proxy <- function(msg) { | 
| 99 | 99 |   } | 
| 100 | 100 |   # cat("SEND:", to_json(body), "\n", sep = "", file = stderr()) | 
| 101 | 101 | 
 | 
| 102 |  | -  nanonext::send_aio(the$server_socket, to_json(body)) | 
|  | 102 | +  # TODO: consider if better / more robust using synchronous sends | 
|  | 103 | +  the$saio <- nanonext::send_aio(the$server_socket, to_json(body), mode = "raw") | 
| 103 | 104 | } | 
| 104 | 105 | 
 | 
| 105 | 106 | schedule_handle_message_from_proxy <- function() { | 
| 106 |  | -  r <- nanonext::recv_aio(the$server_socket) | 
|  | 107 | +  r <- nanonext::recv_aio(the$server_socket, mode = "string") | 
| 107 | 108 |   promises::as.promise(r)$then(handle_message_from_proxy)$catch(function(e) { | 
| 108 | 109 |     print(e) | 
| 109 | 110 |   }) | 
|  | 
0 commit comments