list_projects
Retrieve and filter software projects from package repositories to identify packages by maintainer, category, repository status, or version issues.
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 |
|---|---|---|---|
| start_from | No | ||
| limit | No | ||
| maintainer | No | ||
| category | No | ||
| inrepo | No | ||
| notinrepo | No | ||
| repos | No | ||
| families | No | ||
| newest | No | ||
| outdated | No | ||
| problematic | No |
Implementation Reference
- src/repology_mcp/server.py:186-267 (handler)The MCP tool handler function for 'list_projects'. It enforces limits, builds filters, calls RepologyClient.list_projects, applies client-side repo filtering if needed, formats output as JSON, and handles errors.@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/server.py:186-186 (registration)The @mcp.tool() decorator registers the list_projects function as an MCP tool on the FastMCP server instance.@mcp.tool()
- src/repology_mcp/client.py:212-267 (helper)RepologyClient.list_projects method: Fetches project list from Repology API with pagination and filters, validates packages with Pydantic models.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:69-75 (helper)Helper function to convert the project packages dictionary to indented JSON string for the tool response.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 function for client-side filtering of project packages by repository after API call.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:7-34 (schema)Pydantic model for Package data structure used in list_projects output validation and serialization.class Package(BaseModel): """A package in a repository.""" repo: str = Field(description="Repository name") subrepo: Optional[str] = Field(None, description="Subrepository name") srcname: Optional[str] = Field(None, description="Source package name") binname: Optional[str] = Field(None, description="Binary package name") binnames: Optional[List[str]] = Field(None, description="All binary package names") visiblename: str = Field(description="Package name as shown by Repology") version: str = Field(description="Package version (sanitized)") origversion: Optional[str] = Field(None, description="Original package version") status: Literal[ "newest", "devel", "unique", "outdated", "legacy", "rolling", "noscheme", "incorrect", "untrusted", "ignored", ] = Field(description="Package status") summary: Optional[str] = Field(None, description="Package description") categories: Optional[List[str]] = Field(None, description="Package categories") licenses: Optional[List[str]] = Field(None, description="Package licenses") maintainers: Optional[List[str]] = Field(None, description="Package maintainers")