|
|
"""MCP Registry - Track generated MCPs to avoid duplicates"""
|
|
|
|
|
|
import json
|
|
|
from pathlib import Path
|
|
|
from typing import Optional, Dict, List
|
|
|
from datetime import datetime
|
|
|
import hashlib
|
|
|
|
|
|
from .config import HOSTED_MCPS_DIR
|
|
|
|
|
|
|
|
|
class MCPRegistry:
|
|
|
"""Registry for tracking generated MCPs"""
|
|
|
|
|
|
def __init__(self):
|
|
|
self.registry_file = HOSTED_MCPS_DIR / "registry.json"
|
|
|
self.registry: Dict[str, dict] = {}
|
|
|
self._load()
|
|
|
|
|
|
def _load(self):
|
|
|
"""Load registry from disk"""
|
|
|
if self.registry_file.exists():
|
|
|
try:
|
|
|
with open(self.registry_file, 'r') as f:
|
|
|
self.registry = json.load(f)
|
|
|
except Exception:
|
|
|
self.registry = {}
|
|
|
else:
|
|
|
self.registry = {}
|
|
|
|
|
|
def _save(self):
|
|
|
"""Save registry to disk"""
|
|
|
self.registry_file.parent.mkdir(exist_ok=True)
|
|
|
with open(self.registry_file, 'w') as f:
|
|
|
json.dump(self.registry, indent=2, fp=f)
|
|
|
|
|
|
def _url_hash(self, api_url: str) -> str:
|
|
|
"""Generate a consistent hash for an API URL"""
|
|
|
|
|
|
normalized = api_url.lower().rstrip('/')
|
|
|
return hashlib.md5(normalized.encode()).hexdigest()
|
|
|
|
|
|
def find_by_url(self, api_url: str) -> Optional[dict]:
|
|
|
"""Find an existing MCP for a given API URL
|
|
|
|
|
|
Args:
|
|
|
api_url: The API URL to search for
|
|
|
|
|
|
Returns:
|
|
|
MCP info dict if found, None otherwise
|
|
|
"""
|
|
|
url_hash = self._url_hash(api_url)
|
|
|
|
|
|
if url_hash in self.registry:
|
|
|
mcp_info = self.registry[url_hash]
|
|
|
|
|
|
|
|
|
mcp_path = HOSTED_MCPS_DIR / mcp_info['mcp_id']
|
|
|
if mcp_path.exists():
|
|
|
return mcp_info
|
|
|
else:
|
|
|
|
|
|
del self.registry[url_hash]
|
|
|
self._save()
|
|
|
|
|
|
return None
|
|
|
|
|
|
def register(self, api_url: str, mcp_id: str, api_name: str, metadata: dict = None) -> dict:
|
|
|
"""Register a new MCP
|
|
|
|
|
|
Args:
|
|
|
api_url: The API URL
|
|
|
mcp_id: The generated MCP ID
|
|
|
api_name: The API name
|
|
|
metadata: Optional metadata dict
|
|
|
|
|
|
Returns:
|
|
|
The registered MCP info
|
|
|
"""
|
|
|
url_hash = self._url_hash(api_url)
|
|
|
|
|
|
mcp_info = {
|
|
|
'mcp_id': mcp_id,
|
|
|
'api_url': api_url,
|
|
|
'api_name': api_name,
|
|
|
'created_at': datetime.now().isoformat(),
|
|
|
'last_used': datetime.now().isoformat(),
|
|
|
'metadata': metadata or {}
|
|
|
}
|
|
|
|
|
|
self.registry[url_hash] = mcp_info
|
|
|
self._save()
|
|
|
|
|
|
return mcp_info
|
|
|
|
|
|
def update_last_used(self, api_url: str):
|
|
|
"""Update the last_used timestamp for an MCP
|
|
|
|
|
|
Args:
|
|
|
api_url: The API URL
|
|
|
"""
|
|
|
url_hash = self._url_hash(api_url)
|
|
|
|
|
|
if url_hash in self.registry:
|
|
|
self.registry[url_hash]['last_used'] = datetime.now().isoformat()
|
|
|
self._save()
|
|
|
|
|
|
def list_all(self) -> List[dict]:
|
|
|
"""List all registered MCPs
|
|
|
|
|
|
Returns:
|
|
|
List of MCP info dicts
|
|
|
"""
|
|
|
|
|
|
valid_mcps = []
|
|
|
|
|
|
for url_hash, mcp_info in list(self.registry.items()):
|
|
|
mcp_path = HOSTED_MCPS_DIR / mcp_info['mcp_id']
|
|
|
if mcp_path.exists():
|
|
|
valid_mcps.append(mcp_info)
|
|
|
else:
|
|
|
|
|
|
del self.registry[url_hash]
|
|
|
|
|
|
if len(valid_mcps) != len(self.registry):
|
|
|
self._save()
|
|
|
|
|
|
|
|
|
valid_mcps.sort(key=lambda x: x['last_used'], reverse=True)
|
|
|
|
|
|
return valid_mcps
|
|
|
|
|
|
def delete(self, api_url: str) -> bool:
|
|
|
"""Delete an MCP from registry
|
|
|
|
|
|
Args:
|
|
|
api_url: The API URL
|
|
|
|
|
|
Returns:
|
|
|
True if deleted, False if not found
|
|
|
"""
|
|
|
url_hash = self._url_hash(api_url)
|
|
|
|
|
|
if url_hash in self.registry:
|
|
|
del self.registry[url_hash]
|
|
|
self._save()
|
|
|
return True
|
|
|
|
|
|
return False
|
|
|
|
|
|
def get_stats(self) -> dict:
|
|
|
"""Get registry statistics
|
|
|
|
|
|
Returns:
|
|
|
Stats dict
|
|
|
"""
|
|
|
mcps = self.list_all()
|
|
|
|
|
|
return {
|
|
|
'total_mcps': len(mcps),
|
|
|
'oldest': mcps[-1]['created_at'] if mcps else None,
|
|
|
'newest': mcps[0]['created_at'] if mcps else None,
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
mcp_registry = MCPRegistry()
|
|
|
|