list_projects
Retrieve and filter software projects from Repology repositories by maintainer, category, repository presence, or status like outdated or problematic.
Instructions
List projects with optional filtering.
Args:
start_from: Project name to start listing from
limit: Maximum number of results (default: 10, max: 200)
maintainer: Filter by maintainer email
category: Filter by category
inrepo: Filter by repository presence
notinrepo: Filter by repository absence
repos: Filter by number of repositories (e.g., "1", "5-", "-5", "2-7")
families: Filter by number of repository families
newest: Show only newest projects
outdated: Show only outdated projects
problematic: Show only problematic projects
Returns:
JSON formatted dictionary of projects and their packages
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| category | No | ||
| families | No | ||
| inrepo | No | ||
| limit | No | ||
| maintainer | No | ||
| newest | No | ||
| notinrepo | No | ||
| outdated | No | ||
| problematic | No | ||
| repos | No | ||
| start_from | No |
Implementation Reference
- src/repology_mcp/server.py:186-266 (handler)The @mcp.tool()-decorated handler function implementing the 'list_projects' tool logic. It processes input parameters, builds filters, calls RepologyClient.list_projects, applies additional filtering if needed, and returns formatted JSON.@mcp.tool() async def list_projects( start_from: Optional[str] = None, limit: int = 10, maintainer: Optional[str] = None, category: Optional[str] = None, inrepo: Optional[str] = None, notinrepo: Optional[str] = None, repos: Optional[str] = None, families: Optional[str] = None, newest: Optional[bool] = None, outdated: Optional[bool] = None, problematic: Optional[bool] = None, ctx: Context[ServerSession, AppContext] = None, ) -> str: """List projects with optional filtering. Args: start_from: Project name to start listing from limit: Maximum number of results (default: 10, max: 200) maintainer: Filter by maintainer email category: Filter by category inrepo: Filter by repository presence notinrepo: Filter by repository absence repos: Filter by number of repositories (e.g., "1", "5-", "-5", "2-7") families: Filter by number of repository families newest: Show only newest projects outdated: Show only outdated projects problematic: Show only problematic projects Returns: JSON formatted dictionary of projects and their packages """ if limit > 200: limit = 200 try: client = ctx.request_context.lifespan_context.repology_client # Build filters filters = {} if maintainer: filters["maintainer"] = maintainer if category: filters["category"] = category if inrepo: filters["inrepo"] = inrepo if notinrepo: filters["notinrepo"] = notinrepo if repos: filters["repos"] = repos if families: filters["families"] = families if newest: filters["newest"] = "1" if outdated: filters["outdated"] = "1" if problematic: filters["problematic"] = "1" project_packages = await client.list_projects( start_from=start_from, limit=limit, **filters ) # Apply client-side repository filtering if inrepo is specified if inrepo and project_packages: project_packages = _filter_project_packages_by_repo( project_packages, inrepo ) if not project_packages: return json.dumps({"message": "No projects found matching the criteria"}) return _project_packages_to_json(project_packages) except RepologyAPIError as e: await ctx.error(f"Repology API error: {e}") return json.dumps({"error": str(e)}) except Exception as e: await ctx.error(f"Unexpected error listing projects: {e}") return json.dumps({"error": f"Unexpected error: {e}"})
- src/repology_mcp/client.py:212-267 (helper)RepologyClient.list_projects method: core helper that makes HTTP requests to Repology API /projects/ endpoint, handles pagination and filters, validates and returns ProjectPackages data structure.async def list_projects( self, start_from: Optional[str] = None, end_at: Optional[str] = None, limit: int = 200, **filters: Any, ) -> ProjectPackages: """List projects with optional filtering. Args: start_from: Project name to start from (inclusive) end_at: Project name to end at (inclusive) limit: Maximum number of projects (max 200) **filters: Additional filters (maintainer, category, inrepo, etc.) Returns: Dictionary mapping project names to package lists """ # Build endpoint path if start_from and end_at: endpoint = f"projects/{quote(start_from)}/..{quote(end_at)}/" elif start_from: endpoint = f"projects/{quote(start_from)}/" elif end_at: endpoint = f"projects/..{quote(end_at)}/" else: endpoint = "projects/" # Add query parameters for filters params = {} for key, value in filters.items(): if value is not None: params[key] = value try: data = await self._make_request(endpoint, params) if not isinstance(data, dict): raise RepologyAPIError(f"Expected dict, got {type(data)}") result = {} for project_name, packages_data in data.items(): packages = [] for item in packages_data: try: packages.append(Package.model_validate(item)) except ValidationError as e: print(f"Warning: Failed to validate package data: {e}") continue result[project_name] = packages return result except Exception as e: raise RepologyAPIError(f"Failed to list projects: {e}")
- src/repology_mcp/server.py:68-75 (helper)Utility function to convert ProjectPackages dict to pretty-printed JSON string, used in list_projects handler output.def _project_packages_to_json(project_packages: Dict[str, List[Package]]) -> str: """Convert project packages dict to formatted JSON string.""" result = {} for project_name, packages in project_packages.items(): result[project_name] = [pkg.model_dump() for pkg in packages] return json.dumps(result, indent=2)
- src/repology_mcp/server.py:55-67 (helper)Helper to filter project packages by repository, called conditionally in list_projects handler when 'inrepo' filter is provided.def _filter_project_packages_by_repo( project_packages: Dict[str, List[Package]], repo: str ) -> Dict[str, List[Package]]: """Filter project packages to only include those from a specific repository.""" filtered = {} for project_name, packages in project_packages.items(): filtered_packages = _filter_packages_by_repo(packages, repo) if ( filtered_packages ): # Only include projects that have packages in the specified repo filtered[project_name] = filtered_packages return filtered
- src/repology_mcp/models.py:64-66 (schema)Type definitions including ProjectPackages used throughout the list_projects implementation for return types and data structures.ProjectPackages = Dict[str, List[Package]] ProjectData = List[Package] ProblemsData = List[Problem]