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

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

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")
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden. It discloses the return format (JSON list with packages) and default/max values for 'limit', which is useful behavioral context. However, it doesn't mention pagination behavior, rate limits, authentication requirements, or error conditions that would be important for a search operation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured with a clear purpose statement followed by organized parameter documentation and return format. Every sentence earns its place, with no redundant information. The description is appropriately sized for a 6-parameter search tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool has an output schema (true), the description doesn't need to explain return values in detail. It provides adequate parameter documentation and behavioral context for a search operation. The main gap is lack of guidance on when to use versus sibling tools, but otherwise fairly complete for its complexity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It provides clear explanations for all 6 parameters, including the required 'query' parameter, default values, optional filters, and clarifies what 'inrepo' and 'notinrepo' mean. This adds significant meaning beyond the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool searches for projects by name substring, providing a specific verb (search) and resource (projects). It distinguishes from siblings like 'list_projects' by specifying substring matching, but doesn't explicitly contrast with other search-related siblings like 'get_project'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to use this tool versus alternatives like 'list_projects' or 'get_project' is provided. The description only states what the tool does, not when it's appropriate versus other available tools on the server.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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