Quick start
Already have Certara.R installed? Configure your
assistant with a single call, then reload the client. These use
scope = "user" (enabled in every current and future
project) and run = TRUE (register via the client CLI when
applicable). Pick your client below. New to the details? Read on past
the quick start.
Certara.R::write_mcp_config(client = "cursor", scope = "user", run = TRUE)
Certara.R::write_mcp_config(client = "claude-code", scope = "user", run = TRUE)
Certara.R::write_mcp_config(client = "claude-desktop", scope = "user", run = TRUE)
Certara.R::write_mcp_config(client = "codex", scope = "user", run = TRUE)What the Certara MCP server is
Certara.R hosts an optional, federated Model Context Protocol (MCP)
server that exposes the Certara ecosystem’s knowledge base and tool
providers – NLME modeling via Certara.RsNLME,
machine-learning model search via Certara.RDarwin, VPC via
tidyvpc, and more – to AI coding assistants.
Supported clients:
- Cursor
-
Claude Code – the
claudecommand-line agent (terminal / CLI) - Claude Desktop – the desktop app, including its Code tab
- Codex
The MCP server is optional. Building, fitting, and
reporting models with Certara.RsNLME and the rest of the
suite does not require it; it is a convenience layer for AI-assisted
workflows. When enabled, agents can author and validate PML, launch and
watch fits asynchronously, run diagnostics, and emit audit-ready R
scripts, all grounded in the same knowledge base the packages ship.
Prerequisites and installation
The MCP stack ships as dependencies of Certara.R.
Installing the ecosystem meta-package pulls everything the server
needs:
install.packages(
"Certara.R",
dependencies = TRUE,
repos = c(
"https://certara.jfrog.io/artifactory/certara-cran-release-public/",
"https://cloud.r-project.org"
)
)RsNLME users who install the full suite via the
Certara_Packages vector in the RsNLME
Installation Guide already have Certara.R.
The MCP layer has three required helpers with version floors:
btw (>= 1.2.0)mcptools (>= 0.2.0)ellmer (>= 0.4.0)
A normal release install pulls these automatically. A developer
install with repos = NULL (installing a source tarball
directly) skips Suggests and can leave them missing;
install them explicitly in that case:
install.packages(c("btw", "mcptools", "ellmer"))The provider packages that require Certara’s NLME Engine (for example
Certara.RsNLME) still need the engine installed and
INSTALLDIR set; see the RsNLME
Installation Guide.
Configure a client with write_mcp_config()
write_mcp_config() writes (or merges) the MCP server
entry into the client’s own configuration file and, where supported,
pre-authorizes the Certara tools. You run it once per client from an R
console; the client then launches the server automatically.
# Cursor, project scope -- writes/merges .cursor/mcp.json
Certara.R::write_mcp_config(client = "cursor", scope = "project")
# Claude Code, project scope -- writes/merges .mcp.json
Certara.R::write_mcp_config(client = "claude-code", scope = "project")
# Claude Code, user scope -- registered via the claude CLI when on PATH
Certara.R::write_mcp_config(client = "claude-code", scope = "user", run = TRUE)
# Claude Desktop (user scope) -- merges claude_desktop_config.json
Certara.R::write_mcp_config(client = "claude-desktop")
# Codex -- writes the managed block to ~/.codex/config.toml directly
Certara.R::write_mcp_config(client = "codex")What each client writes:
-
Cursor – the server entry in
.cursor/mcp.json(project) or~/.cursor/mcp.json(user), plus a.cursor/rules/certara-mcp-usage.mdcrouting rule at project scope. -
Claude Code –
.mcp.jsonat project scope; user/local scopes are managed through theclaudeCLI. Acertara-mcp-usage.mdguidance doc is imported viaCLAUDE.md. -
Claude Desktop – merged into
claude_desktop_config.json(user scope only; on Windows the MSIX-virtualized path Desktop actually reads). Fully quit and relaunch Desktop after a config change. On Windows, also see Claude Desktop on Windows: required environment variables – MCP-launched fits need a few variables added to theenvblock. -
Codex – a managed block in
~/.codex/config.toml(the only way to set Codex per-tool approvals); the equivalentcodex mcp addcommand is printed for reference.
Key arguments:
-
scope– where the configuration is written, which controls where the server is available:-
"project"(default) – writes into the current project folder (e.g..cursor/mcp.json), so the server is enabled only when you open that folder. Choose this for per-analysis setup that can be version-controlled with the project. -
"user"– writes into your home-directory config (e.g.~/.cursor/mcp.json), so the server is enabled globally – in every current and future project on your machine. -
"local"(Claude Code only) – a private, per-project entry in~/.claude.jsonthat is not shared with collaborators through the project’s.mcp.json.
-
-
tool_allowlist– whenTRUE(default), pre-authorize the Certara tools so they run without per-tool approval prompts, using each client’s native mechanism (Cursorpermissions.json, Claude Codesettings.json, Codex approval settings). Claude Desktop has no config-file allowlist, so approve tools in the UI per session. -
run– controls whether the CLI-managed clients are configured for you. By default (FALSE)write_mcp_config()only prints the client CLI command so you can inspect and run it yourself. Settingrun = TRUEexecutes that command when the client CLI is on yourPATH(currently the Claude Code user/local scopes, viaclaude mcp add), falling back to printing if the CLI is not found. File-based targets (Cursor, Claude Code project scope, Codex, Claude Desktop) are always written directly, sorundoes not affect them.
Use remove_mcp_config() to undo a configuration:
# Remove the Cursor project-scope server written above.
Certara.R::remove_mcp_config(client = "cursor", scope = "project")Claude Desktop on Windows: required environment variables
Quick tip. Short on time? Point your Claude session at this article (share the page, or paste this section) and ask it to “diagnose and fill in the environment variables that Claude Desktop sanitizes out of my
claude_desktop_config.json.” With filesystem access the agent can run the env-check job below, see which variables are empty, and add theenvblock to your config for you – you then just fully quit and restart Claude Desktop for it to take effect.
This applies to Claude Desktop on Windows when you launch
NLME fits through the MCP job tools
(start_nlme_fit_spec, start_nlme_job,
start_nlme_fitmodel, the SCM scripts, etc.).
Symptom. MCP-launched fits fail at the compile stage
with Compile/Link Failed, even though the same model
fits fine from RStudio or a plain Rscript run in a
terminal.
Cause. Claude Desktop launches MCP servers with a sanitized, minimal environment – it does not pass your full user/system environment to the server process. On Windows a child process inherits a frozen snapshot of its parent’s environment (it never re-reads the registry), and the job launcher copies the server’s environment into every spawned fit. So whatever Claude Desktop omits is missing in the server and in each fit child:
Claude Desktop -> Rscript.exe (MCP server) -> job child (Rscript -> PowerShell -> TDL5/gcc)
The critical omission is ComSpec: the
engine’s PowerShell wrapper shells out with
& $env:comspec /c ..., and with ComSpec
empty every shell-out fails, which surfaces as
ERROR in generating Model.cpp ->
Compile/Link Failed. NLMEGCCDir64 and
INSTALLDIR are also typically dropped, so the compile
either cannot find TDL5.exe/engine libraries or falls back
to an incompatible gcc (e.g. RTools) instead of the Certara-shipped GCC
8.4.0.
Fix. Declare the variables explicitly in the
server’s env block in
claude_desktop_config.json, then fully restart Claude
Desktop:
"mcpServers": {
"certara-r": {
"command": "C:\\Program Files\\R\\R-4.6.0\\bin\\x64\\Rscript.exe",
"args": [
"-e",
"Certara.R::launch_certara_mcp(btw_groups = c('docs'), session_tools = FALSE, server_name = 'certara-r', job_watch_wait_seconds = 600, tool_profile = 'full')"
],
"type": "stdio",
"env": {
"ComSpec": "C:\\WINDOWS\\system32\\cmd.exe",
"INSTALLDIR": "C:\\Program Files\\Certara\\NLME_Engine",
"NLMEGCCDir64": "C:\\Program Files\\Certara\\mingw64\\",
"TMP": "C:\\Users\\<you>\\AppData\\Local\\Temp",
"NUMBER_OF_PROCESSORS": "<your core count>",
"PATHEXT": ".COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC",
"PhoenixMSMPIDir": "C:\\Program Files\\Certara\\MPI\\"
}
}
}Notes:
- The
commandandargsare the oneswrite_mcp_config("claude-desktop")already wrote – you are only adding theenvblock. Edit the exact file the call reported (on Windows this may be the MSIX-virtualized path, not the one Desktop’s “Edit Config” button opens; see the Claude Desktop bullet under Configure a client above). - Replace
<you>and<your core count>with your values. -
ComSpecis the one that actually unblocksCompile/Link Failed; the gcc compatibility warning is a red herring, but setNLMEGCCDir64too so the Certara GCC 8.4.0 is used.INSTALLDIRmust point at your NLME-Engine install. -
TMP,NUMBER_OF_PROCESSORS, andPATHEXTare commonly dropped as well (TEMP/USERPROFILE/SystemRootusually survive); include them to be safe. The full default allowlist Claude Desktop passes on Windows isAPPDATA,HOMEDRIVE,HOMEPATH,LOCALAPPDATA,PATH,PROCESSOR_ARCHITECTURE,SYSTEMDRIVE,SYSTEMROOT,TEMP,USERNAME, andUSERPROFILE– anything else must be declared explicitly. -
PhoenixMSMPIDir(optional – for MPI parallelization). Locates the MS-MPI runtime that RsNLME execution hosts use whenhostParams(parallelMethod = "LOCAL_MPI")(the directory holdingmpiexec.exe/msmpi.dll). Unlike the variables above, its absence is silent, not an error: the run does not fail – the job log printsMPI not found on the system. Using localhost without parallelization.and proceeds serially. A single small fit barely notices, but SCM stepwise/shotgun covariate searches and bootstraps lose all scenario-level parallelism, so MCP-launched runs come out much slower than the identical run from RStudio/terminal. Add it (valueC:\Program Files\Certara\MPI\, trailing backslash) to restore parallel execution; a correctly-wired run instead logsUsing MPI host with N coresand[MPI plan] ... method=local_mpi. - Restart is required. Claude Desktop reads MCP config only at startup – fully quit it (system tray -> Quit, not just closing the window) and relaunch.
Diagnose it yourself. Run a throwaway MCP job that
prints what the child actually sees, then read stdout.log
in the returned run_dir (empty values mean the variables
are not reaching the fit children):
start_nlme_job(
expr = "cat('ComSpec=', Sys.getenv('ComSpec'), '\n');
cat('INSTALLDIR=', Sys.getenv('INSTALLDIR'), '\n');
cat('NLMEGCCDir64=', Sys.getenv('NLMEGCCDir64'), '\n');
cat('PhoenixMSMPIDir=', Sys.getenv('PhoenixMSMPIDir'), '\n')",
label = "env-check", project_dir = "<your project>"
)<run_dir>/artifacts/GEN*/Shared/compilelog.txt
shows the actual compile-stage error rather than the generic
Compile/Link Failed. For parallelization, the job’s stderr
distinguishes the two states directly:
MPI not found on the system. Using localhost without parallelization.
(serial) versus Using MPI host with N cores /
[MPI plan] ... method=local_mpi (parallel).
Claude Code (CLI) generally does not need this. A
terminal-launched MCP server inherits the shell’s full environment, so
ComSpec and the Certara toolchain variables are already
present. Caveats: a terminal only snapshots the environment when it
opens (open a new one after changing system variables), and IDE-embedded
terminals inherit from the IDE process (restart the IDE). If a launcher
ever sanitizes the environment, the same env-block fix
applies to .mcp.json / claude mcp add.
Tool profiles
The launch command bakes in a curated tool subset so the agent’s tool
list stays focused. Pass tool_profile to
write_mcp_config():
-
full(default) – every tool every provider offers. -
core– read/author/validate and data inspection only; no job launches. -
authoring–coreplus model comparison; still no job launches. -
execution–coreplus fit/search job launches and their results/qualification tools. -
diagnostics–executionplus comparison and interpretation.
find_certara_tools(),
certara_mcp_capabilities(), and
certara_session_status() are present in every profile.
full is the default and is a fine choice most of the
time – there is no correctness penalty to exposing every tool. Narrow
the profile when you want to:
- reduce tool overload – a shorter, task-shaped tool list is easier for the agent to reason over and lowers the chance it reaches for a wrong or heavier tool;
-
gate risky actions –
coreandauthoringexpose no job-launch tools (start_*), so the agent can read, author, and validate models but cannot kick off fits or searches on its own; and - trim the startup/latency footprint on constrained clients.
Pick the narrowest profile that still contains the tools your task
needs, and keep full when you want the complete surface
available.
btw_groups and live-session tools
btw_groups controls which general-purpose
btw R tool groups are exposed alongside the Certara tools.
The default "docs" provides read-only R documentation
lookup and is the leanest, lowest-latency option. Other useful groups
include "pkg" (package-development actions), and – for a
live R session bridged with btw::btw_mcp_session() –
"env" (inspect that session’s objects) and
"run" (execute R code there via
btw_tool_run_r).
# Expose docs + package-dev tools, plus a bridge to a live RStudio session.
Certara.R::write_mcp_config(
client = "cursor",
btw_groups = c("docs", "pkg", "env", "run"),
session_tools = TRUE
)
# Then, in the RStudio session you want the agent to reach:
btw::btw_mcp_session()session_tools = TRUE exposes the live-session bridge but
does not by itself connect a session – after writing the config, reload
the MCP server, run btw::btw_mcp_session() in the target
session, and have the agent attach to it.
Three R contexts are in play and are easy to confuse:
- the server process that answers control-plane tool calls,
- the bridged live session (
envinspection andrunexecution), and - the per-job child process launched by the
start_nlme_*tools.
certara_session_status() reports which of these are
active and how to attach a session.
When to enable it. Keep
session_tools = FALSE (the default) for the common case:
the server runs in its own clean R process, starts faster, and cannot
disturb your interactive workspace. Set
session_tools = TRUE when you want the agent to work
against your live RStudio session – to read objects you have
already loaded (a prepared dataset, a fitted model) or to run R code in
that session – instead of re-loading everything in the isolated server
process. Pair it with btw_groups "env"
(inspect objects) and/or "run" (execute code).
Your first MCP session
- Reload the client so it picks up the new configuration (in Cursor: Settings -> Tools & MCPs, then enable the Certara R server).
-
Call
certara_mcp_capabilities()first. It reports the concurrency policy, the Certara-first rule, the discovered tool/KB providers, the active tool profile, and the merged workflow phases (the canonical routing map). -
Mind Cursor’s routing key. The key in
.cursor/mcp.jsoniscertara-r, but Cursor’s tool-call API addresses it asuser-certara-r. The project-scope.cursor/rules/certara-mcp-usage.mdcrecords this, andcertara_mcp_capabilities()reports the mapping underclient_routing. -
Look up knowledge guidance-first. For “what should
I do” / regulatory / fit-for-purpose questions, call
guide_pharmacometrics(task, intended_use)before reaching for reference lookups (search_certara_kb(),lookup_pml_symbol()) orbtwdocumentation tools.
Approving tool calls
By default, coding assistants prompt you to approve
every tool call. Depending on your client and settings,
the agent may ask you to “Approve” the same Certara tools over and over
– which, for a server you trust and use constantly, quickly becomes a
full-time job of clicking Approve. The durable fix is to
allowlist the whole certara-r server so
its tools run without prompts.
write_mcp_config(tool_allowlist = TRUE) (the default)
already sets this up for the clients that support a config-file
allowlist:
-
Cursor – merges
certara-r:*intopermissions.json; takes effect once you enable Cursor Run Mode (Allowlist or Auto-review). -
Claude Code – merges
mcp__certara-rintopermissions$allowof the scope-appropriatesettings.json(~/.claude/settings.jsonfor user,<project>/.claude/settings.jsonfor project,<project>/.claude/settings.local.jsonfor local). -
Codex – writes per-tool approvals into
~/.codex/config.toml(most tools approved; privileged cleanup and arbitrary-R job tools stay prompt-gated).
To do it by hand for Claude Code, add the server-wide rule to
settings.json:
mcp__certara-r authorizes every tool
from the server. To approve a single tool instead, use the
fully-qualified form mcp__certara-r__<tool> (e.g.
mcp__certara-r__certara_mcp_capabilities). Note that Claude
Code does not support * wildcards for MCP
tools, so use the bare server rule mcp__certara-r for “all
tools” rather than mcp__certara-r__*. Reload the client
after editing.
Claude Desktop is the exception. It has no working
config-file allowlist – “Always allow” / “Allow for all tasks” does not
persist across sessions (upstream claude-code#24433,
#56954),
which is why write_mcp_config("claude-desktop") only prints
a note instead of writing one. If the Desktop Code tab
honors ~/.claude/settings.json, the
mcp__certara-r rule above silences the prompts; if it uses
the desktop app’s own agent path instead, you are limited to clicking
“Always allow” per session until Anthropic ships a persistent
setting.
Inventory, status, and removal
Two read-only console helpers distinguish what is configured from what is running:
# What Certara MCP entries are written to client config files right now?
Certara.R::list_certara_mcp_configs(client = "cursor", project_dir = tempdir())
#> client scope
#> 1 cursor project
#> 2 cursor user
#> 3 cursor local
#> path exists
#> 1 C:\\Users\\jcraig\\AppData\\Local\\Temp\\RtmpCUc9r6/.cursor/mcp.json FALSE
#> 2 C:\\Users\\jcraig/.cursor/mcp.json TRUE
#> 3 <NA> NA
#> configured server_key status
#> 1 FALSE <NA> not_configured
#> 2 TRUE certara-r configured
#> 3 FALSE <NA> unsupported
#> command server_name btw_groups
#> 1 <NA> <NA> <NA>
#> 2 C:\\Program Files\\R\\R-4.6.0\\bin\\x64\\Rscript.exe certara-r docs
#> 3 <NA> <NA> <NA>
#> session_tools job_watch_wait_seconds tool_profile
#> 1 NA NA <NA>
#> 2 FALSE 45 full
#> 3 NA NA <NA>
#> note
#> 1 <NA>
#> 2 <NA>
#> 3 Cursor has no 'local' scope.
# What Certara MCP server processes has this machine actually started?
Certara.R::list_certara_mcp_servers()These are ordinary R functions you call from the R console (or
RStudio), not MCP tools. A server that is configured but never listed as
running usually means the client has not been reloaded since
write_mcp_config(). certara_session_status()
is likewise callable from the console, but its fields read
NA unless it runs inside a live server (the launch
options it reports are recorded only at server startup) – in practice
the agent calls it as a tool to report the live wiring (server process,
bridged session, active profile).
Troubleshooting
MCP setup friction is mostly an environment problem, not
something another tool can fix. Start with
certara_mcp_capabilities() and
certara_session_status() to see what is actually wired,
then work through the common cases:
-
MCP tools missing / server not listed –
write_mcp_config()was not run, or the client was not reloaded. Re-run it, reload the client, and enable the server in the client’s tool settings. -
btw/mcptools/ellmernot found – arepos = NULLsource install skippedSuggests. Install from the release repo, orinstall.packages(c("btw", "mcptools", "ellmer"))(floors:btw >= 1.2.0,mcptools >= 0.2.0,ellmer >= 0.4.0). -
Engine / compile failures –
INSTALLDIR/ NLME not resolvable. Confirm the NLME Engine install and thatINSTALLDIRis set (see the RsNLME installation guide). -
Claude Desktop on Windows:
Compile/Link Failedeven though the same model fits from RStudio – Desktop sanitizes the server environment. AddComSpec,INSTALLDIR,NLMEGCCDir64,TMP,NUMBER_OF_PROCESSORS, andPATHEXTto theenvblock; see Claude Desktop on Windows: required environment variables. The same case is summarized in the RsNLME troubleshooting guide. -
Agent behavior feels “old” – the installed package
predates the current MCP knowledge base / rules. Install the current
release and re-run
write_mcp_config(client = "cursor", scope = "project")so the Cursor rule is refreshed too. -
Too many tools – normal when
btwgroups are enabled. Reason by task usingcertara_mcp_capabilities()$workflows; for the leanest server usebtw_groups = "docs"or a narrowertool_profile.
There is no automated “doctor” tool – this checklist plus the two status tools is the supported path.
Memory (optional)
The Certara MCP tools can use a local, opt-in memory
of your preferences, lessons, and past runs, stored on your machine. The
memory is consumed by the agent (its
get_user_preferences / get_lessons /
record_lesson tools read and write it so behavior carries
across sessions), but the functions below are for you, the
human, to control whether that happens and to audit what is
kept. They are ordinary R console functions; none are required to use
the MCP server.
Run them under these conditions:
-
enable_memory()– once, if you want the agent to remember preferences and lessons between sessions. Passredact =a vector of regex patterns to scrub sensitive identifiers (compound or project names) from stored free text. Memory stays off until you call this. -
disable_memory()– to turn it back off (existing records are kept but not used). -
list_memory_records()– to see what is stored (preferences,lessons,run_memory,gaps, and whether memory isenabled). -
export_memory()– to copy the records out for review or backup. -
delete_memory_record()/clear_memory()– to prune a single record or wipe everything.
If you never call enable_memory(), the agent simply
operates without memory – a perfectly normal way to run.