Skip to content

Commit c4bd3f8

Browse files
authored
merge pull request #10 from shikokuchuo/dev: Robustness and Efficiency
2 parents 7489691 + 1be2a73 commit c4bd3f8

File tree

3 files changed

+26
-25
lines changed

3 files changed

+26
-25
lines changed

DESCRIPTION

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,13 @@ Imports:
2626
ellmer,
2727
jsonlite,
2828
later,
29-
nanonext,
29+
nanonext (>= 1.5.2.9009),
3030
promises,
3131
rlang
3232
Depends: R (>= 4.1.0)
3333
URL: https://github.com/simonpcouch/acquaint, https://simonpcouch.github.io/acquaint/
3434
BugReports: https://github.com/simonpcouch/acquaint/issues
3535
Config/Needs/website: tidyverse/tidytemplate
3636
Remotes:
37-
posit-dev/btw
37+
posit-dev/btw,
38+
r-lib/nanonext

R/proxy.R

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ mcp_proxy <- function() {
1111

1212
# Note that we're using file("stdin") instead of stdin(), which are not the
1313
# same.
14-
the$f <- file("stdin")
15-
open(the$f, blocking = FALSE)
14+
the$f <- file("stdin", open = "r")
1615

1716
schedule_handle_message_from_client()
1817
schedule_handle_message_from_server()
@@ -108,19 +107,19 @@ handle_message_from_server <- function(data) {
108107

109108
logcat("FROM SERVER: ", data)
110109

111-
# The response_text is alredy JSON, so we'll use cat() instead of cat_json()
112-
cat(data, "\n", sep = "")
110+
# The response_text is already JSON, so we'll use cat() instead of cat_json()
111+
nanonext::write_stdout(data)
113112
}
114113

115114
schedule_handle_message_from_server <- function() {
116-
r <- nanonext::recv_aio(the$proxy_socket)
115+
r <- nanonext::recv_aio(the$proxy_socket, mode = "string")
117116
promises::as.promise(r)$then(handle_message_from_server)
118117
}
119118

120119
forward_request <- function(data) {
121120
logcat("TO SERVER: ", data)
122121

123-
nanonext::send_aio(the$proxy_socket, data)
122+
the$saio <- nanonext::send_aio(the$proxy_socket, data, mode = "raw")
124123
}
125124

126125
# This process will be launched by the MCP client, so stdout/stderr aren't
@@ -132,7 +131,7 @@ logcat <- function(x, ..., append = TRUE) {
132131
}
133132

134133
cat_json <- function(x) {
135-
cat(to_json(x), "\n", sep = "")
134+
nanonext::write_stdout(to_json(x))
136135
}
137136

138137
capabilities <- function() {

R/server.R

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
#' Model context protocol for your R session
2-
#'
2+
#'
33
#' @description
44
#' Together, these functions implement a model context protocol server for your
55
#' R session.
6-
#'
7-
#' @section Configuration:
8-
#'
6+
#'
7+
#' @section Configuration:
8+
#'
99
#' [mcp_proxy()] should be configured with the MCP clients via the `Rscript`
1010
#' 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
1212
#' `file.edit("~/Library/Application Support/Claude/claude_desktop_config.json")`):
13-
#'
13+
#'
1414
#' ```json
1515
#' {
1616
#' "mcpServers": {
@@ -21,31 +21,31 @@
2121
#' }
2222
#' }
2323
#' ```
24-
#'
24+
#'
2525
#' Or, to use with Claude Code, you might type in a terminal:
26-
#'
26+
#'
2727
#' ```bash
2828
#' claude mcp add -s "user" r-acquaint Rscript -e "acquaint::mcp_proxy()"
2929
#' ```
30-
#'
30+
#'
3131
#' **mcp_proxy() is not intended for interactive use.**
32-
#'
32+
#'
3333
#' The proxy interfaces with the MCP client on behalf of the server hosted in
3434
#' your R session. **Use [mcp_serve()] to start the MCP server in your R session.**
3535
#' Place a call to `acquaint::mcp_serve()` in your `.Rprofile`, perhaps with
3636
#' `usethis::edit_r_profile()`, to start a server for your R session every time
3737
#' you start R.
38-
#'
38+
#'
3939
#' @examples
4040
#' if (interactive()) {
4141
#' mcp_serve()
4242
#' }
43-
#'
43+
#'
4444
#' @name mcp
4545
#' @export
4646
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
4949
# Rscript -e "acquaint::mcp_serve()" and the existing server will be wiped.
5050
# Returning early in this case allows for the desired R session server to be
5151
# running already before the client initiates the proxy.
@@ -99,11 +99,12 @@ handle_message_from_proxy <- function(msg) {
9999
}
100100
# cat("SEND:", to_json(body), "\n", sep = "", file = stderr())
101101

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")
103104
}
104105

105106
schedule_handle_message_from_proxy <- function() {
106-
r <- nanonext::recv_aio(the$server_socket)
107+
r <- nanonext::recv_aio(the$server_socket, mode = "string")
107108
promises::as.promise(r)$then(handle_message_from_proxy)$catch(function(e) {
108109
print(e)
109110
})

0 commit comments

Comments
 (0)