chatspatial 1.1.0__py3-none-any.whl

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 (67) hide show
  1. chatspatial/__init__.py +11 -0
  2. chatspatial/__main__.py +141 -0
  3. chatspatial/cli/__init__.py +7 -0
  4. chatspatial/config.py +53 -0
  5. chatspatial/models/__init__.py +85 -0
  6. chatspatial/models/analysis.py +513 -0
  7. chatspatial/models/data.py +2462 -0
  8. chatspatial/server.py +1763 -0
  9. chatspatial/spatial_mcp_adapter.py +720 -0
  10. chatspatial/tools/__init__.py +3 -0
  11. chatspatial/tools/annotation.py +1903 -0
  12. chatspatial/tools/cell_communication.py +1603 -0
  13. chatspatial/tools/cnv_analysis.py +605 -0
  14. chatspatial/tools/condition_comparison.py +595 -0
  15. chatspatial/tools/deconvolution/__init__.py +402 -0
  16. chatspatial/tools/deconvolution/base.py +318 -0
  17. chatspatial/tools/deconvolution/card.py +244 -0
  18. chatspatial/tools/deconvolution/cell2location.py +326 -0
  19. chatspatial/tools/deconvolution/destvi.py +144 -0
  20. chatspatial/tools/deconvolution/flashdeconv.py +101 -0
  21. chatspatial/tools/deconvolution/rctd.py +317 -0
  22. chatspatial/tools/deconvolution/spotlight.py +216 -0
  23. chatspatial/tools/deconvolution/stereoscope.py +109 -0
  24. chatspatial/tools/deconvolution/tangram.py +135 -0
  25. chatspatial/tools/differential.py +625 -0
  26. chatspatial/tools/embeddings.py +298 -0
  27. chatspatial/tools/enrichment.py +1863 -0
  28. chatspatial/tools/integration.py +807 -0
  29. chatspatial/tools/preprocessing.py +723 -0
  30. chatspatial/tools/spatial_domains.py +808 -0
  31. chatspatial/tools/spatial_genes.py +836 -0
  32. chatspatial/tools/spatial_registration.py +441 -0
  33. chatspatial/tools/spatial_statistics.py +1476 -0
  34. chatspatial/tools/trajectory.py +495 -0
  35. chatspatial/tools/velocity.py +405 -0
  36. chatspatial/tools/visualization/__init__.py +155 -0
  37. chatspatial/tools/visualization/basic.py +393 -0
  38. chatspatial/tools/visualization/cell_comm.py +699 -0
  39. chatspatial/tools/visualization/cnv.py +320 -0
  40. chatspatial/tools/visualization/core.py +684 -0
  41. chatspatial/tools/visualization/deconvolution.py +852 -0
  42. chatspatial/tools/visualization/enrichment.py +660 -0
  43. chatspatial/tools/visualization/integration.py +205 -0
  44. chatspatial/tools/visualization/main.py +164 -0
  45. chatspatial/tools/visualization/multi_gene.py +739 -0
  46. chatspatial/tools/visualization/persistence.py +335 -0
  47. chatspatial/tools/visualization/spatial_stats.py +469 -0
  48. chatspatial/tools/visualization/trajectory.py +639 -0
  49. chatspatial/tools/visualization/velocity.py +411 -0
  50. chatspatial/utils/__init__.py +115 -0
  51. chatspatial/utils/adata_utils.py +1372 -0
  52. chatspatial/utils/compute.py +327 -0
  53. chatspatial/utils/data_loader.py +499 -0
  54. chatspatial/utils/dependency_manager.py +462 -0
  55. chatspatial/utils/device_utils.py +165 -0
  56. chatspatial/utils/exceptions.py +185 -0
  57. chatspatial/utils/image_utils.py +267 -0
  58. chatspatial/utils/mcp_utils.py +137 -0
  59. chatspatial/utils/path_utils.py +243 -0
  60. chatspatial/utils/persistence.py +78 -0
  61. chatspatial/utils/scipy_compat.py +143 -0
  62. chatspatial-1.1.0.dist-info/METADATA +242 -0
  63. chatspatial-1.1.0.dist-info/RECORD +67 -0
  64. chatspatial-1.1.0.dist-info/WHEEL +5 -0
  65. chatspatial-1.1.0.dist-info/entry_points.txt +2 -0
  66. chatspatial-1.1.0.dist-info/licenses/LICENSE +21 -0
  67. chatspatial-1.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,243 @@
1
+ """Path handling utilities for ChatSpatial MCP server.
2
+
3
+ This module provides robust path handling that works correctly regardless of
4
+ the current working directory when the MCP server is launched.
5
+
6
+ Key features:
7
+ - Resolves relative paths against project root (not cwd)
8
+ - Automatic fallback to /tmp for permission issues
9
+ - Security checks to prevent writing outside safe directories
10
+ """
11
+
12
+ import os
13
+ import warnings
14
+ from pathlib import Path
15
+
16
+ # Project root directory (based on utils module location)
17
+ # This is always correct regardless of cwd
18
+ _PROJECT_ROOT = Path(__file__).parent.parent.resolve()
19
+
20
+
21
+ def get_project_root() -> Path:
22
+ """Get ChatSpatial project root directory.
23
+
24
+ Returns:
25
+ Absolute path to project root, regardless of current working directory.
26
+
27
+ Example:
28
+ >>> root = get_project_root()
29
+ >>> print(root)
30
+ /path/to/chatspatial/chatspatial
31
+ """
32
+ return _PROJECT_ROOT
33
+
34
+
35
+ def get_safe_output_path(
36
+ output_dir: str,
37
+ fallback_to_tmp: bool = True,
38
+ create_if_missing: bool = True,
39
+ ) -> Path:
40
+ """Get safe, writable output directory path.
41
+
42
+ This function handles path resolution robustly:
43
+ - Relative paths are resolved against CURRENT WORKING DIRECTORY (respects user config)
44
+ - Tests write permission before returning
45
+ - Falls back to /tmp/chatspatial/outputs if original path not writable
46
+
47
+ Args:
48
+ output_dir: User-provided output directory (relative or absolute)
49
+ fallback_to_tmp: If True, fallback to /tmp if output_dir not writable
50
+ create_if_missing: If True, create directory if it doesn't exist
51
+
52
+ Returns:
53
+ Absolute path to writable output directory
54
+
55
+ Raises:
56
+ PermissionError: If no writable path can be found (when fallback disabled)
57
+
58
+ Examples:
59
+ >>> # Relative path (resolved against cwd)
60
+ >>> path = get_safe_output_path("./outputs")
61
+ >>> # Returns: <cwd>/outputs
62
+
63
+ >>> # Absolute path
64
+ >>> path = get_safe_output_path("/tmp/my_outputs")
65
+ >>> # Returns: /tmp/my_outputs
66
+
67
+ >>> # Read-only path with fallback
68
+ >>> path = get_safe_output_path("/outputs")
69
+ >>> # Returns: /tmp/chatspatial/outputs (with warning)
70
+ """
71
+ # Convert to Path object
72
+ user_path = Path(output_dir)
73
+
74
+ # If absolute path, use directly; otherwise resolve against CWD
75
+ if user_path.is_absolute():
76
+ target_path = user_path
77
+ else:
78
+ # For relative paths, resolve against CWD (respects user configuration!)
79
+ # This follows standard Unix/Python conventions
80
+ target_path = Path.cwd() / user_path
81
+
82
+ # Try to create/verify the directory
83
+ try:
84
+ if create_if_missing:
85
+ target_path.mkdir(parents=True, exist_ok=True)
86
+
87
+ # Test write permission by creating a temporary test file
88
+ test_file = target_path / ".write_test"
89
+ test_file.touch()
90
+ test_file.unlink()
91
+
92
+ return target_path
93
+
94
+ except (OSError, PermissionError) as e:
95
+ # If fallback enabled, try temp directory
96
+ if fallback_to_tmp:
97
+ warnings.warn(
98
+ f"Cannot write to {target_path}: {e}. "
99
+ f"Falling back to /tmp/chatspatial/outputs",
100
+ UserWarning,
101
+ stacklevel=2,
102
+ )
103
+
104
+ fallback_path = Path("/tmp/chatspatial/outputs")
105
+ fallback_path.mkdir(parents=True, exist_ok=True)
106
+ return fallback_path
107
+ else:
108
+ raise PermissionError(
109
+ f"Cannot write to output directory: {target_path}. " f"Error: {e}"
110
+ ) from e
111
+
112
+
113
+ def is_safe_output_path(path: Path) -> bool:
114
+ """Check if output path is safe (within project or /tmp).
115
+
116
+ This provides security by ensuring files are only written to:
117
+ 1. Project directory or its subdirectories
118
+ 2. /tmp/chatspatial directory
119
+
120
+ Args:
121
+ path: Path to check
122
+
123
+ Returns:
124
+ True if path is safe for output, False otherwise
125
+
126
+ Examples:
127
+ >>> # Safe paths
128
+ >>> is_safe_output_path(Path("/Users/.../chatspatial/outputs"))
129
+ True
130
+ >>> is_safe_output_path(Path("/tmp/chatspatial/outputs"))
131
+ True
132
+
133
+ >>> # Unsafe paths
134
+ >>> is_safe_output_path(Path("/etc/outputs"))
135
+ False
136
+ >>> is_safe_output_path(Path("/Users/other_user/outputs"))
137
+ False
138
+ """
139
+ # Normalize path to absolute
140
+ abs_path = path.resolve() if not path.is_absolute() else path
141
+
142
+ project_root = get_project_root()
143
+ tmp_root = Path("/tmp/chatspatial")
144
+
145
+ # Allow paths within project directory
146
+ try:
147
+ abs_path.relative_to(project_root)
148
+ return True
149
+ except ValueError:
150
+ pass
151
+
152
+ # Allow paths within /tmp/chatspatial
153
+ try:
154
+ abs_path.relative_to(tmp_root)
155
+ return True
156
+ except ValueError:
157
+ pass
158
+
159
+ # Disallow all other paths
160
+ return False
161
+
162
+
163
+ def get_output_dir_from_config(default: str = "./outputs") -> str:
164
+ """Get output directory from environment variable or configuration.
165
+
166
+ Priority order:
167
+ 1. CHATSPATIAL_OUTPUT_DIR environment variable (highest priority)
168
+ 2. Default value (usually "./outputs")
169
+
170
+ This allows users to configure the output directory via:
171
+ - Claude Desktop config: env.CHATSPATIAL_OUTPUT_DIR
172
+ - Shell environment: export CHATSPATIAL_OUTPUT_DIR=/path/to/outputs
173
+
174
+ Args:
175
+ default: Default output directory if no configuration found
176
+
177
+ Returns:
178
+ Output directory path (relative or absolute)
179
+
180
+ Examples:
181
+ >>> # With environment variable set
182
+ >>> os.environ["CHATSPATIAL_OUTPUT_DIR"] = "/tmp/results"
183
+ >>> get_output_dir_from_config()
184
+ '/tmp/results'
185
+
186
+ >>> # Without environment variable
187
+ >>> get_output_dir_from_config()
188
+ './outputs'
189
+
190
+ Configuration example for Claude Desktop (claude_desktop_config.json):
191
+ ```json
192
+ {
193
+ "mcpServers": {
194
+ "chatspatial": {
195
+ "command": "python",
196
+ "args": ["-m", "chatspatial"],
197
+ "cwd": "/Users/username/my_project",
198
+ "env": {
199
+ "CHATSPATIAL_OUTPUT_DIR": "./results"
200
+ }
201
+ }
202
+ }
203
+ }
204
+ ```
205
+ """
206
+ # Check environment variable (highest priority)
207
+ if env_dir := os.getenv("CHATSPATIAL_OUTPUT_DIR"):
208
+ return env_dir
209
+
210
+ # Return default
211
+ return default
212
+
213
+
214
+ def resolve_output_path(
215
+ output_dir: str,
216
+ default_dir: str = "./outputs",
217
+ ) -> Path:
218
+ """Resolve output directory path safely.
219
+
220
+ Resolves relative paths against current working directory,
221
+ following standard Unix/Python conventions.
222
+
223
+ Args:
224
+ output_dir: User-provided output directory
225
+ default_dir: Default directory if output_dir is None
226
+
227
+ Returns:
228
+ Resolved absolute path
229
+
230
+ Examples:
231
+ >>> resolve_output_path("./outputs")
232
+ PosixPath('<cwd>/outputs')
233
+
234
+ >>> resolve_output_path("/tmp/test")
235
+ PosixPath('/tmp/test')
236
+ """
237
+ path = Path(output_dir or default_dir)
238
+
239
+ if path.is_absolute():
240
+ return path
241
+ else:
242
+ # Resolve against CWD (respects user configuration)
243
+ return Path.cwd() / path
@@ -0,0 +1,78 @@
1
+ """
2
+ Data persistence utilities for spatial transcriptomics data.
3
+
4
+ Handles saving AnnData objects to disk with proper path management.
5
+ """
6
+
7
+ import os
8
+ from pathlib import Path
9
+ from typing import TYPE_CHECKING
10
+
11
+ if TYPE_CHECKING:
12
+ from anndata import AnnData
13
+
14
+
15
+ def get_save_path(data_id: str, original_path: str) -> Path:
16
+ """
17
+ Get save path for adata, supports environment variable configuration.
18
+
19
+ Priority:
20
+ 1. CHATSPATIAL_DATA_DIR environment variable
21
+ 2. .chatspatial_saved/ directory next to original data (default)
22
+
23
+ Args:
24
+ data_id: Dataset identifier
25
+ original_path: Original data file path
26
+
27
+ Returns:
28
+ Directory path for saving
29
+ """
30
+ env_dir = os.getenv("CHATSPATIAL_DATA_DIR")
31
+ if env_dir:
32
+ save_dir = Path(env_dir)
33
+ save_dir.mkdir(parents=True, exist_ok=True)
34
+ return save_dir
35
+
36
+ # Default: use directory next to original data
37
+ path_obj = Path(original_path)
38
+
39
+ # Determine parent directory based on whether path looks like a file
40
+ # Check if path has a file extension or ends with a known data format
41
+ if path_obj.suffix in [".h5ad", ".h5", ".csv", ".txt", ".mtx", ".gz"]:
42
+ # It's a file path, use parent directory
43
+ parent_dir = path_obj.parent
44
+ elif path_obj.is_dir():
45
+ # It's an existing directory
46
+ parent_dir = path_obj
47
+ else:
48
+ # Assume it's a file path (even if doesn't exist yet)
49
+ parent_dir = path_obj.parent
50
+
51
+ save_dir = parent_dir / ".chatspatial_saved"
52
+ save_dir.mkdir(parents=True, exist_ok=True)
53
+ return save_dir
54
+
55
+
56
+ def save_adata(data_id: str, adata: "AnnData", original_path: str) -> Path:
57
+ """
58
+ Save AnnData object to disk.
59
+
60
+ Args:
61
+ data_id: Dataset identifier
62
+ adata: AnnData object to save
63
+ original_path: Original data file path
64
+
65
+ Returns:
66
+ Path where data was saved
67
+
68
+ Raises:
69
+ IOError: If save fails
70
+ """
71
+ save_dir = get_save_path(data_id, original_path)
72
+ save_path = save_dir / f"{data_id}.h5ad"
73
+
74
+ try:
75
+ adata.write_h5ad(save_path, compression="gzip", compression_opts=4)
76
+ return save_path
77
+ except Exception as e:
78
+ raise IOError(f"Failed to save data to {save_path}: {e}") from e
@@ -0,0 +1,143 @@
1
+ """
2
+ Scipy compatibility layer for deprecated functions.
3
+
4
+ This module provides compatibility shims for scipy functions that have been
5
+ removed or relocated in newer versions, enabling packages like SpatialDE
6
+ that depend on these deprecated APIs to work with modern scipy (>=1.14).
7
+
8
+ Background:
9
+ - scipy.misc.derivative was deprecated in scipy 1.10.0
10
+ - scipy.misc.derivative was removed in scipy 1.14.0
11
+ - SpatialDE 1.1.3 still imports from scipy.misc.derivative
12
+ - This module patches scipy.misc to restore the derivative function
13
+
14
+ Usage:
15
+ Call `patch_scipy_misc_derivative()` before importing SpatialDE:
16
+
17
+ from chatspatial.utils.scipy_compat import patch_scipy_misc_derivative
18
+ patch_scipy_misc_derivative()
19
+ import SpatialDE # Now works with scipy >= 1.14
20
+ """
21
+
22
+ import numpy as np
23
+ from scipy import misc as scipy_misc
24
+
25
+
26
+ def _derivative_compat(func, x0, dx=1.0, n=1, args=(), order=3):
27
+ """Compute the nth derivative of a function at a point.
28
+
29
+ This is a compatibility implementation of the deprecated scipy.misc.derivative,
30
+ using central difference formulas.
31
+
32
+ Parameters
33
+ ----------
34
+ func : callable
35
+ Function of which to compute the derivative. Should accept a single
36
+ float argument and return a float.
37
+ x0 : float
38
+ Point at which to evaluate the derivative.
39
+ dx : float, optional
40
+ Spacing for finite difference. Default is 1.0.
41
+ n : int, optional
42
+ Order of the derivative. Default is 1.
43
+ args : tuple, optional
44
+ Extra arguments to pass to func.
45
+ order : int, optional
46
+ Number of points to use for the central difference formula.
47
+ Must be odd. Default is 3.
48
+
49
+ Returns
50
+ -------
51
+ float
52
+ The nth derivative of func at x0.
53
+
54
+ Notes
55
+ -----
56
+ This implementation uses central difference formulas. The accuracy
57
+ depends on the `order` parameter and the smoothness of the function.
58
+
59
+ For SpatialDE, only n=1 and n=2 are used with default dx=1.0 and order=3.
60
+ """
61
+ if order < n + 1:
62
+ raise ValueError(
63
+ f"'order' ({order}) must be at least the derivative order 'n' ({n}) plus 1"
64
+ )
65
+ if order % 2 == 0:
66
+ raise ValueError(f"'order' ({order}) must be odd")
67
+
68
+ # Central difference weights for various orders and derivative degrees
69
+ # These are the standard finite difference coefficients
70
+ # Reference: https://en.wikipedia.org/wiki/Finite_difference_coefficient
71
+
72
+ # For simplicity, we implement the most common cases used by SpatialDE
73
+ # First derivative using central difference (3-point formula)
74
+ if n == 1 and order >= 3:
75
+ # f'(x) ≈ (f(x+h) - f(x-h)) / (2h)
76
+ return (func(x0 + dx, *args) - func(x0 - dx, *args)) / (2 * dx)
77
+ # Second derivative using central difference (3-point formula)
78
+ elif n == 2 and order >= 3:
79
+ # f''(x) ≈ (f(x+h) - 2f(x) + f(x-h)) / h²
80
+ return (
81
+ func(x0 + dx, *args) - 2 * func(x0, *args) + func(x0 - dx, *args)
82
+ ) / (dx**2)
83
+
84
+ # For higher derivatives or orders, use Richardson extrapolation
85
+ # This is a more general but slower approach
86
+ return _derivative_richardson(func, x0, dx, n, args, order)
87
+
88
+
89
+ def _derivative_richardson(func, x0, dx, n, args, order):
90
+ """Compute derivative using Richardson extrapolation.
91
+
92
+ This is a more general implementation that handles arbitrary
93
+ derivative orders using Richardson extrapolation for improved accuracy.
94
+ """
95
+ # Number of points for central difference
96
+ num_points = order
97
+
98
+ # Generate sample points symmetrically around x0
99
+ half = num_points // 2
100
+ points = np.arange(-half, half + 1) * dx + x0
101
+
102
+ # Evaluate function at all points
103
+ f_values = np.array([func(p, *args) for p in points])
104
+
105
+ # Compute derivative using finite differences
106
+ # For nth derivative, we need to apply the difference operator n times
107
+ result = f_values.copy()
108
+ for _ in range(n):
109
+ result = np.diff(result) / dx
110
+
111
+ # Return the central value
112
+ return result[len(result) // 2]
113
+
114
+
115
+ def patch_scipy_misc_derivative():
116
+ """Patch scipy.misc to include the deprecated derivative function.
117
+
118
+ This function adds a `derivative` attribute to `scipy.misc` module,
119
+ allowing packages like SpatialDE that import `from scipy.misc import derivative`
120
+ to work with scipy >= 1.14.
121
+
122
+ This patch is idempotent - calling it multiple times has no additional effect.
123
+
124
+ Example
125
+ -------
126
+ >>> from chatspatial.utils.scipy_compat import patch_scipy_misc_derivative
127
+ >>> patch_scipy_misc_derivative()
128
+ >>> import SpatialDE # Now works!
129
+ """
130
+ if not hasattr(scipy_misc, 'derivative'):
131
+ scipy_misc.derivative = _derivative_compat
132
+
133
+
134
+ # Auto-check: provide helpful message if scipy already has derivative
135
+ def check_scipy_derivative_status():
136
+ """Check the status of scipy.misc.derivative and return diagnostic info."""
137
+ import scipy
138
+ has_derivative = hasattr(scipy_misc, 'derivative')
139
+ return {
140
+ 'scipy_version': scipy.__version__,
141
+ 'has_derivative': has_derivative,
142
+ 'needs_patch': not has_derivative,
143
+ }
@@ -0,0 +1,242 @@
1
+ Metadata-Version: 2.4
2
+ Name: chatspatial
3
+ Version: 1.1.0
4
+ Summary: ChatSpatial: Natural language-driven spatial transcriptomics analysis via Model Context Protocol (MCP) integration
5
+ Author-email: Chen Yang <cafferychen777@tamu.edu>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/cafferychen777/ChatSpatial
8
+ Project-URL: Documentation, https://chatspatial.readthedocs.io
9
+ Project-URL: Repository, https://github.com/cafferychen777/ChatSpatial
10
+ Project-URL: Issues, https://github.com/cafferychen777/ChatSpatial/issues
11
+ Keywords: spatial-transcriptomics,single-cell,mcp,llm,bioinformatics,scanpy,squidpy
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
21
+ Requires-Python: <3.14,>=3.11
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: mcp>=1.17.0
25
+ Requires-Dist: numpy>=1.21.0
26
+ Requires-Dist: pandas>=1.3.0
27
+ Requires-Dist: scipy>=1.7.0
28
+ Requires-Dist: scikit-learn>=1.0.0
29
+ Requires-Dist: matplotlib>=3.5.0
30
+ Requires-Dist: seaborn>=0.11.0
31
+ Requires-Dist: Pillow>=8.0.0
32
+ Requires-Dist: scanpy<2.0,>=1.9.0
33
+ Requires-Dist: anndata>=0.11.0
34
+ Requires-Dist: squidpy>=1.2.0
35
+ Requires-Dist: umap-learn>=0.5.0
36
+ Requires-Dist: igraph>=0.9.0
37
+ Requires-Dist: leidenalg>=0.8.0
38
+ Requires-Dist: pydantic<3.0,>=2.0.0
39
+ Requires-Dist: click>=8.0.0
40
+ Requires-Dist: aiohttp>=3.8.0
41
+ Requires-Dist: jinja2>=3.0.0
42
+ Requires-Dist: scvi-tools<2.0,>=1.0.0
43
+ Requires-Dist: mudata>=0.2.0
44
+ Requires-Dist: scvelo>=0.2.5
45
+ Requires-Dist: liana
46
+ Requires-Dist: cellphonedb<6.0,>=5.0.0
47
+ Requires-Dist: ktplotspy>=0.3.0
48
+ Requires-Dist: fastccc>=0.1.0
49
+ Requires-Dist: harmonypy>=0.0.9
50
+ Requires-Dist: bbknn>=1.5.0
51
+ Requires-Dist: esda>=2.4.0
52
+ Requires-Dist: libpysal>=4.6.0
53
+ Requires-Dist: gseapy>=1.0.0
54
+ Requires-Dist: flashdeconv>=0.1.0
55
+ Provides-Extra: full
56
+ Requires-Dist: torch<3.0,>=2.0.0; extra == "full"
57
+ Requires-Dist: SpaGCN<2.0,>=1.2.5; extra == "full"
58
+ Requires-Dist: GraphST>=1.1.0; extra == "full"
59
+ Requires-Dist: cell2location>=0.1.4; extra == "full"
60
+ Requires-Dist: cellrank<3.0,>=2.0.0; extra == "full"
61
+ Requires-Dist: palantir>=1.0.0; extra == "full"
62
+ Requires-Dist: petsc4py<4.0,>=3.18.0; sys_platform != "win32" and extra == "full"
63
+ Requires-Dist: slepc4py<4.0,>=3.18.0; sys_platform != "win32" and extra == "full"
64
+ Requires-Dist: scanorama>=1.7.0; extra == "full"
65
+ Requires-Dist: spatialde>=1.1.3; extra == "full"
66
+ Requires-Dist: scikit-misc>=0.5.0; extra == "full"
67
+ Requires-Dist: tangram-sc>=1.0.0; extra == "full"
68
+ Requires-Dist: singler>=0.4.0; sys_platform != "win32" and extra == "full"
69
+ Requires-Dist: singlecellexperiment>=0.4.0; sys_platform != "win32" and extra == "full"
70
+ Requires-Dist: celldex>=0.3.0; sys_platform != "win32" and extra == "full"
71
+ Requires-Dist: mllmcelltype>=0.1.0; extra == "full"
72
+ Requires-Dist: rpy2<3.7,>=3.5.0; extra == "full"
73
+ Requires-Dist: anndata2ri>=1.3.0; extra == "full"
74
+ Requires-Dist: pysal>=2.6.0; extra == "full"
75
+ Requires-Dist: statsmodels>=0.13.0; extra == "full"
76
+ Requires-Dist: networkx>=2.6.0; extra == "full"
77
+ Requires-Dist: POT>=0.9.0; extra == "full"
78
+ Requires-Dist: paste-bio>=1.0.0; extra == "full"
79
+ Requires-Dist: infercnvpy>=0.4.0; extra == "full"
80
+ Requires-Dist: pydeseq2>=0.4.0; extra == "full"
81
+ Requires-Dist: enrichmap; extra == "full"
82
+ Requires-Dist: statannotations>=0.2.0; extra == "full"
83
+ Requires-Dist: pygam>=0.8.0; extra == "full"
84
+ Requires-Dist: scikit-gstat>=1.0.0; extra == "full"
85
+ Requires-Dist: adjustText>=0.8.0; extra == "full"
86
+ Requires-Dist: splot>=1.1.0; extra == "full"
87
+ Requires-Dist: spatialdata>=0.2.0; extra == "full"
88
+ Provides-Extra: dev
89
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
90
+ Requires-Dist: pytest-asyncio<2.0,>=0.23.0; extra == "dev"
91
+ Requires-Dist: black<26.0,>=22.0.0; extra == "dev"
92
+ Requires-Dist: mypy<2.0,>=1.0.0; extra == "dev"
93
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
94
+ Requires-Dist: isort>=5.0.0; extra == "dev"
95
+ Requires-Dist: pre-commit<5.0,>=3.0.0; extra == "dev"
96
+ Dynamic: license-file
97
+
98
+ <div align="center">
99
+
100
+ # ChatSpatial
101
+
102
+ **MCP server for spatial transcriptomics analysis via natural language**
103
+
104
+ [![PyPI](https://img.shields.io/pypi/v/chatspatial)](https://pypi.org/project/chatspatial/)
105
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
106
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
107
+ [![Docs](https://img.shields.io/badge/docs-available-blue)](https://cafferychen777.github.io/ChatSpatial/)
108
+
109
+ </div>
110
+
111
+ ---
112
+
113
+ <table>
114
+ <tr>
115
+ <td width="50%">
116
+
117
+ ### ❌ Before
118
+ ```python
119
+ import scanpy as sc
120
+ import squidpy as sq
121
+ adata = sc.read_h5ad("data.h5ad")
122
+ sc.pp.filter_cells(adata, min_genes=200)
123
+ sc.pp.normalize_total(adata)
124
+ sc.pp.log1p(adata)
125
+ sc.pp.highly_variable_genes(adata)
126
+ sc.tl.pca(adata)
127
+ sc.pp.neighbors(adata)
128
+ # ... 40 more lines
129
+ ```
130
+
131
+ </td>
132
+ <td width="50%">
133
+
134
+ ### ✅ After
135
+ ```text
136
+ "Load my Visium data and identify
137
+ spatial domains"
138
+ ```
139
+
140
+ ```
141
+ ✓ Loaded 3,456 spots, 18,078 genes
142
+ ✓ Identified 7 spatial domains
143
+ ✓ Generated visualization
144
+ ```
145
+
146
+ </td>
147
+ </tr>
148
+ </table>
149
+
150
+ ---
151
+
152
+ ## Install
153
+
154
+ ```bash
155
+ pip install chatspatial
156
+ ```
157
+
158
+ ## Configure
159
+
160
+ **Claude Desktop** — add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
161
+
162
+ ```json
163
+ {
164
+ "mcpServers": {
165
+ "chatspatial": {
166
+ "command": "python",
167
+ "args": ["-m", "chatspatial", "server"]
168
+ }
169
+ }
170
+ }
171
+ ```
172
+
173
+ **Claude Code**:
174
+
175
+ ```bash
176
+ claude mcp add chatspatial python -- -m chatspatial server
177
+ ```
178
+
179
+ > Restart Claude after configuration.
180
+
181
+ ---
182
+
183
+ ## Use
184
+
185
+ Open Claude and chat:
186
+
187
+ ```text
188
+ Load /path/to/spatial_data.h5ad and show me the tissue structure
189
+ ```
190
+
191
+ ```text
192
+ Identify spatial domains using SpaGCN
193
+ ```
194
+
195
+ ```text
196
+ Find spatially variable genes and create a heatmap
197
+ ```
198
+
199
+ ---
200
+
201
+ ## Capabilities
202
+
203
+ | Category | Methods |
204
+ |----------|---------|
205
+ | **Spatial Domains** | SpaGCN, STAGATE, Leiden |
206
+ | **Deconvolution** | Cell2location, RCTD, Tangram, SPOTlight |
207
+ | **Cell Communication** | LIANA+, CellPhoneDB |
208
+ | **Cell Type Annotation** | Tangram, scANVI, CellAssign, mLLMCelltype |
209
+ | **Trajectory** | CellRank, Palantir, scVelo |
210
+ | **Spatial Statistics** | Moran's I, Getis-Ord Gi*, Ripley's K |
211
+ | **Gene Set Enrichment** | GSEA, ORA, Enrichr |
212
+
213
+ **60+ methods** across 12 categories. **Supports** 10x Visium, Xenium, Slide-seq v2, MERFISH, seqFISH.
214
+
215
+ ---
216
+
217
+ ## Docs
218
+
219
+ - [Installation Guide](INSTALLATION.md) — detailed setup for all platforms
220
+ - [Examples](docs/examples/) — step-by-step workflows
221
+ - [Full Documentation](https://cafferychen777.github.io/ChatSpatial/) — complete reference
222
+
223
+ ---
224
+
225
+ ## Citation
226
+
227
+ ```bibtex
228
+ @software{chatspatial2025,
229
+ title={ChatSpatial: Agentic Workflow for Spatial Transcriptomics},
230
+ author={Chen Yang and Xianyang Zhang and Jun Chen},
231
+ year={2025},
232
+ url={https://github.com/cafferychen777/ChatSpatial}
233
+ }
234
+ ```
235
+
236
+ <div align="center">
237
+
238
+ **MIT License** · [GitHub](https://github.com/cafferychen777/ChatSpatial) · [Issues](https://github.com/cafferychen777/ChatSpatial/issues)
239
+
240
+ </div>
241
+
242
+ <!-- mcp-name: io.github.cafferychen777/chatspatial -->