diff --git a/litellm/proxy/anthropic_endpoints/claude_code_endpoints/claude_code_marketplace.py b/litellm/proxy/anthropic_endpoints/claude_code_endpoints/claude_code_marketplace.py index 78cce865a21..7b4354c4b82 100644 --- a/litellm/proxy/anthropic_endpoints/claude_code_endpoints/claude_code_marketplace.py +++ b/litellm/proxy/anthropic_endpoints/claude_code_endpoints/claude_code_marketplace.py @@ -139,6 +139,56 @@ _VALID_GIT_SUBDIR_PATH_RE = re.compile( ) +def _validate_plugin_source(source: Dict[str, Any]) -> None: + """Validate plugin source format, raising HTTPException on invalid input.""" + source_type = source.get("source") + if source_type == "github": + if "repo" not in source: + raise HTTPException( + status_code=400, + detail={ + "error": "GitHub source must include 'repo' field (e.g., 'org/repo')" + }, + ) + elif source_type == "url": + if "url" not in source: + raise HTTPException( + status_code=400, + detail={ + "error": "URL source must include 'url' field (e.g., 'https://github.com/org/repo.git')" + }, + ) + elif source_type == "git-subdir": + if not source.get("url"): + raise HTTPException( + status_code=400, + detail={ + "error": "git-subdir source must include 'url' field (e.g., 'https://github.com/org/repo.git')" + }, + ) + if not source.get("path"): + raise HTTPException( + status_code=400, + detail={ + "error": "git-subdir source must include 'path' field (e.g., 'plugins/plugin-name')" + }, + ) + if not _VALID_GIT_SUBDIR_PATH_RE.match(source["path"]): + raise HTTPException( + status_code=400, + detail={ + "error": "git-subdir 'path' must be a relative path of the form 'segment/segment' (alphanumeric, dots, hyphens, underscores only)" + }, + ) + else: + raise HTTPException( + status_code=400, + detail={ + "error": "source.source must be 'github', 'url', or 'git-subdir'" + }, + ) + + @router.post( "/claude-code/plugins", tags=["Claude Code Marketplace"], @@ -195,53 +245,7 @@ async def register_plugin( # Validate source format source = request.source - source_type = source.get("source") - - if source_type == "github": - if "repo" not in source: - raise HTTPException( - status_code=400, - detail={ - "error": "GitHub source must include 'repo' field (e.g., 'org/repo')" - }, - ) - elif source_type == "url": - if "url" not in source: - raise HTTPException( - status_code=400, - detail={ - "error": "URL source must include 'url' field (e.g., 'https://github.com/org/repo.git')" - }, - ) - elif source_type == "git-subdir": - if not source.get("url"): - raise HTTPException( - status_code=400, - detail={ - "error": "git-subdir source must include 'url' field (e.g., 'https://github.com/org/repo.git')" - }, - ) - if not source.get("path"): - raise HTTPException( - status_code=400, - detail={ - "error": "git-subdir source must include 'path' field (e.g., 'plugins/plugin-name')" - }, - ) - if not _VALID_GIT_SUBDIR_PATH_RE.match(source["path"]): - raise HTTPException( - status_code=400, - detail={ - "error": "git-subdir 'path' must be a relative path of the form 'segment/segment' (alphanumeric, dots, hyphens, underscores only)" - }, - ) - else: - raise HTTPException( - status_code=400, - detail={ - "error": "source.source must be 'github', 'url', or 'git-subdir'" - }, - ) + _validate_plugin_source(source) # Build manifest for storage manifest: Dict[str, Any] = {