Skip to main content
Glama
tschoonj

Repology MCP Server

by tschoonj

search_projects

Find software projects by name with filters for maintainer, category, and repository presence to locate packages across distributions.

Instructions

Search for projects by name substring.

Args:
    query: Search term to match against project names
    limit: Maximum number of results (default: 10, max: 100)
    maintainer: Optional maintainer email filter
    category: Optional category filter
    inrepo: Optional repository presence filter
    notinrepo: Optional repository absence filter

Returns:
    JSON formatted list of matching projects with their packages

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYes
limitNo
maintainerNo
categoryNo
inrepoNo
notinrepoNo

Implementation Reference

  • The primary MCP tool handler for 'search_projects'. Decorated with @mcp.tool(), it processes inputs, enforces the limit, calls RepologyClient.search_projects, applies optional repo filtering, formats results as JSON, and handles errors.
    @mcp.tool()
    async def search_projects(
        query: str,
        limit: int = 10,
        maintainer: Optional[str] = None,
        category: Optional[str] = None,
        inrepo: Optional[str] = None,
        notinrepo: Optional[str] = None,
        ctx: Context[ServerSession, AppContext] = None,
    ) -> str:
        """Search for projects by name substring.
    
        Args:
            query: Search term to match against project names
            limit: Maximum number of results (default: 10, max: 100)
            maintainer: Optional maintainer email filter
            category: Optional category filter
            inrepo: Optional repository presence filter
            notinrepo: Optional repository absence filter
    
        Returns:
            JSON formatted list of matching projects with their packages
        """
        if limit > 100:
            limit = 100
    
        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
    
            project_packages = await client.search_projects(
                query=query, 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": f"No projects found matching '{query}'"})
    
            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 searching projects: {e}")
            return json.dumps({"error": f"Unexpected error: {e}"})
  • RepologyClient helper method implementing the search logic by adding 'search' filter to list_projects call. Directly invoked by the MCP handler.
    async def search_projects(
        self, query: str, limit: int = 10, **filters: Any
    ) -> ProjectPackages:
        """Search for projects by name.
    
        Args:
            query: Search term to match against project names
            limit: Maximum number of results
            **filters: Additional filters
    
        Returns:
            Dictionary mapping project names to package lists
        """
        # Use the search filter in list_projects
        filters["search"] = query
        return await self.list_projects(limit=min(limit, 200), **filters)
  • Server-side helper function to serialize project packages dictionary into pretty-printed JSON, used by the handler for response formatting.
    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 additional client-side filtering of results by repository when 'inrepo' parameter is provided in the tool 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 BaseModel for Package data structure used for input validation and output serialization in search_projects results (ProjectPackages = Dict[str, List[Package]]).
    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