facto-core 0.1.0-alpha.1

Core models, registry/forge/runtime clients for facto: real-time package ecosystem data for AI agents
Documentation

facto

Stale models, fresh data.

CI crates.io docs.rs MSRV License: MIT


facto is a local MCP server that gives AI coding agents live lookups across package registries (9), code forges (3), runtime catalogs (14+), and lockfiles, so Claude Code, Cursor, Copilot, or any MCP-capable agent stops recommending outdated packages, deprecated runtimes, or metadata past its training cutoff.

No proxy, no telemetry. Your tokens, your machine, your data.

Why facto?

  • Training data goes stale within weeks.
    Agents confidently recommend yanked crates, outdated npm packages, and deprecated runtimes.
  • Most "latest version" answers are one HTTPS call away.
    facto just makes the call for the agent, locally, with your tokens.
  • Discovery belongs in the agent loop.
    Search packages by popularity or find projects on forges by stars: fresh, ranked data rather than a guess from training.
  • Token-efficient by design.
    Structured JSON responses, no markdown fluff. Lockfiles are read from an absolute path rather than shuttled through the LLM context (often 100x fewer tokens than inlining a large pnpm-lock.yaml).

Quickstart

Wire facto mcp into Claude Code using the ephemeral uvx invocation: no install, auto-cached, always fetches the published version:

claude mcp add facto -- uvx facto.run mcp

That's it. The agent can now call get_latest_version, search_repositories, pin_action, and a dozen more tools whenever it needs current package data.

Example

> User: What's the latest stable fastapi on PyPI?
> Agent (calls get_latest_version): 0.120.3, released 2026-03-28.
>
> User: Pin actions/checkout@v4 for me.
> Agent (calls pin_action): actions/checkout@34e1148... (v4)
>
> User: Find popular Rust HTTP client libraries on GitHub.
> Agent (calls search_repositories): hyper (14.2k stars), reqwest (10.4k stars), ...

Without facto, those answers come from the model's training cutoff, possibly months behind, sometimes just wrong.

Installation

Via uvx (recommended for MCP wiring, zero-install)

Requires uv. Runs facto on the fly, auto-caches by version:

# Ephemeral: what Claude Code / Cursor / Copilot wiring typically uses
uvx facto.run mcp

For a persistent install that puts facto on your PATH:

uv tool install facto.run
facto mcp

Pin a specific version (either form):

uvx 'facto.run==0.1.0' mcp
uv tool install 'facto.run==0.1.0'

Via cargo

Requires a Rust toolchain:

cargo install facto-run

Pre-built binary

Download the archive for your platform from the latest release, extract, and move facto into a directory on your PATH:

# Linux x86_64 (adjust URL for your platform)
curl -L https://github.com/ggueret/facto/releases/latest/download/facto-x86_64-unknown-linux-gnu.tar.gz | tar xz
sudo mv facto /usr/local/bin/

From source

git clone https://github.com/ggueret/facto.git
cd facto
cargo install --path facto-run

How it works

facto runs as a local subprocess launched by your agent (Claude Code, Cursor, Copilot, ...). They talk over MCP stdio JSON-RPC. facto makes outbound HTTPS calls to public registries, forges, and runtime catalogs using the tokens you provide via environment variables.

  • Transport: stdio JSON-RPC (MCP) between agent and facto mcp
  • Upstreams: 9 package registries, 3 code forges, 14 runtime catalogs
  • Credentials: FACTO_GITHUB_TOKEN / FACTO_GITLAB_TOKEN from env only, never logged
  • Safety nets: 5 s connect / 10 s total timeouts, 50 MB response cap per upstream

The binary never stores state and never calls home. The only files it reads are (1) lockfile paths the agent explicitly passes to parse_lockfile / check_lockfile, and (2) an optional facto.toml auto-loaded from the working directory at launch (or from $FACTO_CONFIG if set). Both reads are documented in SECURITY.md.

Privacy

facto runs locally and reads only what you explicitly pass it.

Lockfile contents stay on your machine. When you ask facto to audit a lockfile, the agent passes only the absolute path; facto reads the file itself and returns structured data. The lockfile body never transits through the LLM context:

Inline approach (content transits LLM):
  lockfile body (~500k tokens) -> LLM context -> MCP -> registry

facto approach (path transits, content stays local):
  path (~30 tokens) -> LLM context -> MCP reads locally -> registry

For private monorepos with internal package names, private-registry URLs, or sensitive dependency hashes, this matters. Your agent still benefits from the audit without leaking the lockfile through its provider's infrastructure.

Token efficiency from the same mechanism. Keeping lockfiles out of the LLM context protects privacy and slashes token usage. A 2 MB pnpm-lock.yaml weighs about 500 000 tokens, more than the full context of most models. Via facto, the agent spends ~30 tokens on the path and receives ~2 000 tokens of structured output. Across tool calls this compounds: every response is compact JSON, no markdown prose to reparse.

Your credentials stay on your machine. FACTO_GITHUB_TOKEN and other credentials are read once from the environment and used only to authenticate outbound calls to the relevant registry. They are never logged, written to disk, or transmitted to any other host.

No telemetry. facto makes no calls home. The only outbound traffic is to the public registries, forges, and runtime catalogs you are actively querying. See SECURITY.md for the full threat model.

Supply-chain hygiene

facto ships two primitives that help keep CI safe:

  • pin_action: resolves a GitHub Action tag (e.g. v4) to a full commit SHA. Pinning prevents a compromised or remapped tag from silently flowing into your workflow.
  • check_lockfile: concurrently audits every resolved dependency in a lockfile against the live registry, flagging outdated versions.

facto itself pins every GitHub Action in CI to a full commit SHA; see .github/workflows/. We plan OSV.dev integration in v0.3 for explicit CVE correlation (see ROADMAP.md).

Tools

A high-level overview. See docs/tools.md for the full reference with parameters, return shapes, and examples.

Package facts

Tool Description
get_latest_version Latest stable version of any package
get_package Full metadata: description, license, authors, repository URL
list_versions All published versions with release dates

Search & discovery

Tool Description
search_packages Search packages by keyword; sort by relevance or popularity
search_repositories Search forge projects by keyword; sort by stars

Lockfiles

Tool Description
parse_lockfile Read a lockfile at an absolute path and return resolved dependencies
check_lockfile Same, plus concurrent registry lookups to flag outdated deps
discover_lockfiles Filter a list of paths down to recognised lockfiles (no I/O)

Runtime

Tool Description
get_runtime_info Runtime version lifecycle, EOL dates, LTS status

Forge

Tool Description
pin_action Resolve GitHub Action tag to commit SHA
list_forge_releases List releases of a repo on a forge

Discovery

Tool Description
list_registries List supported package registries
list_forges List supported code forges
list_runtimes List supported runtimes

Full reference ->

Supported upstreams

  • Registries: PyPI, npm, crates.io, Go, RubyGems, Maven, NuGet, Packagist, Docker Hub
  • Forges: GitHub, GitLab, Codeberg
  • Runtimes: Python, Go, Rust, Node.js, Ruby, PHP, Java, .NET, Deno, Bun, Elixir, Kotlin, Perl, Scala
  • Lockfiles: uv.lock, poetry.lock, pdm.lock, Pipfile.lock, package-lock.json, pnpm-lock.yaml, Cargo.lock

Limitations

  • Search is not available on every registry. search_packages works on npm, crates.io, RubyGems, Maven, NuGet, Packagist, and Docker Hub. PyPI and Go expose no public search API, so search_packages returns not_supported there. A dedicated PyPI search index is planned.

  • get_latest_version is not meaningful on Docker Hub. Docker tags are free-form (not semver) and ordered by push date, so there is no reliable "latest"; the tool returns not_supported. Use list_versions to inspect the available tags. get_package, search_packages, and list_versions all work on Docker Hub.

Works great with Context7

facto and Context7 are complementary pieces of the AI coding agent ground-truth stack:

facto Context7
Question answered What's the current state of X? How do I use X?
Output Version numbers, EOL dates, search results, metadata Code examples, version-specific API docs
Data source Live calls to registries, forges, runtime catalogs Crawled and indexed library documentation

Typical pipeline:

Agent: "Add Supabase auth to my Next.js app."
  |- facto.get_latest_version("supabase", "npm")      -> 2.48.1
  |- facto.get_runtime_info("nodejs", "22")           -> LTS, EOL 2027-04
  +- Context7.query-docs("/supabase/supabase", "auth") -> current API

Install both for full coverage:

claude mcp add facto -- uvx facto.run mcp
npx ctx7 setup

Comparison

Tools in the same category (package / registry MCP)

facto is the only one covering package registries, code forges, runtime lifecycle, and lockfile parsing in a single focused Rust binary with zero-install via uvx.

Tools we complement (not compete)

  • Context7: version-specific library docs
  • endoflife.date: upstream runtime EOL data (facto consumes it)
  • OSV.dev: vulnerability database (v0.3 integration planned)

Configuration

For basic usage, no configuration is required. Unauthenticated calls work everywhere with lower rate limits.

Environment variables

export FACTO_GITHUB_TOKEN="ghp_..."    # GitHub API (higher rate limits, private repos)
export FACTO_GITLAB_TOKEN="glpat-..."  # GitLab API

Optional facto.toml

Drop a facto.toml next to wherever you launch facto mcp (or point FACTO_CONFIG at it) to tune defaults:

[timeouts]
per_registry_secs = 5
global_secs = 15

[registries]
enabled = ["pypi", "npm", "crates", "go"]  # opt-in subset

[forges]
enabled = ["github"]
gitlab_base_url = "https://gitlab.example.com"  # self-hosted GitLab

Tokens given via env variables always override values in the file.

Upstream sources

facto stands on the shoulders of:

  • Package registries: PyPI, npm, crates.io, Go Proxy, RubyGems, Maven Central, NuGet, Packagist, Docker Hub
  • Code forges: GitHub, GitLab, Codeberg
  • Runtime lifecycle data: endoflife.date, community-maintained, indispensable, consider supporting them
  • Protocol: Model Context Protocol by Anthropic

Crates

Crate Role
facto-core Shared library: models, registry / forge / runtime / lockfile clients
facto-mcp MCP server library: FactoMcp struct, rmcp tool router, serve_stdio()
facto-run Thin CLI binary that produces the facto executable

Development

git clone https://github.com/ggueret/facto.git
cd facto
make help               # list all available targets
make check              # fmt + clippy (fast)
make test               # unit tests (no network)
make test-integration   # full integration (calls real APIs, needs network)

See CONTRIBUTING.md for details.