Skip to main content
Glama
tschoonj

Repology MCP Server

by tschoonj

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
NameRequiredDescriptionDefault
start_fromNo
limitNo
maintainerNo
categoryNo
inrepoNo
notinrepoNo
reposNo
familiesNo
newestNo
outdatedNo
problematicNo

Implementation Reference

  • 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}"})
  • The @mcp.tool() decorator registers the list_projects function as an MCP tool on the FastMCP server instance.
    @mcp.tool()
  • 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}")
  • 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)
  • 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
  • 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")

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/tschoonj/repology-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server