Skip to content

Instantly share code, notes, and snippets.

@simonpcouch
Created May 20, 2025 14:44
Show Gist options
  • Select an option

  • Save simonpcouch/b8e9f07993b0f2c9f863d505f2c3005f to your computer and use it in GitHub Desktop.

Select an option

Save simonpcouch/b8e9f07993b0f2c9f863d505f2c3005f to your computer and use it in GitHub Desktop.

Revisions

  1. simonpcouch created this gist May 20, 2025.
    95 changes: 95 additions & 0 deletions mcp_client_demo.R
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,95 @@
    # an example server: acquaint's MCP server for R sessions
    r_process <- callr::r_bg(
    function() acquaint::mcp_server(),
    stdout = "|",
    stdin = "|"
    )

    Sys.sleep(1)

    log_cat_client <- function(x, append = TRUE) {
    log_file <- "~/mcp_client_test.txt"
    cat(x, "\n\n", sep = "", append = append, file = log_file)
    }

    send_and_receive <- function(process, message) {
    # send the message
    json_msg <- jsonlite::toJSON(message, auto_unbox = TRUE)
    log_cat_client(c("FROM CLIENT: ", json_msg))
    process$write_input(paste0(json_msg, "\n"))

    # poll for response
    output <- NULL
    attempts <- 0
    max_attempts <- 20

    while (length(output) == 0 && attempts < max_attempts) {
    Sys.sleep(0.2)
    output <- process$read_output_lines()
    attempts <- attempts + 1
    }

    if (!is.null(output) && length(output) > 0) {
    log_cat_client(c("FROM SERVER: ", output[1]))
    return(fromJSON(output[1]))
    }

    log_cat_client(c("ALERT: No response received after ", attempts, " attempts"))
    return(NULL)
    }

    # step 1: Initialize the MCP connection
    initialize_request <- list(
    jsonrpc = "2.0",
    id = 1,
    method = "initialize",
    params = list(
    protocolVersion = "2024-11-05",
    capabilities = list(
    tools = list(
    listChanged = FALSE
    )
    ),
    clientInfo = list(
    name = "MCP Test Client",
    version = "0.1.0"
    )
    )
    )

    init_response <- send_and_receive(r_process, initialize_request)

    # step 2: Send initialized notification
    # For some reason, if I send this, the buffer gets tied up and the following
    # tools/list isn't appropriately responded to.
    # Don't expect a response here, so no `receive()`
    # initialized_notification <- list(
    # jsonrpc = "2.0",
    # method = "notifications/initialized"
    # )
    #
    # r_process$write_input(jsonlite::toJSON(initialized_notification, auto_unbox = TRUE))

    # step 3: request the list of tools
    tools_request <- list(
    jsonrpc = "2.0",
    id = 2,
    method = "tools/list"
    )

    tools_response <- send_and_receive(r_process, tools_request)

    # step 4: call a tool
    platform_request <- list(
    jsonrpc = "2.0",
    id = 3,
    method = "tools/call",
    params = list(
    name = "btw_tool_session_platform_info"
    )
    )

    platform_response <- send_and_receive(r_process, platform_request)

    # clean up
    r_process$kill()