RG-agentic_terminal 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.
- rg_agentic_terminal-0.1.0/PKG-INFO +8 -0
- rg_agentic_terminal-0.1.0/README.md +0 -0
- rg_agentic_terminal-0.1.0/pyproject.toml +22 -0
- rg_agentic_terminal-0.1.0/setup.cfg +4 -0
- rg_agentic_terminal-0.1.0/src/RG_agentic_terminal.egg-info/PKG-INFO +8 -0
- rg_agentic_terminal-0.1.0/src/RG_agentic_terminal.egg-info/SOURCES.txt +12 -0
- rg_agentic_terminal-0.1.0/src/RG_agentic_terminal.egg-info/dependency_links.txt +1 -0
- rg_agentic_terminal-0.1.0/src/RG_agentic_terminal.egg-info/entry_points.txt +2 -0
- rg_agentic_terminal-0.1.0/src/RG_agentic_terminal.egg-info/requires.txt +1 -0
- rg_agentic_terminal-0.1.0/src/RG_agentic_terminal.egg-info/top_level.txt +2 -0
- rg_agentic_terminal-0.1.0/src/__init__.py +0 -0
- rg_agentic_terminal-0.1.0/src/agentic_terminal/__init__.py +0 -0
- rg_agentic_terminal-0.1.0/src/agentic_terminal/main.py +7 -0
- rg_agentic_terminal-0.1.0/src/agentic_terminal/tools.py +460 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: RG-agentic_terminal
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: An MCP which adds the terminal capabilities to an agent.
|
|
5
|
+
Author-email: Rohan <rohangorde0505@gmail.com>
|
|
6
|
+
Requires-Python: >=3.13
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: fastmcp>=4.2.0
|
|
File without changes
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "RG-agentic_terminal"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "An MCP which adds the terminal capabilities to an agent."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "Rohan", email = "rohangorde0505@gmail.com" }
|
|
8
|
+
]
|
|
9
|
+
requires-python = ">=3.13"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"fastmcp>=4.2.0",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[build-system]
|
|
15
|
+
requires = ["setuptools>=42", "wheel"]
|
|
16
|
+
build-backend = "setuptools.build_meta"
|
|
17
|
+
|
|
18
|
+
[tools.setuptools.packages.find]
|
|
19
|
+
where = ["src"]
|
|
20
|
+
|
|
21
|
+
[project.scripts]
|
|
22
|
+
agentic_terminal = "agentic_terminal.main:main"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: RG-agentic_terminal
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: An MCP which adds the terminal capabilities to an agent.
|
|
5
|
+
Author-email: Rohan <rohangorde0505@gmail.com>
|
|
6
|
+
Requires-Python: >=3.13
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: fastmcp>=4.2.0
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/__init__.py
|
|
4
|
+
src/RG_agentic_terminal.egg-info/PKG-INFO
|
|
5
|
+
src/RG_agentic_terminal.egg-info/SOURCES.txt
|
|
6
|
+
src/RG_agentic_terminal.egg-info/dependency_links.txt
|
|
7
|
+
src/RG_agentic_terminal.egg-info/entry_points.txt
|
|
8
|
+
src/RG_agentic_terminal.egg-info/requires.txt
|
|
9
|
+
src/RG_agentic_terminal.egg-info/top_level.txt
|
|
10
|
+
src/agentic_terminal/__init__.py
|
|
11
|
+
src/agentic_terminal/main.py
|
|
12
|
+
src/agentic_terminal/tools.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
fastmcp>=4.2.0
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
|
|
2
|
+
from fastmcp import FastMCP
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
import platform
|
|
9
|
+
import hashlib
|
|
10
|
+
import json
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
|
|
14
|
+
mcp = FastMCP()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# ============================================================
|
|
18
|
+
# 1. ORIGINAL TOOLS - PRESERVED
|
|
19
|
+
# ============================================================
|
|
20
|
+
|
|
21
|
+
@mcp.tool()
|
|
22
|
+
def bash(command: str):
|
|
23
|
+
'''This function will execute a bash command and return the output. This function
|
|
24
|
+
is useful for executing bash commands including creating files, directories, and other bash commands.'''
|
|
25
|
+
import subprocess
|
|
26
|
+
try:
|
|
27
|
+
result = subprocess.run(command, shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
28
|
+
return result.stdout.decode('utf-8')
|
|
29
|
+
except subprocess.CalledProcessError as e:
|
|
30
|
+
return e.stderr.decode('utf-8')
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@mcp.tool()
|
|
34
|
+
def python_code(code: str):
|
|
35
|
+
'''This function will execute a python code and return the output.'''
|
|
36
|
+
import subprocess
|
|
37
|
+
try:
|
|
38
|
+
result = subprocess.run(['python', '-c', code], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
39
|
+
return result.stdout.decode('utf-8')
|
|
40
|
+
except subprocess.CalledProcessError as e:
|
|
41
|
+
return e.stderr.decode('utf-8')
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@mcp.tool()
|
|
45
|
+
def python_file(file: str):
|
|
46
|
+
'''This function will execute a python file and return the output.'''
|
|
47
|
+
import subprocess
|
|
48
|
+
try:
|
|
49
|
+
result = subprocess.run(['python', file], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
50
|
+
return result.stdout.decode('utf-8')
|
|
51
|
+
except subprocess.CalledProcessError as e:
|
|
52
|
+
return e.stderr.decode('utf-8')
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@mcp.tool()
|
|
56
|
+
def glob(pattern: str):
|
|
57
|
+
'''This function will return a list of files that match the given pattern. This function is useful for finding files in a directory.'''
|
|
58
|
+
import glob
|
|
59
|
+
return glob.glob(pattern)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@mcp.tool()
|
|
63
|
+
def grep(pattern: str, file: str):
|
|
64
|
+
'''This function will search for the given pattern in the specified file and return the matching lines.'''
|
|
65
|
+
import subprocess
|
|
66
|
+
|
|
67
|
+
result = subprocess.run(
|
|
68
|
+
['findstr', pattern, file],
|
|
69
|
+
stdout=subprocess.PIPE,
|
|
70
|
+
text=True
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
print(result.stdout)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@mcp.tool()
|
|
77
|
+
def read_file(file: str):
|
|
78
|
+
'''This function will read the contents of a file and return it as a string.'''
|
|
79
|
+
try:
|
|
80
|
+
with open(file, 'r') as f:
|
|
81
|
+
return f.read()
|
|
82
|
+
except Exception as e:
|
|
83
|
+
return str(e)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@mcp.tool()
|
|
87
|
+
def write_file(file: str, content: str):
|
|
88
|
+
'''This function will write the given content to a file. If the file does not exist, it will be created.'''
|
|
89
|
+
try:
|
|
90
|
+
with open(file, 'w') as f:
|
|
91
|
+
f.write(content)
|
|
92
|
+
return f"Content written to {file} successfully."
|
|
93
|
+
except Exception as e:
|
|
94
|
+
return str(e)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@mcp.tool()
|
|
98
|
+
def create_folder(folder: str):
|
|
99
|
+
'''This function will create a folder with the given name. If the folder already exists, it will return a message indicating that the folder already exists.'''
|
|
100
|
+
import os
|
|
101
|
+
try:
|
|
102
|
+
os.makedirs(folder)
|
|
103
|
+
return f"Folder '{folder}' created successfully."
|
|
104
|
+
except FileExistsError:
|
|
105
|
+
return f"Folder '{folder}' already exists."
|
|
106
|
+
except Exception as e:
|
|
107
|
+
return str(e)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@mcp.tool()
|
|
111
|
+
def delete_folder(folder: str):
|
|
112
|
+
'''This function will delete a folder with the given name. If the folder does not exist, it will return a message indicating that the folder does not exist.'''
|
|
113
|
+
import shutil
|
|
114
|
+
import os
|
|
115
|
+
try:
|
|
116
|
+
if os.path.exists(folder):
|
|
117
|
+
shutil.rmtree(folder)
|
|
118
|
+
return f"Folder '{folder}' deleted successfully."
|
|
119
|
+
else:
|
|
120
|
+
return f"Folder '{folder}' does not exist."
|
|
121
|
+
except Exception as e:
|
|
122
|
+
return str(e)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
# ============================================================
|
|
126
|
+
# 2. DIRECTORY MANAGEMENT TOOLS
|
|
127
|
+
# ============================================================
|
|
128
|
+
|
|
129
|
+
@mcp.tool()
|
|
130
|
+
def list_directory(directory: str = "."):
|
|
131
|
+
'''List all files and folders inside a directory with their types.'''
|
|
132
|
+
try:
|
|
133
|
+
path = Path(directory)
|
|
134
|
+
|
|
135
|
+
if not path.exists():
|
|
136
|
+
return f"Directory '{directory}' does not exist."
|
|
137
|
+
|
|
138
|
+
if not path.is_dir():
|
|
139
|
+
return f"'{directory}' is not a directory."
|
|
140
|
+
|
|
141
|
+
items = []
|
|
142
|
+
|
|
143
|
+
for item in sorted(path.iterdir()):
|
|
144
|
+
item_type = "Directory" if item.is_dir() else "File"
|
|
145
|
+
items.append({
|
|
146
|
+
"name": item.name,
|
|
147
|
+
"type": item_type,
|
|
148
|
+
"path": str(item.resolve())
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
return json.dumps(items, indent=4)
|
|
152
|
+
|
|
153
|
+
except Exception as e:
|
|
154
|
+
return str(e)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
@mcp.tool()
|
|
158
|
+
def directory_tree(directory: str = ".", max_depth: int = 3):
|
|
159
|
+
'''Generate a visual tree structure of directories and files.'''
|
|
160
|
+
try:
|
|
161
|
+
root = Path(directory)
|
|
162
|
+
|
|
163
|
+
if not root.is_dir():
|
|
164
|
+
return "Invalid directory."
|
|
165
|
+
|
|
166
|
+
lines = [root.name]
|
|
167
|
+
|
|
168
|
+
def build_tree(path, prefix="", depth=0):
|
|
169
|
+
if depth >= max_depth:
|
|
170
|
+
return
|
|
171
|
+
|
|
172
|
+
try:
|
|
173
|
+
entries = sorted(
|
|
174
|
+
path.iterdir(),
|
|
175
|
+
key=lambda p: (p.is_file(), p.name.lower())
|
|
176
|
+
)
|
|
177
|
+
except PermissionError:
|
|
178
|
+
lines.append(prefix + "[Permission denied]")
|
|
179
|
+
return
|
|
180
|
+
|
|
181
|
+
for index, entry in enumerate(entries):
|
|
182
|
+
last = index == len(entries) - 1
|
|
183
|
+
connector = "└── " if last else "├── "
|
|
184
|
+
|
|
185
|
+
lines.append(prefix + connector + entry.name)
|
|
186
|
+
|
|
187
|
+
if entry.is_dir():
|
|
188
|
+
extension = " " if last else "│ "
|
|
189
|
+
build_tree(entry, prefix + extension, depth + 1)
|
|
190
|
+
|
|
191
|
+
build_tree(root)
|
|
192
|
+
|
|
193
|
+
return "\n".join(lines)
|
|
194
|
+
|
|
195
|
+
except Exception as e:
|
|
196
|
+
return str(e)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
# ============================================================
|
|
200
|
+
# 3. ADVANCED FILE MANAGEMENT
|
|
201
|
+
# ============================================================
|
|
202
|
+
|
|
203
|
+
@mcp.tool()
|
|
204
|
+
def append_file(file: str, content: str):
|
|
205
|
+
'''Append new content to an existing file or create a new file.'''
|
|
206
|
+
try:
|
|
207
|
+
with open(file, "a", encoding="utf-8") as f:
|
|
208
|
+
f.write(content)
|
|
209
|
+
|
|
210
|
+
return f"Content appended successfully to {file}."
|
|
211
|
+
|
|
212
|
+
except Exception as e:
|
|
213
|
+
return str(e)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@mcp.tool()
|
|
217
|
+
def copy_file(source: str, destination: str):
|
|
218
|
+
'''Copy a file from source to destination.'''
|
|
219
|
+
try:
|
|
220
|
+
if not os.path.isfile(source):
|
|
221
|
+
return "Source file does not exist."
|
|
222
|
+
|
|
223
|
+
shutil.copy2(source, destination)
|
|
224
|
+
|
|
225
|
+
return f"File copied from {source} to {destination}."
|
|
226
|
+
|
|
227
|
+
except Exception as e:
|
|
228
|
+
return str(e)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
@mcp.tool()
|
|
232
|
+
def move_file(source: str, destination: str):
|
|
233
|
+
'''Move a file from one location to another.'''
|
|
234
|
+
try:
|
|
235
|
+
if not os.path.exists(source):
|
|
236
|
+
return "Source does not exist."
|
|
237
|
+
|
|
238
|
+
shutil.move(source, destination)
|
|
239
|
+
|
|
240
|
+
return f"Moved {source} to {destination}."
|
|
241
|
+
|
|
242
|
+
except Exception as e:
|
|
243
|
+
return str(e)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
@mcp.tool()
|
|
247
|
+
def rename_file(file: str, new_name: str):
|
|
248
|
+
'''Rename a file or directory.'''
|
|
249
|
+
try:
|
|
250
|
+
path = Path(file)
|
|
251
|
+
|
|
252
|
+
if not path.exists():
|
|
253
|
+
return "File or directory does not exist."
|
|
254
|
+
|
|
255
|
+
new_path = path.with_name(new_name)
|
|
256
|
+
path.rename(new_path)
|
|
257
|
+
|
|
258
|
+
return f"Renamed successfully to {new_path}."
|
|
259
|
+
|
|
260
|
+
except Exception as e:
|
|
261
|
+
return str(e)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
@mcp.tool()
|
|
265
|
+
def file_info(file: str):
|
|
266
|
+
'''Get detailed information about a file including size, extension and timestamps.'''
|
|
267
|
+
try:
|
|
268
|
+
path = Path(file)
|
|
269
|
+
|
|
270
|
+
if not path.exists():
|
|
271
|
+
return "File does not exist."
|
|
272
|
+
|
|
273
|
+
stats = path.stat()
|
|
274
|
+
|
|
275
|
+
information = {
|
|
276
|
+
"name": path.name,
|
|
277
|
+
"absolute_path": str(path.resolve()),
|
|
278
|
+
"extension": path.suffix,
|
|
279
|
+
"size_bytes": stats.st_size,
|
|
280
|
+
"size_kb": round(stats.st_size / 1024, 2),
|
|
281
|
+
"created_time": datetime.fromtimestamp(stats.st_ctime).isoformat(),
|
|
282
|
+
"modified_time": datetime.fromtimestamp(stats.st_mtime).isoformat(),
|
|
283
|
+
"is_file": path.is_file(),
|
|
284
|
+
"is_directory": path.is_dir()
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return json.dumps(information, indent=4)
|
|
288
|
+
|
|
289
|
+
except Exception as e:
|
|
290
|
+
return str(e)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
# ============================================================
|
|
294
|
+
# 4. CODE INTELLIGENCE TOOLS
|
|
295
|
+
# ============================================================
|
|
296
|
+
|
|
297
|
+
@mcp.tool()
|
|
298
|
+
def search_files(directory: str, pattern: str, file_extension: str = "*"):
|
|
299
|
+
'''Search for text inside files recursively.
|
|
300
|
+
Supports filtering by file extension, for example .py or .js.
|
|
301
|
+
'''
|
|
302
|
+
try:
|
|
303
|
+
root = Path(directory)
|
|
304
|
+
|
|
305
|
+
if not root.is_dir():
|
|
306
|
+
return "Invalid directory."
|
|
307
|
+
|
|
308
|
+
results = []
|
|
309
|
+
|
|
310
|
+
for file in root.rglob("*"):
|
|
311
|
+
if not file.is_file():
|
|
312
|
+
continue
|
|
313
|
+
|
|
314
|
+
if file_extension != "*" and file.suffix.lower() != file_extension.lower():
|
|
315
|
+
continue
|
|
316
|
+
|
|
317
|
+
try:
|
|
318
|
+
with file.open("r", encoding="utf-8") as f:
|
|
319
|
+
for line_number, line in enumerate(f, start=1):
|
|
320
|
+
if pattern.lower() in line.lower():
|
|
321
|
+
results.append({
|
|
322
|
+
"file": str(file),
|
|
323
|
+
"line": line_number,
|
|
324
|
+
"content": line.strip()
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
except (UnicodeDecodeError, PermissionError, OSError):
|
|
328
|
+
continue
|
|
329
|
+
|
|
330
|
+
return json.dumps(results, indent=4)
|
|
331
|
+
|
|
332
|
+
except Exception as e:
|
|
333
|
+
return str(e)
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
@mcp.tool()
|
|
337
|
+
def count_lines(file: str):
|
|
338
|
+
'''Count total lines, non-empty lines and characters in a source-code file.'''
|
|
339
|
+
try:
|
|
340
|
+
path = Path(file)
|
|
341
|
+
|
|
342
|
+
if not path.is_file():
|
|
343
|
+
return "File does not exist."
|
|
344
|
+
|
|
345
|
+
with path.open("r", encoding="utf-8") as f:
|
|
346
|
+
content = f.read()
|
|
347
|
+
|
|
348
|
+
lines = content.splitlines()
|
|
349
|
+
|
|
350
|
+
result = {
|
|
351
|
+
"file": str(path),
|
|
352
|
+
"total_lines": len(lines),
|
|
353
|
+
"non_empty_lines": sum(1 for line in lines if line.strip()),
|
|
354
|
+
"characters": len(content),
|
|
355
|
+
"words": len(content.split())
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return json.dumps(result, indent=4)
|
|
359
|
+
|
|
360
|
+
except Exception as e:
|
|
361
|
+
return str(e)
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
@mcp.tool()
|
|
365
|
+
def file_hash(file: str):
|
|
366
|
+
'''Calculate SHA-256 hash of a file for integrity verification.'''
|
|
367
|
+
try:
|
|
368
|
+
path = Path(file)
|
|
369
|
+
|
|
370
|
+
if not path.is_file():
|
|
371
|
+
return "File does not exist."
|
|
372
|
+
|
|
373
|
+
sha256 = hashlib.sha256()
|
|
374
|
+
|
|
375
|
+
with path.open("rb") as f:
|
|
376
|
+
for chunk in iter(lambda: f.read(1024 * 1024), b""):
|
|
377
|
+
sha256.update(chunk)
|
|
378
|
+
|
|
379
|
+
return {
|
|
380
|
+
"file": str(path),
|
|
381
|
+
"algorithm": "SHA-256",
|
|
382
|
+
"hash": sha256.hexdigest()
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
except Exception as e:
|
|
386
|
+
return str(e)
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
# ============================================================
|
|
390
|
+
# 5. SYSTEM INFORMATION TOOLS
|
|
391
|
+
# ============================================================
|
|
392
|
+
|
|
393
|
+
@mcp.tool()
|
|
394
|
+
def system_info():
|
|
395
|
+
'''Get operating system, architecture, Python version and machine information.'''
|
|
396
|
+
try:
|
|
397
|
+
information = {
|
|
398
|
+
"operating_system": platform.system(),
|
|
399
|
+
"os_release": platform.release(),
|
|
400
|
+
"os_version": platform.version(),
|
|
401
|
+
"architecture": platform.machine(),
|
|
402
|
+
"processor": platform.processor(),
|
|
403
|
+
"hostname": platform.node(),
|
|
404
|
+
"python_version": sys.version,
|
|
405
|
+
"python_executable": sys.executable,
|
|
406
|
+
"current_directory": os.getcwd()
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return json.dumps(information, indent=4)
|
|
410
|
+
|
|
411
|
+
except Exception as e:
|
|
412
|
+
return str(e)
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
@mcp.tool()
|
|
416
|
+
def disk_usage(path: str = "."):
|
|
417
|
+
'''Check total, used and available disk space for a given path.'''
|
|
418
|
+
try:
|
|
419
|
+
total, used, free = shutil.disk_usage(path)
|
|
420
|
+
|
|
421
|
+
information = {
|
|
422
|
+
"path": str(Path(path).resolve()),
|
|
423
|
+
"total_gb": round(total / (1024 ** 3), 2),
|
|
424
|
+
"used_gb": round(used / (1024 ** 3), 2),
|
|
425
|
+
"free_gb": round(free / (1024 ** 3), 2),
|
|
426
|
+
"usage_percent": round((used / total) * 100, 2)
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
return json.dumps(information, indent=4)
|
|
430
|
+
|
|
431
|
+
except Exception as e:
|
|
432
|
+
return str(e)
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
@mcp.tool()
|
|
436
|
+
def installed_packages():
|
|
437
|
+
'''List all Python packages installed in the active Python environment.'''
|
|
438
|
+
try:
|
|
439
|
+
result = subprocess.run(
|
|
440
|
+
[sys.executable, "-m", "pip", "list", "--format=json"],
|
|
441
|
+
capture_output=True,
|
|
442
|
+
text=True,
|
|
443
|
+
check=True
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
return result.stdout
|
|
447
|
+
|
|
448
|
+
except subprocess.CalledProcessError as e:
|
|
449
|
+
return e.stderr
|
|
450
|
+
|
|
451
|
+
except Exception as e:
|
|
452
|
+
return str(e)
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
# ============================================================
|
|
456
|
+
# 6. SERVER ENTRY POINT
|
|
457
|
+
# ============================================================
|
|
458
|
+
|
|
459
|
+
if __name__ == "__main__":
|
|
460
|
+
mcp.run()
|