Python Server (Remote Code Execution)#
Overview#
The isaacsim.code_editor.python_server extension provides a TCP socket server that enables remote Python code execution within a running Isaac Sim instance.
Any client — VS Code, LLM agents, custom automation scripts — can connect over TCP, send Python source code, and receive structured JSON results.
The extension is automatically loaded as a dependency of isaacsim.code_editor.vscode, but it can also be enabled independently for headless or programmatic workflows. Its optional UI is provided by isaacsim.code_editor.python_server.ui.
Enabling the Extension#
Enable the extension using the Extension Manager by searching for isaacsim.code_editor.python_server.
By default the server listens on 127.0.0.1:8226.
These values can be changed through the Carbonite settings (see Settings below).
Status Indicator and Runtime Controls#
Enable isaacsim.code_editor.python_server.ui in a headed application to add a PY Server indicator at the right side of the application menu beside the LIVE
and CACHE widgets. Its colored status dot and tooltip report the listener state; the tooltip and modal show the actual
bound endpoint. Click the indicator to open a modal with the endpoint, connection count, and security state.
The modal can stop and start the listener for the current session without disabling the extension. It can also apply a new IPv4 host and port using a transactional restart: if the new endpoint cannot bind, the previous working listener is kept or restored. Authentication status is visible but read-only.
The UI requires confirmation for non-loopback changes and before interrupting active connections. Temporary stopped state is not persisted. The core server has no UI dependency and can run independently in headless applications.
Other extensions can read the same immutable status snapshot with
isaacsim.code_editor.python_server.get_server_status().
Wire Protocol#
The wire protocol is intentionally simple so that any TCP client can use it:
Request
Send raw UTF-8 Python source code over a TCP connection to the configured host and port.
After sending all code, the client must signal end-of-input by performing a TCP half-close
(write_eof() in Python, shutdown(SHUT_WR) at the socket level, or -q 0 with netcat).
The server buffers incoming data until EOF is received, ensuring that TCP-fragmented payloads
are fully reassembled before execution.
Warning
If the client does not signal EOF, the server will wait indefinitely for more data and the connection will hang until the client disconnects or a timeout occurs.
Response
A single JSON object is returned, then the connection is closed by the server.
Field |
Description |
|---|---|
|
|
|
Captured standard output ( |
|
(present only for expression evaluation) The evaluated expression value.
JSON-native types ( |
|
(present only on error) List of traceback strings. |
|
(present only on error) Exception class name. |
|
(present only on error) Exception message string. |
Usage Examples#
Python Client#
Connect from any Python script or LLM tool to execute code in the running Isaac Sim instance:
import asyncio
import json
async def execute_in_isaac(source: str, host: str = "127.0.0.1", port: int = 8226) -> dict:
"""Send Python source to a running Isaac Sim instance and return the result."""
reader, writer = await asyncio.open_connection(host, port)
writer.write(source.encode())
writer.write_eof()
data = await reader.read()
writer.close()
await writer.wait_closed()
return json.loads(data.decode())
def main() -> None:
# Execute a statement
result = asyncio.run(execute_in_isaac('print("Hello from Isaac Sim!")'))
print(result)
# {'status': 'ok', 'output': 'Hello from Isaac Sim!'}
# Evaluate an expression
result = asyncio.run(execute_in_isaac("1 + 1"))
print(result)
# {'status': 'ok', 'output': '', 'result': 2}
# Handle errors
result = asyncio.run(execute_in_isaac("1 / 0"))
print(result["status"]) # 'error'
print(result["ename"]) # 'ZeroDivisionError'
if __name__ == "__main__":
main()
Command-line (netcat)#
For quick testing, use netcat or similar tools:
echo 'print("Hello")' | nc 127.0.0.1 8226
Async Code Support#
The server supports top-level await expressions.
When submitted code contains await, the server compiles it as an async coroutine,
schedules it on the Kit event loop, and awaits the result before sending the JSON response.
import asyncio
async def run_async_example() -> None:
"""Example body that can be sent to the Python server with top-level await."""
await asyncio.sleep(0.1)
print("this output is captured")
if __name__ == "__main__":
asyncio.run(run_async_example())
Standard output from print() calls inside awaited coroutines is captured and included in the
JSON response output field, just like synchronous code.
State Persistence#
The server maintains a shared Python globals dictionary across all connections within a session. Variables, imports, and function definitions from one request are available in subsequent requests. This enables incremental workflows such as building a scene step by step:
import asyncio
async def first_request() -> None:
"""Create a stage and leave ``stage_utils`` in the shared request context."""
global stage_utils
import isaacsim.core.experimental.utils.stage as stage_utils
await stage_utils.create_new_stage_async(template="empty")
def second_request() -> None:
"""Reuse the import retained from the first request."""
stage_utils.define_prim("/World", "Xform")
async def main() -> None:
await first_request()
second_request()
if __name__ == "__main__":
asyncio.run(main())
Each new TCP connection reuses the same globals, so there is no need to re-import modules or re-define variables between calls.
LLM Integration#
The Python server is designed to be easy for LLM agents to use. An LLM tool implementation needs only to:
Open a TCP connection to the configured host and port.
Send the Python code as UTF-8 bytes.
Signal end-of-input by calling
write_eof()(required — the server buffers until EOF).Read the JSON response.
Parse
statusto determine success or failure,outputfor printed text, andresultfor expression values.
Because the protocol is a single request/response per connection, there is no connection-level state to manage. However, Python-level state (variables, imports) persists across connections within a session (see State Persistence above).
Settings#
The extension is configured through Carbonite settings under /exts/isaacsim.code_editor.python_server/.
Setting |
Default |
Description |
|---|---|---|
|
|
IP address the server listens on. Set to |
|
|
TCP port number. |
|
|
Require a bearer token before executing requests. |
|
|
Token accepted when authentication is required. An empty token is generated at startup. |
|
|
Enable UDP broadcasting of Carbonite log messages to connected clients. May cause the application to freeze in certain circumstances. |
Warning
Setting host to "0.0.0.0" allows any machine on the network to execute arbitrary Python code
in your Isaac Sim session. Only do this in trusted network environments.
Carbonite Log Broadcasting (UDP)#
When carb_logs is enabled, the extension opens a UDP socket on the same host and port.
Clients register by sending any datagram to that address, after which all Carbonite log messages
(Info, Warning, Error, Fatal) are broadcast to registered clients as UTF-8 strings in the format:
[Level][Source] Message
This is primarily used by the Isaac Sim VS Code Edition extension to display Isaac Sim logs in the VS Code output panel.