The MCP Run Python package is an MCP server that allows agents to execute Python code in a secure, sandboxed environment. It uses Pyodide to run Python code in a JavaScript environment with Deno, isolating execution from the host system.
Features
Secure Execution: Run Python code in a sandboxed WebAssembly environment
Package Management: Automatically detects and installs required dependencies
Complete Results: Captures standard output, standard error, and return values
Asynchronous Support: Runs async code properly
Error Handling: Provides detailed error reports for debugging
Installation
Switch from npx to deno
We previously distributed mcp-run-python as an npm package to use via npx.
We now recommend using deno instead as it provides better sandboxing and security.
The MCP Run Python server is distributed as a JSR package and can be run directly using deno run:
-N -R=node_modules -W=node_modules (alias of
--allow-net --allow-read=node_modules --allow-write=node_modules) allows
network access and read+write access to ./node_modules. These are required
so Pyodide can download and cache the Python standard library and packages
--node-modules-dir=auto tells deno to use a local node_modules directory
stdio runs the server with the
Stdio MCP transport
— suitable for running the process as a subprocess locally
sse runs the server with the
SSE MCP transport
— running the server as an HTTP server to connect locally or remotely
warmup will run a minimal Python script to download and cache the Python
standard library. This is also useful to check the server is running
correctly.
Usage of jsr:@pydantic/mcp-run-python with PydanticAI is described in the client documentation.
Direct Usage
As well as using this server with PydanticAI, it can be connected to other MCP clients. For clarity, in this example we connect directly using the Python MCP client.
If an exception occurs, status will be install-error or run-error and return_value will be replaced
by error which will include the traceback and exception message.
Dependencies
Dependencies are installed when code is run.
Dependencies can be defined in one of two ways:
Inferred from imports
If there's no metadata, dependencies are inferred from imports in the code,
as shown in the example above.
Inline script metadata
As introduced in PEP 723, explained here, and popularized by uv — dependencies can be defined in a comment at the top of the file.
This allows use of dependencies that aren't imported in the code, and is more explicit.
inline_script_metadata.py
frommcpimportClientSessionfrommcp.client.stdioimportstdio_client# using `server_params` from the above example.frommcp_run_pythonimportserver_paramscode="""\# /// script# dependencies = ["pydantic", "email-validator"]# ///import pydanticclass Model(pydantic.BaseModel): email: pydantic.EmailStrprint(Model(email='hello@pydantic.dev'))"""asyncdefmain():asyncwithstdio_client(server_params)as(read,write):asyncwithClientSession(read,write)assession:awaitsession.initialize()result=awaitsession.call_tool('run_python_code',{'python_code':code})print(result.content[0].text)""" <status>success</status> <dependencies>["pydantic","email-validator"]</dependencies> <output> email='hello@pydantic.dev' </output> """
It also allows versions to be pinned for non-binary packages (Pyodide only supports a single version for the binary packages it supports, like pydantic and numpy).
E.g. you could set the dependencies to
# /// script# dependencies = ["rich<13"]# ///
Logging
MCP Run Python supports emitting stdout and stderr from the python execution as MCP logging messages.
For logs to be emitted you must set the logging level when connecting to the server. By default, the log level is set to the highest level, emergency.