Skip to content

v0.1.0 release candidate #55

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Jul 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Package: mcptools
Title: Model Context Protocol Server For Your R Sessions
Title: Model Context Protocol Servers and Clients
Version: 0.0.0.9000
Authors@R: c(
person("Simon", "Couch", , "[email protected]", role = c("aut", "cre"),
Expand All @@ -10,12 +10,13 @@ Authors@R: c(
person("Posit Software, PBC", role = c("cph", "fnd"),
comment = c(ROR = "03wc8by49"))
)
Description: Implements a model context protocol (MCP) server for your R
sessions, allowing MCP-compatible apps like Claude Desktop and Claude
Code to run R code in your interactive R sessions in order to better answer
questions. By default, the package supplies a set of tools that allow models
to inspect your R environment and peruse package documentation, but
supports arbitrary R functions as tools.
Description: Implements the Model Context Protocol (MCP). Users can start
'R'-based servers, serving functions as tools for large language models to
call before responding to the user in MCP-compatible apps like
'Claude Desktop' and 'Claude Code', with options to run those tools inside
of interactive 'R' sessions. On the other end, when 'R' is the client via
the 'ellmer' package, users can register tools from third-party MCP
servers to integrate additional context into chats.
License: MIT + file LICENSE
Suggests:
knitr,
Expand Down
5 changes: 5 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# mcptools (development version)

Initial CRAN submission.

Before the initial release of the package, mcptools was called acquaint and supplied a default set of tools from btw, currently a GitHub-only package, when R was used as an MCP server. The direction of the dependency has been reversed; to use the same functionality from before, transition `acquaint::mcp_server()` to `btw::btw_mcp_server()` and `acquaint::mcp_session()` to `btw::btw_mcp_session()`.
15 changes: 14 additions & 1 deletion R/client.R
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,19 @@ the$mcp_servers <- list()
#' to the `$set_tools()` method of an [ellmer::Chat] object. If the file at
#' `config` doesn't exist, an error.
#'
#' @examples
#' # setup
#' config_file <- tempfile(fileext = "json")
#' file.create(config_file)
#'
#' # usually, `config` would be a persistent, user-level
#' # configuration file for a set of MCP server
#' mcp_tools(config = config_file)
#'
#' # teardown
#' file.remove(config_file)
#'
#'
#' @name client
#' @aliases mcp_client
#' @export
Expand Down Expand Up @@ -374,7 +387,7 @@ jsonrpc_id <- function(server_name) {
# client protocol --------------------------------------------------------------
## stdio
log_cat_client <- function(x, append = TRUE) {
log_file <- "~/mcp_client_test.txt"
log_file <- mcptools_client_log()
cat(x, "\n\n", sep = "", append = append, file = log_file)
}

Expand Down
12 changes: 10 additions & 2 deletions R/server.R
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@
#' `Chat$set_tools()` can be passed here. By default, the package won't serve
#' any tools other than those needed to communicate with interactive R sessions.
#'
#' @returns
#' `mcp_server()` and `mcp_session()` are both called primarily for side-effects.
#'
#' * `mcp_server()` blocks the R process it's called in indefinitely and isn't
#' intended for interactive use.
#' * `mcp_session()` makes the interactive R session it's called in available to
#' MCP servers. It returns a promise via [promises::promise()].
#'
#' @seealso
#' - The "R as an MCP server" vignette at
#' `vignette("server", package = "mcptools")` delves into further detail
Expand All @@ -68,7 +76,7 @@
#' @examples
#' # should only be run non-interactively, and will block the current R process
#' # once called.
#' if (FALSE) {
#' if (identical(Sys.getenv("MCPTOOLS_CAN_BLOCK_PROCESS"), "true")) {
#' # to start a server with a tool to draw numbers from a random normal:
#' library(ellmer)
#'
Expand Down Expand Up @@ -220,7 +228,7 @@ forward_request <- function(data) {
# visible. This function will log output to the `logfile` so that you can view
# it.
logcat <- function(x, ..., append = TRUE) {
log_file <- mcptools_log_file()
log_file <- mcptools_server_log()
cat(x, "\n", sep = "", append = append, file = log_file)
}

Expand Down
8 changes: 6 additions & 2 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ to_json <- function(x, ...) {

interactive <- NULL

mcptools_log_file <- function() {
Sys.getenv("MCPTOOLS_LOG_FILE", tempfile(fileext = ".txt"))
mcptools_server_log <- function() {
Sys.getenv("MCPTOOLS_SERVER_LOG", tempfile(fileext = ".txt"))
}

mcptools_client_log <- function() {
Sys.getenv("MCPTOOLS_CLIENT_LOG", tempfile(fileext = ".txt"))
}
6 changes: 6 additions & 0 deletions README.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ e.g. [shinychat](https://github.com/posit-dev/shinychat) and

## Installation

Install mcptools from CRAN with:

```r
install.packages("mcptools")
```

You can install the development version of mcptools like so:

```r
Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@

[![Lifecycle:
experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://lifecycle.r-lib.org/articles/stages.html#experimental)
[![CRAN
status](https://www.r-pkg.org/badges/version/mcptools)](https://CRAN.R-project.org/package=mcptools)
[![R-CMD-check](https://github.com/posit-dev/mcptools/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/posit-dev/mcptools/actions/workflows/R-CMD-check.yaml)
<!-- badges: end -->

Expand Down Expand Up @@ -50,6 +48,12 @@ e.g. [shinychat](https://github.com/posit-dev/shinychat) and

## Installation

Install mcptools from CRAN with:

``` r
install.packages("mcptools")
```

You can install the development version of mcptools like so:

``` r
Expand Down
8 changes: 5 additions & 3 deletions cran-comments.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## R CMD check results
**Comments for CRAN team**:

0 errors | 0 warnings | 1 note
There's a NOTE since this is an initial release.

* This is a new release.
There are not references describing the methods in this package to cite in the Description field.

This is a **resubmission**; the first submission of the package was rejected due to the name of the protocol being single-quoted in the Description field an example's execution being wrapped in `if (FALSE)`. Note that there's now a new NOTE "Possibly misspelled words in DESCRIPTION" because of this change. Thank you for the feedback—both of these issues have been addressed.
14 changes: 14 additions & 0 deletions man/client.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions man/mcptools-package.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 10 additions & 1 deletion man/server.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions tests/testthat/test-client.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ test_that("mcp_tools works", {
skip_if_not_installed("withr")
skip_if(identical(Sys.getenv("GITHUB_PAT"), ""))
skip_on_os(c("windows", "mac"))
skip_if(
identical(unname(Sys.which("docker")), ""),
message = "Docker is not installed."
)

tmp_file <- withr::local_tempfile()

Expand Down
14 changes: 7 additions & 7 deletions tests/testthat/test-utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,15 @@ test_that("to_json works", {
expect_false(is.list(parsed$value))
})

test_that("mcptools_log_file works", {
# mcptools_log_file returns environment variable when set
withr::local_envvar(MCPTOOLS_LOG_FILE = "/custom/log/file.txt")
result <- mcptools_log_file()
test_that("mcptools_server_log works", {
# mcptools_server_log returns environment variable when set
withr::local_envvar(MCPTOOLS_SERVER_LOG = "/custom/log/file.txt")
result <- mcptools_server_log()
expect_equal(result, "/custom/log/file.txt")

# mcptools_log_file returns tempfile when environment variable not set
withr::local_envvar(MCPTOOLS_LOG_FILE = NULL)
result <- mcptools_log_file()
# mcptools_server_log returns tempfile when environment variable not set
withr::local_envvar(MCPTOOLS_SERVER_LOG = NULL)
result <- mcptools_server_log()
expect_true(grepl("\\.txt$", result))
expect_true(file.exists(dirname(result)))
})
Loading