mle-kit-mcp 0.0.1__tar.gz → 0.0.3__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.
Files changed (26) hide show
  1. {mle_kit_mcp-0.0.1 → mle_kit_mcp-0.0.3}/PKG-INFO +1 -1
  2. mle_kit_mcp-0.0.3/mle_kit_mcp/files.py +25 -0
  3. mle_kit_mcp-0.0.3/mle_kit_mcp/server.py +40 -0
  4. {mle_kit_mcp-0.0.1 → mle_kit_mcp-0.0.3}/mle_kit_mcp/tools/bash.py +2 -2
  5. {mle_kit_mcp-0.0.1 → mle_kit_mcp-0.0.3}/mle_kit_mcp/tools/remote_gpu.py +6 -6
  6. {mle_kit_mcp-0.0.1 → mle_kit_mcp-0.0.3}/mle_kit_mcp/tools/text_editor.py +2 -2
  7. {mle_kit_mcp-0.0.1 → mle_kit_mcp-0.0.3}/mle_kit_mcp.egg-info/PKG-INFO +1 -1
  8. {mle_kit_mcp-0.0.1 → mle_kit_mcp-0.0.3}/pyproject.toml +1 -1
  9. {mle_kit_mcp-0.0.1 → mle_kit_mcp-0.0.3}/tests/test_bash.py +3 -3
  10. {mle_kit_mcp-0.0.1 → mle_kit_mcp-0.0.3}/tests/test_text_editor.py +28 -28
  11. {mle_kit_mcp-0.0.1 → mle_kit_mcp-0.0.3}/tests/test_truncate_context.py +1 -1
  12. mle_kit_mcp-0.0.1/mle_kit_mcp/files.py +0 -12
  13. mle_kit_mcp-0.0.1/mle_kit_mcp/server.py +0 -36
  14. {mle_kit_mcp-0.0.1 → mle_kit_mcp-0.0.3}/LICENSE +0 -0
  15. {mle_kit_mcp-0.0.1 → mle_kit_mcp-0.0.3}/README.md +0 -0
  16. {mle_kit_mcp-0.0.1 → mle_kit_mcp-0.0.3}/mle_kit_mcp/__init__.py +0 -0
  17. {mle_kit_mcp-0.0.1 → mle_kit_mcp-0.0.3}/mle_kit_mcp/__main__.py +0 -0
  18. {mle_kit_mcp-0.0.1 → mle_kit_mcp-0.0.3}/mle_kit_mcp/py.typed +0 -0
  19. {mle_kit_mcp-0.0.1 → mle_kit_mcp-0.0.3}/mle_kit_mcp/tools/__init__.py +0 -0
  20. {mle_kit_mcp-0.0.1 → mle_kit_mcp-0.0.3}/mle_kit_mcp/utils.py +0 -0
  21. {mle_kit_mcp-0.0.1 → mle_kit_mcp-0.0.3}/mle_kit_mcp.egg-info/SOURCES.txt +0 -0
  22. {mle_kit_mcp-0.0.1 → mle_kit_mcp-0.0.3}/mle_kit_mcp.egg-info/dependency_links.txt +0 -0
  23. {mle_kit_mcp-0.0.1 → mle_kit_mcp-0.0.3}/mle_kit_mcp.egg-info/entry_points.txt +0 -0
  24. {mle_kit_mcp-0.0.1 → mle_kit_mcp-0.0.3}/mle_kit_mcp.egg-info/requires.txt +0 -0
  25. {mle_kit_mcp-0.0.1 → mle_kit_mcp-0.0.3}/mle_kit_mcp.egg-info/top_level.txt +0 -0
  26. {mle_kit_mcp-0.0.1 → mle_kit_mcp-0.0.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mle-kit-mcp
3
- Version: 0.0.1
3
+ Version: 0.0.3
4
4
  Summary: MCP server that provides different tools for MLE
5
5
  Author-email: Ilya Gusev <phoenixilya@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/IlyaGusev/mle_kit_mcp
@@ -0,0 +1,25 @@
1
+ import os
2
+ from typing import Optional
3
+ from pathlib import Path
4
+
5
+ DIR_PATH = Path(__file__).parent
6
+ ROOT_PATH = DIR_PATH.parent
7
+ DEFAULT_WORKSPACE_DIR_PATH: Path = DIR_PATH / "workdir"
8
+
9
+
10
+ class WorkspaceDirectory:
11
+ workspace_dir: Optional[Path] = None
12
+
13
+ @classmethod
14
+ def get_dir(cls) -> Path:
15
+ if cls.workspace_dir is None:
16
+ return Path(os.getenv("WORKSPACE_DIR", DEFAULT_WORKSPACE_DIR_PATH))
17
+ return cls.workspace_dir
18
+
19
+ @classmethod
20
+ def set_dir(cls, workspace_dir: Path) -> None:
21
+ cls.workspace_dir = workspace_dir
22
+
23
+
24
+ def get_workspace_dir() -> Path:
25
+ return WorkspaceDirectory.get_dir()
@@ -0,0 +1,40 @@
1
+ from pathlib import Path
2
+ from typing import Optional
3
+
4
+ import fire # type: ignore
5
+ import uvicorn
6
+ from mcp.server.fastmcp import FastMCP
7
+
8
+ from .tools.bash import bash
9
+ from .tools.text_editor import text_editor
10
+ from .tools.remote_gpu import (
11
+ remote_bash,
12
+ create_remote_text_editor,
13
+ remote_download,
14
+ )
15
+ from .files import get_workspace_dir, WorkspaceDirectory
16
+
17
+
18
+ def run(host: str = "0.0.0.0", port: int = 5050, workspace: Optional[str] = None) -> None:
19
+ if workspace:
20
+ WorkspaceDirectory.set_dir(Path(workspace))
21
+ workspace_path = get_workspace_dir()
22
+ workspace_path.mkdir(parents=True, exist_ok=True)
23
+
24
+ server = FastMCP("MLE kit MCP", stateless_http=True)
25
+
26
+ remote_text_editor = create_remote_text_editor(text_editor)
27
+
28
+ server.add_tool(bash)
29
+ server.add_tool(text_editor)
30
+ server.add_tool(remote_bash)
31
+ server.add_tool(remote_text_editor)
32
+ server.add_tool(remote_download)
33
+
34
+ http_app = server.streamable_http_app()
35
+
36
+ uvicorn.run(http_app, host=host, port=port)
37
+
38
+
39
+ if __name__ == "__main__":
40
+ fire.Fire(run)
@@ -3,7 +3,7 @@ import atexit
3
3
  import signal
4
4
  from typing import Optional, Any
5
5
 
6
- from mle_kit_mcp.files import WORKSPACE_DIR_PATH
6
+ from mle_kit_mcp.files import get_workspace_dir
7
7
 
8
8
 
9
9
  _container = None
@@ -63,7 +63,7 @@ def bash(command: str) -> str:
63
63
  tty=True,
64
64
  stdin_open=True,
65
65
  volumes={
66
- WORKSPACE_DIR_PATH: {
66
+ get_workspace_dir(): {
67
67
  "bind": DOCKER_WORKSPACE_DIR_PATH,
68
68
  "mode": "rw",
69
69
  }
@@ -12,7 +12,7 @@ from dataclasses import dataclass
12
12
  from dotenv import load_dotenv
13
13
  from vastai_sdk import VastAI # type: ignore
14
14
 
15
- from mle_kit_mcp.files import WORKSPACE_DIR_PATH
15
+ from mle_kit_mcp.files import get_workspace_dir
16
16
 
17
17
  BASE_IMAGE = "phoenix120/holosophos_mle"
18
18
  DEFAULT_GPU_TYPE = "RTX_3090"
@@ -257,9 +257,9 @@ def launch_instance(vast_sdk: VastAI, gpu_name: str) -> Optional[InstanceInfo]:
257
257
 
258
258
  def send_scripts() -> None:
259
259
  assert _instance_info
260
- for name in os.listdir(WORKSPACE_DIR_PATH):
260
+ for name in os.listdir(get_workspace_dir()):
261
261
  if name.endswith(".py"):
262
- send_rsync(_instance_info, f"{WORKSPACE_DIR_PATH}/{name}", "/root")
262
+ send_rsync(_instance_info, f"{get_workspace_dir()}/{name}", "/root")
263
263
 
264
264
 
265
265
  def init_all() -> None:
@@ -316,7 +316,7 @@ def remote_download(file_path: str) -> str:
316
316
  """
317
317
  init_all()
318
318
  assert _instance_info
319
- recieve_rsync(_instance_info, f"/root/{file_path}", f"{WORKSPACE_DIR_PATH}")
319
+ recieve_rsync(_instance_info, f"/root/{file_path}", f"{get_workspace_dir()}")
320
320
  return f"File '{file_path}' downloaded!"
321
321
 
322
322
 
@@ -335,12 +335,12 @@ def create_remote_text_editor(
335
335
  command = args_dict["command"]
336
336
 
337
337
  if command != "write":
338
- recieve_rsync(_instance_info, f"/root/{path}", f"{WORKSPACE_DIR_PATH}")
338
+ recieve_rsync(_instance_info, f"/root/{path}", f"{get_workspace_dir()}")
339
339
 
340
340
  result: str = text_editor_func(*args, **kwargs)
341
341
 
342
342
  if command != "view":
343
- send_rsync(_instance_info, f"{WORKSPACE_DIR_PATH}/{path}", "/root")
343
+ send_rsync(_instance_info, f"{get_workspace_dir()}/{path}", "/root")
344
344
 
345
345
  return result
346
346
 
@@ -2,7 +2,7 @@ from collections import defaultdict
2
2
  from typing import Dict, List, Optional
3
3
  from pathlib import Path
4
4
 
5
- from mle_kit_mcp.files import WORKSPACE_DIR_PATH
5
+ from mle_kit_mcp.files import get_workspace_dir
6
6
  from mle_kit_mcp.utils import truncate_content
7
7
 
8
8
  WRITE_MAX_OUTPUT_LENGTH = 500
@@ -174,7 +174,7 @@ def text_editor(
174
174
  ), "Absolute path is not supported, only relative to the work directory"
175
175
  valid_commands = ("view", "write", "str_replace", "insert", "undo_edit", "append")
176
176
 
177
- path_obj = WORKSPACE_DIR_PATH / path
177
+ path_obj = get_workspace_dir() / path
178
178
 
179
179
  if command == "view":
180
180
  show_lines = show_lines if show_lines is not None else False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mle-kit-mcp
3
- Version: 0.0.1
3
+ Version: 0.0.3
4
4
  Summary: MCP server that provides different tools for MLE
5
5
  Author-email: Ilya Gusev <phoenixilya@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/IlyaGusev/mle_kit_mcp
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mle-kit-mcp"
7
- version = "0.0.1"
7
+ version = "0.0.3"
8
8
  description = "MCP server that provides different tools for MLE"
9
9
  readme = "README.md"
10
10
  authors = [
@@ -1,7 +1,7 @@
1
1
  import os
2
2
 
3
3
  from mle_kit_mcp.tools import bash
4
- from mle_kit_mcp.files import WORKSPACE_DIR_PATH
4
+ from mle_kit_mcp.files import get_workspace_dir
5
5
 
6
6
 
7
7
  def test_bash() -> None:
@@ -12,7 +12,7 @@ def test_bash() -> None:
12
12
  assert result == "/workdir"
13
13
 
14
14
  result = bash("touch dummy")
15
- assert os.path.exists(WORKSPACE_DIR_PATH / "dummy")
15
+ assert os.path.exists(get_workspace_dir() / "dummy")
16
16
 
17
17
  result = bash("fddafad")
18
- assert "fddafad: command not found" in result
18
+ assert "fddafad: command not found" in result
@@ -4,7 +4,7 @@ import os
4
4
  import pytest
5
5
 
6
6
  from mle_kit_mcp.tools import text_editor
7
- from mle_kit_mcp.files import WORKSPACE_DIR_PATH
7
+ from mle_kit_mcp.files import get_workspace_dir
8
8
 
9
9
  DOCUMENT1 = """
10
10
  The dominant sequence transduction models are based on complex recurrent or convolutional
@@ -23,11 +23,11 @@ English constituency parsing both with large and limited training data.
23
23
 
24
24
 
25
25
  def test_text_editor_view() -> None:
26
- with tempfile.NamedTemporaryFile(dir=WORKSPACE_DIR_PATH, mode="w+") as f:
26
+ with tempfile.NamedTemporaryFile(dir=get_workspace_dir(), mode="w+") as f:
27
27
  f.write(DOCUMENT1)
28
28
  f.flush()
29
29
  name = os.path.basename(f.name)
30
- test_file = WORKSPACE_DIR_PATH / name
30
+ test_file = get_workspace_dir() / name
31
31
 
32
32
  result = text_editor("view", name, show_lines=True)
33
33
  cmd_result = os.popen(f"cat -n {str(test_file.resolve())}").read()
@@ -52,9 +52,9 @@ def test_text_editor_view() -> None:
52
52
 
53
53
 
54
54
  def test_text_editor_write() -> None:
55
- with tempfile.NamedTemporaryFile(dir=WORKSPACE_DIR_PATH, mode="w+") as f:
55
+ with tempfile.NamedTemporaryFile(dir=get_workspace_dir(), mode="w+") as f:
56
56
  name = os.path.basename(f.name)
57
- test_file = WORKSPACE_DIR_PATH / name
57
+ test_file = get_workspace_dir() / name
58
58
  test_file.unlink(missing_ok=True)
59
59
  assert not test_file.exists()
60
60
 
@@ -64,9 +64,9 @@ def test_text_editor_write() -> None:
64
64
 
65
65
 
66
66
  def test_text_editor_insert() -> None:
67
- with tempfile.NamedTemporaryFile(dir=WORKSPACE_DIR_PATH, mode="w+") as f:
67
+ with tempfile.NamedTemporaryFile(dir=get_workspace_dir(), mode="w+") as f:
68
68
  name = os.path.basename(f.name)
69
- test_file = WORKSPACE_DIR_PATH / name
69
+ test_file = get_workspace_dir() / name
70
70
  test_file.write_text(DOCUMENT1)
71
71
 
72
72
  text_editor("insert", name, insert_line=0, new_str="Hello there!")
@@ -77,9 +77,9 @@ def test_text_editor_insert() -> None:
77
77
 
78
78
 
79
79
  def test_text_editor_str_replace() -> None:
80
- with tempfile.NamedTemporaryFile(dir=WORKSPACE_DIR_PATH, mode="w+") as f:
80
+ with tempfile.NamedTemporaryFile(dir=get_workspace_dir(), mode="w+") as f:
81
81
  name = os.path.basename(f.name)
82
- test_file = WORKSPACE_DIR_PATH / name
82
+ test_file = get_workspace_dir() / name
83
83
  test_file.write_text(DOCUMENT1)
84
84
 
85
85
  text_editor("str_replace", name, old_str="41.8", new_str="41.9")
@@ -88,9 +88,9 @@ def test_text_editor_str_replace() -> None:
88
88
 
89
89
 
90
90
  def test_text_editor_undo_edit() -> None:
91
- with tempfile.NamedTemporaryFile(dir=WORKSPACE_DIR_PATH, mode="w+") as f:
91
+ with tempfile.NamedTemporaryFile(dir=get_workspace_dir(), mode="w+") as f:
92
92
  name = os.path.basename(f.name)
93
- test_file = WORKSPACE_DIR_PATH / name
93
+ test_file = get_workspace_dir() / name
94
94
  test_file.write_text(DOCUMENT1)
95
95
 
96
96
  text_editor("str_replace", name, old_str="41.8", new_str="41.9")
@@ -114,9 +114,9 @@ def test_text_editor_view_directory() -> None:
114
114
 
115
115
 
116
116
  def test_text_editor_view_invalid_range() -> None:
117
- with tempfile.NamedTemporaryFile(dir=WORKSPACE_DIR_PATH, mode="w+") as f:
117
+ with tempfile.NamedTemporaryFile(dir=get_workspace_dir(), mode="w+") as f:
118
118
  name = os.path.basename(f.name)
119
- test_file = WORKSPACE_DIR_PATH / name
119
+ test_file = get_workspace_dir() / name
120
120
  test_file.write_text(DOCUMENT1)
121
121
 
122
122
  with pytest.raises(AssertionError):
@@ -127,17 +127,17 @@ def test_text_editor_view_invalid_range() -> None:
127
127
 
128
128
 
129
129
  def test_text_editor_write_existing_file() -> None:
130
- with tempfile.NamedTemporaryFile(dir=WORKSPACE_DIR_PATH, mode="w+") as f:
130
+ with tempfile.NamedTemporaryFile(dir=get_workspace_dir(), mode="w+") as f:
131
131
  name = os.path.basename(f.name)
132
- test_file = WORKSPACE_DIR_PATH / name
132
+ test_file = get_workspace_dir() / name
133
133
  test_file.write_text(DOCUMENT1)
134
134
  text_editor("write", name, file_text="New content")
135
135
 
136
136
 
137
137
  def test_text_editor_missing_required_params() -> None:
138
- with tempfile.NamedTemporaryFile(dir=WORKSPACE_DIR_PATH, mode="w+") as f:
138
+ with tempfile.NamedTemporaryFile(dir=get_workspace_dir(), mode="w+") as f:
139
139
  name = os.path.basename(f.name)
140
- test_file = WORKSPACE_DIR_PATH / name
140
+ test_file = get_workspace_dir() / name
141
141
  test_file.write_text(DOCUMENT1)
142
142
 
143
143
  with pytest.raises(AssertionError):
@@ -151,9 +151,9 @@ def test_text_editor_missing_required_params() -> None:
151
151
 
152
152
 
153
153
  def test_text_editor_undo_multiple() -> None:
154
- with tempfile.NamedTemporaryFile(dir=WORKSPACE_DIR_PATH, mode="w+") as f:
154
+ with tempfile.NamedTemporaryFile(dir=get_workspace_dir(), mode="w+") as f:
155
155
  name = os.path.basename(f.name)
156
- test_file = WORKSPACE_DIR_PATH / name
156
+ test_file = get_workspace_dir() / name
157
157
  test_file.write_text(DOCUMENT1)
158
158
 
159
159
  text_editor("str_replace", name, old_str="41.8", new_str="41.9")
@@ -168,9 +168,9 @@ def test_text_editor_undo_multiple() -> None:
168
168
 
169
169
 
170
170
  def test_text_editor_str_replace_no_match() -> None:
171
- with tempfile.NamedTemporaryFile(dir=WORKSPACE_DIR_PATH, mode="w+") as f:
171
+ with tempfile.NamedTemporaryFile(dir=get_workspace_dir(), mode="w+") as f:
172
172
  name = os.path.basename(f.name)
173
- test_file = WORKSPACE_DIR_PATH / name
173
+ test_file = get_workspace_dir() / name
174
174
  test_file.write_text(DOCUMENT1)
175
175
 
176
176
  with pytest.raises(AssertionError):
@@ -178,9 +178,9 @@ def test_text_editor_str_replace_no_match() -> None:
178
178
 
179
179
 
180
180
  def test_text_editor_str_replace_multiple_matches() -> None:
181
- with tempfile.NamedTemporaryFile(dir=WORKSPACE_DIR_PATH, mode="w+") as f:
181
+ with tempfile.NamedTemporaryFile(dir=get_workspace_dir(), mode="w+") as f:
182
182
  name = os.path.basename(f.name)
183
- test_file = WORKSPACE_DIR_PATH / name
183
+ test_file = get_workspace_dir() / name
184
184
  test_file.write_text("Hello\nHello\nHello")
185
185
 
186
186
  with pytest.raises(AssertionError):
@@ -188,9 +188,9 @@ def test_text_editor_str_replace_multiple_matches() -> None:
188
188
 
189
189
 
190
190
  def test_text_editor_large_file_handling() -> None:
191
- with tempfile.NamedTemporaryFile(dir=WORKSPACE_DIR_PATH, mode="w+") as f:
191
+ with tempfile.NamedTemporaryFile(dir=get_workspace_dir(), mode="w+") as f:
192
192
  name = os.path.basename(f.name)
193
- test_file = WORKSPACE_DIR_PATH / name
193
+ test_file = get_workspace_dir() / name
194
194
 
195
195
  large_content = ""
196
196
  for i in range(1000):
@@ -207,12 +207,12 @@ def test_text_editor_large_file_handling() -> None:
207
207
 
208
208
 
209
209
  def test_text_editor_append() -> None:
210
- with tempfile.NamedTemporaryFile(dir=WORKSPACE_DIR_PATH, mode="w+") as f:
210
+ with tempfile.NamedTemporaryFile(dir=get_workspace_dir(), mode="w+") as f:
211
211
  name = os.path.basename(f.name)
212
- test_file = WORKSPACE_DIR_PATH / name
212
+ test_file = get_workspace_dir() / name
213
213
  test_file.write_text(DOCUMENT1)
214
214
 
215
215
  text_editor("append", name, new_str="New line")
216
216
 
217
217
  assert DOCUMENT1.strip() in test_file.read_text().strip()
218
- assert "New line" in test_file.read_text().strip()
218
+ assert "New line" in test_file.read_text().strip()
@@ -32,4 +32,4 @@ def test_truncate_content_suffix() -> None:
32
32
  result = truncate_content(DOCUMENT, max_length=40, suffix_only=True)
33
33
  parts = result.split("\n\n")
34
34
  assert DOCUMENT.endswith(parts[2])
35
- assert len(parts[2]) == 40
35
+ assert len(parts[2]) == 40
@@ -1,12 +0,0 @@
1
- from pathlib import Path
2
-
3
- DIR_PATH = Path(__file__).parent
4
- ROOT_PATH = DIR_PATH.parent
5
-
6
- WORKSPACE_DIR_PATH: Path = ROOT_PATH / "workdir"
7
-
8
-
9
- def set_workspace_dir(path: Path) -> None:
10
- global WORKSPACE_DIR_PATH
11
- path.mkdir(parents=True, exist_ok=True)
12
- WORKSPACE_DIR_PATH = path
@@ -1,36 +0,0 @@
1
- import fire # type: ignore
2
- from pathlib import Path
3
- import uvicorn
4
- from mcp.server.fastmcp import FastMCP
5
-
6
- from .files import set_workspace_dir
7
- from .tools.bash import bash
8
- from .tools.text_editor import text_editor
9
- from .tools.remote_gpu import (
10
- remote_bash,
11
- create_remote_text_editor,
12
- remote_download,
13
- )
14
-
15
-
16
- server = FastMCP("MLE kit MCP", stateless_http=True)
17
-
18
- remote_text_editor = create_remote_text_editor(text_editor)
19
-
20
- server.add_tool(bash)
21
- server.add_tool(text_editor)
22
- server.add_tool(remote_bash)
23
- server.add_tool(remote_text_editor)
24
- server.add_tool(remote_download)
25
-
26
- http_app = server.streamable_http_app()
27
-
28
-
29
- def run(host: str = "0.0.0.0", port: int = 5050, workspace: str = "workdir") -> None:
30
- workspace_path = Path(workspace)
31
- set_workspace_dir(workspace_path)
32
- uvicorn.run(http_app, host=host, port=port)
33
-
34
-
35
- if __name__ == "__main__":
36
- fire.Fire(run)
File without changes
File without changes
File without changes