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

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

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

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It clarifies this is a listing operation with filtering, and specifies return format ('JSON formatted dictionary of projects and their packages'), which is helpful. However, it lacks details on pagination behavior (beyond the 'start_from' parameter), rate limits, authentication requirements, or whether this is a read-only operation—critical context for a tool with 11 parameters.

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

Conciseness4/5

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

The description is well-structured with a clear purpose statement followed by categorized parameter explanations and return format. While comprehensive for 11 parameters, it remains efficient—each sentence serves a purpose. Minor improvement could be front-loading more critical behavioral context before parameter details.

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 complexity (11 parameters, 0% schema coverage, no annotations) and presence of an output schema (implied by 'Returns' section), the description does well. It explains all parameters semantically and specifies the return format. However, it lacks context on operational constraints (e.g., rate limits, side effects) which would be valuable for a filtering-heavy tool.

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

Parameters5/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 fully compensate. It provides clear semantic explanations for all 11 parameters, including examples for complex ones like 'repos' (e.g., '1', '5-', '-5', '2-7') and clarifying boolean filters like 'newest,' 'outdated,' and 'problematic.' This adds significant value beyond the bare schema, making parameter purposes understandable.

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's purpose: 'List projects with optional filtering.' This specifies the verb ('List') and resource ('projects') with mention of filtering capability. However, it doesn't distinguish this tool from sibling tools like 'search_projects' or 'get_project,' which likely have different scopes or approaches to project retrieval.

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?

The description provides no guidance on when to use this tool versus alternatives like 'search_projects' or 'get_project.' It mentions filtering capabilities but doesn't specify scenarios where this listing approach is preferred over searching or direct retrieval, leaving the agent to guess based on tool names alone.

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