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
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | ||
| limit | No | ||
| maintainer | No | ||
| category | No | ||
| inrepo | No | ||
| notinrepo | No |
Implementation Reference
- src/repology_mcp/server.py:77-138 (handler)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}"})
- src/repology_mcp/client.py:268-284 (helper)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)
- src/repology_mcp/server.py:69-75 (helper)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)
- src/repology_mcp/server.py:55-67 (helper)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
- src/repology_mcp/models.py:7-34 (schema)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")