scmcp-shared 0.3.0__tar.gz → 0.3.5__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.
- {scmcp_shared-0.3.0 → scmcp_shared-0.3.5}/PKG-INFO +2 -2
- {scmcp_shared-0.3.0 → scmcp_shared-0.3.5}/pyproject.toml +1 -1
- scmcp_shared-0.3.5/src/scmcp_shared/__init__.py +3 -0
- scmcp_shared-0.3.5/src/scmcp_shared/cli.py +111 -0
- {scmcp_shared-0.3.0 → scmcp_shared-0.3.5}/src/scmcp_shared/schema/tl.py +5 -0
- {scmcp_shared-0.3.0 → scmcp_shared-0.3.5}/src/scmcp_shared/server/tl.py +1 -1
- {scmcp_shared-0.3.0 → scmcp_shared-0.3.5}/src/scmcp_shared/util.py +2 -6
- scmcp_shared-0.3.0/src/scmcp_shared/__init__.py +0 -3
- scmcp_shared-0.3.0/src/scmcp_shared/cli.py +0 -96
- {scmcp_shared-0.3.0 → scmcp_shared-0.3.5}/.github/release.yml +0 -0
- {scmcp_shared-0.3.0 → scmcp_shared-0.3.5}/.github/workflows/publish.yml +0 -0
- {scmcp_shared-0.3.0 → scmcp_shared-0.3.5}/LICENSE +0 -0
- {scmcp_shared-0.3.0 → scmcp_shared-0.3.5}/README.md +0 -0
- {scmcp_shared-0.3.0 → scmcp_shared-0.3.5}/src/scmcp_shared/logging_config.py +0 -0
- {scmcp_shared-0.3.0 → scmcp_shared-0.3.5}/src/scmcp_shared/schema/__init__.py +0 -0
- {scmcp_shared-0.3.0 → scmcp_shared-0.3.5}/src/scmcp_shared/schema/io.py +0 -0
- {scmcp_shared-0.3.0 → scmcp_shared-0.3.5}/src/scmcp_shared/schema/pl.py +0 -0
- {scmcp_shared-0.3.0 → scmcp_shared-0.3.5}/src/scmcp_shared/schema/pp.py +0 -0
- {scmcp_shared-0.3.0 → scmcp_shared-0.3.5}/src/scmcp_shared/schema/util.py +0 -0
- {scmcp_shared-0.3.0 → scmcp_shared-0.3.5}/src/scmcp_shared/server/__init__.py +0 -0
- {scmcp_shared-0.3.0 → scmcp_shared-0.3.5}/src/scmcp_shared/server/base.py +0 -0
- {scmcp_shared-0.3.0 → scmcp_shared-0.3.5}/src/scmcp_shared/server/io.py +0 -0
- {scmcp_shared-0.3.0 → scmcp_shared-0.3.5}/src/scmcp_shared/server/pl.py +0 -0
- {scmcp_shared-0.3.0 → scmcp_shared-0.3.5}/src/scmcp_shared/server/pp.py +0 -0
- {scmcp_shared-0.3.0 → scmcp_shared-0.3.5}/src/scmcp_shared/server/util.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: scmcp_shared
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.5
|
4
4
|
Summary: A shared function libray for scmcphub
|
5
5
|
Author-email: shuang <hsh-me@outlook.com>
|
6
6
|
License: BSD 3-Clause License
|
@@ -35,7 +35,7 @@ License-File: LICENSE
|
|
35
35
|
Requires-Python: >=3.10
|
36
36
|
Requires-Dist: fastmcp>=2.3.4
|
37
37
|
Requires-Dist: mcp>=1.8.0
|
38
|
-
Requires-Dist:
|
38
|
+
Requires-Dist: nest-asyncio
|
39
39
|
Requires-Dist: scanpy
|
40
40
|
Description-Content-Type: text/markdown
|
41
41
|
|
@@ -0,0 +1,111 @@
|
|
1
|
+
import argparse
|
2
|
+
from typing import Optional, Union, Type, Dict, Callable
|
3
|
+
from enum import Enum
|
4
|
+
from .util import add_figure_route, set_env
|
5
|
+
import os
|
6
|
+
|
7
|
+
|
8
|
+
class MCPCLI:
|
9
|
+
"""Base class for CLI applications with support for dynamic modules and parameters."""
|
10
|
+
|
11
|
+
def __init__(self, name: str, help_text: str, mcp=None, manager=None):
|
12
|
+
self.name = name
|
13
|
+
self.mcp = mcp
|
14
|
+
self.manager = manager
|
15
|
+
self.parser = argparse.ArgumentParser(
|
16
|
+
description=help_text,
|
17
|
+
prog=name
|
18
|
+
)
|
19
|
+
self.subcommands: Dict[str, tuple[argparse.ArgumentParser, Callable]] = {}
|
20
|
+
self._setup_commands()
|
21
|
+
|
22
|
+
def _setup_commands(self):
|
23
|
+
"""Setup the main commands for the CLI."""
|
24
|
+
subparsers = self.parser.add_subparsers(dest='command', help='Available commands')
|
25
|
+
run_parser = subparsers.add_parser('run', help='Start the server with the specified configuration')
|
26
|
+
self._setup_run_command(run_parser)
|
27
|
+
self.subcommands['run'] = (run_parser, self._run_command)
|
28
|
+
|
29
|
+
def _setup_run_command(self, parser: argparse.ArgumentParser):
|
30
|
+
"""Setup run command arguments."""
|
31
|
+
parser.add_argument('-t', '--transport', default="stdio",
|
32
|
+
choices=["stdio", "shttp", "sse"],
|
33
|
+
help='specify transport type')
|
34
|
+
parser.add_argument('-p', '--port', type=int, default=8000, help='transport port')
|
35
|
+
parser.add_argument('--host', default='127.0.0.1', help='transport host')
|
36
|
+
parser.add_argument('-f', '--forward', help='forward request to another server')
|
37
|
+
parser.add_argument('-wd', '--working-dir', default=".", help='working directory')
|
38
|
+
parser.add_argument('--log-file', help='log file path, use stdout if None')
|
39
|
+
|
40
|
+
def add_command(self, name: str, help_text: str, handler: Callable) -> argparse.ArgumentParser:
|
41
|
+
"""add new subcommand
|
42
|
+
|
43
|
+
Args:
|
44
|
+
name: subcommand name
|
45
|
+
help_text: help text
|
46
|
+
handler: handler function
|
47
|
+
|
48
|
+
Returns:
|
49
|
+
ArgumentParser: parser for the subcommand
|
50
|
+
"""
|
51
|
+
subparsers = self.parser._subparsers._group_actions[0]
|
52
|
+
parser = subparsers.add_parser(name, help=help_text)
|
53
|
+
self.subcommands[name] = (parser, handler)
|
54
|
+
return parser
|
55
|
+
|
56
|
+
def get_command_parser(self, name: str) -> Optional[argparse.ArgumentParser]:
|
57
|
+
"""get the parser for the subcommand
|
58
|
+
|
59
|
+
Args:
|
60
|
+
name: subcommand name
|
61
|
+
|
62
|
+
Returns:
|
63
|
+
ArgumentParser: parser for the subcommand, return None if the subcommand does not exist
|
64
|
+
"""
|
65
|
+
if name in self.subcommands:
|
66
|
+
return self.subcommands[name][0]
|
67
|
+
return None
|
68
|
+
|
69
|
+
def _run_command(self, args):
|
70
|
+
"""Start the server with the specified configuration."""
|
71
|
+
os.chdir(args.working_dir)
|
72
|
+
if hasattr(args, 'module'):
|
73
|
+
if "all" in args.module:
|
74
|
+
modules = None
|
75
|
+
elif isinstance(args.module, list) and bool(args.module):
|
76
|
+
modules = args.module
|
77
|
+
else:
|
78
|
+
modules = None
|
79
|
+
if self.manager is not None:
|
80
|
+
self.mcp = self.manager(self.name, include_modules=modules).mcp
|
81
|
+
elif self.mcp is not None:
|
82
|
+
pass
|
83
|
+
else:
|
84
|
+
raise ValueError("No manager or mcp provided")
|
85
|
+
transport = args.transport
|
86
|
+
self.run_mcp(args.log_file, args.forward, transport, args.host, args.port)
|
87
|
+
|
88
|
+
def run_mcp(self, log_file, forward, transport, host, port):
|
89
|
+
set_env(log_file, forward, transport, host, port)
|
90
|
+
from .logging_config import setup_logger
|
91
|
+
setup_logger(log_file)
|
92
|
+
if transport == "stdio":
|
93
|
+
self.mcp.run()
|
94
|
+
elif transport in ["sse", "shttp"]:
|
95
|
+
transport = "streamable-http" if transport == "shttp" else transport
|
96
|
+
add_figure_route(self.mcp)
|
97
|
+
self.mcp.run(
|
98
|
+
transport=transport,
|
99
|
+
host=host,
|
100
|
+
port=port,
|
101
|
+
log_level="info"
|
102
|
+
)
|
103
|
+
|
104
|
+
def run(self):
|
105
|
+
"""Run the CLI application."""
|
106
|
+
args = self.parser.parse_args()
|
107
|
+
if args.command in self.subcommands:
|
108
|
+
handler = self.subcommands[args.command][1]
|
109
|
+
handler(args)
|
110
|
+
else:
|
111
|
+
self.parser.print_help()
|
@@ -938,6 +938,11 @@ class PCAModel(BaseModel):
|
|
938
938
|
gt=0
|
939
939
|
)
|
940
940
|
|
941
|
+
key_added: str = Field(
|
942
|
+
default="X_pca",
|
943
|
+
description="PCA embedding stored key in adata.obsm."
|
944
|
+
)
|
945
|
+
|
941
946
|
@field_validator('n_comps', 'chunk_size')
|
942
947
|
def validate_positive_integers(cls, v: Optional[int]) -> Optional[int]:
|
943
948
|
"""Validate positive integers"""
|
@@ -376,7 +376,7 @@ class ScanpyToolsMCP(BaseMCP):
|
|
376
376
|
return _score_genes_cell_cycle
|
377
377
|
|
378
378
|
def _tool_pca(self):
|
379
|
-
def _pca(request: PCAModel, adinfo: self.AdataInfo=self.AdataInfo()):
|
379
|
+
def _pca(request: PCAModel=PCAModel(), adinfo: self.AdataInfo=self.AdataInfo()):
|
380
380
|
"""Compute PCA (Principal Component Analysis)."""
|
381
381
|
try:
|
382
382
|
result = forward_request("tl_pca", request, adinfo)
|
@@ -260,14 +260,10 @@ def setup_mcp(mcp, sub_mcp_dic, modules=None):
|
|
260
260
|
return mcp
|
261
261
|
|
262
262
|
def _update_args(mcp, func, args_dic : dict):
|
263
|
-
defs = mcp._tool_manager._tools[func].parameters['$defs']
|
264
|
-
model_names = list(defs.keys())
|
265
|
-
args_model = model_names[0] if model_names[0] != "AdataModel" else model_names[1]
|
266
263
|
for args, property_dic in args_dic.items():
|
267
264
|
for pk, v in property_dic.items():
|
268
|
-
|
269
|
-
|
270
|
-
mcp._tool_manager._tools[func].parameters['$defs'][model]["properties"][args][pk] = v
|
265
|
+
mcp._tool_manager._tools[func].parameters["properties"]["request"].setdefault(pk, {})
|
266
|
+
mcp._tool_manager._tools[func].parameters["properties"]["request"][pk][args] = v
|
271
267
|
|
272
268
|
|
273
269
|
def update_mcp_args(mcp, tool_args : dict):
|
@@ -1,96 +0,0 @@
|
|
1
|
-
import typer
|
2
|
-
from typing import Optional, Union, Type
|
3
|
-
from enum import Enum
|
4
|
-
from .util import add_figure_route, set_env,setup_mcp
|
5
|
-
|
6
|
-
|
7
|
-
class TransportEnum(str, Enum):
|
8
|
-
STDIO = "stdio"
|
9
|
-
SSE = "sse"
|
10
|
-
SHTTP = "shttp"
|
11
|
-
|
12
|
-
@property
|
13
|
-
def transport_value(self) -> str:
|
14
|
-
"""Get the actual transport value to use."""
|
15
|
-
if self == TransportEnum.SHTTP:
|
16
|
-
return "streamable-http"
|
17
|
-
return self.value
|
18
|
-
|
19
|
-
|
20
|
-
class ModuleEnum(str, Enum):
|
21
|
-
"""Base class for module types."""
|
22
|
-
ALL = "all"
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
class MCPCLI:
|
27
|
-
"""Base class for CLI applications with support for dynamic modules and parameters."""
|
28
|
-
|
29
|
-
def __init__(self, name: str, help_text: str, manager=None, modules=ModuleEnum):
|
30
|
-
self.name = name
|
31
|
-
self.modules = modules
|
32
|
-
self.manager = manager
|
33
|
-
self.app = typer.Typer(
|
34
|
-
name=name,
|
35
|
-
help=help_text,
|
36
|
-
add_completion=False,
|
37
|
-
no_args_is_help=True,
|
38
|
-
)
|
39
|
-
self._setup_commands()
|
40
|
-
|
41
|
-
def _setup_commands(self):
|
42
|
-
"""Setup the main commands for the CLI."""
|
43
|
-
self.app.command(name="run", help="Start the server with the specified configuration")(self.run_command())
|
44
|
-
self.app.callback()(self._callback)
|
45
|
-
|
46
|
-
def run_command(self):
|
47
|
-
def _run_command(
|
48
|
-
log_file: Optional[str] = typer.Option(None, "--log-file", help="log file path, use stdout if None"),
|
49
|
-
transport: TransportEnum = typer.Option(TransportEnum.STDIO, "-t", "--transport", help="specify transport type",
|
50
|
-
case_sensitive=False),
|
51
|
-
port: int = typer.Option(8000, "-p", "--port", help="transport port"),
|
52
|
-
host: str = typer.Option("127.0.0.1", "--host", help="transport host"),
|
53
|
-
forward: str = typer.Option(None, "-f", "--forward", help="forward request to another server"),
|
54
|
-
module: list[self.modules] = typer.Option(
|
55
|
-
[self.modules.ALL],
|
56
|
-
"-m",
|
57
|
-
"--module",
|
58
|
-
help="specify module to run"
|
59
|
-
),
|
60
|
-
):
|
61
|
-
"""Start the server with the specified configuration."""
|
62
|
-
if "all" in module:
|
63
|
-
modules = None
|
64
|
-
elif isinstance(module, list) and bool(module):
|
65
|
-
modules = [m.value for m in module]
|
66
|
-
self.mcp = self.manager(self.name, include_modules=modules).mcp
|
67
|
-
|
68
|
-
self.run_mcp(log_file, forward, transport, host, port)
|
69
|
-
return _run_command
|
70
|
-
|
71
|
-
|
72
|
-
def _callback(self):
|
73
|
-
"""Liana MCP CLI root command."""
|
74
|
-
pass
|
75
|
-
|
76
|
-
def run_mcp(self, log_file, forward, transport, host, port):
|
77
|
-
set_env(log_file, forward, transport, host, port)
|
78
|
-
from .logging_config import setup_logger
|
79
|
-
setup_logger(log_file)
|
80
|
-
if transport == "stdio":
|
81
|
-
self.mcp.run()
|
82
|
-
elif transport in ["sse", "shttp"]:
|
83
|
-
add_figure_route(self.mcp)
|
84
|
-
transport = transport.transport_value
|
85
|
-
self.mcp.run(
|
86
|
-
transport=transport,
|
87
|
-
host=host,
|
88
|
-
port=port,
|
89
|
-
log_level="info"
|
90
|
-
)
|
91
|
-
|
92
|
-
def run_cli(self, mcp=None, module_dic=None):
|
93
|
-
"""Run the CLI application."""
|
94
|
-
self.mcp = mcp
|
95
|
-
self.module_dic = module_dic
|
96
|
-
self.app()
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|