napistu 0.2.5.dev7__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.
- napistu/__main__.py +126 -96
- napistu/constants.py +35 -41
- napistu/context/__init__.py +10 -0
- napistu/context/discretize.py +462 -0
- napistu/context/filtering.py +387 -0
- napistu/gcs/__init__.py +1 -1
- napistu/identifiers.py +74 -15
- napistu/indices.py +68 -0
- napistu/ingestion/__init__.py +1 -1
- napistu/ingestion/bigg.py +47 -62
- napistu/ingestion/constants.py +18 -133
- napistu/ingestion/gtex.py +113 -0
- napistu/ingestion/hpa.py +147 -0
- napistu/ingestion/sbml.py +0 -97
- napistu/ingestion/string.py +2 -2
- napistu/matching/__init__.py +10 -0
- napistu/matching/constants.py +18 -0
- napistu/matching/interactions.py +518 -0
- napistu/matching/mount.py +529 -0
- napistu/matching/species.py +510 -0
- napistu/mcp/__init__.py +7 -4
- napistu/mcp/__main__.py +128 -72
- napistu/mcp/client.py +16 -25
- napistu/mcp/codebase.py +201 -145
- napistu/mcp/component_base.py +170 -0
- napistu/mcp/config.py +223 -0
- napistu/mcp/constants.py +45 -2
- napistu/mcp/documentation.py +253 -136
- napistu/mcp/documentation_utils.py +13 -48
- napistu/mcp/execution.py +372 -305
- napistu/mcp/health.py +47 -65
- napistu/mcp/profiles.py +10 -6
- napistu/mcp/server.py +161 -80
- napistu/mcp/tutorials.py +139 -87
- napistu/modify/__init__.py +1 -1
- napistu/modify/gaps.py +1 -1
- napistu/network/__init__.py +1 -1
- napistu/network/constants.py +101 -34
- napistu/network/data_handling.py +388 -0
- napistu/network/ig_utils.py +351 -0
- napistu/network/napistu_graph_core.py +354 -0
- napistu/network/neighborhoods.py +40 -40
- napistu/network/net_create.py +373 -309
- napistu/network/net_propagation.py +47 -19
- napistu/network/{net_utils.py → ng_utils.py} +124 -272
- napistu/network/paths.py +67 -51
- napistu/network/precompute.py +11 -11
- napistu/ontologies/__init__.py +10 -0
- napistu/ontologies/constants.py +129 -0
- napistu/ontologies/dogma.py +243 -0
- napistu/ontologies/genodexito.py +649 -0
- napistu/ontologies/mygene.py +369 -0
- napistu/ontologies/renaming.py +198 -0
- napistu/rpy2/__init__.py +229 -86
- napistu/rpy2/callr.py +47 -77
- napistu/rpy2/constants.py +24 -23
- napistu/rpy2/rids.py +61 -648
- napistu/sbml_dfs_core.py +587 -222
- napistu/scverse/__init__.py +15 -0
- napistu/scverse/constants.py +28 -0
- napistu/scverse/loading.py +727 -0
- napistu/utils.py +118 -10
- {napistu-0.2.5.dev7.dist-info → napistu-0.3.1.dist-info}/METADATA +8 -3
- napistu-0.3.1.dist-info/RECORD +133 -0
- tests/conftest.py +22 -0
- tests/test_context_discretize.py +56 -0
- tests/test_context_filtering.py +267 -0
- tests/test_identifiers.py +100 -0
- tests/test_indices.py +65 -0
- tests/{test_edgelist.py → test_ingestion_napistu_edgelist.py} +2 -2
- tests/test_matching_interactions.py +108 -0
- tests/test_matching_mount.py +305 -0
- tests/test_matching_species.py +394 -0
- tests/test_mcp_config.py +193 -0
- tests/test_mcp_documentation_utils.py +12 -3
- tests/test_mcp_server.py +156 -19
- tests/test_network_data_handling.py +397 -0
- tests/test_network_ig_utils.py +23 -0
- tests/test_network_neighborhoods.py +19 -0
- tests/test_network_net_create.py +459 -0
- tests/test_network_ng_utils.py +30 -0
- tests/test_network_paths.py +56 -0
- tests/{test_precomputed_distances.py → test_network_precompute.py} +8 -6
- tests/test_ontologies_genodexito.py +58 -0
- tests/test_ontologies_mygene.py +39 -0
- tests/test_ontologies_renaming.py +110 -0
- tests/test_rpy2_callr.py +79 -0
- tests/test_rpy2_init.py +151 -0
- tests/test_sbml.py +0 -31
- tests/test_sbml_dfs_core.py +134 -10
- tests/test_scverse_loading.py +778 -0
- tests/test_set_coverage.py +2 -2
- tests/test_utils.py +121 -1
- napistu/mechanism_matching.py +0 -1353
- napistu/rpy2/netcontextr.py +0 -467
- napistu-0.2.5.dev7.dist-info/RECORD +0 -98
- tests/test_igraph.py +0 -367
- tests/test_mechanism_matching.py +0 -784
- tests/test_net_utils.py +0 -149
- tests/test_netcontextr.py +0 -105
- tests/test_rpy2.py +0 -61
- /napistu/ingestion/{cpr_edgelist.py → napistu_edgelist.py} +0 -0
- {napistu-0.2.5.dev7.dist-info → napistu-0.3.1.dist-info}/WHEEL +0 -0
- {napistu-0.2.5.dev7.dist-info → napistu-0.3.1.dist-info}/entry_points.txt +0 -0
- {napistu-0.2.5.dev7.dist-info → napistu-0.3.1.dist-info}/licenses/LICENSE +0 -0
- {napistu-0.2.5.dev7.dist-info → napistu-0.3.1.dist-info}/top_level.txt +0 -0
- /tests/{test_obo.py → test_ingestion_obo.py} +0 -0
napistu/mcp/codebase.py
CHANGED
@@ -2,173 +2,229 @@
|
|
2
2
|
Codebase exploration components for the Napistu MCP server.
|
3
3
|
"""
|
4
4
|
|
5
|
-
from napistu.mcp.constants import NAPISTU_PY_READTHEDOCS_API
|
6
|
-
|
7
|
-
from fastmcp import FastMCP
|
8
|
-
|
9
5
|
from typing import Dict, Any
|
10
6
|
import json
|
7
|
+
import logging
|
11
8
|
|
9
|
+
from fastmcp import FastMCP
|
10
|
+
|
11
|
+
from napistu.mcp.component_base import ComponentState, MCPComponent
|
12
|
+
from napistu.mcp.constants import NAPISTU_PY_READTHEDOCS_API
|
12
13
|
from napistu.mcp import codebase_utils
|
13
14
|
from napistu.mcp import utils as mcp_utils
|
14
15
|
|
15
|
-
|
16
|
-
_codebase_cache = {
|
17
|
-
"modules": {},
|
18
|
-
"classes": {},
|
19
|
-
"functions": {},
|
20
|
-
}
|
16
|
+
logger = logging.getLogger(__name__)
|
21
17
|
|
22
18
|
|
23
|
-
|
24
|
-
"""
|
25
|
-
Initialize codebase components.
|
19
|
+
class CodebaseState(ComponentState):
|
20
|
+
"""State management for codebase component."""
|
26
21
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
_codebase_cache["modules"] = await codebase_utils.read_read_the_docs(
|
35
|
-
NAPISTU_PY_READTHEDOCS_API
|
36
|
-
)
|
37
|
-
# Extract functions and classes from the modules
|
38
|
-
_codebase_cache["functions"], _codebase_cache["classes"] = (
|
39
|
-
codebase_utils.extract_functions_and_classes_from_modules(
|
40
|
-
_codebase_cache["modules"]
|
41
|
-
)
|
42
|
-
)
|
43
|
-
return True
|
44
|
-
|
45
|
-
|
46
|
-
def register_components(mcp: FastMCP):
|
47
|
-
"""
|
48
|
-
Register codebase components with the MCP server.
|
22
|
+
def __init__(self):
|
23
|
+
super().__init__()
|
24
|
+
self.codebase_cache: Dict[str, Dict[str, Any]] = {
|
25
|
+
"modules": {},
|
26
|
+
"classes": {},
|
27
|
+
"functions": {},
|
28
|
+
}
|
49
29
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
global _codebase_cache
|
30
|
+
def is_healthy(self) -> bool:
|
31
|
+
"""Component is healthy if it has loaded any codebase information."""
|
32
|
+
return any(bool(section) for section in self.codebase_cache.values())
|
54
33
|
|
55
|
-
|
56
|
-
|
57
|
-
async def get_codebase_summary():
|
58
|
-
"""
|
59
|
-
Get a summary of all available codebase information.
|
60
|
-
"""
|
34
|
+
def get_health_details(self) -> Dict[str, Any]:
|
35
|
+
"""Provide codebase-specific health details."""
|
61
36
|
return {
|
62
|
-
"
|
63
|
-
"
|
64
|
-
"
|
37
|
+
"modules_count": len(self.codebase_cache["modules"]),
|
38
|
+
"classes_count": len(self.codebase_cache["classes"]),
|
39
|
+
"functions_count": len(self.codebase_cache["functions"]),
|
40
|
+
"total_items": sum(
|
41
|
+
len(section) for section in self.codebase_cache.values()
|
42
|
+
),
|
65
43
|
}
|
66
44
|
|
67
|
-
@mcp.resource("napistu://codebase/modules/{module_name}")
|
68
|
-
async def get_module_details(module_name: str) -> Dict[str, Any]:
|
69
|
-
"""
|
70
|
-
Get detailed information about a specific module.
|
71
45
|
|
72
|
-
|
73
|
-
|
74
|
-
"""
|
75
|
-
if module_name not in _codebase_cache["modules"]:
|
76
|
-
return {"error": f"Module {module_name} not found"}
|
46
|
+
class CodebaseComponent(MCPComponent):
|
47
|
+
"""MCP component for codebase exploration and search."""
|
77
48
|
|
78
|
-
|
49
|
+
def _create_state(self) -> CodebaseState:
|
50
|
+
"""Create codebase-specific state."""
|
51
|
+
return CodebaseState()
|
79
52
|
|
80
|
-
|
81
|
-
@mcp.tool()
|
82
|
-
async def search_codebase(query: str) -> Dict[str, Any]:
|
53
|
+
async def initialize(self) -> bool:
|
83
54
|
"""
|
84
|
-
|
55
|
+
Initialize codebase component by loading documentation from ReadTheDocs.
|
85
56
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
Dictionary with search results organized by code element type, including snippets for context.
|
57
|
+
Returns
|
58
|
+
-------
|
59
|
+
bool
|
60
|
+
True if codebase information was loaded successfully
|
91
61
|
"""
|
92
|
-
|
93
|
-
"
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
)
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
"snippet": snippet,
|
124
|
-
}
|
125
|
-
)
|
126
|
-
|
127
|
-
# Search functions
|
128
|
-
for func_name, info in _codebase_cache["functions"].items():
|
129
|
-
doc = info.get("doc") or info.get("description") or ""
|
130
|
-
func_text = json.dumps(info)
|
131
|
-
if query.lower() in func_text.lower():
|
132
|
-
snippet = mcp_utils.get_snippet(doc, query)
|
133
|
-
results["functions"].append(
|
134
|
-
{
|
135
|
-
"name": func_name,
|
136
|
-
"description": doc,
|
137
|
-
"signature": info.get("signature", ""),
|
138
|
-
"snippet": snippet,
|
139
|
-
}
|
140
|
-
)
|
141
|
-
|
142
|
-
return results
|
143
|
-
|
144
|
-
@mcp.tool()
|
145
|
-
async def get_function_documentation(function_name: str) -> Dict[str, Any]:
|
62
|
+
try:
|
63
|
+
logger.info("Loading codebase documentation from ReadTheDocs...")
|
64
|
+
|
65
|
+
# Load documentation from the ReadTheDocs API
|
66
|
+
modules = await codebase_utils.read_read_the_docs(
|
67
|
+
NAPISTU_PY_READTHEDOCS_API
|
68
|
+
)
|
69
|
+
self.state.codebase_cache["modules"] = modules
|
70
|
+
|
71
|
+
# Extract functions and classes from the modules
|
72
|
+
functions, classes = (
|
73
|
+
codebase_utils.extract_functions_and_classes_from_modules(modules)
|
74
|
+
)
|
75
|
+
self.state.codebase_cache["functions"] = functions
|
76
|
+
self.state.codebase_cache["classes"] = classes
|
77
|
+
|
78
|
+
logger.info(
|
79
|
+
f"Codebase loading complete: "
|
80
|
+
f"{len(modules)} modules, "
|
81
|
+
f"{len(classes)} classes, "
|
82
|
+
f"{len(functions)} functions"
|
83
|
+
)
|
84
|
+
|
85
|
+
# Consider successful if we loaded any modules
|
86
|
+
return len(modules) > 0
|
87
|
+
|
88
|
+
except Exception as e:
|
89
|
+
logger.error(f"Failed to load codebase documentation: {e}")
|
90
|
+
return False
|
91
|
+
|
92
|
+
def register(self, mcp: FastMCP) -> None:
|
146
93
|
"""
|
147
|
-
|
148
|
-
|
149
|
-
Args:
|
150
|
-
function_name: Name of the function
|
94
|
+
Register codebase resources and tools with the MCP server.
|
151
95
|
|
152
|
-
|
153
|
-
|
96
|
+
Parameters
|
97
|
+
----------
|
98
|
+
mcp : FastMCP
|
99
|
+
FastMCP server instance
|
154
100
|
"""
|
155
|
-
if function_name not in _codebase_cache["functions"]:
|
156
|
-
return {"error": f"Function {function_name} not found"}
|
157
101
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
102
|
+
# Register resources
|
103
|
+
@mcp.resource("napistu://codebase/summary")
|
104
|
+
async def get_codebase_summary():
|
105
|
+
"""Get a summary of all available codebase information."""
|
106
|
+
return {
|
107
|
+
"modules": list(self.state.codebase_cache["modules"].keys()),
|
108
|
+
"classes": list(self.state.codebase_cache["classes"].keys()),
|
109
|
+
"functions": list(self.state.codebase_cache["functions"].keys()),
|
110
|
+
}
|
111
|
+
|
112
|
+
@mcp.resource("napistu://codebase/modules/{module_name}")
|
113
|
+
async def get_module_details(module_name: str) -> Dict[str, Any]:
|
114
|
+
"""Get detailed information about a specific module."""
|
115
|
+
if module_name not in self.state.codebase_cache["modules"]:
|
116
|
+
return {"error": f"Module {module_name} not found"}
|
117
|
+
|
118
|
+
return self.state.codebase_cache["modules"][module_name]
|
119
|
+
|
120
|
+
# Register tools
|
121
|
+
@mcp.tool()
|
122
|
+
async def search_codebase(query: str) -> Dict[str, Any]:
|
123
|
+
"""
|
124
|
+
Search the codebase for a specific query.
|
125
|
+
|
126
|
+
Args:
|
127
|
+
query: Search term
|
128
|
+
|
129
|
+
Returns:
|
130
|
+
Dictionary with search results organized by code element type, including snippets for context.
|
131
|
+
"""
|
132
|
+
results = {
|
133
|
+
"modules": [],
|
134
|
+
"classes": [],
|
135
|
+
"functions": [],
|
136
|
+
}
|
137
|
+
|
138
|
+
# Search modules
|
139
|
+
for module_name, info in self.state.codebase_cache["modules"].items():
|
140
|
+
# Use docstring or description for snippet
|
141
|
+
doc = info.get("doc") or info.get("description") or ""
|
142
|
+
module_text = json.dumps(info)
|
143
|
+
if query.lower() in module_text.lower():
|
144
|
+
snippet = mcp_utils.get_snippet(doc, query)
|
145
|
+
results["modules"].append(
|
146
|
+
{
|
147
|
+
"name": module_name,
|
148
|
+
"description": doc,
|
149
|
+
"snippet": snippet,
|
150
|
+
}
|
151
|
+
)
|
152
|
+
|
153
|
+
# Search classes
|
154
|
+
for class_name, info in self.state.codebase_cache["classes"].items():
|
155
|
+
doc = info.get("doc") or info.get("description") or ""
|
156
|
+
class_text = json.dumps(info)
|
157
|
+
if query.lower() in class_text.lower():
|
158
|
+
snippet = mcp_utils.get_snippet(doc, query)
|
159
|
+
results["classes"].append(
|
160
|
+
{
|
161
|
+
"name": class_name,
|
162
|
+
"description": doc,
|
163
|
+
"snippet": snippet,
|
164
|
+
}
|
165
|
+
)
|
166
|
+
|
167
|
+
# Search functions
|
168
|
+
for func_name, info in self.state.codebase_cache["functions"].items():
|
169
|
+
doc = info.get("doc") or info.get("description") or ""
|
170
|
+
func_text = json.dumps(info)
|
171
|
+
if query.lower() in func_text.lower():
|
172
|
+
snippet = mcp_utils.get_snippet(doc, query)
|
173
|
+
results["functions"].append(
|
174
|
+
{
|
175
|
+
"name": func_name,
|
176
|
+
"description": doc,
|
177
|
+
"signature": info.get("signature", ""),
|
178
|
+
"snippet": snippet,
|
179
|
+
}
|
180
|
+
)
|
181
|
+
|
182
|
+
return results
|
183
|
+
|
184
|
+
@mcp.tool()
|
185
|
+
async def get_function_documentation(function_name: str) -> Dict[str, Any]:
|
186
|
+
"""
|
187
|
+
Get detailed documentation for a specific function.
|
188
|
+
|
189
|
+
Args:
|
190
|
+
function_name: Name of the function
|
191
|
+
|
192
|
+
Returns:
|
193
|
+
Dictionary with function documentation
|
194
|
+
"""
|
195
|
+
if function_name not in self.state.codebase_cache["functions"]:
|
196
|
+
return {"error": f"Function {function_name} not found"}
|
197
|
+
|
198
|
+
return self.state.codebase_cache["functions"][function_name]
|
199
|
+
|
200
|
+
@mcp.tool()
|
201
|
+
async def get_class_documentation(class_name: str) -> Dict[str, Any]:
|
202
|
+
"""
|
203
|
+
Get detailed documentation for a specific class.
|
204
|
+
|
205
|
+
Args:
|
206
|
+
class_name: Name of the class
|
207
|
+
|
208
|
+
Returns:
|
209
|
+
Dictionary with class documentation
|
210
|
+
"""
|
211
|
+
if class_name not in self.state.codebase_cache["classes"]:
|
212
|
+
return {"error": f"Class {class_name} not found"}
|
213
|
+
|
214
|
+
return self.state.codebase_cache["classes"][class_name]
|
215
|
+
|
216
|
+
|
217
|
+
# Module-level component instance
|
218
|
+
_component = CodebaseComponent()
|
219
|
+
|
220
|
+
|
221
|
+
def get_component() -> CodebaseComponent:
|
222
|
+
"""
|
223
|
+
Get the codebase component instance.
|
173
224
|
|
174
|
-
|
225
|
+
Returns
|
226
|
+
-------
|
227
|
+
CodebaseComponent
|
228
|
+
The codebase component instance
|
229
|
+
"""
|
230
|
+
return _component
|
@@ -0,0 +1,170 @@
|
|
1
|
+
"""
|
2
|
+
Base classes for MCP server components.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from abc import ABC, abstractmethod
|
6
|
+
from typing import Dict, Any
|
7
|
+
import logging
|
8
|
+
|
9
|
+
from fastmcp import FastMCP
|
10
|
+
|
11
|
+
logger = logging.getLogger(__name__)
|
12
|
+
|
13
|
+
|
14
|
+
class ComponentState(ABC):
|
15
|
+
"""
|
16
|
+
Base class for component state management.
|
17
|
+
|
18
|
+
Provides standard interface for health checking and initialization tracking.
|
19
|
+
"""
|
20
|
+
|
21
|
+
def __init__(self):
|
22
|
+
self.initialized = False
|
23
|
+
self.initialization_error = None
|
24
|
+
|
25
|
+
@abstractmethod
|
26
|
+
def is_healthy(self) -> bool:
|
27
|
+
"""
|
28
|
+
Check if component has successfully loaded data and is functioning.
|
29
|
+
|
30
|
+
Returns
|
31
|
+
-------
|
32
|
+
bool
|
33
|
+
True if component is healthy (has data and no errors)
|
34
|
+
"""
|
35
|
+
pass
|
36
|
+
|
37
|
+
def is_available(self) -> bool:
|
38
|
+
"""
|
39
|
+
Check if component is available (initialized without critical errors).
|
40
|
+
|
41
|
+
Returns
|
42
|
+
-------
|
43
|
+
bool
|
44
|
+
True if component initialized successfully, regardless of data status
|
45
|
+
"""
|
46
|
+
return self.initialized and self.initialization_error is None
|
47
|
+
|
48
|
+
def get_health_status(self) -> Dict[str, Any]:
|
49
|
+
"""
|
50
|
+
Get standardized health status for health checks.
|
51
|
+
|
52
|
+
Returns
|
53
|
+
-------
|
54
|
+
Dict[str, Any]
|
55
|
+
Health status dictionary with standard format:
|
56
|
+
- status: 'initializing', 'unavailable', 'inactive', or 'healthy'
|
57
|
+
- error: error message if unavailable
|
58
|
+
- additional component-specific details if healthy
|
59
|
+
"""
|
60
|
+
if not self.initialized:
|
61
|
+
return {"status": "initializing"}
|
62
|
+
elif self.initialization_error:
|
63
|
+
return {"status": "unavailable", "error": str(self.initialization_error)}
|
64
|
+
elif not self.is_healthy():
|
65
|
+
return {"status": "inactive"}
|
66
|
+
else:
|
67
|
+
return {"status": "healthy", **self.get_health_details()}
|
68
|
+
|
69
|
+
def get_health_details(self) -> Dict[str, Any]:
|
70
|
+
"""
|
71
|
+
Get component-specific health details.
|
72
|
+
|
73
|
+
Override in subclasses to provide additional health information.
|
74
|
+
|
75
|
+
Returns
|
76
|
+
-------
|
77
|
+
Dict[str, Any]
|
78
|
+
Component-specific health details
|
79
|
+
"""
|
80
|
+
return {}
|
81
|
+
|
82
|
+
|
83
|
+
class MCPComponent(ABC):
|
84
|
+
"""
|
85
|
+
Base class for MCP server components.
|
86
|
+
|
87
|
+
Provides standard interface for initialization and registration.
|
88
|
+
"""
|
89
|
+
|
90
|
+
def __init__(self):
|
91
|
+
self.state = self._create_state()
|
92
|
+
|
93
|
+
@abstractmethod
|
94
|
+
def _create_state(self) -> ComponentState:
|
95
|
+
"""
|
96
|
+
Create the component state object.
|
97
|
+
|
98
|
+
Returns
|
99
|
+
-------
|
100
|
+
ComponentState
|
101
|
+
Component-specific state instance
|
102
|
+
"""
|
103
|
+
pass
|
104
|
+
|
105
|
+
@abstractmethod
|
106
|
+
async def initialize(self) -> bool:
|
107
|
+
"""
|
108
|
+
Initialize the component asynchronously.
|
109
|
+
|
110
|
+
Should populate component state and handle any external data loading.
|
111
|
+
|
112
|
+
Returns
|
113
|
+
-------
|
114
|
+
bool
|
115
|
+
True if initialization successful
|
116
|
+
"""
|
117
|
+
pass
|
118
|
+
|
119
|
+
@abstractmethod
|
120
|
+
def register(self, mcp: FastMCP) -> None:
|
121
|
+
"""
|
122
|
+
Register component resources and tools with the MCP server.
|
123
|
+
|
124
|
+
Parameters
|
125
|
+
----------
|
126
|
+
mcp : FastMCP
|
127
|
+
FastMCP server instance to register with
|
128
|
+
"""
|
129
|
+
pass
|
130
|
+
|
131
|
+
def get_state(self) -> ComponentState:
|
132
|
+
"""
|
133
|
+
Get the component state for health checks and testing.
|
134
|
+
|
135
|
+
Returns
|
136
|
+
-------
|
137
|
+
ComponentState
|
138
|
+
Current component state
|
139
|
+
"""
|
140
|
+
return self.state
|
141
|
+
|
142
|
+
async def safe_initialize(self) -> bool:
|
143
|
+
"""
|
144
|
+
Initialize with error handling and state tracking.
|
145
|
+
|
146
|
+
Returns
|
147
|
+
-------
|
148
|
+
bool
|
149
|
+
True if initialization successful
|
150
|
+
"""
|
151
|
+
try:
|
152
|
+
logger.info(f"Initializing {self.__class__.__name__}...")
|
153
|
+
|
154
|
+
result = await self.initialize()
|
155
|
+
|
156
|
+
self.state.initialized = True
|
157
|
+
self.state.initialization_error = None
|
158
|
+
|
159
|
+
if result:
|
160
|
+
logger.info(f"✅ {self.__class__.__name__} initialized successfully")
|
161
|
+
else:
|
162
|
+
logger.warning(f"⚠️ {self.__class__.__name__} initialized with issues")
|
163
|
+
|
164
|
+
return result
|
165
|
+
|
166
|
+
except Exception as e:
|
167
|
+
logger.error(f"❌ {self.__class__.__name__} failed to initialize: {e}")
|
168
|
+
self.state.initialized = True
|
169
|
+
self.state.initialization_error = e
|
170
|
+
return False
|