ros2_dev_mcp
# Phase 1 - Safe ROS 2 Project Creation
## Goal
Phase 1 establishes the development-focused `ros2_dev_mcp` server and provides controlled ROS 2 project creation through MCP.
The main goal is to allow an MCP-compatible client such as Codex to create ROS 2 development projects without giving the client unrestricted filesystem or shell access.
This phase also establishes the architectural separation between ROS 2 runtime operations and ROS 2 development operations.
Runtime operations belong to:
```text
ros2_mcp
```
Development operations belong to:
```text
ros2_dev_mcp
```
Generated and managed ROS 2 workspaces are stored separately below:
```text
~/projects/robotics/mcp_workspaces/
```
The result is a clear separation:
```text
~/projects/robotics/
├── ros2_mcp/ # ROS 2 runtime MCP server
├── ros2_dev_mcp/ # ROS 2 development MCP server
├── mcp_workspaces/ # MCP-managed ROS 2 workspaces
└── ...
```
---
## Why a Separate Development MCP Server?
ROS 2 runtime interaction and ROS 2 software development have different responsibilities and different safety requirements.
A runtime MCP server needs access to the running ROS graph.
Typical runtime operations include:
```text
list_nodes
list_topics
read_topic
list_services
list_parameters
get_parameter
```
A development MCP server needs controlled access to project files and development commands.
Typical development operations include:
```text
create_workspace
create_package
create_node
create_launch_file
create_parameter_file
create_tests
build_project
run_tests
```
Combining both responsibilities in one server would create a larger security boundary and a more complex architecture.
Therefore the projects are intentionally separated.
```text
MCP Client
|
+---------+---------+
| |
v v
ros2_dev_mcp ros2_mcp
| |
Development Runtime
| |
Workspace Nodes
Packages Topics
Nodes Services
Launch files Parameters
Parameter files Runtime state
Tests
Build
Test
```
This also makes future specialized MCP servers easier to add.
Examples:
```text
ros2_control_mcp
moveit2_mcp
nav2_mcp
```
---
# Phase 1 Responsibilities
Phase 1 focuses on ROS 2 project generation.
The project creation tools are:
```text
create_workspace
create_package
create_node
create_launch_file
create_parameter_file
create_tests
```
Controlled build and test execution are implemented by the same development server but documented separately in:
```text
docs/README_PHASE_2_BUILD_TEST.md
```
using:
```text
build_project
run_tests
```
---
# Architecture
The project creation path is:
```text
MCP Client
|
v
ros2_dev_mcp
|
v
MCP Project Tools
|
v
ProjectService
|
v
ProjectAdapter
|
v
FilesystemProjectAdapter
|
v
SafeFilesystem
|
v
Configured Managed Root
|
v
ROS 2 Workspace
```
The layers have separate responsibilities.
---
## MCP Layer
Location:
```text
src/ros2_dev_mcp/mcp/project_tools.py
```
Responsibilities:
- expose project operations as MCP tools
- validate MCP tool arguments
- obtain the application service from the MCP context
- delegate operations to `ProjectService`
- return structured results to the MCP client
The MCP layer does not directly manipulate project files.
---
## Application Layer
Location:
```text
src/ros2_dev_mcp/application/project/service.py
```
The `ProjectService` contains the development use cases.
Responsibilities include delegating:
```text
create_workspace
create_package
create_node
create_launch_file
create_parameter_file
create_tests
build_project
run_tests
```
to the appropriate adapters.
This keeps MCP-specific behavior separate from project implementation details.
---
## Project Adapter
Location:
```text
src/ros2_dev_mcp/project/adapter.py
```
The Project Adapter defines the interface required by the application layer.
The application service therefore does not need to know how project files are physically created.
The concrete filesystem implementation is located at:
```text
src/ros2_dev_mcp/project/filesystem/adapter.py
```
This design allows the implementation to evolve without tightly coupling the application layer to filesystem details.
---
# Filesystem Security Boundary
One of the most important requirements of `ros2_dev_mcp` is that Codex or another MCP client must not receive unrestricted filesystem access through the server.
All project operations are restricted to a configured root directory.
Current managed root:
```text
/home/sarvg/projects/robotics/mcp_workspaces
```
Equivalent user path:
```text
~/projects/robotics/mcp_workspaces
```
Configuration file:
```text
config/ros2_dev_mcp.toml
```
Current configuration:
```toml
[project]
allowed_root = "/home/sarvg/projects/robotics/mcp_workspaces"
[execution]
build_timeout_sec = 120.0
test_timeout_sec = 120.0
```
The managed root is intentionally outside the MCP server repositories themselves.
This means generated projects can be placed here:
```text
~/projects/robotics/mcp_workspaces/test_ws
~/projects/robotics/mcp_workspaces/pubsub_ws
~/projects/robotics/mcp_workspaces/my_robot_ws
```
while existing projects outside that directory remain outside the project filesystem boundary.
Examples include:
```text
~/projects/robotics/ros2_mcp
~/projects/robotics/ros2_dev_mcp
~/projects/robotics/openmanipulator
~/projects/robotics/turtlebot3
~/projects/robotics/universal_robots
~/projects/robotics/robotics_portfolio_1
```
The development MCP should not use these existing projects as writable project targets.
---
# SafeFilesystem
The filesystem boundary is implemented by:
```text
src/ros2_dev_mcp/project/filesystem/safe_filesystem.py
```
`SafeFilesystem` resolves requested project paths against the configured allowed root.
Its responsibilities include:
- resolving relative paths below the allowed root
- accepting absolute paths only when they remain below the allowed root
- rejecting parent-directory traversal outside the root
- resolving paths before validating them
- preventing symlink-based escapes from the allowed root
Conceptually:
```text
Requested Path
|
v
Resolve Path
|
v
Is Path Inside allowed_root?
|
+--+--+
| |
Yes No
| |
v v
Allow Reject
```
Examples of valid targets:
```text
test_ws
pubsub_ws
/home/sarvg/projects/robotics/mcp_workspaces/test_ws
```
Examples that must be rejected:
```text
../ros2_mcp
../openmanipulator
/tmp/test_ws
/home/sarvg/projects/robotics/ros2_mcp
/home/sarvg/projects/robotics/openmanipulator
```
This boundary was explicitly verified during development.
---
# Workspace Creation
The MCP tool:
```text
create_workspace
```
creates a ROS 2 workspace below the configured managed root.
Example MCP request conceptually:
```text
create_workspace(
workspace_path="test_ws"
)
```
Result:
```text
~/projects/robotics/mcp_workspaces/test_ws/
└── src/
```
The workspace follows the standard ROS 2 workspace layout:
```text
workspace/
└── src/
```
Build directories are not created during workspace creation.
They are generated later by `colcon build`.
Typical build result:
```text
workspace/
├── build/
├── install/
├── log/
└── src/
```
---
# Package Creation
The MCP tool:
```text
create_package
```
creates a ROS 2 Python package inside an existing managed workspace.
Example:
```text
Workspace:
pubsub_ws
Package:
demo_pubsub
```
Resulting structure:
```text
pubsub_ws/
└── src/
└── demo_pubsub/
├── demo_pubsub/
│ └── __init__.py
├── package.xml
├── resource/
│ └── demo_pubsub
├── setup.cfg
└── setup.py
```
The generated package uses the ROS 2 Python package structure based on:
```text
ament_python
```
The package metadata and Python package directory are created automatically.
---
# Node Creation
The MCP tool:
```text
create_node
```
creates a Python ROS 2 node inside an existing package.
Example:
```text
package:
demo_pubsub
node:
publisher_node
```
Generated file:
```text
pubsub_ws/
└── src/
└── demo_pubsub/
└── demo_pubsub/
└── publisher_node.py
```
Another node can be added independently:
```text
subscriber_node.py
```
Result:
```text
demo_pubsub/
└── demo_pubsub/
├── __init__.py
├── publisher_node.py
└── subscriber_node.py
```
---
# ROS 2 Executable Registration
Creating a Python file alone is not enough for:
```bash
ros2 run
```
Therefore generated nodes are registered as Python console scripts.
Conceptually:
```python
entry_points={
"console_scripts": [
"publisher_node = demo_pubsub.publisher_node:main",
"subscriber_node = demo_pubsub.subscriber_node:main",
],
}
```
After building and sourcing the workspace, ROS 2 can discover the executables.
Example verification:
```bash
cd ~/projects/robotics/mcp_workspaces/pubsub_ws
source /opt/ros/jazzy/setup.bash
source install/setup.bash
ros2 pkg executables demo_pubsub
```
A successfully generated package can expose:
```text
demo_pubsub publisher_node
demo_pubsub subscriber_node
```
The nodes can then be started with:
```bash
ros2 run demo_pubsub publisher_node
```
and:
```bash
ros2 run demo_pubsub subscriber_node
```
---
# setup.cfg
Generated Python ROS 2 packages include:
```text
setup.cfg
```
with ROS 2 executable installation paths.
Conceptually:
```ini
[develop]
script_dir=$base/lib/demo_pubsub
[install]
install_scripts=$base/lib/demo_pubsub
```
This is required so installed Python executables are placed where ROS 2 expects them.
---
# Launch File Creation
The MCP tool:
```text
create_launch_file
```
creates a Python ROS 2 launch file.
Typical result:
```text
demo_pubsub/
└── launch/
└── demo.launch.py
```
The launch file can reference one or more nodes from the package.
This allows a higher-level development request such as:
```text
Create a publisher and subscriber and create a launch file
that starts both nodes.
```
The long-term goal is that Codex can express the intent while `ros2_dev_mcp` performs the controlled project operations.
---
# Parameter File Creation
The MCP tool:
```text
create_parameter_file
```
creates a ROS 2 YAML parameter file.
Typical structure:
```text
demo_pubsub/
└── config/
└── demo_params.yaml
```
A minimal ROS 2 parameter structure can look like:
```yaml
publisher_node:
ros__parameters: {}
```
Parameter values can later be expanded according to the project requirements.
Configuration files belong to the generated ROS 2 package and remain inside the managed workspace boundary.
---
# Test Creation
The MCP tool:
```text
create_tests
```
creates basic tests for a generated Python ROS 2 package.
Typical structure:
```text
demo_pubsub/
└── test/
└── test_package_import.py
```
The initial test verifies that the generated Python package can be imported correctly.
Additional ROS 2-specific tests can be added in later development phases.
---
# Complete Generated Project Structure
A generated project can therefore look like:
```text
pubsub_ws/
└── src/
└── demo_pubsub/
├── config/
│ └── demo_params.yaml
├── demo_pubsub/
│ ├── __init__.py
│ ├── publisher_node.py
│ └── subscriber_node.py
├── launch/
│ └── demo.launch.py
├── package.xml
├── resource/
│ └── demo_pubsub
├── setup.cfg
├── setup.py
└── test/
└── test_package_import.py
```
After building, the workspace can contain:
```text
pubsub_ws/
├── build/
├── install/
├── log/
└── src/
└── demo_pubsub/
└── ...
```
---
# Configuration
Application configuration is loaded from:
```text
config/ros2_dev_mcp.toml
```
Configuration loading is implemented in:
```text
src/ros2_dev_mcp/config/settings.py
```
Current configurable values include:
```text
project.allowed_root
execution.build_timeout_sec
execution.test_timeout_sec
```
These values are intentionally stored outside the application logic.
This avoids hard-coding environment-specific configuration throughout the source code.
---
# Current Source Structure
The development server is organized as:
```text
src/ros2_dev_mcp/
├── application/
│ ├── __init__.py
│ └── project/
│ ├── __init__.py
│ └── service.py
├── config/
│ ├── __init__.py
│ └── settings.py
├── __init__.py
├── mcp/
│ ├── __init__.py
│ └── project_tools.py
├── project/
│ ├── adapter.py
│ ├── execution/
│ │ ├── adapter.py
│ │ ├── __init__.py
│ │ ├── policy.py
│ │ └── subprocess_adapter.py
│ ├── filesystem/
│ │ ├── adapter.py
│ │ ├── __init__.py
│ │ └── safe_filesystem.py
│ └── __init__.py
└── server.py
```
The `execution` layer belongs primarily to the controlled build and test functionality documented in Phase 2.
---
# Development Environment
The current development environment is based on:
```text
Ubuntu 24.04
ROS 2 Jazzy
Python 3.12
uv
MCP Python SDK
colcon
```
---
# Installation
Create or synchronize the Python environment:
```bash
cd ~/projects/robotics/ros2_dev_mcp
uv sync
source .venv/bin/activate
```
Verify Python:
```bash
python --version
```
The project currently targets:
```text
Python >=3.12,<3.13
```
---
# Start ros2_dev_mcp Directly
The server can be started directly for development or debugging.
```bash
cd ~/projects/robotics/ros2_dev_mcp
source .venv/bin/activate
python -m ros2_dev_mcp.server
```
The server uses MCP standard I/O transport.
When Codex starts the configured MCP server, starting it manually is normally unnecessary.
---
# Codex Integration
`ros2_dev_mcp` can be registered as an MCP server in Codex.
Register it with:
```bash
cd ~/projects/robotics/ros2_dev_mcp
source .venv/bin/activate
codex mcp add ros2_dev_mcp \
-- \
bash -lc 'cd /home/sarvg/projects/robotics/ros2_dev_mcp && source .venv/bin/activate && exec python -m ros2_dev_mcp.server'
```
Check the registration:
```bash
codex mcp get ros2_dev_mcp
```
List all configured MCP servers:
```bash
codex mcp list
```
Start Codex:
```bash
cd ~/projects/robotics/ros2_dev_mcp
source .venv/bin/activate
codex
```
Inside Codex, inspect available MCP tools with:
```text
/mcp
```
The expected `ros2_dev_mcp` tools are:
```text
build_project
create_launch_file
create_node
create_package
create_parameter_file
create_tests
create_workspace
run_tests
```
---
# Real Codex Verification
The separated development MCP server was tested with Codex.
The request was:
```text
Use only the ros2_dev_mcp MCP server.
Create a new ROS 2 workspace named split_test_ws.
Do not use shell commands.
Do not use direct filesystem operations.
Do not use ros2_mcp.
Do not modify any existing project.
```
Codex invoked:
```text
ros2_dev_mcp.create_workspace
```
The resulting workspace was:
```text
/home/sarvg/projects/robotics/mcp_workspaces/split_test_ws
└── src/
```
This verifies the complete path:
```text
User
|
v
Codex
|
v
ros2_dev_mcp
|
v
Project MCP Tool
|
v
ProjectService
|
v
FilesystemProjectAdapter
|
v
SafeFilesystem
|
v
mcp_workspaces
```
No existing ROS 2 project needed to be modified.
---
# Publisher / Subscriber Development Example
A more complete development workflow was also tested using a publisher/subscriber workspace.
The development target was:
```text
pubsub_ws
```
with package:
```text
demo_pubsub
```
and nodes:
```text
publisher_node
subscriber_node
```
The intended project structure is:
```text
mcp_workspaces/
└── pubsub_ws/
└── src/
└── demo_pubsub/
├── demo_pubsub/
│ ├── __init__.py
│ ├── publisher_node.py
│ └── subscriber_node.py
├── package.xml
├── resource/
│ └── demo_pubsub
├── setup.cfg
└── setup.py
```
The workspace was successfully built using the development MCP.
After sourcing the built workspace, ROS 2 reported the generated executables:
```text
demo_pubsub publisher_node
demo_pubsub subscriber_node
```
The nodes were started and discovered by ROS 2.
Example node list:
```text
/publisher_node
/ros2_mcp_runtime
/subscriber_node
```
This demonstrated that generated project artifacts could progress from MCP project creation to an actual ROS 2 workspace build and runtime discovery.
---
# Example Codex Project Creation Request
A project creation request can be expressed at a high level.
Example:
```text
Use only the ros2_dev_mcp MCP server.
Create a new ROS 2 workspace named pubsub_ws.
Inside it, create a Python package named demo_pubsub.
Create two nodes:
- publisher_node
- subscriber_node
Create a launch file for both nodes.
Create basic tests.
Do not use shell commands.
Do not use direct filesystem operations.
Do not use ros2_mcp.
Do not modify any existing project.
```
The intended MCP workflow is:
```text
Codex
|
v
create_workspace
|
v
create_package
|
+------------------+
| |
v v
create_node create_node
publisher subscriber
| |
+--------+---------+
|
v
create_launch_file
|
v
create_tests
```
Build and test operations then continue through Phase 2.
---
# Desired Higher-Level Workflow
The long-term goal is not to require the user to know every individual MCP tool.
Instead, a user should eventually be able to request:
```text
Create a ROS 2 publisher/subscriber example with a launch file,
build it, test it, and report the result.
```
Codex can then plan the workflow and invoke the required MCP tools:
```text
User Intent
|
v
Codex
|
+--> create_workspace
|
+--> create_package
|
+--> create_node
|
+--> create_node
|
+--> create_launch_file
|
+--> create_tests
|
+--> build_project
|
+--> run_tests
|
v
Result
```
The MCP server provides controlled capabilities.
The MCP client performs the higher-level orchestration.
---
# Important Security Principle
`ros2_dev_mcp` is not intended to expose a generic shell.
For example, an MCP client should not receive a tool such as:
```text
execute_arbitrary_shell_command
```
Instead, specific development operations are exposed:
```text
create_workspace
create_package
create_node
create_launch_file
create_parameter_file
create_tests
build_project
run_tests
```
This creates a much smaller and more understandable security boundary.
---
# Existing Project Protection
The managed workspace boundary is particularly important because the robotics directory contains existing projects.
For example:
```text
~/projects/robotics/
├── mcp_workspaces/
├── oakd/
├── openmanipulator/
├── robotics_portfolio_1/
├── ros2_dev_mcp/
├── ros2_mcp/
├── turtlebot3/
├── universal_robots/
└── zed2/
```
`ros2_dev_mcp` project operations are restricted to:
```text
mcp_workspaces/
```
The MCP development tools therefore have a dedicated area for generated projects instead of operating across the entire robotics directory.
---
# MCP Client Independence
The server uses the Model Context Protocol rather than Codex-specific APIs.
The architecture is therefore:
```text
MCP-compatible Client
|
v
ros2_dev_mcp
```
Codex is currently used as the primary development client.
The design should not intentionally depend on Codex-specific behavior inside the MCP server implementation.
Other MCP-compatible clients can potentially use the same server if they support the required MCP transport and tools.
---
# Independent Implementation
`ros2_dev_mcp` is developed as an independent implementation.
Other ROS 2 MCP projects can be studied for:
- feature comparison
- architectural ideas
- identifying useful ROS 2 operations
- understanding MCP use cases
- comparing safety approaches
Their source code is not used as a copy-and-paste implementation basis.
The project should evolve according to its own architecture and requirements.
---
# Relationship to ros2_mcp
The generic ROS MCP architecture is intentionally divided into two primary servers.
## ros2_mcp
Responsibility:
```text
ROS 2 Runtime
```
Examples:
```text
list_nodes
list_topics
topic_info
read_topic
list_services
service_info
node_info
list_parameters
get_parameter
```
Future runtime capabilities may include controlled:
```text
publish_topic
call_service
set_parameter
ROS 2 actions
process monitoring
launch management
logs
diagnostics
```
## ros2_dev_mcp
Responsibility:
```text
ROS 2 Development
```
Current capabilities:
```text
create_workspace
create_package
create_node
create_launch_file
create_parameter_file
create_tests
build_project
run_tests
```
This separation keeps runtime control and source/project manipulation independent.
---
# Future Specialized MCP Servers
The long-term architecture can grow through specialized MCP servers.
```text
MCP Clients
|
+-------------------- ros2_mcp
| Generic ROS 2 runtime
|
+-------------------- ros2_dev_mcp
| ROS 2 development
|
+-------------------- ros2_control_mcp
| ros2_control
|
+-------------------- moveit2_mcp
| MoveIt 2
|
+-------------------- nav2_mcp
Nav2
```
This prevents the generic ROS MCP server from becoming a monolithic implementation containing every ROS 2 subsystem.
---
# Completed Phase 1 Capabilities
The current development foundation provides:
```text
Separate ros2_dev_mcp server ✅
Runtime / development separation ✅
Configurable managed root ✅
Safe filesystem boundary ✅
Parent traversal protection ✅
Symlink escape protection ✅
Workspace creation ✅
Python package creation ✅
Python node creation ✅
ROS executable registration ✅
Launch file creation ✅
Parameter file creation ✅
Basic test creation ✅
Codex MCP integration ✅
Real Codex workspace creation ✅
Existing project isolation ✅
```
---
# Phase 1 Design Rules
Phase 1 establishes the following rules:
1. ROS 2 development and runtime operations remain separated.
2. Project writes are restricted to a configured managed root.
3. Existing ROS 2 projects outside that root are not development targets.
4. Project paths are validated before filesystem operations.
5. Symlink and parent-path escapes must be rejected.
6. MCP tools expose specific development capabilities instead of unrestricted filesystem access.
7. The MCP layer does not directly implement filesystem behavior.
8. Application logic is separated from concrete adapters.
9. Configuration values are loaded from configuration files.
10. MCP clients should remain replaceable.
11. The implementation remains independent from other ROS MCP projects.
12. Generated ROS 2 projects should follow normal ROS 2 conventions.
13. Development features should remain understandable and testable individually.
14. Specialized ROS 2 subsystems should later receive separate MCP servers where appropriate.
---
# Next Phase
Phase 2 focuses on controlled project execution.
The primary tools are:
```text
build_project
run_tests
```
The execution path is:
```text
MCP Client
|
v
Project MCP Tools
|
v
ProjectService
|
v
ExecutionAdapter
|
v
SubprocessExecutionAdapter
|
+--> CommandPolicy
|
+--> SafeFilesystem
|
v
Controlled Process
|
+--> colcon build
|
+--> colcon test
```
Phase 2 adds:
- explicit command policy
- controlled `colcon build`
- controlled `colcon test`
- package selection
- working-directory validation
- build timeout
- test timeout
- structured execution results
See:
```text
docs/README_PHASE_2_BUILD_TEST.md
```
for the build and test execution architecture.
TDQS
Scored across 8 tools
Each tool targets a distinct artifact or action in the ROS 2 development lifecycle: workspace, package, node, launch file, parameter file, tests, building, and running tests. There is no overlap or ambiguity between them.
All tool names follow a consistent verb_noun pattern: create_* for scaffolding actions and build_project/run_tests for build/test actions. No mixed conventions or vague verbs.
8 tools is well-scoped for a ROS 2 development server, covering the essential create, build, and test operations without unnecessary redundancy. The count feels proportionate to the server's purpose.
The server covers the core Python ROS 2 scaffolding workflow: workspace, package, node, launch, parameters, tests, build, and test. Missing operations like C++ package support or custom interface generation, but the stated Python-focused scope makes these minor gaps.