Backend Functions Reference
The get_nbm
and get_ads
functions are essential utility functions in the scmcphub ecosystem that provide access to the backend managers during MCP server execution. These functions are used to retrieve the appropriate backend context based on the execution mode.
Overview
These functions serve as accessors to the backend managers that are injected into the MCP server’s lifespan context. They are crucial for:
- Tool Mode: Accessing
AdataManager
for AnnData operations
- Code Mode: Accessing
NotebookManager
for Jupyter notebook operations
Function Reference
get_ads()
Retrieves the AdataManager
instance from the current request context.
Signature
def get_ads():
ctx = get_context()
ads = ctx.request_context.lifespan_context
return ads
Returns
- Type:
AdataManager
- Description: The AdataManager instance that manages AnnData objects
Usage Context
- Mode: Tool Mode (
tool-mode
)
- Backend:
AdataManager
- Purpose: Managing AnnData objects for single-cell analysis
Example
from scmcp_shared.util import get_ads
from scmcp_shared.schema import AdataInfo
@mcp.tool()
def read_data(request: AdataInfo):
"""Read AnnData from file."""
ads = get_ads() # Get AdataManager instance
adata = ads.get_adata(adinfo=request)
return {"message": f"Loaded data with {adata.n_obs} cells"}
get_nbm()
Retrieves the NotebookManager
instance from the current request context.
Signature
def get_nbm():
ctx = get_context()
nbm = ctx.request_context.lifespan_context
return nbm
Returns
- Type:
NotebookManager
- Description: The NotebookManager instance that manages Jupyter notebooks
Usage Context
- Mode: Code Mode (
code-mode
)
- Backend:
NotebookManager
- Purpose: Managing Jupyter notebook execution
Example
from scmcp_shared.util import get_nbm
@mcp.tool()
def execute_code(code: str):
"""Execute code in Jupyter notebook."""
nbm = get_nbm() # Get NotebookManager instance
jce = nbm.active_notebook
result = jce.execute(code)
return result
Backend Managers
AdataManager
The AdataManager
is responsible for managing AnnData objects in tool mode.
Key Features
- Multi-type Support: Manages different types of AnnData (exp, activity, cnv, splicing)
- Active Sample Management: Tracks the currently active sample ID
- Sample ID Validation: Ensures data consistency across operations
Main Methods
class AdataManager:
def get_adata(self, sampleid=None, adtype="exp", adinfo=None):
"""Retrieve AnnData object."""
def set_adata(self, adata, sampleid=None, sdtype="exp", adinfo=None):
"""Store AnnData object."""
@property
def active_id(self):
"""Get the currently active sample ID."""
Example Usage
def process_data(adinfo: AdataInfo):
ads = get_ads()
# Get AnnData for current sample
adata = ads.get_adata(adinfo=adinfo)
# Process the data
# ... processing logic ...
# Store the processed data
ads.set_adata(adata, adinfo=adinfo)
return {"message": "Data processed successfully"}
NotebookManager
The NotebookManager
is responsible for managing Jupyter notebooks in code mode.
Key Features
- Multiple Notebook Support: Manages multiple notebook instances
- Active Notebook Tracking: Maintains the currently active notebook
- Kernel Management: Handles Jupyter kernel lifecycle
Main Methods
class NotebookManager:
def create_notebook(self, nbid, path, kernel="python"):
"""Create a new notebook instance."""
def delete_notebook(self, nbid):
"""Delete a notebook instance."""
def switch_notebook(self, nbid):
"""Switch to a different notebook."""
@property
def active_notebook(self):
"""Get the currently active notebook."""
Example Usage
def run_analysis(code: str):
nbm = get_nbm()
# Get the active notebook
jce = nbm.active_notebook
# Execute code
result = jce.execute(code, add_cell=True)
return {
"notebook_id": nbm.active_nbid,
"result": result
}
Context System
Both functions rely on FastMCP’s context system to access the backend managers.
Context Flow
- Server Initialization: Backend manager is created and injected into lifespan context
- Request Processing: Context is available during tool execution
- Function Call:
get_context()
retrieves the current request context
- Backend Access: Backend manager is extracted from lifespan context
Context Structure
ctx = get_context()
# ctx.request_context.lifespan_context contains the backend manager
Mode-Specific Usage
# Server configuration
manager = MyMCPManager(
name="my-mcp",
backend=AdataManager # Uses AdataManager
)
# Tool implementation
@mcp.tool()
def my_tool(request: MyRequest):
ads = get_ads() # Get AdataManager
adata = ads.get_adata(adinfo=request.adinfo)
# ... tool logic ...
Code Mode
# Server configuration
manager = MyMCPManager(
name="my-mcp",
backend=NotebookManager, # Uses NotebookManager
include_tags=["nb"] # Include notebook tools
)
# Tool implementation
@mcp.tool(tags=["nb"])
def execute_code(code: str):
nbm = get_nbm() # Get NotebookManager
jce = nbm.active_notebook
result = jce.execute(code)
return result
Error Handling
Common Issues
- Context Not Available: Ensure the function is called within an MCP tool context
- Wrong Backend Type: Verify the correct backend is configured for your use case
- Missing Dependencies: Ensure FastMCP dependencies are properly installed
Error Examples
# This will fail if not in MCP context
def standalone_function():
try:
ads = get_ads()
except Exception as e:
print(f"Context not available: {e}")
# This will fail if wrong backend is used
def wrong_backend_usage():
# If server uses NotebookManager but tool expects AdataManager
ads = get_ads() # Will return NotebookManager, not AdataManager
Best Practices
1. Import Location
Import these functions at the module level:
from scmcp_shared.util import get_ads, get_nbm
2. Error Checking
Always check if the backend is available:
def safe_backend_access():
try:
ads = get_ads()
return ads.get_adata(adinfo=adinfo)
except Exception as e:
raise ToolError(f"Failed to access backend: {e}")
3. Type Hints
Use proper type hints for better code clarity:
from scmcp_shared.backend import AdataManager, NotebookManager
def get_ads() -> AdataManager:
ctx = get_context()
return ctx.request_context.lifespan_context
def get_nbm() -> NotebookManager:
ctx = get_context()
return ctx.request_context.lifespan_context
4. Context Validation
Validate the backend type when needed:
def validate_backend():
backend = get_ads() # or get_nbm()
if not isinstance(backend, AdataManager):
raise ValueError("Expected AdataManager backend")
Integration Examples
from fastmcp import FastMCP
from scmcp_shared.util import get_ads
from scmcp_shared.schema import AdataInfo
from pydantic import BaseModel
class ReadDataRequest(BaseModel):
file_path: str
adinfo: AdataInfo = AdataInfo()
@mcp.tool()
def read_h5ad(request: ReadDataRequest):
"""Read AnnData from H5AD file."""
try:
ads = get_ads()
import scanpy as sc
# Read the data
adata = sc.read_h5ad(request.file_path)
# Store in backend
ads.set_adata(adata, adinfo=request.adinfo)
return {
"message": f"Successfully loaded {adata.n_obs} cells and {adata.n_vars} genes",
"sampleid": request.adinfo.sampleid or ads.active_id
}
except Exception as e:
raise ToolError(f"Failed to read data: {e}")
Complete Code Mode Example
from fastmcp import FastMCP
from scmcp_shared.util import get_nbm
from pydantic import BaseModel
class ExecuteCodeRequest(BaseModel):
code: str
backup_var: str = None
@mcp.tool(tags=["nb"])
def execute_code(request: ExecuteCodeRequest):
"""Execute code in Jupyter notebook."""
try:
nbm = get_nbm()
jce = nbm.active_notebook
# Execute the code
result = jce.execute(
request.code,
backup_var=request.backup_var
)
return {
"notebook_id": nbm.active_nbid,
"result": result
}
except Exception as e:
raise ToolError(f"Failed to execute code: {e}")