Skip to main content
Glama
hex_search.ex4.78 kB
defmodule HexdocsMcp.CLI.HexSearch do @moduledoc """ CLI interface for searching packages on Hex.pm. """ alias HexdocsMcp.CLI.Utils alias HexdocsMcp.Config @usage """ Usage: [SYSTEM_COMMAND] hex_search [PACKAGE] [VERSION] [options] Searches for packages on Hex.pm by name or description. Arguments: PACKAGE - Optional package name to search within its versions VERSION - Optional version (only with PACKAGE) Options: --query QUERY - Search query (required) --sort SORT - Sort results by: downloads (default), recent, or name --limit LIMIT - Maximum number of results (default: 10) --help, -h - Show this help Examples: [SYSTEM_COMMAND] hex_search --query "authentication" [SYSTEM_COMMAND] hex_search --query "phoenix" --sort recent [SYSTEM_COMMAND] hex_search phoenix --query "1.7" [SYSTEM_COMMAND] hex_search phoenix 1.7.0 --query "info" """ defmodule Context do @moduledoc false @enforce_keys [:query] defstruct query: nil, package: nil, version: nil, sort: "downloads", limit: 10, help?: false end def main(args) do case parse(args) do {:ok, %Context{help?: true}} -> Utils.output_info(usage()) {:ok, context} -> search(context) end end def usage do String.replace(@usage, "[SYSTEM_COMMAND]", Config.system_command()) end defp search(%Context{query: query, package: package, version: version, sort: sort, limit: limit}) do search_desc = cond do package && version -> "package #{package} version #{version}" package -> "package #{package} versions" true -> "packages" end Utils.output_info("Searching Hex.pm #{search_desc} matching \"#{query}\"...") opts = [sort: sort, limit: limit, package: package, version: version] hex_search = Config.hex_search_module() case hex_search.search_packages(query, opts) do {:ok, []} -> Utils.output_info("No results found matching \"#{query}\"") {:ok, results} -> Utils.output_info("#{Utils.check()} Found #{length(results)} results:\n") display_results(results) {:error, reason} -> Utils.output_error("Search failed: #{reason}") end end defp display_results(results) do Enum.each(results, fn result -> # Check if it's a version result or package result if Map.has_key?(result, :version) do display_version_result(result) else display_package_result(result) end end) end defp display_package_result(package) do Utils.output_info("#{IO.ANSI.bright()}#{package.name}#{IO.ANSI.reset()} (v#{package.latest_version})") if package.description != "" do Utils.output_info(" #{package.description}") end Utils.output_info( " Downloads: #{format_number(package.downloads.all)} total, #{format_number(package.downloads.recent)} recent" ) if Map.get(package, :docs_url) do Utils.output_info(" Docs: #{package.docs_url}") end Utils.output_info(" Hex: #{package.html_url}") Utils.output_info("") end defp display_version_result(result) do Utils.output_info("#{IO.ANSI.bright()}#{result.name} v#{result.version}#{IO.ANSI.reset()}") if result.description != "" do Utils.output_info(" #{result.description}") end Utils.output_info(" Has docs: #{result.has_docs}") Utils.output_info(" Released: #{result.inserted_at}") if Map.get(result, :docs_url) do Utils.output_info(" Docs: #{result.docs_url}") end Utils.output_info(" API: #{result.url}") Utils.output_info("") end defp format_number(n) when n >= 1_000_000_000 do "#{Float.round(n / 1_000_000_000, 1)}B" end defp format_number(n) when n >= 1_000_000 do "#{Float.round(n / 1_000_000, 1)}M" end defp format_number(n) when n >= 1_000 do "#{Float.round(n / 1_000, 1)}K" end defp format_number(n), do: "#{n}" defp parse(args) do {opts, remaining_args} = OptionParser.parse!(args, aliases: [ q: :query, s: :sort, l: :limit, h: :help ], strict: [ query: :string, sort: :string, limit: :integer, help: :boolean ] ) {package, version} = Utils.parse_package_args(remaining_args) if opts[:query] do {:ok, %Context{ query: opts[:query], package: package, version: version, sort: opts[:sort] || "downloads", limit: opts[:limit] || 10, help?: opts[:help] || false }} else {:ok, %Context{query: nil, help?: true}} end end end

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/bradleygolden/hexdocs-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server