napistu 0.2.5.dev6__py3-none-any.whl → 0.3.1__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 (107) hide show
  1. napistu/__main__.py +126 -96
  2. napistu/constants.py +35 -41
  3. napistu/context/__init__.py +10 -0
  4. napistu/context/discretize.py +462 -0
  5. napistu/context/filtering.py +387 -0
  6. napistu/gcs/__init__.py +1 -1
  7. napistu/identifiers.py +74 -15
  8. napistu/indices.py +68 -0
  9. napistu/ingestion/__init__.py +1 -1
  10. napistu/ingestion/bigg.py +47 -62
  11. napistu/ingestion/constants.py +18 -133
  12. napistu/ingestion/gtex.py +113 -0
  13. napistu/ingestion/hpa.py +147 -0
  14. napistu/ingestion/sbml.py +0 -97
  15. napistu/ingestion/string.py +2 -2
  16. napistu/matching/__init__.py +10 -0
  17. napistu/matching/constants.py +18 -0
  18. napistu/matching/interactions.py +518 -0
  19. napistu/matching/mount.py +529 -0
  20. napistu/matching/species.py +510 -0
  21. napistu/mcp/__init__.py +7 -4
  22. napistu/mcp/__main__.py +128 -72
  23. napistu/mcp/client.py +16 -25
  24. napistu/mcp/codebase.py +201 -153
  25. napistu/mcp/component_base.py +170 -0
  26. napistu/mcp/config.py +223 -0
  27. napistu/mcp/constants.py +45 -2
  28. napistu/mcp/documentation.py +253 -136
  29. napistu/mcp/documentation_utils.py +13 -48
  30. napistu/mcp/execution.py +372 -305
  31. napistu/mcp/health.py +49 -67
  32. napistu/mcp/profiles.py +10 -6
  33. napistu/mcp/server.py +161 -80
  34. napistu/mcp/tutorials.py +139 -87
  35. napistu/modify/__init__.py +1 -1
  36. napistu/modify/gaps.py +1 -1
  37. napistu/network/__init__.py +1 -1
  38. napistu/network/constants.py +101 -34
  39. napistu/network/data_handling.py +388 -0
  40. napistu/network/ig_utils.py +351 -0
  41. napistu/network/napistu_graph_core.py +354 -0
  42. napistu/network/neighborhoods.py +40 -40
  43. napistu/network/net_create.py +373 -309
  44. napistu/network/net_propagation.py +47 -19
  45. napistu/network/{net_utils.py → ng_utils.py} +124 -272
  46. napistu/network/paths.py +67 -51
  47. napistu/network/precompute.py +11 -11
  48. napistu/ontologies/__init__.py +10 -0
  49. napistu/ontologies/constants.py +129 -0
  50. napistu/ontologies/dogma.py +243 -0
  51. napistu/ontologies/genodexito.py +649 -0
  52. napistu/ontologies/mygene.py +369 -0
  53. napistu/ontologies/renaming.py +198 -0
  54. napistu/rpy2/__init__.py +229 -86
  55. napistu/rpy2/callr.py +47 -77
  56. napistu/rpy2/constants.py +24 -23
  57. napistu/rpy2/rids.py +61 -648
  58. napistu/sbml_dfs_core.py +587 -222
  59. napistu/scverse/__init__.py +15 -0
  60. napistu/scverse/constants.py +28 -0
  61. napistu/scverse/loading.py +727 -0
  62. napistu/utils.py +118 -10
  63. {napistu-0.2.5.dev6.dist-info → napistu-0.3.1.dist-info}/METADATA +8 -3
  64. napistu-0.3.1.dist-info/RECORD +133 -0
  65. tests/conftest.py +22 -0
  66. tests/test_context_discretize.py +56 -0
  67. tests/test_context_filtering.py +267 -0
  68. tests/test_identifiers.py +100 -0
  69. tests/test_indices.py +65 -0
  70. tests/{test_edgelist.py → test_ingestion_napistu_edgelist.py} +2 -2
  71. tests/test_matching_interactions.py +108 -0
  72. tests/test_matching_mount.py +305 -0
  73. tests/test_matching_species.py +394 -0
  74. tests/test_mcp_config.py +193 -0
  75. tests/test_mcp_documentation_utils.py +12 -3
  76. tests/test_mcp_server.py +356 -0
  77. tests/test_network_data_handling.py +397 -0
  78. tests/test_network_ig_utils.py +23 -0
  79. tests/test_network_neighborhoods.py +19 -0
  80. tests/test_network_net_create.py +459 -0
  81. tests/test_network_ng_utils.py +30 -0
  82. tests/test_network_paths.py +56 -0
  83. tests/{test_precomputed_distances.py → test_network_precompute.py} +8 -6
  84. tests/test_ontologies_genodexito.py +58 -0
  85. tests/test_ontologies_mygene.py +39 -0
  86. tests/test_ontologies_renaming.py +110 -0
  87. tests/test_rpy2_callr.py +79 -0
  88. tests/test_rpy2_init.py +151 -0
  89. tests/test_sbml.py +0 -31
  90. tests/test_sbml_dfs_core.py +134 -10
  91. tests/test_scverse_loading.py +778 -0
  92. tests/test_set_coverage.py +2 -2
  93. tests/test_utils.py +121 -1
  94. napistu/mechanism_matching.py +0 -1353
  95. napistu/rpy2/netcontextr.py +0 -467
  96. napistu-0.2.5.dev6.dist-info/RECORD +0 -97
  97. tests/test_igraph.py +0 -367
  98. tests/test_mechanism_matching.py +0 -784
  99. tests/test_net_utils.py +0 -149
  100. tests/test_netcontextr.py +0 -105
  101. tests/test_rpy2.py +0 -61
  102. /napistu/ingestion/{cpr_edgelist.py → napistu_edgelist.py} +0 -0
  103. {napistu-0.2.5.dev6.dist-info → napistu-0.3.1.dist-info}/WHEEL +0 -0
  104. {napistu-0.2.5.dev6.dist-info → napistu-0.3.1.dist-info}/entry_points.txt +0 -0
  105. {napistu-0.2.5.dev6.dist-info → napistu-0.3.1.dist-info}/licenses/LICENSE +0 -0
  106. {napistu-0.2.5.dev6.dist-info → napistu-0.3.1.dist-info}/top_level.txt +0 -0
  107. /tests/{test_obo.py → test_ingestion_obo.py} +0 -0
napistu/mcp/__main__.py CHANGED
@@ -1,4 +1,3 @@
1
- # src/napistu/mcp/__main__.py
2
1
  """
3
2
  MCP (Model Context Protocol) Server CLI for Napistu.
4
3
  """
@@ -16,6 +15,15 @@ from napistu.mcp.client import (
16
15
  list_server_resources,
17
16
  read_server_resource,
18
17
  )
18
+ from napistu.mcp.config import (
19
+ validate_server_config_flags,
20
+ validate_client_config_flags,
21
+ server_config_options,
22
+ client_config_options,
23
+ local_server_config,
24
+ local_client_config,
25
+ production_client_config,
26
+ )
19
27
 
20
28
  logger = logging.getLogger(napistu.__name__)
21
29
  click_logging.basic_config(logger)
@@ -35,157 +43,205 @@ def server():
35
43
 
36
44
  @server.command(name="start")
37
45
  @click.option(
38
- "--profile", type=click.Choice(["local", "remote", "full"]), default="remote"
46
+ "--profile", type=click.Choice(["execution", "docs", "full"]), default="docs"
39
47
  )
40
- @click.option("--host", type=str, default="127.0.0.1")
41
- @click.option("--port", type=int, default=8765)
42
- @click.option("--server-name", type=str)
48
+ @server_config_options
43
49
  @click_logging.simple_verbosity_option(logger)
44
- def start_server(profile, host, port, server_name):
50
+ def start_server(profile, production, local, host, port, server_name):
45
51
  """Start an MCP server with the specified profile."""
46
- start_mcp_server(profile, host, port, server_name)
52
+ try:
53
+ config = validate_server_config_flags(
54
+ local, production, host, port, server_name
55
+ )
56
+
57
+ click.echo("Starting server with configuration:")
58
+ click.echo(f" Profile: {profile}")
59
+ click.echo(f" Host: {config.host}")
60
+ click.echo(f" Port: {config.port}")
61
+ click.echo(f" Server Name: {config.server_name}")
62
+
63
+ start_mcp_server(profile, config)
64
+
65
+ except click.BadParameter as e:
66
+ raise click.ClickException(str(e))
47
67
 
48
68
 
49
69
  @server.command(name="local")
50
- @click.option("--server-name", type=str, default="napistu-local")
51
70
  @click_logging.simple_verbosity_option(logger)
52
- def start_local(server_name):
71
+ def start_local():
53
72
  """Start a local MCP server optimized for function execution."""
54
- start_mcp_server("local", "127.0.0.1", 8765, server_name)
73
+ config = local_server_config()
74
+ click.echo("Starting local development server (execution profile)")
75
+ click.echo(f" Host: {config.host}")
76
+ click.echo(f" Port: {config.port}")
77
+ click.echo(f" Server Name: {config.server_name}")
78
+
79
+ start_mcp_server("execution", config)
55
80
 
56
81
 
57
82
  @server.command(name="full")
58
- @click.option("--server-name", type=str, default="napistu-full")
59
83
  @click_logging.simple_verbosity_option(logger)
60
- def start_full(server_name):
84
+ def start_full():
61
85
  """Start a full MCP server with all components enabled (local debugging)."""
62
- start_mcp_server("full", "127.0.0.1", 8765, server_name)
86
+ config = local_server_config()
87
+ # Override server name for full profile
88
+ config.server_name = "napistu-full"
89
+
90
+ click.echo("Starting full development server (all components)")
91
+ click.echo(f" Host: {config.host}")
92
+ click.echo(f" Port: {config.port}")
93
+ click.echo(f" Server Name: {config.server_name}")
94
+
95
+ start_mcp_server("full", config)
63
96
 
64
97
 
65
98
  @cli.command()
66
- @click.option("--url", default="http://127.0.0.1:8765", help="Server URL")
99
+ @client_config_options
67
100
  @click_logging.simple_verbosity_option(logger)
68
- def health(url):
101
+ def health(production, local, host, port, https):
69
102
  """Quick health check of MCP server."""
70
103
 
71
104
  async def run_health_check():
72
- print("🏥 Napistu MCP Server Health Check")
73
- print("=" * 40)
74
- print(f"Server URL: {url}")
75
- print()
105
+ try:
106
+ config = validate_client_config_flags(local, production, host, port, https)
107
+
108
+ print("🏥 Napistu MCP Server Health Check")
109
+ print("=" * 40)
110
+ print(f"Server URL: {config.base_url}")
111
+ print()
76
112
 
77
- health = await check_server_health(server_url=url)
78
- print_health_status(health)
113
+ health = await check_server_health(config)
114
+ print_health_status(health)
115
+
116
+ except click.BadParameter as e:
117
+ raise click.ClickException(str(e))
79
118
 
80
119
  asyncio.run(run_health_check())
81
120
 
82
121
 
83
122
  @cli.command()
84
- @click.option("--url", default="http://127.0.0.1:8765", help="Server URL")
123
+ @client_config_options
85
124
  @click_logging.simple_verbosity_option(logger)
86
- def resources(url):
125
+ def resources(production, local, host, port, https):
87
126
  """List all available resources on the MCP server."""
88
127
 
89
128
  async def run_list_resources():
90
- print("📋 Napistu MCP Server Resources")
91
- print("=" * 40)
92
- print(f"Server URL: {url}")
93
- print()
94
-
95
- resources = await list_server_resources(server_url=url)
96
-
97
- if resources:
98
- print(f"Found {len(resources)} resources:")
99
- for resource in resources:
100
- print(f" 📄 {resource.uri}")
101
- if resource.name != resource.uri:
102
- print(f" Name: {resource.name}")
103
- if hasattr(resource, "description") and resource.description:
104
- print(f" Description: {resource.description}")
105
- print()
106
- else:
107
- print(" Could not retrieve resources")
129
+ try:
130
+ config = validate_client_config_flags(local, production, host, port, https)
131
+
132
+ print("📋 Napistu MCP Server Resources")
133
+ print("=" * 40)
134
+ print(f"Server URL: {config.base_url}")
135
+ print()
136
+
137
+ resources = await list_server_resources(config)
138
+
139
+ if resources:
140
+ print(f"Found {len(resources)} resources:")
141
+ for resource in resources:
142
+ print(f" 📄 {resource.uri}")
143
+ if resource.name != resource.uri:
144
+ print(f" Name: {resource.name}")
145
+ if hasattr(resource, "description") and resource.description:
146
+ print(f" Description: {resource.description}")
147
+ print()
148
+ else:
149
+ print("❌ Could not retrieve resources")
150
+
151
+ except click.BadParameter as e:
152
+ raise click.ClickException(str(e))
108
153
 
109
154
  asyncio.run(run_list_resources())
110
155
 
111
156
 
112
157
  @cli.command()
113
158
  @click.argument("resource_uri")
114
- @click.option("--url", default="http://127.0.0.1:8765", help="Server URL")
159
+ @client_config_options
115
160
  @click.option(
116
161
  "--output", type=click.File("w"), default="-", help="Output file (default: stdout)"
117
162
  )
118
163
  @click_logging.simple_verbosity_option(logger)
119
- def read(resource_uri, url, output):
164
+ def read(resource_uri, production, local, host, port, https, output):
120
165
  """Read a specific resource from the MCP server."""
121
166
 
122
167
  async def run_read_resource():
123
- print(
124
- f"📖 Reading Resource: {resource_uri}",
125
- file=output if output.name != "<stdout>" else None,
126
- )
127
- print(f"Server URL: {url}", file=output if output.name != "<stdout>" else None)
128
- print("=" * 50, file=output if output.name != "<stdout>" else None)
168
+ try:
169
+ config = validate_client_config_flags(local, production, host, port, https)
129
170
 
130
- content = await read_server_resource(resource_uri, server_url=url)
131
-
132
- if content:
133
- print(content, file=output)
134
- else:
135
171
  print(
136
- " Could not read resource",
172
+ f"📖 Reading Resource: {resource_uri}",
173
+ file=output if output.name != "<stdout>" else None,
174
+ )
175
+ print(
176
+ f"Server URL: {config.base_url}",
137
177
  file=output if output.name != "<stdout>" else None,
138
178
  )
179
+ print("=" * 50, file=output if output.name != "<stdout>" else None)
180
+
181
+ content = await read_server_resource(resource_uri, config)
182
+
183
+ if content:
184
+ print(content, file=output)
185
+ else:
186
+ print(
187
+ "❌ Could not read resource",
188
+ file=output if output.name != "<stdout>" else None,
189
+ )
190
+
191
+ except click.BadParameter as e:
192
+ raise click.ClickException(str(e))
139
193
 
140
194
  asyncio.run(run_read_resource())
141
195
 
142
196
 
143
197
  @cli.command()
144
- @click.option("--local-url", default="http://127.0.0.1:8765", help="Local server URL")
145
- @click.option("--remote-url", required=True, help="Remote server URL")
146
198
  @click_logging.simple_verbosity_option(logger)
147
- def compare(local_url, remote_url):
148
- """Compare health between local and remote servers."""
199
+ def compare():
200
+ """Compare health between local development and production servers."""
149
201
 
150
202
  async def run_comparison():
151
- print("🔍 Local vs Remote Server Comparison")
203
+
204
+ local_config = local_client_config()
205
+ production_config = production_client_config()
206
+
207
+ print("🔍 Local vs Production Server Comparison")
152
208
  print("=" * 50)
153
209
 
154
- print(f"\n📍 Local Server: {local_url}")
155
- local_health = await check_server_health(server_url=local_url)
210
+ print(f"\n📍 Local Server: {local_config.base_url}")
211
+ local_health = await check_server_health(local_config)
156
212
  print_health_status(local_health)
157
213
 
158
- print(f"\n🌐 Remote Server: {remote_url}")
159
- remote_health = await check_server_health(server_url=remote_url)
160
- print_health_status(remote_health)
214
+ print(f"\n🌐 Production Server: {production_config.base_url}")
215
+ production_health = await check_server_health(production_config)
216
+ print_health_status(production_health)
161
217
 
162
218
  # Compare results
163
219
  print("\n📊 Comparison Summary:")
164
- if local_health and remote_health:
220
+ if local_health and production_health:
165
221
  local_components = local_health.get("components", {})
166
- remote_components = remote_health.get("components", {})
222
+ production_components = production_health.get("components", {})
167
223
 
168
224
  all_components = set(local_components.keys()) | set(
169
- remote_components.keys()
225
+ production_components.keys()
170
226
  )
171
227
 
172
228
  for component in sorted(all_components):
173
229
  local_status = local_components.get(component, {}).get(
174
230
  "status", "missing"
175
231
  )
176
- remote_status = remote_components.get(component, {}).get(
232
+ production_status = production_components.get(component, {}).get(
177
233
  "status", "missing"
178
234
  )
179
235
 
180
- if local_status == remote_status == "healthy":
236
+ if local_status == production_status == "healthy":
181
237
  icon = "✅"
182
- elif local_status != remote_status:
238
+ elif local_status != production_status:
183
239
  icon = "⚠️ "
184
240
  else:
185
241
  icon = "❌"
186
242
 
187
243
  print(
188
- f" {icon} {component}: Local={local_status}, Remote={remote_status}"
244
+ f" {icon} {component}: Local={local_status}, Production={production_status}"
189
245
  )
190
246
  else:
191
247
  print(" ❌ Cannot compare - one or both servers unreachable")
napistu/mcp/client.py CHANGED
@@ -1,4 +1,3 @@
1
- # src/napistu/mcp/client.py
2
1
  """
3
2
  MCP client for testing and interacting with Napistu MCP servers.
4
3
  """
@@ -8,20 +7,19 @@ import logging
8
7
  from typing import Optional, Dict, Any, Mapping
9
8
 
10
9
  from fastmcp import Client
10
+ from napistu.mcp.config import MCPClientConfig
11
11
 
12
12
  logger = logging.getLogger(__name__)
13
13
 
14
14
 
15
- async def check_server_health(
16
- server_url: str = "http://127.0.0.1:8765",
17
- ) -> Optional[Dict[str, Any]]:
15
+ async def check_server_health(config: MCPClientConfig) -> Optional[Dict[str, Any]]:
18
16
  """
19
17
  Health check using FastMCP client.
20
18
 
21
19
  Parameters
22
20
  ----------
23
- server_url : str, optional
24
- Server URL for HTTP transport. Defaults to 'http://127.0.0.1:8765'.
21
+ config : MCPClientConfig
22
+ Client configuration object with validated settings.
25
23
 
26
24
  Returns
27
25
  -------
@@ -37,13 +35,10 @@ async def check_server_health(
37
35
  - components : Dict[str, Dict[str, str]]
38
36
  Status of each component ('healthy', 'inactive', or 'unavailable')
39
37
  """
40
-
41
38
  try:
42
- # FastMCP streamable-http requires the /mcp path
43
- mcp_url = server_url.rstrip("/") + "/mcp"
44
- logger.info(f"Connecting to MCP server at: {mcp_url}")
39
+ logger.info(f"Connecting to MCP server at: {config.mcp_url}")
45
40
 
46
- client = Client(mcp_url)
41
+ client = Client(config.mcp_url)
47
42
 
48
43
  async with client:
49
44
  logger.info("✅ FastMCP client connected")
@@ -137,16 +132,14 @@ def print_health_status(health: Optional[Mapping[str, Any]]) -> None:
137
132
  print(f"Version: {health['version']}")
138
133
 
139
134
 
140
- async def list_server_resources(
141
- server_url: str = "http://127.0.0.1:8765",
142
- ) -> Optional[list]:
135
+ async def list_server_resources(config: MCPClientConfig) -> Optional[list]:
143
136
  """
144
137
  List all available resources on the MCP server.
145
138
 
146
139
  Parameters
147
140
  ----------
148
- server_url : str, optional
149
- Server URL for HTTP transport. Defaults to 'http://127.0.0.1:8765'.
141
+ config : MCPClientConfig
142
+ Client configuration object with validated settings.
150
143
 
151
144
  Returns
152
145
  -------
@@ -154,10 +147,9 @@ async def list_server_resources(
154
147
  List of available resources, or None if failed.
155
148
  """
156
149
  try:
157
- mcp_url = server_url.rstrip("/") + "/mcp"
158
- logger.info(f"Listing resources from: {mcp_url}")
150
+ logger.info(f"Listing resources from: {config.mcp_url}")
159
151
 
160
- client = Client(mcp_url)
152
+ client = Client(config.mcp_url)
161
153
 
162
154
  async with client:
163
155
  resources = await client.list_resources()
@@ -170,7 +162,7 @@ async def list_server_resources(
170
162
 
171
163
 
172
164
  async def read_server_resource(
173
- resource_uri: str, server_url: str = "http://127.0.0.1:8765"
165
+ resource_uri: str, config: MCPClientConfig
174
166
  ) -> Optional[str]:
175
167
  """
176
168
  Read a specific resource from the MCP server.
@@ -179,8 +171,8 @@ async def read_server_resource(
179
171
  ----------
180
172
  resource_uri : str
181
173
  URI of the resource to read (e.g., 'napistu://health')
182
- server_url : str, optional
183
- Server URL for HTTP transport. Defaults to 'http://127.0.0.1:8765'.
174
+ config : MCPClientConfig
175
+ Client configuration object with validated settings.
184
176
 
185
177
  Returns
186
178
  -------
@@ -188,10 +180,9 @@ async def read_server_resource(
188
180
  Resource content as text, or None if failed.
189
181
  """
190
182
  try:
191
- mcp_url = server_url.rstrip("/") + "/mcp"
192
- logger.info(f"Reading resource {resource_uri} from: {mcp_url}")
183
+ logger.info(f"Reading resource {resource_uri} from: {config.mcp_url}")
193
184
 
194
- client = Client(mcp_url)
185
+ client = Client(config.mcp_url)
195
186
 
196
187
  async with client:
197
188
  result = await client.read_resource(resource_uri)