mirror of https://github.com/kortix-ai/suna.git
128 lines
5.0 KiB
Python
128 lines
5.0 KiB
Python
|
from abc import abstractmethod
|
||
|
from typing import List, Optional, Dict, Any
|
||
|
import os
|
||
|
import httpx
|
||
|
from urllib.parse import quote
|
||
|
|
||
|
from .base import Repository
|
||
|
from ..domain.entities import MCPServer, MCPServerDetail, MCPServerListResult, PopularServersResult
|
||
|
from ..domain.exceptions import MCPRegistryError, MCPServerNotFoundError
|
||
|
from ..protocols import Logger
|
||
|
|
||
|
|
||
|
class SmitheryRepository(Repository[MCPServer]):
|
||
|
def __init__(self, logger: Logger):
|
||
|
self._logger = logger
|
||
|
self._base_url = "https://registry.smithery.ai"
|
||
|
self._server_base_url = "https://server.smithery.ai"
|
||
|
self._api_key = os.getenv("SMITHERY_API_KEY")
|
||
|
|
||
|
async def find_by_id(self, qualified_name: str) -> Optional[MCPServer]:
|
||
|
try:
|
||
|
details = await self.get_server_details(qualified_name)
|
||
|
return MCPServer(
|
||
|
qualified_name=details.qualified_name,
|
||
|
display_name=details.display_name,
|
||
|
description="",
|
||
|
created_at="",
|
||
|
use_count=0,
|
||
|
homepage="",
|
||
|
icon_url=details.icon_url,
|
||
|
is_deployed=details.deployment_url is not None,
|
||
|
tools=details.tools,
|
||
|
security=details.security
|
||
|
)
|
||
|
except MCPServerNotFoundError:
|
||
|
return None
|
||
|
|
||
|
async def find_all(self) -> List[MCPServer]:
|
||
|
result = await self.list_servers()
|
||
|
return result.servers
|
||
|
|
||
|
async def list_servers(
|
||
|
self,
|
||
|
query: Optional[str] = None,
|
||
|
page: int = 1,
|
||
|
page_size: int = 20
|
||
|
) -> MCPServerListResult:
|
||
|
try:
|
||
|
params = {
|
||
|
"page": page,
|
||
|
"pageSize": page_size
|
||
|
}
|
||
|
if query:
|
||
|
params["q"] = query
|
||
|
|
||
|
async with httpx.AsyncClient() as client:
|
||
|
response = await client.get(f"{self._base_url}/api/v1/packages", params=params)
|
||
|
response.raise_for_status()
|
||
|
data = response.json()
|
||
|
|
||
|
servers = []
|
||
|
for item in data.get("packages", []):
|
||
|
servers.append(MCPServer(
|
||
|
qualified_name=item["qualifiedName"],
|
||
|
display_name=item["displayName"],
|
||
|
description=item["description"],
|
||
|
created_at=item["createdAt"],
|
||
|
use_count=item["useCount"],
|
||
|
homepage=item["homepage"]
|
||
|
))
|
||
|
|
||
|
return MCPServerListResult(
|
||
|
servers=servers,
|
||
|
pagination=data.get("pagination", {})
|
||
|
)
|
||
|
|
||
|
except httpx.HTTPError as e:
|
||
|
self._logger.error(f"Error fetching MCP servers: {str(e)}")
|
||
|
raise MCPRegistryError(f"Failed to fetch MCP servers: {str(e)}")
|
||
|
|
||
|
async def get_server_details(self, qualified_name: str) -> MCPServerDetail:
|
||
|
try:
|
||
|
encoded_name = quote(qualified_name, safe='')
|
||
|
|
||
|
async with httpx.AsyncClient() as client:
|
||
|
response = await client.get(f"{self._base_url}/api/v1/packages/{encoded_name}")
|
||
|
|
||
|
if response.status_code == 404:
|
||
|
raise MCPServerNotFoundError(f"MCP server not found: {qualified_name}")
|
||
|
|
||
|
response.raise_for_status()
|
||
|
data = response.json()
|
||
|
|
||
|
return MCPServerDetail(
|
||
|
qualified_name=data["qualifiedName"],
|
||
|
display_name=data["displayName"],
|
||
|
icon_url=data.get("iconUrl"),
|
||
|
deployment_url=data.get("deploymentUrl"),
|
||
|
connections=data.get("connections", []),
|
||
|
security=data.get("security"),
|
||
|
tools=data.get("tools")
|
||
|
)
|
||
|
|
||
|
except httpx.HTTPError as e:
|
||
|
if e.response and e.response.status_code == 404:
|
||
|
raise MCPServerNotFoundError(f"MCP server not found: {qualified_name}")
|
||
|
self._logger.error(f"Error fetching MCP server details: {str(e)}")
|
||
|
raise MCPRegistryError(f"Failed to fetch MCP server details: {str(e)}")
|
||
|
|
||
|
async def get_popular_servers(self) -> PopularServersResult:
|
||
|
try:
|
||
|
async with httpx.AsyncClient() as client:
|
||
|
response = await client.get(f"{self._base_url}/api/v1/packages/popular")
|
||
|
response.raise_for_status()
|
||
|
data = response.json()
|
||
|
|
||
|
return PopularServersResult(
|
||
|
success=data.get("success", True),
|
||
|
servers=data.get("servers", []),
|
||
|
categorized=data.get("categorized", {}),
|
||
|
total=data.get("total", 0),
|
||
|
category_count=data.get("categoryCount", 0),
|
||
|
pagination=data.get("pagination", {})
|
||
|
)
|
||
|
|
||
|
except httpx.HTTPError as e:
|
||
|
self._logger.error(f"Error fetching popular MCP servers: {str(e)}")
|
||
|
raise MCPRegistryError(f"Failed to fetch popular MCP servers: {str(e)}")
|