We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/team-tissis/nolang-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
import json
from pathlib import Path
from typing import Any, Dict, List, Optional, Union
from uuid import UUID
import httpx
from pydantic import FilePath, TypeAdapter, ValidationError
from nolang_mcp.config import nolang_mcp_config
from nolang_mcp.models import (
FilesPayload,
MultipartFile,
TemplateRecommendationResponse,
VideoGenerationResponse,
VideoListResponse,
VideoModeEnum,
VideoSettingsResponse,
VideoStatusResponse,
)
class NoLangAPI:
def __init__(self, api_key: str) -> None:
self.base_url = nolang_mcp_config.nolang_api_base_url
self.headers = {"Authorization": f"Bearer {api_key}"}
@staticmethod
def _validate_file_path(file_path: str) -> Path:
"""Validate that the given file path exists and return Path instance."""
try:
TypeAdapter(FilePath).validate_python(file_path)
return Path(file_path)
except ValidationError:
raise FileNotFoundError(f"File not found: {file_path}")
async def _post(
self,
path: str,
data: Optional[Dict[str, Any]] = None,
files: Optional[FilesPayload] = None,
) -> Dict[str, Any]:
files_to_send = {k: v.to_tuple() for k, v in files.items()} if files else None
async with httpx.AsyncClient(timeout=httpx.Timeout(60.0)) as client:
url = f"{self.base_url}{path}"
if data and "setting" in data and isinstance(data["setting"], dict):
data = {**data, "setting": json.dumps(data["setting"], ensure_ascii=False)}
r = await client.post(url, headers=self.headers, data=data, files=files_to_send)
r.raise_for_status()
return r.json()
async def _get(self, path: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
async with httpx.AsyncClient() as client:
url = f"{self.base_url}{path}"
r = await client.get(url, headers=self.headers, params=params)
r.raise_for_status()
try:
return r.json()
except Exception:
raise
def _prepare_setting_data(self, setting: Union[UUID, str, Dict[str, Any]]) -> tuple[str, Dict[str, Any]]:
"""Internal utility: returns API URL and data base from setting"""
if isinstance(setting, dict):
return "/unstable/videos/generate/", {"setting": setting}
else:
return "/videos/generate/", {"video_setting_id": str(setting)}
async def _generate_video_with_file(
self,
setting: Union[UUID, str, Dict[str, Any]],
file_path: str,
file_field_name: str,
mime_type: str,
extra_data: Optional[Dict[str, Any]] = None,
) -> VideoGenerationResponse:
path = self._validate_file_path(file_path)
api_url, data = self._prepare_setting_data(setting)
if extra_data:
data.update(extra_data)
with open(path, "rb") as f:
files: FilesPayload = {
file_field_name: MultipartFile(filename=path.name, content=f.read(), mime_type=mime_type)
}
response_data = await self._post(api_url, data=data, files=files)
return VideoGenerationResponse(**response_data)
async def generate_video_with_text(
self,
setting: Union[UUID, str, Dict[str, Any]],
text: str,
image_files: Optional[List[str]] = None,
) -> VideoGenerationResponse:
api_url, data = self._prepare_setting_data(setting)
data["text"] = text
files: FilesPayload = {}
if image_files:
for image_path in image_files:
try:
path = self._validate_file_path(image_path)
except FileNotFoundError:
# Skip non-existent image paths gracefully
continue
with open(path, "rb") as f:
files["image_files"] = MultipartFile(
filename=path.name,
content=f.read(),
mime_type="image/jpeg",
)
if files:
response_data = await self._post(api_url, data=data, files=files)
else:
response_data = await self._post(api_url, data=data)
return VideoGenerationResponse(**response_data)
async def generate_video_with_pdf(
self,
setting: Union[UUID, str, Dict[str, Any]],
pdf_path: str,
) -> VideoGenerationResponse:
"""Generate video by uploading PDF"""
return await self._generate_video_with_file(
setting,
pdf_path,
"pdf_file",
"application/pdf",
)
async def generate_video_with_pptx(
self,
setting: Union[UUID, str, Dict[str, Any]],
pptx_path: str,
) -> VideoGenerationResponse:
"""Generate video by uploading PPTX"""
return await self._generate_video_with_file(
setting,
pptx_path,
"pptx_file",
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
)
async def generate_video_with_audio(
self,
setting: Union[UUID, str, Dict[str, Any]],
audio_path: str,
) -> VideoGenerationResponse:
"""Generate video by uploading audio file"""
path = self._validate_file_path(audio_path)
mime_types = {
".mp3": "audio/mpeg",
".wav": "audio/wav",
".m4a": "audio/mp4",
".aac": "audio/aac",
}
mime_type = mime_types.get(path.suffix.lower(), "audio/mpeg")
return await self._generate_video_with_file(
setting,
audio_path,
"audio_file",
mime_type,
)
async def generate_video_with_video(
self,
setting: Union[UUID, str, Dict[str, Any]],
video_path: str,
) -> VideoGenerationResponse:
"""Generate video by uploading video file (encoding only)"""
return await self._generate_video_with_file(
setting,
video_path,
"video_file",
"video/mp4",
)
async def generate_video_with_pdf_and_text(
self,
setting: Union[UUID, str, Dict[str, Any]],
pdf_path: str,
text: str,
) -> VideoGenerationResponse:
"""Generate video with combination of PDF and text"""
return await self._generate_video_with_file(
setting,
pdf_path,
"pdf_file",
"application/pdf",
extra_data={"text": text},
)
async def get_video_status(self, video_id: UUID) -> VideoStatusResponse:
response_data = await self._get(f"/videos/{video_id}/")
return VideoStatusResponse(**response_data)
async def list_videos(self, page: int = 1) -> VideoListResponse:
response_data = await self._get("/videos/", params={"page": page})
return VideoListResponse(**response_data)
async def list_video_settings(self, page: int = 1) -> VideoSettingsResponse:
response_data = await self._get("/unstable/video-settings/", params={"page": page})
return VideoSettingsResponse(**response_data)
async def get_video_setting_from_video_id(self, video_id: UUID) -> Dict[str, Any]:
return await self._get(
f"/unstable/video-settings/{video_id}/",
)
async def recommend_template(
self,
video_mode: VideoModeEnum,
query: Optional[str] = None,
is_mobile_format: Optional[bool] = None,
) -> TemplateRecommendationResponse:
"""Template recommendation endpoint (unstable)"""
params: Dict[str, Any] = {"video_mode": video_mode.value}
if query is not None:
params["query"] = query
if is_mobile_format is not None:
params["is_mobile_format"] = is_mobile_format
try:
response_data = await self._get(
"/unstable/template/recommend/",
params=params,
)
return TemplateRecommendationResponse(**response_data)
except Exception:
raise
def format_http_error(e: httpx.HTTPStatusError) -> str:
try:
data = e.response.json()
return (
f"API Error {e.response.status_code}\n"
f"Code : {data.get('code', '—')}\n"
f"Message: {data.get('error', 'Unknown error')}"
)
except Exception:
return f"API Error {e.response.status_code}\nBody: {e.response.text}"
nolang_api = NoLangAPI(nolang_mcp_config.nolang_api_key)