npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@theplatformlog/catalog-assistant-backend

v0.3.0

Published

Backstage backend plugin that answers natural-language questions about the Software Catalog with grounded LLM answers and entity-ref citations.

Readme

Catalog Assistant Backend

A Backstage backend plugin that answers natural-language questions about the Software Catalog using an LLM, grounded on catalog entities.

Ask things like:

  • "Who owns the payments service?"
  • "What services depend on auth-db?"
  • "Which components are tagged tier-1 and use Postgres?"

The plugin retrieves the top-N relevant catalog entities for a question, builds a grounded prompt, and asks Claude to answer using only those entities as the source of truth. The response includes the entity refs cited as context so the caller can verify or link them.

Status

First slice. Backend HTTP endpoint only, no UI. Retrieval is keyword / substring scoring across entity name, title, description, tags, kind, and spec.type — intentionally simple and deterministic, no embedding store required. Designed to be swapped behind the same CatalogContextRetriever interface for semantic retrieval later.

Installation

yarn --cwd packages/backend add @backstage/plugin-catalog-assistant-backend

Register the plugin:

// packages/backend/src/index.ts
backend.add(import('@theplatformlog/catalog-assistant-backend'));

Configuration

catalogAssistant:
  # LLM provider: anthropic (default) | openai | google | mistral
  provider: anthropic
  # Model id for the chosen provider. Defaults to 'claude-opus-4-8'
  # for anthropic; required for any other provider.
  model: claude-opus-4-8
  # API key for the provider. If omitted, the provider SDK reads its
  # conventional env var (ANTHROPIC_API_KEY, OPENAI_API_KEY, etc.).
  apiKey: ${ANTHROPIC_API_KEY}
  # Defaults to 20
  maxContextEntities: 20
  # Defaults to 1024
  maxOutputTokens: 1024

apiKey is marked secret in the config schema; provide it via env var in production. (anthropicApiKey is still accepted as a deprecated alias.)

Using a non-Anthropic provider

@ai-sdk/anthropic ships with this plugin. To use another provider, install its Vercel AI SDK package in your backend and set provider + model:

# OpenAI
yarn --cwd packages/backend add @ai-sdk/openai
catalogAssistant:
  provider: openai
  model: gpt-5
  apiKey: ${OPENAI_API_KEY}

Supported providers: anthropic, openai, google, mistral. Any model id the chosen provider's SDK accepts works — Claude (claude-opus-4-8, claude-sonnet-4-6, claude-haiku-4-5, …), GPT, Gemini, Mistral, etc.

Free and local models (cost-sensitive)

Two paths to $0 (or near-$0):

Local — Ollama (truly free, runs Gemma 3 / Llama on your hardware). Set the openai provider's baseURL to Ollama's OpenAI-compatible endpoint:

ollama pull gemma3
yarn --cwd packages/backend add @ai-sdk/openai
catalogAssistant:
  provider: openai
  model: gemma3            # or gemma3:27b, llama3.1, qwen2.5, …
  baseURL: http://localhost:11434/v1
  apiKey: ollama           # any non-empty value; Ollama ignores it

Hosted free tiers. Google AI Studio (gemini-2.5-flash) has a generous free tier; Groq and OpenRouter serve Gemma 3 free/cheap and are OpenAI-compatible:

# Google free tier
catalogAssistant: { provider: google, model: gemini-2.5-flash, apiKey: ${GOOGLE_GENERATIVE_AI_API_KEY} }

# Groq (free, fast) — serves Gemma
catalogAssistant: { provider: openai, model: gemma2-9b-it, baseURL: https://api.groq.com/openai/v1, apiKey: ${GROQ_API_KEY} }

# OpenRouter — free Gemma 3 variant
catalogAssistant: { provider: openai, model: "google/gemma-3-27b-it:free", baseURL: https://openrouter.ai/api/v1, apiKey: ${OPENROUTER_API_KEY} }

Because retrieval is deterministic and the prompt is grounded, a small free model handles most catalog Q&A well — reserve a frontier model for hard cases.

API

POST /api/catalog-assistant/v1/query

Request:

{ "question": "who owns the payments service?" }

Response:

{
  "answer": "The payments service is owned by group:default/platform.",
  "citations": ["component:default/payments-api", "api:default/payments"]
}

Authentication uses the standard Backstage httpAuth service and accepts either a user or service credential.

Architecture

┌──────────────────┐    ┌──────────────────────────┐    ┌────────────────┐
│ HTTP /v1/query   │ ─▶ │ CatalogContextRetriever  │ ─▶ │ Catalog API    │
└──────────────────┘    │  (keyword + scoring)     │    └────────────────┘
        │               └──────────────────────────┘
        ▼
┌──────────────────┐    ┌──────────────────────────┐
│ QueryService     │ ─▶ │ Vercel AI SDK            │
│  (build prompt)  │    │  generateText({ system,  │
└──────────────────┘    │    prompt, model: ... }) │
                        └──────────────────────────┘

The LLM call uses @ai-sdk/anthropic + ai's generateText, deliberately matching the surface area proposed in BEP-0015: AI Model Provider Service. When the AI Provider Service lands as a core extension point, replacing generateText with provider.getLanguageModelFactory()(modelId) is a small, contained refactor.

Why this is the inverse of mcp-actions-backend

  • mcp-actions-backend exposes Backstage's actions as MCP tools so external AI agents can act on the catalog.
  • catalog-assistant-backend consumes the catalog from within Backstage via an LLM call, so a human (or another Backstage plugin) can query it.

Both feed off the same underlying catalog; the audiences are opposite.

Limitations

  • No conversation memory. Each request is one-shot.
  • Keyword retrieval only. Compound questions ("services tagged X that depend on Y") are answered as well as the LLM can reason over the retrieved page; there is no graph traversal at retrieval time.
  • No tool use. The LLM cannot fetch additional entities mid-answer. Once tool-use ships via BEP-0015, the assistant will be able to follow relations on demand.
  • No streaming. v1 returns the full response in one body. SSE / streaming will land alongside the chat UI plugin.