package_info
Look up package information from npm, PyPI, crates.io, or Go modules to evaluate libraries before adding them to your project. Returns version, downloads, license, dependencies, security status, and repository links.
Instructions
Look up package information from npm, PyPI, crates.io, or Go modules.
Returns version, downloads, license, dependencies, security status, and repository links.
Use this to quickly evaluate libraries before adding them to your project.
Examples:
- package_info("express", reasoning="Need web framework", registry="npm")
- package_info("requests", reasoning="HTTP client for API", registry="pypi")
- package_info("serde", reasoning="JSON serialization", registry="crates")
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | ||
| reasoning | Yes | ||
| registry | No | npm |
Input Schema (JSON Schema)
{
"properties": {
"name": {
"title": "Name",
"type": "string"
},
"reasoning": {
"title": "Reasoning",
"type": "string"
},
"registry": {
"default": "npm",
"enum": [
"npm",
"pypi",
"crates",
"go"
],
"title": "Registry",
"type": "string"
}
},
"required": [
"name",
"reasoning"
],
"type": "object"
}
Implementation Reference
- src/searxng_mcp/server.py:177-236 (handler)Main handler function for the 'package_info' MCP tool. Dispatches to registry_client based on registry parameter, formats the PackageInfo object, handles errors, and tracks usage.@mcp.tool() async def package_info( name: Annotated[str, "Package or module name to look up"], reasoning: Annotated[str, "Why you're looking up this package (required for analytics)"], registry: Annotated[ Literal["npm", "pypi", "crates", "go"], "Package registry to search (npm, pypi, crates, go)", ] = "npm", ) -> str: """ Look up package information from npm, PyPI, crates.io, or Go modules. Returns version, downloads, license, dependencies, security status, and repository links. Use this to quickly evaluate libraries before adding them to your project. Examples: - package_info("express", reasoning="Need web framework", registry="npm") - package_info("requests", reasoning="HTTP client for API", registry="pypi") - package_info("serde", reasoning="JSON serialization", registry="crates") """ start_time = time.time() success = False error_msg = None result = "" try: if registry == "npm": info = await registry_client.search_npm(name) elif registry == "pypi": info = await registry_client.search_pypi(name) elif registry == "crates": info = await registry_client.search_crates(name) else: # go info = await registry_client.search_go(name) result = clamp_text(_format_package_info(info), MAX_RESPONSE_CHARS) success = True except httpx.HTTPStatusError as exc: error_msg = f"HTTP {exc.response.status_code}" if exc.response.status_code == 404: result = f"Package '{name}' not found on {registry}.\n\nDouble-check the package name and try again." else: result = f"Failed to fetch {registry} package '{name}': HTTP {exc.response.status_code}" except Exception as exc: # noqa: BLE001 error_msg = str(exc) result = f"Failed to fetch {registry} package '{name}': {exc}" finally: # Track usage response_time = (time.time() - start_time) * 1000 tracker.track_usage( tool_name="package_info", reasoning=reasoning, parameters={"name": name, "registry": registry}, response_time_ms=response_time, success=success, error_message=error_msg, response_size=len(result.encode("utf-8")), ) return result
- src/searxng_mcp/registry.py:12-27 (schema)Dataclass defining the structured PackageInfo output model used by the package_info tool and related helpers.@dataclass(slots=True) class PackageInfo: """Structured package metadata from various registries.""" name: str registry: str version: str description: str license: str | None downloads: str | None last_updated: str repository: str | None homepage: str | None dependencies_count: int | None security_issues: int
- src/searxng_mcp/server.py:138-174 (helper)Helper function to format PackageInfo object into a human-readable string response for the package_info tool.def _format_package_info(info: PackageInfo) -> str: """Format PackageInfo into readable text response.""" lines = [ f"Package: {info.name} ({info.registry})", "─" * 50, f"Version: {info.version}", ] if info.license: lines.append(f"License: {info.license}") if info.downloads: lines.append(f"Downloads: {info.downloads}") lines.append(f"Last Updated: {info.last_updated}") if info.dependencies_count is not None: lines.append(f"Dependencies: {info.dependencies_count}") if info.security_issues > 0: lines.append(f"⚠️ Security Issues: {info.security_issues}") else: lines.append("Security: ✅ No known vulnerabilities") lines.append("") # blank line if info.repository: lines.append(f"Repository: {info.repository}") if info.homepage and info.homepage != info.repository: lines.append(f"Homepage: {info.homepage}") if info.description: lines.append(f"\nDescription: {info.description}") return "\n".join(lines)
- src/searxng_mcp/registry.py:210-255 (helper)Core helper method in PackageRegistryClient for fetching detailed npm package information, called by package_info handler.async def search_npm(self, name: str) -> PackageInfo: """Fetch package information from npm registry.""" url = f"https://registry.npmjs.org/{name}" async with httpx.AsyncClient(timeout=self.timeout, headers=self._headers) as client: response = await client.get(url) response.raise_for_status() data = response.json() latest_version = data.get("dist-tags", {}).get("latest", "unknown") version_data = data.get("versions", {}).get(latest_version, {}) # Get weekly downloads from npm API downloads = await self._get_npm_downloads(name) # Parse last update time time_data = data.get("time", {}) last_updated = time_data.get(latest_version) or time_data.get("modified", "unknown") if last_updated != "unknown": last_updated = self._format_time_ago(last_updated) repository = ( version_data.get("repository", {}).get("url") if isinstance(version_data.get("repository"), dict) else version_data.get("repository") ) if repository and repository.startswith("git+"): repository = repository[4:] dependencies = version_data.get("dependencies", {}) return PackageInfo( name=name, registry="npm", version=latest_version, description=version_data.get("description", "No description available"), license=version_data.get("license", "Unknown"), downloads=downloads, last_updated=last_updated, repository=repository, homepage=version_data.get("homepage"), dependencies_count=len(dependencies) if dependencies else 0, security_issues=0, # Would need separate npm audit API call )
- src/searxng_mcp/registry.py:271-325 (helper)Core helper method for fetching PyPI package information, one of the dispatch targets from package_info handler.async def search_pypi(self, name: str) -> PackageInfo: """Fetch package information from PyPI.""" url = f"https://pypi.org/pypi/{name}/json" async with httpx.AsyncClient(timeout=self.timeout, headers=self._headers) as client: response = await client.get(url) response.raise_for_status() data = response.json() info = data.get("info", {}) latest_version = info.get("version", "unknown") # PyPI doesn't provide download stats in API anymore, show placeholder downloads = None # Get last release date releases = data.get("releases") or {} last_updated = "unknown" if ( latest_version in releases and releases[latest_version] and len(releases[latest_version]) > 0 ): first_release = releases[latest_version][0] if isinstance(first_release, dict): upload_time = first_release.get("upload_time", "") if upload_time: last_updated = self._format_time_ago(upload_time) # Count dependencies from requires_dist requires_dist = info.get("requires_dist") or [] dependencies_count = len([r for r in requires_dist if "extra ==" not in r]) # Safely handle project_urls - it can be None project_urls = info.get("project_urls") or {} # Truncate long licenses for readability license_text = info.get("license", "Unknown") if isinstance(license_text, str) and len(license_text) > 100: license_text = license_text[:97] + "..." return PackageInfo( name=name, registry="PyPI", version=latest_version, description=info.get("summary", "No description available"), license=license_text, downloads=downloads, last_updated=last_updated, repository=project_urls.get("Source") or project_urls.get("Repository"), homepage=info.get("home_page") or project_urls.get("Homepage"), dependencies_count=dependencies_count, security_issues=0, )