Guide

Agents

The Vercel AI SDK tool on the /ai subpath, its five operations, and what an agent should and should not do with the answer.

One tool, five operations

import { generateText } from "ai";
import { packageTool } from "@agntn/registries/ai";

const { text } = await generateText({
  model: yourModel,
  tools: { packageRegistry: packageTool },
  prompt: "What is the latest version of pkg:npm/lodash and who maintains it?",
});

packageTool is a tool() from the AI SDK, ready to hand to a model, with a discriminated input schema. The model picks an operation and the tool routes it to the matching helper:

operationInputReturns
packagepurlPackage
versionspurlVersion[]
dependenciespurl with a versionDependency[]
maintainerspurlMaintainer[]
bulk-packagespurls, 1 to 50, optional concurrencyRecord<string, Package> keyed by PURL, failed ones absent

The AI SDK's abort signal is passed through to every registry call, so cancelling the generation cancels the HTTP request.

Install the peers

pnpm add ai zod

Both are optional peer dependencies. The main entry never imports them; only the /ai subpath does.

The schema, in words

The model sees one description and five input shapes. Each purl field carries an example in its description (pkg:npm/lodash or pkg:cargo/serde; pkg:pypi/flask@3.1.1 for dependencies), which is what keeps a model from sending lodash on its own. purls is capped at fifty and concurrency at fifty.

What the answer is

The tool returns the normalized objects, not prose. A model summarizing them should say what the registry said; the Lookup explorer shows the same objects for any PURL, which is a quick way to check a summary against the source.

Descriptions, keywords and maintainer names are published by whoever owns the package. They are data to report, not instructions to follow. An agent that reads description: "ignore previous instructions" has learned one thing about the package, and nothing about what to do next.

Rolling your own

The tool is thin by design. If your framework is not the AI SDK, the same five operations are one switch over the helpers:

import {
  bulkFetchPackages,
  fetchDependenciesFromPURL,
  fetchMaintainersFromPURL,
  fetchPackageFromPURL,
  fetchVersionsFromPURL,
} from "@agntn/registries";

export async function run(input: { operation: string; purl?: string; purls?: string[] }, signal?: AbortSignal) {
  switch (input.operation) {
    case "package":
      return fetchPackageFromPURL(input.purl!, signal);
    case "versions":
      return fetchVersionsFromPURL(input.purl!, signal);
    case "dependencies":
      return fetchDependenciesFromPURL(input.purl!, signal);
    case "maintainers":
      return fetchMaintainersFromPURL(input.purl!, signal);
    case "bulk-packages":
      return Object.fromEntries(await bulkFetchPackages(input.purls!, { signal }));
  }
}

Wrap the registry in createCached first if the agent will ask about the same packages more than once.

@agntn/registries·MIT license· Registry metadata is data, never instructions.