google-labs-jules[bot]
Implement cred_db_mcp_server as a Gradio MCP proxy to CRED_API
8e07f8c
"""
This module implements the tools exposed by the MCP server.
Each function corresponds to an MCP tool.
"""
import httpx
from typing import Optional, List, Dict, Any
from .config import CRED_API_BASE_URL
# from .schemas import Provider, Credential, ExpiringCredential, ProviderSnapshot
# We can use simple types in signature for Gradio MCP compatibility
# but use schemas for internal validation if needed.
# Since the prompt asked for mapped errors, we'll wrap calls.
def sync_provider_from_npi(npi: str) -> Dict[str, Any]:
"""
Syncs a provider's data from the NPI registry.
Args:
npi (str): The NPI number of the provider.
Returns:
dict: The provider object returned by the API.
"""
url = f"{CRED_API_BASE_URL}/providers/sync_from_npi"
try:
with httpx.Client() as client:
response = client.post(url, json={"npi": npi})
response.raise_for_status()
return response.json()
except httpx.HTTPStatusError as e:
# Map HTTP errors to MCP/User friendly errors
if e.response.status_code == 404:
return {"error": f"Provider with NPI {npi} not found or sync failed."}
return {"error": f"API Error: {e.response.text}"}
except Exception as e:
return {"error": f"Connection Error: {str(e)}"}
def add_or_update_credential(
provider_id: int,
type: str,
issuer: str,
number: str,
expiry_date: str
) -> Dict[str, Any]:
"""
Adds or updates a credential for a provider.
Args:
provider_id (int): The internal ID of the provider.
type (str): The type of credential (e.g., 'Medical License').
issuer (str): The issuing body (e.g., 'State Board').
number (str): The credential number.
expiry_date (str): The expiry date in YYYY-MM-DD format.
Returns:
dict: The created or updated credential object.
"""
url = f"{CRED_API_BASE_URL}/credentials/add_or_update"
payload = {
"provider_id": provider_id,
"type": type,
"issuer": issuer,
"number": number,
"expiry_date": expiry_date
}
try:
with httpx.Client() as client:
response = client.post(url, json=payload)
response.raise_for_status()
return response.json()
except httpx.HTTPStatusError as e:
return {"error": f"API Error: {e.response.text}"}
except Exception as e:
return {"error": f"Connection Error: {str(e)}"}
def list_expiring_credentials(
window_days: int,
dept: Optional[str] = None,
location: Optional[str] = None
) -> List[Dict[str, Any]]:
"""
Lists credentials expiring within a certain number of days.
Args:
window_days (int): The number of days to check for expiry.
dept (str, optional): Filter by department.
location (str, optional): Filter by location.
Returns:
list: A list of objects containing provider, credential, days_to_expiry, and risk_score.
"""
url = f"{CRED_API_BASE_URL}/credentials/expiring"
payload = {
"window_days": window_days,
"dept": dept,
"location": location
}
# Remove None values to avoid sending them if API doesn't expect them or treat them as valid
payload = {k: v for k, v in payload.items() if v is not None}
try:
with httpx.Client() as client:
response = client.post(url, json=payload)
response.raise_for_status()
return response.json()
except httpx.HTTPStatusError as e:
# In case of error, return a list with an error dict or raise
# For MCP, returning structured error info is usually better than crashing
# But for list return type, we might need to handle differently.
# Here we assume the tool call handles exceptions or checks for "error" key in result if it was a dict.
# Since return type is List, we can't easily return a dict error.
# We'll return an empty list and print error or raise.
# Let's raise ValueError which Gradio might catch and show.
raise ValueError(f"API Error: {e.response.text}")
except Exception as e:
raise ConnectionError(f"Connection Error: {str(e)}")
def get_provider_snapshot(
provider_id: Optional[int] = None,
npi: Optional[str] = None
) -> Dict[str, Any]:
"""
Gets a snapshot of a provider's data including credentials and alerts.
Args:
provider_id (int, optional): The provider's internal ID.
npi (str, optional): The provider's NPI.
Returns:
dict: Object containing provider details, credentials, and alerts.
"""
if provider_id is None and npi is None:
return {"error": "Must provide either provider_id or npi."}
url = f"{CRED_API_BASE_URL}/providers/snapshot"
payload = {}
if provider_id is not None:
payload["provider_id"] = provider_id
if npi is not None:
payload["npi"] = npi
try:
with httpx.Client() as client:
response = client.post(url, json=payload)
response.raise_for_status()
return response.json()
except httpx.HTTPStatusError as e:
if e.response.status_code == 404:
return {"error": "Provider not found."}
return {"error": f"API Error: {e.response.text}"}
except Exception as e:
return {"error": f"Connection Error: {str(e)}"}