search_projects
Find software projects by name substring with optional 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 |
|---|---|---|---|
| category | No | ||
| inrepo | No | ||
| limit | No | ||
| maintainer | No | ||
| notinrepo | No | ||
| query | Yes |
Implementation Reference
- src/repology_mcp/server.py:77-137 (handler)MCP tool handler for 'search_projects': decorated with @mcp.tool(), processes input parameters, calls RepologyClient.search_projects, applies filters, formats output as JSON.@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/server.py:69-74 (helper)Helper function to convert the project packages dictionary to formatted JSON string, used in search_projects handler.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 to filter project packages by repository, used conditionally in search_projects handler.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/client.py:268-283 (helper)RepologyClient helper method implementing the actual API search by forwarding to list_projects with search filter.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/models.py:7-34 (schema)Pydantic schema/model for Package objects used to validate and serialize the data in tool responses.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")