@@ -38,12 +38,12 @@ the$mcp_servers <- list()
3838# ' file with `file.edit(file.path("~", ".config", "mcptools", "config.json"))`.
3939# '
4040# ' The mcptools config file should be valid .json with an entry `mcpServers`.
41- # ' That entry should contain named elements, each with at least a `command`
42- # ' and `args` entry .
41+ # ' That entry should contain named elements, each configured for either
42+ # ' **stdio** or **HTTP** transport .
4343# '
44- # ' For example, to configure `mcp_tools()` with GitHub's official MCP Server
45- # ' <https://github.com/github/github-mcp-server>, you could write the following
46- # ' in that file :
44+ # ' ## Local servers (via stdio)
45+ # '
46+ # ' For stdio-based servers, provide `command` and `args` entries :
4747# '
4848# ' ```json
4949# ' {
@@ -66,6 +66,23 @@ the$mcp_servers <- list()
6666# ' }
6767# ' ```
6868# '
69+ # ' ## Remote servers (via http)
70+ # '
71+ # ' For HTTP-based servers, provide a `url` entry instead of `command`/`args`:
72+ # '
73+ # ' ```json
74+ # ' {
75+ # ' "mcpServers": {
76+ # ' "local-http": {
77+ # ' "url": "https://localhost:8080"
78+ # ' },
79+ # ' "remote-http": {
80+ # ' "url": "https://mcp.example.com/mcp"
81+ # ' }
82+ # ' }
83+ # ' }
84+ # ' ```
85+ # '
6986# ' @returns
7087# ' * `mcp_tools()` returns a list of ellmer tools that can be passed directly
7188# ' to the `$set_tools()` method of an [ellmer::Chat] object. If the file at
@@ -100,33 +117,91 @@ mcp_tools <- function(config = NULL) {
100117 for (i in seq_along(config )) {
101118 config_i <- config [[i ]]
102119 name_i <- names(config )[i ]
103- config_i_env <- if (" env" %in% names(config_i )) {
104- unlist(config_i $ env )
120+
121+ if (" url" %in% names(config_i )) {
122+ add_mcp_server_http(config = config_i , name = name_i )
105123 } else {
106- NULL
124+ add_mcp_server_stdio( config = config_i , name = name_i )
107125 }
126+ }
108127
109- process <- processx :: process $ new(
110- # seems like the R process has a different PATH than process_exec
111- command = Sys.which(config_i $ command ),
112- args = config_i $ args ,
113- env = config_i_env ,
114- stdin = " |" ,
115- stdout = " |" ,
116- stderr = " |"
117- )
128+ servers_as_ellmer_tools()
129+ }
118130
119- the $ server_processes <- c(
120- the $ server_processes ,
121- list2(
122- !! paste0(c(config_i $ command , config_i $ args ), collapse = " " ) : = process
123- )
131+ add_mcp_server_stdio <- function (config , name ) {
132+ config_env <- if (" env" %in% names(config )) {
133+ unlist(config $ env )
134+ } else {
135+ NULL
136+ }
137+
138+ process <- processx :: process $ new(
139+ command = Sys.which(config $ command ),
140+ args = config $ args ,
141+ env = config_env ,
142+ stdin = " |" ,
143+ stdout = " |" ,
144+ stderr = " |"
145+ )
146+
147+ the $ server_processes <- c(
148+ the $ server_processes ,
149+ list2(
150+ !! paste0(c(config $ command , config $ args ), collapse = " " ) : = process
124151 )
152+ )
125153
126- add_mcp_server(process = process , name = name_i )
127- }
154+ response_initialize <- send_and_receive_stdio(
155+ process ,
156+ mcp_request_initialize()
157+ )
158+ send_and_receive_stdio(process , mcp_request_initialized())
159+ response_tools_list <- send_and_receive_stdio(
160+ process ,
161+ mcp_request_tools_list()
162+ )
128163
129- servers_as_ellmer_tools()
164+ the $ mcp_servers [[name ]] <- list (
165+ name = name ,
166+ type = " stdio" ,
167+ process = process ,
168+ tools = response_tools_list $ result ,
169+ id = 3
170+ )
171+
172+ the $ mcp_servers [[name ]]
173+ }
174+
175+ add_mcp_server_http <- function (config , name ) {
176+ response_initialize <- send_and_receive_http(
177+ url = config $ url ,
178+ request = mcp_request_initialize()
179+ )
180+
181+ session_id <- response_initialize $ session_id
182+
183+ send_and_receive_http(
184+ url = config $ url ,
185+ request = mcp_request_initialized(),
186+ session_id = session_id
187+ )
188+
189+ response_tools_list <- send_and_receive_http(
190+ url = config $ url ,
191+ request = mcp_request_tools_list(),
192+ session_id = session_id
193+ )
194+
195+ the $ mcp_servers [[name ]] <- list (
196+ name = name ,
197+ type = " http" ,
198+ url = config $ url ,
199+ session_id = session_id ,
200+ tools = response_tools_list $ result ,
201+ id = 3
202+ )
203+
204+ the $ mcp_servers [[name ]]
130205}
131206
132207mcp_client_config <- function () {
@@ -193,19 +268,34 @@ error_no_mcp_config <- function(call) {
193268 )
194269}
195270
196- add_mcp_server <- function (process , name ) {
197- response_initialize <- send_and_receive(process , mcp_request_initialize())
198- send_and_receive(process , mcp_request_initialized())
199- response_tools_list <- send_and_receive(process , mcp_request_tools_list())
271+ send_and_receive_http <- function (url , request , session_id = NULL ) {
272+ req <- httr2 :: request(url ) | >
273+ httr2 :: req_method(" POST" ) | >
274+ httr2 :: req_headers(
275+ " Content-Type" = " application/json" ,
276+ " Accept" = " application/json" ,
277+ " MCP-Protocol-Version" = " 2025-06-18"
278+ ) | >
279+ httr2 :: req_body_json(request )
280+
281+ if (! is.null(session_id )) {
282+ req <- httr2 :: req_headers(req , " Mcp-Session-Id" = session_id )
283+ }
284+
285+ resp <- httr2 :: req_perform(req )
200286
201- the $ mcp_servers [[name ]] <- list (
202- name = name ,
203- process = process ,
204- tools = response_tools_list $ result ,
205- id = 3
206- )
287+ if (httr2 :: resp_status(resp ) == 202L || httr2 :: resp_body_string(resp ) == " " ) {
288+ return (NULL )
289+ }
207290
208- the $ mcp_servers [[name ]]
291+ body <- httr2 :: resp_body_json(resp )
292+
293+ session_id_header <- httr2 :: resp_header(resp , " Mcp-Session-Id" )
294+ if (! is.null(session_id_header )) {
295+ body $ session_id <- session_id_header
296+ }
297+
298+ body
209299}
210300
211301servers_as_ellmer_tools <- function () {
@@ -347,13 +437,21 @@ tool_ref <- function(server, tool, arguments) {
347437}
348438
349439call_tool <- function (... , server , tool ) {
350- server_process <- the $ mcp_servers [[server ]]$ process
351- send_and_receive(
352- server_process ,
353- mcp_request_tool_call(
354- id = jsonrpc_id(server ),
355- tool = tool ,
356- arguments = list (... )
440+ server_config <- the $ mcp_servers [[server ]]
441+
442+ request <- mcp_request_tool_call(
443+ id = jsonrpc_id(server ),
444+ tool = tool ,
445+ arguments = list (... )
446+ )
447+
448+ switch (
449+ server_config $ type ,
450+ stdio = send_and_receive_stdio(server_config $ process , request ),
451+ http = send_and_receive_http(
452+ url = server_config $ url ,
453+ request = request ,
454+ session_id = server_config $ session_id
357455 )
358456 )
359457}
@@ -372,13 +470,11 @@ log_cat_client <- function(x, append = TRUE) {
372470 cat(x , " \n\n " , sep = " " , append = append , file = log_file )
373471}
374472
375- send_and_receive <- function (process , message ) {
376- # send the message
473+ send_and_receive_stdio <- function (process , message ) {
377474 json_msg <- jsonlite :: toJSON(message , auto_unbox = TRUE )
378475 log_cat_client(c(" FROM CLIENT: " , json_msg ))
379476 process $ write_input(paste0(json_msg , " \n " ))
380477
381- # poll for response
382478 output <- NULL
383479 attempts <- 0
384480 max_attempts <- 20
0 commit comments