mcp-python-repl 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,6 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ dist/
5
+ *.egg-info/
6
+ .ruff_cache/
@@ -0,0 +1,10 @@
1
+ # Default ignored files
2
+ /shelf/
3
+ /workspace.xml
4
+ # Ignored default folder with query files
5
+ /queries/
6
+ # Datasource local storage ignored files
7
+ /dataSources/
8
+ /dataSources.local.xml
9
+ # Editor-based HTTP Client requests
10
+ /httpRequests/
@@ -0,0 +1,6 @@
1
+ <component name="InspectionProjectProfileManager">
2
+ <settings>
3
+ <option name="USE_PROJECT_PROFILE" value="false" />
4
+ <version value="1.0" />
5
+ </settings>
6
+ </component>
@@ -0,0 +1,19 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="PYTHON_MODULE" version="4">
3
+ <component name="NewModuleRootManager">
4
+ <content url="file://$MODULE_DIR$">
5
+ <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
6
+ <sourceFolder url="file://$MODULE_DIR$/src/mcp_python_repl" isTestSource="false" />
7
+ <excludeFolder url="file://$MODULE_DIR$/.venv" />
8
+ </content>
9
+ <orderEntry type="jdk" jdkName="Python 3.10 (mcp-python-repl)" jdkType="Python SDK" />
10
+ <orderEntry type="sourceFolder" forTests="false" />
11
+ </component>
12
+ <component name="PyDocumentationSettings">
13
+ <option name="format" value="PLAIN" />
14
+ <option name="myDocStringFormat" value="Plain" />
15
+ </component>
16
+ <component name="TestRunnerService">
17
+ <option name="PROJECT_TEST_RUNNER" value="py.test" />
18
+ </component>
19
+ </module>
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="Black">
4
+ <option name="sdkName" value="Python 3.13" />
5
+ </component>
6
+ <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (mcp-python-repl)" project-jdk-type="Python SDK" />
7
+ </project>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/mcp-python-repl.iml" filepath="$PROJECT_DIR$/.idea/mcp-python-repl.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="$PROJECT_DIR$" vcs="Git" />
5
+ </component>
6
+ </project>
@@ -0,0 +1,106 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="AutoImportSettings">
4
+ <option name="autoReloadType" value="SELECTIVE" />
5
+ </component>
6
+ <component name="ChangeListManager">
7
+ <list default="true" id="e740e8cb-c5fc-42c9-902f-eeafbc67b973" name="Changes" comment="" />
8
+ <option name="SHOW_DIALOG" value="false" />
9
+ <option name="HIGHLIGHT_CONFLICTS" value="true" />
10
+ <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
11
+ <option name="LAST_RESOLUTION" value="IGNORE" />
12
+ </component>
13
+ <component name="FileTemplateManagerImpl">
14
+ <option name="RECENT_TEMPLATES">
15
+ <list>
16
+ <option value="Python Script" />
17
+ </list>
18
+ </option>
19
+ </component>
20
+ <component name="Git.Settings">
21
+ <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
22
+ </component>
23
+ <component name="ProjectColorInfo">{
24
+ &quot;associatedIndex&quot;: 3
25
+ }</component>
26
+ <component name="ProjectId" id="39Iqh21oBUOTYzdMa6gzov8igkx" />
27
+ <component name="ProjectViewState">
28
+ <option name="hideEmptyMiddlePackages" value="true" />
29
+ <option name="showLibraryContents" value="true" />
30
+ </component>
31
+ <component name="PropertiesComponent"><![CDATA[{
32
+ "keyToString": {
33
+ "ModuleVcsDetector.initialDetectionPerformed": "true",
34
+ "Python.mcp-python-repl.executor": "Run",
35
+ "RunOnceActivity.ShowReadmeOnStart": "true",
36
+ "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
37
+ "git-widget-placeholder": "main",
38
+ "last_opened_file_path": "/Users/soufiane/Documents/private_project/mcp-python-repl"
39
+ }
40
+ }]]></component>
41
+ <component name="RecentsManager">
42
+ <key name="CopyFile.RECENT_KEYS">
43
+ <recent name="$PROJECT_DIR$/src" />
44
+ <recent name="$PROJECT_DIR$/tests" />
45
+ <recent name="$PROJECT_DIR$" />
46
+ <recent name="pyproject.toml README.md" />
47
+ </key>
48
+ </component>
49
+ <component name="RunManager">
50
+ <configuration name="mcp-python-repl" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
51
+ <module name="mcp-python-repl" />
52
+ <option name="ENV_FILES" value="" />
53
+ <option name="INTERPRETER_OPTIONS" value="" />
54
+ <option name="PARENT_ENVS" value="true" />
55
+ <envs>
56
+ <env name="PYTHONUNBUFFERED" value="1" />
57
+ </envs>
58
+ <option name="SDK_HOME" value="" />
59
+ <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/src" />
60
+ <option name="IS_MODULE_SDK" value="true" />
61
+ <option name="ADD_CONTENT_ROOTS" value="true" />
62
+ <option name="ADD_SOURCE_ROOTS" value="true" />
63
+ <EXTENSION ID="net.ashald.envfile">
64
+ <option name="IS_ENABLED" value="false" />
65
+ <option name="IS_SUBST" value="false" />
66
+ <option name="IS_PATH_MACRO_SUPPORTED" value="false" />
67
+ <option name="IS_IGNORE_MISSING_FILES" value="false" />
68
+ <option name="IS_ENABLE_EXPERIMENTAL_INTEGRATIONS" value="false" />
69
+ <ENTRIES>
70
+ <ENTRY IS_ENABLED="true" PARSER="runconfig" IS_EXECUTABLE="false" />
71
+ </ENTRIES>
72
+ </EXTENSION>
73
+ <option name="RUN_TOOL" value="" />
74
+ <option name="SCRIPT_NAME" value="$PROJECT_DIR$/.venv/bin/mcp-python-repl" />
75
+ <option name="PARAMETERS" value="" />
76
+ <option name="SHOW_COMMAND_LINE" value="false" />
77
+ <option name="EMULATE_TERMINAL" value="false" />
78
+ <option name="MODULE_MODE" value="false" />
79
+ <option name="REDIRECT_INPUT" value="false" />
80
+ <option name="INPUT_FILE" value="" />
81
+ <method v="2" />
82
+ </configuration>
83
+ <recent_temporary>
84
+ <list>
85
+ <item itemvalue="Python.mcp-python-repl" />
86
+ </list>
87
+ </recent_temporary>
88
+ </component>
89
+ <component name="SharedIndexes">
90
+ <attachedChunks>
91
+ <set>
92
+ <option value="bundled-python-sdk-0978a7220b69-9867c7d4c0a4-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-253.28294.336" />
93
+ </set>
94
+ </attachedChunks>
95
+ </component>
96
+ <component name="TaskManager">
97
+ <task active="true" id="Default" summary="Default task">
98
+ <changelist id="e740e8cb-c5fc-42c9-902f-eeafbc67b973" name="Changes" comment="" />
99
+ <created>1770394047623</created>
100
+ <option name="number" value="Default" />
101
+ <option name="presentableId" value="Default" />
102
+ <updated>1770394047623</updated>
103
+ </task>
104
+ <servers />
105
+ </component>
106
+ </project>
@@ -0,0 +1,187 @@
1
+ Metadata-Version: 2.4
2
+ Name: mcp-python-repl
3
+ Version: 0.1.0
4
+ Summary: A production-grade MCP server providing a persistent Python REPL with multi-session support, sandboxing, and timeout protection.
5
+ Project-URL: Homepage, https://github.com/soufiane-aazizi/mcp-python-repl
6
+ Project-URL: Repository, https://github.com/soufiane-aazizi/mcp-python-repl
7
+ Project-URL: Issues, https://github.com/soufiane-aazizi/mcp-python-repl/issues
8
+ Author: Soufiane Aazizi
9
+ License-Expression: MIT
10
+ Keywords: agent,interpreter,llm,mcp,model-context-protocol,python,repl
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Classifier: Topic :: Software Development :: Interpreters
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: mcp>=1.7.0
23
+ Requires-Dist: pydantic>=2.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
26
+ Requires-Dist: pytest>=8.0; extra == 'dev'
27
+ Requires-Dist: ruff>=0.8; extra == 'dev'
28
+ Description-Content-Type: text/markdown
29
+
30
+ # 🐍 mcp-python-repl
31
+
32
+ A **production-grade** MCP server providing a persistent Python REPL with multi-session support, sandboxing, and timeout protection.
33
+
34
+ Built for LLM agents that need to execute Python code across multiple turns with **variables that persist between calls**.
35
+
36
+ ## ✨ Features
37
+
38
+ | Feature | Description |
39
+ |---|---|
40
+ | **Multi-session** | Isolated sessions with unique IDs — run parallel workflows |
41
+ | **Persistent namespace** | Variables survive across calls within a session |
42
+ | **Timeout protection** | Configurable execution timeout (SIGALRM on Unix) |
43
+ | **Sandboxing** | Optional mode blocks dangerous modules (`subprocess`, `socket`, etc.) |
44
+ | **Package install** | Install pip packages on-the-fly (prefers `uv` for speed) |
45
+ | **File execution** | Run `.py` files inside the persistent session |
46
+ | **Dual transport** | stdio (local) and streamable-http (remote) |
47
+ | **Full introspection** | List variables, get history, check server status |
48
+ | **Env-based config** | All settings via `REPL_*` environment variables |
49
+
50
+ ## 🚀 Quick Start
51
+
52
+ ### With Claude Desktop / Cursor (stdio)
53
+
54
+ Add to your MCP config:
55
+
56
+ ```json
57
+ {
58
+ "mcpServers": {
59
+ "python-repl": {
60
+ "command": "uvx",
61
+ "args": ["mcp-python-repl"]
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ ### With uv (local dev)
68
+
69
+ ```bash
70
+ # Clone and run
71
+ git clone https://github.com/soufiane-aazizi/mcp-python-repl.git
72
+ cd mcp-python-repl
73
+ uv run mcp-python-repl
74
+ ```
75
+
76
+ ### HTTP transport (remote / multi-client)
77
+
78
+ ```bash
79
+ REPL_TRANSPORT=streamable-http REPL_PORT=8000 uv run mcp-python-repl
80
+ ```
81
+
82
+ ## 🛠️ Tools
83
+
84
+ ### Code Execution
85
+
86
+ | Tool | Description |
87
+ |---|---|
88
+ | `repl_run_code` | Execute Python code with persistent namespace |
89
+ | `repl_run_file` | Execute a `.py` file in the session |
90
+ | `repl_install_package` | Install a pip package (uses `uv` if available) |
91
+
92
+ ### Namespace Management
93
+
94
+ | Tool | Description |
95
+ |---|---|
96
+ | `repl_list_namespace` | List all variables in a session |
97
+ | `repl_get_variable` | Get the full value of a variable |
98
+ | `repl_set_variable` | Inject a variable from JSON |
99
+ | `repl_delete_variable` | Delete a specific variable |
100
+ | `repl_clear_namespace` | Clear all variables in a session |
101
+
102
+ ### Session Management
103
+
104
+ | Tool | Description |
105
+ |---|---|
106
+ | `repl_list_sessions` | List all active sessions |
107
+ | `repl_delete_session` | Delete a session and its data |
108
+
109
+ ### Debugging
110
+
111
+ | Tool | Description |
112
+ |---|---|
113
+ | `repl_get_history` | Get execution history for a session |
114
+ | `repl_server_status` | Server config, Python version, session count |
115
+
116
+ ## 🔄 How Persistence Works
117
+
118
+ ```
119
+ Call 1: repl_run_code(code="data = [1,2,3]; total = sum(data); result = total")
120
+ → returns: {"result": 6, "session_id": "a1b2c3d4e5f6", "new_variables": ["data", "total"]}
121
+
122
+ Call 2: repl_run_code(code="doubled = [x*2 for x in data]; result = doubled", session_id="a1b2c3d4e5f6")
123
+ → returns: {"result": [2,4,6], "new_variables": ["doubled"]}
124
+ ```
125
+
126
+ > **Important:** The `result` variable is for returning output to the caller. It does **NOT** persist. Use named variables instead.
127
+
128
+ ## ⚙️ Configuration
129
+
130
+ All settings are configurable via environment variables:
131
+
132
+ | Variable | Default | Description |
133
+ |---|---|---|
134
+ | `REPL_TIMEOUT` | `30` | Max execution time in seconds |
135
+ | `REPL_MAX_SESSIONS` | `50` | Maximum concurrent sessions |
136
+ | `REPL_SESSION_TTL` | `120` | Session expiry in minutes |
137
+ | `REPL_MAX_OUTPUT` | `1048576` | Max stdout/stderr capture (bytes) |
138
+ | `REPL_SANDBOX` | `false` | Enable sandboxing (`true`/`false`) |
139
+ | `REPL_TRANSPORT` | `stdio` | Transport: `stdio` or `streamable-http` |
140
+ | `REPL_HOST` | `127.0.0.1` | HTTP host (when using HTTP transport) |
141
+ | `REPL_PORT` | `8000` | HTTP port (when using HTTP transport) |
142
+ | `REPL_WORKDIR` | `cwd` | Working directory for executions |
143
+
144
+ ### Sandbox Mode
145
+
146
+ When `REPL_SANDBOX=true`, the following modules are blocked:
147
+
148
+ `subprocess`, `shutil`, `ctypes`, `socket`, `http.server`, `xmlrpc`, `ftplib`, `smtplib`, `telnetlib`, `webbrowser`
149
+
150
+ And the following builtins are removed: `exec`, `eval`, `compile`, `__import__` (replaced with a restricted version).
151
+
152
+ ## 🧪 Development
153
+
154
+ ```bash
155
+ # Install dev dependencies
156
+ uv sync --extra dev
157
+
158
+ # Run tests
159
+ uv run pytest -v
160
+
161
+ # Lint
162
+ uv run ruff check src/ tests/
163
+
164
+ # Test with MCP Inspector
165
+ npx @modelcontextprotocol/inspector uv run mcp-python-repl
166
+ ```
167
+
168
+ ## 📦 Project Structure
169
+
170
+ ```
171
+ mcp-python-repl/
172
+ ├── src/mcp_python_repl/
173
+ │ ├── __init__.py # Package metadata
174
+ │ ├── config.py # Env-based configuration
175
+ │ ├── session.py # Multi-session manager with TTL
176
+ │ ├── executor.py # Python code executor (timeout + sandbox)
177
+ │ └── server.py # MCP server with all tools
178
+ ├── tests/
179
+ │ └── test_core.py # Unit + integration tests
180
+ ├── pyproject.toml # uv/hatch project config
181
+ ├── LICENSE # MIT
182
+ └── README.md
183
+ ```
184
+
185
+ ## 📄 License
186
+
187
+ MIT — See [LICENSE](LICENSE).
@@ -0,0 +1,158 @@
1
+ # 🐍 mcp-python-repl
2
+
3
+ A **production-grade** MCP server providing a persistent Python REPL with multi-session support, sandboxing, and timeout protection.
4
+
5
+ Built for LLM agents that need to execute Python code across multiple turns with **variables that persist between calls**.
6
+
7
+ ## ✨ Features
8
+
9
+ | Feature | Description |
10
+ |---|---|
11
+ | **Multi-session** | Isolated sessions with unique IDs — run parallel workflows |
12
+ | **Persistent namespace** | Variables survive across calls within a session |
13
+ | **Timeout protection** | Configurable execution timeout (SIGALRM on Unix) |
14
+ | **Sandboxing** | Optional mode blocks dangerous modules (`subprocess`, `socket`, etc.) |
15
+ | **Package install** | Install pip packages on-the-fly (prefers `uv` for speed) |
16
+ | **File execution** | Run `.py` files inside the persistent session |
17
+ | **Dual transport** | stdio (local) and streamable-http (remote) |
18
+ | **Full introspection** | List variables, get history, check server status |
19
+ | **Env-based config** | All settings via `REPL_*` environment variables |
20
+
21
+ ## 🚀 Quick Start
22
+
23
+ ### With Claude Desktop / Cursor (stdio)
24
+
25
+ Add to your MCP config:
26
+
27
+ ```json
28
+ {
29
+ "mcpServers": {
30
+ "python-repl": {
31
+ "command": "uvx",
32
+ "args": ["mcp-python-repl"]
33
+ }
34
+ }
35
+ }
36
+ ```
37
+
38
+ ### With uv (local dev)
39
+
40
+ ```bash
41
+ # Clone and run
42
+ git clone https://github.com/soufiane-aazizi/mcp-python-repl.git
43
+ cd mcp-python-repl
44
+ uv run mcp-python-repl
45
+ ```
46
+
47
+ ### HTTP transport (remote / multi-client)
48
+
49
+ ```bash
50
+ REPL_TRANSPORT=streamable-http REPL_PORT=8000 uv run mcp-python-repl
51
+ ```
52
+
53
+ ## 🛠️ Tools
54
+
55
+ ### Code Execution
56
+
57
+ | Tool | Description |
58
+ |---|---|
59
+ | `repl_run_code` | Execute Python code with persistent namespace |
60
+ | `repl_run_file` | Execute a `.py` file in the session |
61
+ | `repl_install_package` | Install a pip package (uses `uv` if available) |
62
+
63
+ ### Namespace Management
64
+
65
+ | Tool | Description |
66
+ |---|---|
67
+ | `repl_list_namespace` | List all variables in a session |
68
+ | `repl_get_variable` | Get the full value of a variable |
69
+ | `repl_set_variable` | Inject a variable from JSON |
70
+ | `repl_delete_variable` | Delete a specific variable |
71
+ | `repl_clear_namespace` | Clear all variables in a session |
72
+
73
+ ### Session Management
74
+
75
+ | Tool | Description |
76
+ |---|---|
77
+ | `repl_list_sessions` | List all active sessions |
78
+ | `repl_delete_session` | Delete a session and its data |
79
+
80
+ ### Debugging
81
+
82
+ | Tool | Description |
83
+ |---|---|
84
+ | `repl_get_history` | Get execution history for a session |
85
+ | `repl_server_status` | Server config, Python version, session count |
86
+
87
+ ## 🔄 How Persistence Works
88
+
89
+ ```
90
+ Call 1: repl_run_code(code="data = [1,2,3]; total = sum(data); result = total")
91
+ → returns: {"result": 6, "session_id": "a1b2c3d4e5f6", "new_variables": ["data", "total"]}
92
+
93
+ Call 2: repl_run_code(code="doubled = [x*2 for x in data]; result = doubled", session_id="a1b2c3d4e5f6")
94
+ → returns: {"result": [2,4,6], "new_variables": ["doubled"]}
95
+ ```
96
+
97
+ > **Important:** The `result` variable is for returning output to the caller. It does **NOT** persist. Use named variables instead.
98
+
99
+ ## ⚙️ Configuration
100
+
101
+ All settings are configurable via environment variables:
102
+
103
+ | Variable | Default | Description |
104
+ |---|---|---|
105
+ | `REPL_TIMEOUT` | `30` | Max execution time in seconds |
106
+ | `REPL_MAX_SESSIONS` | `50` | Maximum concurrent sessions |
107
+ | `REPL_SESSION_TTL` | `120` | Session expiry in minutes |
108
+ | `REPL_MAX_OUTPUT` | `1048576` | Max stdout/stderr capture (bytes) |
109
+ | `REPL_SANDBOX` | `false` | Enable sandboxing (`true`/`false`) |
110
+ | `REPL_TRANSPORT` | `stdio` | Transport: `stdio` or `streamable-http` |
111
+ | `REPL_HOST` | `127.0.0.1` | HTTP host (when using HTTP transport) |
112
+ | `REPL_PORT` | `8000` | HTTP port (when using HTTP transport) |
113
+ | `REPL_WORKDIR` | `cwd` | Working directory for executions |
114
+
115
+ ### Sandbox Mode
116
+
117
+ When `REPL_SANDBOX=true`, the following modules are blocked:
118
+
119
+ `subprocess`, `shutil`, `ctypes`, `socket`, `http.server`, `xmlrpc`, `ftplib`, `smtplib`, `telnetlib`, `webbrowser`
120
+
121
+ And the following builtins are removed: `exec`, `eval`, `compile`, `__import__` (replaced with a restricted version).
122
+
123
+ ## 🧪 Development
124
+
125
+ ```bash
126
+ # Install dev dependencies
127
+ uv sync --extra dev
128
+
129
+ # Run tests
130
+ uv run pytest -v
131
+
132
+ # Lint
133
+ uv run ruff check src/ tests/
134
+
135
+ # Test with MCP Inspector
136
+ npx @modelcontextprotocol/inspector uv run mcp-python-repl
137
+ ```
138
+
139
+ ## 📦 Project Structure
140
+
141
+ ```
142
+ mcp-python-repl/
143
+ ├── src/mcp_python_repl/
144
+ │ ├── __init__.py # Package metadata
145
+ │ ├── config.py # Env-based configuration
146
+ │ ├── session.py # Multi-session manager with TTL
147
+ │ ├── executor.py # Python code executor (timeout + sandbox)
148
+ │ └── server.py # MCP server with all tools
149
+ ├── tests/
150
+ │ └── test_core.py # Unit + integration tests
151
+ ├── pyproject.toml # uv/hatch project config
152
+ ├── LICENSE # MIT
153
+ └── README.md
154
+ ```
155
+
156
+ ## 📄 License
157
+
158
+ MIT — See [LICENSE](LICENSE).
@@ -0,0 +1,66 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "mcp-python-repl"
7
+ version = "0.1.0"
8
+ description = "A production-grade MCP server providing a persistent Python REPL with multi-session support, sandboxing, and timeout protection."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.10"
12
+ authors = [
13
+ { name = "Soufiane Aazizi" },
14
+ ]
15
+ keywords = ["mcp", "python", "repl", "interpreter", "llm", "agent", "model-context-protocol"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Topic :: Software Development :: Interpreters",
26
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
27
+ ]
28
+
29
+ dependencies = [
30
+ "mcp>=1.7.0",
31
+ "pydantic>=2.0",
32
+ ]
33
+
34
+ [project.optional-dependencies]
35
+ dev = [
36
+ "pytest>=8.0",
37
+ "pytest-asyncio>=0.24",
38
+ "ruff>=0.8",
39
+ ]
40
+
41
+ [project.urls]
42
+ Homepage = "https://github.com/soufiane-aazizi/mcp-python-repl"
43
+ Repository = "https://github.com/soufiane-aazizi/mcp-python-repl"
44
+ Issues = "https://github.com/soufiane-aazizi/mcp-python-repl/issues"
45
+
46
+ [project.scripts]
47
+ mcp-python-repl = "mcp_python_repl.server:main"
48
+
49
+
50
+ [tool.ruff]
51
+ target-version = "py310"
52
+ line-length = 100
53
+
54
+ [tool.ruff.lint]
55
+ select = ["E", "F", "I", "W", "UP"]
56
+
57
+ [tool.hatch.build.targets.wheel]
58
+ packages = ["src/mcp_python_repl"]
59
+
60
+ [tool.hatch.build.targets.wheel.sources]
61
+ "src" = ""
62
+
63
+ [tool.pytest.ini_options]
64
+ pythonpath = ["src"]
65
+ asyncio_mode = "auto"
66
+ testpaths = ["tests"]
File without changes
@@ -0,0 +1,82 @@
1
+ """
2
+ Configuration for mcp-python-repl.
3
+
4
+ All settings can be overridden via environment variables prefixed with REPL_.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import os
10
+ from dataclasses import dataclass, field
11
+
12
+ # ---------------------------------------------------------------------------
13
+ # Defaults
14
+ # ---------------------------------------------------------------------------
15
+ DEFAULT_TIMEOUT_SECONDS = 30
16
+ DEFAULT_MAX_SESSIONS = 50
17
+ DEFAULT_SESSION_TTL_MINUTES = 120
18
+ DEFAULT_MAX_OUTPUT_BYTES = 1_048_576 # 1 MB
19
+ DEFAULT_LOG_ENTRIES = 200
20
+
21
+ # Modules / builtins that are blocked in sandboxed mode
22
+ SANDBOXED_BLOCKED_MODULES = frozenset(
23
+ {
24
+ "subprocess",
25
+ "shutil",
26
+ "ctypes",
27
+ "socket",
28
+ "http.server",
29
+ "xmlrpc",
30
+ "ftplib",
31
+ "smtplib",
32
+ "telnetlib",
33
+ "webbrowser",
34
+ "antigravity",
35
+ }
36
+ )
37
+
38
+ SANDBOXED_BLOCKED_BUILTINS = frozenset(
39
+ {
40
+ "exec",
41
+ "eval",
42
+ "compile",
43
+ "__import__",
44
+ }
45
+ )
46
+
47
+
48
+ @dataclass(frozen=True)
49
+ class Config:
50
+ """Immutable server configuration resolved from environment."""
51
+
52
+ timeout_seconds: int = DEFAULT_TIMEOUT_SECONDS
53
+ max_sessions: int = DEFAULT_MAX_SESSIONS
54
+ session_ttl_minutes: int = DEFAULT_SESSION_TTL_MINUTES
55
+ max_output_bytes: int = DEFAULT_MAX_OUTPUT_BYTES
56
+ max_log_entries: int = DEFAULT_LOG_ENTRIES
57
+ sandbox_enabled: bool = False
58
+ working_directory: str = field(default_factory=os.getcwd)
59
+ transport: str = "stdio"
60
+ host: str = "127.0.0.1"
61
+ port: int = 8000
62
+
63
+ # ------------------------------------------------------------------
64
+ @classmethod
65
+ def from_env(cls) -> Config:
66
+ """Build configuration from REPL_* environment variables."""
67
+
68
+ def _env(key: str, default: str) -> str:
69
+ return os.environ.get(f"REPL_{key}", default)
70
+
71
+ return cls(
72
+ timeout_seconds=int(_env("TIMEOUT", str(DEFAULT_TIMEOUT_SECONDS))),
73
+ max_sessions=int(_env("MAX_SESSIONS", str(DEFAULT_MAX_SESSIONS))),
74
+ session_ttl_minutes=int(_env("SESSION_TTL", str(DEFAULT_SESSION_TTL_MINUTES))),
75
+ max_output_bytes=int(_env("MAX_OUTPUT", str(DEFAULT_MAX_OUTPUT_BYTES))),
76
+ max_log_entries=int(_env("MAX_LOG_ENTRIES", str(DEFAULT_LOG_ENTRIES))),
77
+ sandbox_enabled=_env("SANDBOX", "false").lower() in ("1", "true", "yes"),
78
+ working_directory=_env("WORKDIR", os.getcwd()),
79
+ transport=_env("TRANSPORT", "stdio"),
80
+ host=_env("HOST", "127.0.0.1"),
81
+ port=int(_env("PORT", "8000")),
82
+ )