Public API for querying hex.pm and hexdocs.pm.
All functions return {:ok, result} or {:error, reason} and are designed
to be used from iex, other Elixir code, or as the backend for MCP tools.
Quick Start
iex> HexpmMcp.search("json")
{:ok, [%{name: "jason", version: "1.4.4", downloads_all: 197_000_000, ...}, ...]}
iex> HexpmMcp.get_info("plug")
{:ok, %{name: "plug", description: "Composable modules for web applications",
downloads: %{all: 156_000_000, recent: 3_100_000, week: 250_000, day: 35_000},
licenses: ["Apache-2.0"], latest_stable_version: "1.19.1", ...}}
iex> HexpmMcp.health_check("req")
{:ok, %{name: "req",
maintenance: %{status: "Active", age: "4 years ago", days_since_release: 57, total_versions: 52},
popularity: %{all: 11_500_000, recent: 1_600_000, week: 141_000},
quality: %{has_docs: true, licenses: ["Apache-2.0"], required_deps: 3, optional_deps: 4},
risk: %{owner_count: 1, retired_count: 0},
links: %{...}}}Function Groups
Simple lookups
search/2, get_info/1, get_downloads/1, get_owners/1, get_versions/1
Version-resolving lookups
get_release/2, get_dependencies/2, get_features/2 -- pass nil for version
to automatically resolve to the latest stable version.
Composite analysis
compare_packages/1, health_check/1, audit_dependencies/2,
find_alternatives/1, dependency_tree/3 -- these make multiple API calls
in parallel and return aggregated results.
HexDocs browsing
get_readme/2, get_docs/2, get_doc_item/3, search_docs/3
Error Values
All functions return {:error, reason} on failure. Common reasons:
:not_found-- package or version does not exist on hex.pm:rate_limited-- hex.pm API rate limit exceeded:too_few_packages/:too_many_packages-- invalid input tocompare_packages/1{:api_error, status, body}-- unexpected HTTP status from hex.pm{:request_failed, reason}-- network error
Summary
Functions
Audit a package's dependencies for risks.
Audit a list of mix.exs dependencies for risks.
Compare 2-5 hex.pm packages side by side.
Get the full transitive dependency tree for a package (BFS, max depth 5).
Find and compare alternative packages for a given hex.pm package.
Get the dependencies of a package version.
Get full documentation for a specific module or function.
Get the module listing for a package's documentation.
Get download statistics for a hex.pm package.
Get optional features/extras for a package release.
Get detailed information about a hex.pm package.
Get the owners/maintainers of a hex.pm package.
Get the README content for a package as markdown.
Get detailed information about a specific release.
List all versions of a hex.pm package with retirement status.
Comprehensive health check for a hex.pm package.
Search for packages on hex.pm by name/keywords.
Search within a package's documentation by name.
List curated projects in an Elixir Toolbox category.
Get a single Elixir Toolbox group by slug.
List all Elixir Toolbox groups with their categories.
Search packages via Elixir Toolbox.
List trending Elixir Toolbox projects.
Check which mix.exs dependencies have newer versions available.
Types
Functions
Audit a package's dependencies for risks.
Checks each dependency in parallel for:
- Retired versions
- Stale packages (no release in 2+ years)
- Single-maintainer packages (bus factor risk)
- Known vulnerabilities via OSV.dev
Examples
iex> HexpmMcp.audit_dependencies("phoenix")
{:ok, %{
name: "phoenix", version: "1.8.5",
total_checked: 10, total_warnings: 8, deps_with_warnings: 7,
results: [
%{name: "jason", issues: ["single maintainer"]},
%{name: "plug", issues: ["2 retired version(s)", "3 known vulnerability(ies)"]},
%{name: "telemetry", issues: []},
...
]
}}
iex> HexpmMcp.audit_dependencies("phoenix", "1.7.0")
{:ok, %{name: "phoenix", version: "1.7.0", ...}}
Audit a list of mix.exs dependencies for risks.
Accepts a deps string (as it appears in mix.exs) and runs a comprehensive audit on each package: staleness, retirement, bus factor, and CVEs via OSV.dev.
Examples
iex> deps = """
...> {:phoenix, "~> 1.7"},
...> {:ecto, "~> 3.10"},
...> {:jason, "~> 1.0"}
...> """
iex> HexpmMcp.audit_mix_deps(deps)
{:ok, %{
total_checked: 3,
total_warnings: 2,
deps_with_warnings: 2,
results: [
%{name: "ecto", pinned_version: "~> 3.10", issues: []},
%{name: "jason", pinned_version: "~> 1.0", issues: ["single maintainer"]},
%{name: "phoenix", pinned_version: "~> 1.7", issues: ["3 retired version(s)"]},
...
]
}}
@spec compare_packages([String.t()]) :: {:ok, [map()]} | {:error, :too_few_packages | :too_many_packages}
Compare 2-5 hex.pm packages side by side.
Fetches package info and dependency counts in parallel for each package.
Examples
iex> HexpmMcp.compare_packages(["plug", "bandit", "cowboy"])
{:ok, [
%{name: "plug", downloads_all: 156_000_000, downloads_recent: 3_100_000,
latest_version: "1.19.1", updated_at: "2025-12-09T...",
licenses: "Apache-2.0", dep_count: 3},
%{name: "bandit", downloads_all: 9_900_000, ...},
%{name: "cowboy", downloads_all: 78_000_000, ...}
]}
iex> HexpmMcp.compare_packages(["only_one"])
{:error, :too_few_packages}
Get the full transitive dependency tree for a package (BFS, max depth 5).
Traverses dependencies breadth-first, resolving each to its latest version. Deduplicates by package name (each package appears once in the tree).
Options
:max_depth-- maximum depth to traverse (default 5, capped at 5)
Examples
iex> HexpmMcp.dependency_tree("req", nil, max_depth: 2)
{:ok, %{
name: "req", version: "0.5.17", total_unique_deps: 8,
tree: [
%{name: "req", version: "0.5.17", depth: 0, deps: [
%{name: "finch", requirement: "~> 0.17", optional: false, depth: 1},
%{name: "mime", requirement: "~> 2.0 or ~> 1.0", optional: false, depth: 1},
...
]},
%{name: "finch", version: "0.21.0", depth: 1, deps: [...]},
...
]
}}
Find and compare alternative packages for a given hex.pm package.
Extracts keywords from the package description, searches for similar packages, deduplicates, and returns the top 10 sorted by recent downloads.
Examples
iex> HexpmMcp.find_alternatives("httpoison")
{:ok, %{
package: %{name: "httpoison", description: "HTTP client for Elixir",
downloads_all: 42_000_000, downloads_recent: 500_000},
alternatives: [
%{name: "req", version: "0.5.17", downloads_all: 11_500_000,
downloads_recent: 1_600_000, updated_at: "2026-02-07T...",
status: "Active", description: "Req is a batteries-included HTTP client...",
licenses: "Apache-2.0"},
%{name: "finch", ...},
...
]
}}
Get the dependencies of a package version.
If version is nil, resolves to the latest stable version.
Examples
iex> HexpmMcp.get_dependencies("req")
{:ok, %{name: "req", version: "0.5.17", dependencies: [
%{name: "finch", requirement: "~> 0.17", optional: false},
%{name: "jason", requirement: "~> 1.0", optional: true},
%{name: "mime", requirement: "~> 2.0 or ~> 1.0", optional: false},
...
]}}
iex> HexpmMcp.get_dependencies("plug", "1.19.1")
{:ok, %{name: "plug", version: "1.19.1", dependencies: [...]}}
@spec get_doc_item(String.t(), String.t(), String.t() | nil) :: {:ok, String.t()} | {:error, error()}
Get full documentation for a specific module or function.
Fetches the module's hexdocs.pm page and converts it to markdown.
Examples
iex> HexpmMcp.get_doc_item("plug", "Plug.Conn")
{:ok, "# Plug.Conn\n\nThe Plug connection.\n\n..."}
iex> HexpmMcp.get_doc_item("plug", "Plug.Conn", "1.19.1")
{:ok, "# Plug.Conn\n\n..."}
Get the module listing for a package's documentation.
Parses the hexdocs.pm sidebar data to extract all modules, behaviours, and protocols with their function/type counts.
Examples
iex> HexpmMcp.get_docs("plug")
{:ok, [
%{name: "Plug", type: "module", doc: "Types: 1, Callbacks: 2, Functions: 2"},
%{name: "Plug.Conn", type: "module", doc: "Types: 22, Functions: 52"},
%{name: "Plug.Router", type: "module", doc: "Functions: 12"},
...
]}
Get download statistics for a hex.pm package.
Examples
iex> HexpmMcp.get_downloads("phoenix")
{:ok, %{name: "phoenix", all: 148_000_000, recent: 2_600_000, week: 223_000, day: 13_000}}
Get optional features/extras for a package release.
If version is nil, resolves to the latest stable version.
Examples
iex> HexpmMcp.get_features("req")
{:ok, %{
name: "req", version: "0.5.17",
optional_deps: [
%{name: "brotli", requirement: "~> 0.3.1"},
%{name: "jason", requirement: "~> 1.0"},
...
],
extra_metadata: %{}
}}
Get detailed information about a hex.pm package.
Returns metadata, download stats, licenses, links, and version info.
Examples
iex> HexpmMcp.get_info("plug")
{:ok, %{
name: "plug",
description: "Composable modules for web applications",
latest_stable_version: "1.19.1",
latest_version: "1.19.1",
downloads: %{all: 156_000_000, recent: 3_100_000, week: 250_000, day: 35_000},
licenses: ["Apache-2.0"],
build_tools: ["mix"],
elixir_requirement: "~> 1.14",
inserted_at: "2013-12-31T...",
updated_at: "2025-12-09T...",
docs_url: "https://hexdocs.pm/plug/",
hex_url: "https://hex.pm/packages/plug",
links: %{"GitHub" => "https://github.com/elixir-plug/plug"}
}}
iex> HexpmMcp.get_info("nonexistent")
{:error, :not_found}
Get the owners/maintainers of a hex.pm package.
Examples
iex> HexpmMcp.get_owners("phoenix")
{:ok, [%{username: "josevalim", email: "jose@example.com"}, ...]}
Get the README content for a package as markdown.
Fetches the README from hexdocs.pm and converts HTML to markdown.
Examples
iex> HexpmMcp.get_readme("req")
{:ok, "# Req\n\nReq is a batteries-included HTTP client for Elixir.\n\n..."}
iex> HexpmMcp.get_readme("req", "0.5.0")
{:ok, "# Req\n\n..."}
Get detailed information about a specific release.
If version is nil, resolves to the latest stable version.
Examples
iex> HexpmMcp.get_release("plug", "1.19.1")
{:ok, %{
name: "plug", version: "1.19.1", publisher: "josevalim",
inserted_at: "2025-12-09T...", updated_at: "2025-12-09T...",
downloads: 500_000, has_docs: true,
build_tools: ["mix"], elixir_requirement: "~> 1.14",
dependencies: [
%{name: "mime", requirement: "~> 2.0", optional: false},
%{name: "plug_crypto", requirement: "~> 2.1", optional: false}
],
retired: nil
}}
# Resolves to latest stable when version is nil
iex> HexpmMcp.get_release("plug")
{:ok, %{name: "plug", version: "1.19.1", ...}}
List all versions of a hex.pm package with retirement status.
Examples
iex> HexpmMcp.get_versions("plug")
{:ok, %{name: "plug", versions: [
%{version: "1.19.1", inserted_at: "2025-12-09T...", has_docs: true, retired: nil},
%{version: "1.14.0", inserted_at: "2023-03-15T...", has_docs: true,
retired: %{reason: "security", message: "CVE-2024-..."}},
...
]}}
Comprehensive health check for a hex.pm package.
Fetches package info, owners, and latest release in parallel, then computes maintenance status, popularity metrics, quality indicators, and risk factors.
Examples
iex> HexpmMcp.health_check("req")
{:ok, %{
name: "req",
maintenance: %{
age: "4 years ago",
total_versions: 52,
status: "Active", # Active (<90d), Recent (<1y), Aging (<2y), Stale (2y+)
days_since_release: 57
},
popularity: %{all: 11_500_000, recent: 1_600_000, week: 141_000},
quality: %{
has_docs: true,
licenses: ["Apache-2.0"],
build_tools: ["mix"],
elixir_requirement: "~> 1.14",
required_deps: 3,
optional_deps: 4
},
risk: %{
owner_count: 1, # 1 = "single maintainer" warning
retired_count: 0
},
links: %{
hex_url: "https://hex.pm/packages/req",
docs_url: "https://hexdocs.pm/req/",
"GitHub" => "https://github.com/wojtekmach/req"
}
}}
Search for packages on hex.pm by name/keywords.
Options
:sort-- sort order:"name","recent_downloads","total_downloads","inserted_at","updated_at":page-- page number (1-indexed, 100 results per page)
Examples
iex> HexpmMcp.search("json")
{:ok, [%{name: "jason", version: "1.4.4", description: "A blazing fast JSON parser...",
downloads_all: 197_000_000, downloads_recent: 4_000_000,
url: "https://hex.pm/packages/jason"}, ...]}
iex> HexpmMcp.search("http client", sort: "recent_downloads", page: 1)
{:ok, [%{name: "req", ...}, %{name: "finch", ...}, ...]}
Search within a package's documentation by name.
Searches module names and doc snippets in the hexdocs.pm sidebar data. Returns up to 20 matching items.
Examples
iex> HexpmMcp.search_docs("phoenix", "Router")
{:ok, [
%{"title" => "Phoenix.Router", "type" => "module", "doc" => "Reflection: 3, Functions: 21"},
%{"title" => "Phoenix.Router.NoRouteError", "type" => "module", "doc" => ""},
...
]}
List curated projects in an Elixir Toolbox category.
Get a single Elixir Toolbox group by slug.
List all Elixir Toolbox groups with their categories.
Search packages via Elixir Toolbox.
List trending Elixir Toolbox projects.
Check which mix.exs dependencies have newer versions available.
Accepts a deps string and checks each against the latest version on hex.pm. Flags major version bumps as potentially breaking.
Examples
iex> deps = """
...> {:phoenix, "~> 1.7"},
...> {:jason, "~> 1.0"}
...> """
iex> HexpmMcp.upgrade_check(deps)
{:ok, %{
total_checked: 2,
upgrades_available: 1,
results: [
%{name: "jason", pinned_version: "~> 1.0", latest_version: "1.4.4",
status: :up_to_date},
%{name: "phoenix", pinned_version: "~> 1.7", latest_version: "1.8.5",
status: :minor_upgrade, retired: false}
]
}}