sequenceDiagram
participant AI as AI Agent
participant Server as MCPR Server
participant Session as R Session
Note over Session: User runs mcpr_session_start()
Session->>Session: Creates mcprSession instance & finds an open port
Session->>Session: Starts an asynchronous listener on a nanonext socket
Note over AI: User asks agent to connect to R
AI->>Server: Launches MCPR Server process
Server->>Server: Initializes private/local active session
AI->>Server: Sends 'manage_r_sessions("list")'
Server->>Server: Scans local ports for attachable sessions
Server->>Session: Sends discovery ping via socket
Session->>Server: Responds with session metadata (ID, PID, etc.)
Server->>AI: Forwards available session list (result of 'manage_r_sessions("list")')
Note over AI: User asks agent to join a specific session
AI->>Server: Sends 'manage_r_sessions("join", session=ID)'
Server->>Session: Establishes persistent connection for attached execution
Session->>Server: Acknowledges connection
Server->>AI: Confirms the attached session is now active
The MCPR framework is built on a robust client-server architecture designed to bridge the gap between stateless AI agents and the stateful, interactive nature of R programming. This article delves into the technical design, communication flow, and core principles that enable this integration.
At its heart, MCPR implements a client-server model with a private R runtime and optional session attachment, distinguishing it from typical stateless execution environments.
┌─────────────┐ JSON-RPC 2.0 ┌────────────────────────────┐
│ AI Agent │<──────────────────>│ MCPR Server + Private R │
│ (Client) │ via stdin/ │ Session │
└─────────────┘ stdout └─────────────┬──────────────┘
│ optional socket attach
▼
┌────────────────────────┐
│ Human/Secondary R │
│ Session │
└────────────────────────┘
This architecture consists of three primary components:
-
The AI Agent (Client): This is the large language model operating within its environment (e.g., Claude Desktop, a VS Code extension). It communicates using a standardized JSON-RPC 2.0 protocol over
stdin/stdout. -
The MCPR Server + Private R Session: The process running
MCPR::mcpr_server(). It handles JSON-RPC and is also the default persistent R workspace where ordinary tools execute. -
Optional Attached R Sessions: A human-owned interactive R session started with
mcpr_session_start(), or an MCPR-owned secondary process created withmanage_r_sessions("start"). These sessions are used only after explicit attachment.
Communication Workflow
The default workflow is direct: the MCP client launches MCPR::mcpr_server(), and ordinary tools execute in that private process. Optional attachment adds discovery and connection steps only when the user wants the agent to work inside another R session.
Optional Session Discovery and Connection
Before an agent can execute code in a human-owned session, it must discover and attach to that session. This process is separate from ordinary tool calls; ordinary tools do not take a session argument.
Tool Execution
The agent can use provided tools (execute_r_code, show_plot, etc.) immediately in the private session. If manage_r_sessions() has attached another session, the runtime sends ordinary tools to that active session until detach returns execution to private/local.
sequenceDiagram
participant AI as AI Agent
participant Server as MCPR Server
participant Session as R Session
participant Tool as Tool Function
AI->>Server: JSON-RPC Request: {tool: "execute_r_code", args: {code: "..."}, ...}
Server->>Server: Validates tool exists
Server->>Server: Resolves active session (private by default)
Server->>Tool: Executes locally, or forwards only when attached
Tool->>Server: Returns a standard R object (list, data.frame, etc.)
Server->>Server: Serializes the R result into a type-preserving JSON structure
Server->>AI: Forwards the final JSON response via stdout
Key Design Principles
The architecture is guided by several modern R development practices to ensure it is robust, maintainable, and extensible.
R6 Class-Based System
MCPR heavily utilizes the R6 class system to manage state and behavior.
-
BaseMCPR: A foundational class providing shared utilities like logging, state management, and resource cleanup. -
mcprServer&mcprClient: Concrete implementations for the server/runtime and client roles, inheriting common functionality fromBaseMCPR. -
mcprSessionManager: Owns the server-local active session binding, including private/local execution and optional attachment state. -
mcprSession: Manages the lifecycle of a single R session listener, including handling timeouts and ensuring proper resource cleanup on exit.
Layered Communication
The communication stack is divided into distinct layers, each with a specific responsibility:
- Protocol Layer: Handles the JSON-RPC 2.0 message structure, including requests, responses, and error notifications.
-
Transport Layer: Manages asynchronous, non-blocking communication using the
nanonextpackage, which provides a robust socket-based messaging implementation. - Execution Layer: Dispatches requests to tool functions in the private session by default, or to the active attached session when session management has explicitly attached one.
- Type Conversion Layer: A critical component that handles the serialization (R to JSON) and deserialization (JSON to R) of data, preserving R’s specific data types across the wire.
Advanced Type Preservation
A significant challenge in cross-language communication is preserving data fidelity. A data.frame in R is not the same as a generic JSON array of objects. MCPR solves this by using a custom serialization system.
When an R object is converted to JSON, a special _mcp_type marker is added to annotate the data. When the JSON is received, this marker is used to reconstruct the original R object with its correct class and attributes (e.g., factor, Date, data.frame). This ensures that code executed by the agent behaves exactly as if it were run directly in the R console.
sequenceDiagram
participant R_Side as R Environment
participant JSON_Transport as JSON Payload
participant Target_Side as Receiving Environment
Note over R_Side: Has a data.frame object
R_Side->>R_Side: to_mcpr_json() serializes it
R_Side->>JSON_Transport: Sends JSON with {"_mcp_type": "data.frame", "value": [...] }
JSON_Transport->>Target_Side: Receives JSON with type metadata
Target_Side->>Target_Side: from_mcpr_json() uses "_mcp_type" to reconstruct the data.frame
Note over Target_Side: Now has a native data.frame object
This bidirectional, type-preserving communication is fundamental to MCPR’s ability to maintain the integrity of the R workspace throughout a long and complex human-AI collaborative session.
