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

@weldall/sdk

v0.12.2

Published

Weldall resource-server SDK for Fetch, Hono, Next.js, and Astro

Readme

@weldall/sdk

@weldall/sdk adds Weldall authentication to a resource server. It verifies DPoP-bound requests, provides the OAuth endpoints Weldall needs, and works with Fetch, Hono, Next.js, and Astro.

The package implements the protocol drafts pinned by Weldall. Before deploying it, read the production checklist, especially the notes about stable keys and replay storage.

Installation

npm install @weldall/sdk

The SDK requires Node.js 22.15 or newer. Hono, Next.js, and Astro are optional peer dependencies, so only install the framework used by your service. Deployed services must use HTTPS; Next.js route handlers must use the Node.js runtime.

Hono example

Install the adapter and server packages:

npm install @weldall/sdk hono @hono/node-server

Then configure the resource, mount Weldall's protocol routes, and protect the application routes:

import { serve } from "@hono/node-server";
import { generateEs256KeyPair, inMemory } from "@weldall/sdk";
import { initWeldall, type WeldallVariables } from "@weldall/sdk/hono";
import { Hono } from "hono";

const key = await generateEs256KeyPair(); // load a stable key in production

const weldall = initWeldall("https://weldall.example.com", {
  resource: "https://contracts.example.com/api",
  publicOrigin: "https://contracts.example.com",
  clientId: "weldall-cli-at-contracts",
  supportedScopes: ["contracts:read"],
  signingKey: { kid: "development-only", ...key },
  replayStore: inMemory(),
  skills: {
    items: [
      {
        id: "list",
        title: "List contracts",
        requiredScopes: ["contracts:read"],
        visibility: "HIDDEN_IF_UNALLOWED",
        content:
          "# List contracts\n\nRun `weldall request --scope contracts:read https://contracts.example.com/api/contracts`.",
      },
    ],
  },
});

const app = new Hono<{ Variables: WeldallVariables }>();
weldall.registerRoutes(app);

app.get("/api/contracts", weldall.protect({ scopes: ["contracts:read"] }), (context) => {
  const auth = weldall.getAuth(context);
  const requestedBy =
    auth.identity.type === "machine" ? auth.identity.clientId : auth.identity.email;
  return context.json({ requestedBy, contracts: [] });
});

serve({ fetch: app.fetch, port: 8787 });

registerRoutes mounts the metadata, key, token, and optional skill endpoints. protect validates the request before the handler runs. Inside the handler, getAuth returns the subject, granted scopes, token ID, client ID, and caller identity.

A caller is either a user or a machine. Check auth.identity.type before reading caller-specific fields such as email or the machine's clientId.

Discovery happens on the first request that needs it. Call await weldall.ready() during startup if the service should fail early when Weldall cannot be reached.

If the resource cannot connect to the Weldall issuer directly, set discoveryProxyOrigin to a trusted HTTPS reverse proxy:

const weldall = initWeldall("https://weldall.example.com", {
  // Keep the remaining resource options unchanged.
  discoveryProxyOrigin: "https://weldall-discovery-proxy.internal.example.com",
  // ...
});

The proxy only transports authorization-server metadata and JWKS requests. The SDK still requires metadata and signed assertions to identify the canonical Weldall issuer, and it requires the advertised jwks_uri to remain on that canonical origin. The proxy must expose GET /.well-known/oauth-authorization-server and the canonical JWKS path advertised by that response (Weldall currently uses GET /api/oauth/jwks) without rewriting their JSON responses.

Native MCP (development preview)

Native serving and shared resource/connector/skill discovery are available on the development branch, with no published minimum version yet. Python serving is also implemented; full parity, performance and packaged-host acceptance remain Task 1 release gates.

Install the optional runtime only in services that expose MCP:

npm install @modelcontextprotocol/sdk@^1.32.1 ajv@^8.20.0

Add this provider as the mcp option of the existing initWeldall configuration. Keep the existing HTTP routes and authentication configuration:

import { createNativeMcp } from "@weldall/sdk/mcp/server";

const mcp = createNativeMcp({
  path: "/api/mcp",
  description: "Contracts visible to the authenticated caller",
  discoveryScopes: ["contracts:read"],
  tools: [
    {
      name: "whoami",
      description: "Return the verified caller, not an identity supplied in arguments",
      inputSchema: { type: "object", properties: {}, additionalProperties: false },
      scopes: ["contracts:read"],
      annotations: { readOnlyHint: true },
      async handler(_, { auth, signal }) {
        signal.throwIfAborted();
        return { content: [{ type: "text", text: auth.subject }] };
      },
    },
  ],
});
// initWeldall(host, { ...existingOptions, mcp });

Hono's registerRoutes mounts the provider automatically. Fetch/Next expose weldall.handlers.mcp(request); mount it at the exact configured path, exporting the handler as the route's POST. Astro exposes weldall.handlers.mcp(context). Core HTTP consumers do not load the optional MCP runtime. A provider is an immutable catalog; replace/restart it to change definitions or policies.

  • SDK declarations own description, endpoint path, discovery scopes, operation scopes and the catalog revision. Protected-resource metadata advertises them as weldall_mcp (schema version 1). There is no second editable policy copy or new persisted resource field. The authenticated registry's opt-in ?mcp=1 read synchronizes validated advertisements at request time, omitting missing/unreachable/incompatible or unauthorized advertisements. It uses four concurrent metadata requests, a five-second timeout and 64 KiB per document; no stale catalog grants access. Ordinary registry reads do not contact resources.
  • The enabled registry resource, its authorization-server origin, supported scopes and approved request prefixes remain the trust boundary. Keep /api/mcp inside those prefixes; the CLI rejects metadata pointing elsewhere. Existing IaC resource registration is unchanged.
  • Discovery scopes and each operation's scopes must be explicit, nonempty, unique and supported. Every scope is required. Initialization and all listings authenticate and enforce discovery scopes; invocation also requires the operation's scopes. The CLI obtains the union before invoking, using ordinary nonempty-scope exchanges. It never retries a denied operation.
  • Tool input/output schemas use JSON Schema Draft 2020-12 (also the default when $schema is omitted); other declared dialects and asynchronous validation are rejected. format is an annotation, not an enforced constraint. Handlers receive the same verified user/machine context as HTTP and must enforce business/record-level authorization themselves. Honor signal; cancelling transport cannot undo a completed write.
  • Ordinary tools and fixed resources are supported; resource-template listing is empty. Transport is stateless Streamable HTTP with JSON responses, no sessions/reconnection. Only the native _meta["weldall/scopes"] field is used for operation policy. Native tools can additionally link a native MCP App as described below; connector UI metadata stays stripped.
  • Resource owners must audit execution at the handler/business layer without recording tokens or sensitive arguments. Central token/discovery activity does not audit direct MCP execution. Locally verified user tokens can remain valid for their ten-minute lifetime (machine tokens: five minutes); this is not immediate central grant revocation.
  • Removing the mcp option disables advertisement and the MCP route without changing HTTP.

Tool behavior hints (not permissions)

Tool annotations describe expected behavior to clients/models. They do not enforce it: scopes and handler-level checks control access. These are standard MCP field names:

| Hint | Meaning when true | MCP default if omitted | | ----------------- | ---------------------------------------------------------------------------------------------- | ---------------------- | | readOnlyHint | Only reads; does not modify its environment. | false | | destructiveHint | May delete or overwrite existing data; false means only additive changes. | true | | idempotentHint | Repeating the same arguments has no additional side effects. | false | | openWorldHint | Interacts with an open-ended set of external entities, such as web search or email recipients. | true |

destructiveHint and idempotentHint matter only for tools that are not read-only. An internal expense database or memory store is a closed domain (openWorldHint: false). A web search is both read-only and open-world: this hint is not a synonym for write access, network permission or UI bridge access. Idempotency also does not mean identical results or authorize automatic retries. Weldall preserves supplied hints without inserting defaults.

Skills can declare mcpDependencies: [{ kind: "resource", resourceKey: "billing" }, { kind: "connector", connectorType: "atlassian-mcp" }]. These select whole available catalogs; existing { connectorKey, tools, includeResources } entries still select only those exact tools. No skill dependency selects an account. New-form administrative configuration uses the existing IaC skill field and requires server feature mcp-discovery-v1. Registration identity, enabled state, supported scopes and request prefixes remain existing IaC resource fields; SDK metadata is derived, not another independently editable IaC policy.

The executable local fixture is apps/expenses/src/app.ts; its create_expense tool is a demonstration only and does not persist records. It is not an approved real-service pilot.

Native MCP Apps (TypeScript preview)

Native resource tools can link a server-rendered HTML/CSS view. Keep the normal tool result: text-only hosts and older CLIs still receive it. There is no central HTML proxy or additional HTML endpoint: the host reads the registered ui:// resource through the same authenticated MCP endpoint as the tools.

import { createNativeMcp, createMcpAppResource, escapeMcpAppText } from "@weldall/sdk/mcp/server";

const view = createMcpAppResource({
  name: "caller",
  scopes: ["contracts:read"],
  resourceOrigin: "https://contracts.example.com",
  displayModes: ["inline", "fullscreen", "pip"],
  styleUrls: ["https://contracts.example.com/assets/caller.css"],
  connectDomains: ["https://contracts.example.com"],
  render: ({ auth }) =>
    `<main><h1>Requested by</h1><p>${escapeMcpAppText(auth.subject)}</p></main>`,
});

const mcp = createNativeMcp({
  path: "/api/mcp",
  description: "Contracts",
  discoveryScopes: ["contracts:read"],
  resources: [view],
  tools: [
    {
      name: "whoami",
      inputSchema: { type: "object", additionalProperties: false },
      scopes: ["contracts:read"],
      _meta: { ui: { resourceUri: view.uri } },
      async handler(_, { auth }) {
        return { content: [{ type: "text", text: auth.subject }] };
      },
    },
  ],
});

The helper generates the URI, document, MIME type (text/html;profile=mcp-app) and a fixed, CSP-hashed display bootstrap. displayModes defaults to ["inline"]; inline is required. Add fullscreen and/or pip to offer the host controls, not to switch modes automatically. The bootstrap negotiates the MCP Apps lifecycle and reports natural content height, including changes after CSS loads. Rendering still runs on the server with { auth, signal }, independently of a tool call; it does not receive tool arguments/results. Escape interpolated data.

resourceOrigin authorizes public CSS, images, fonts and JavaScript ES modules from that exact origin (scheme + host + port). URLs must be absolute HTTPS, or HTTP on explicit loopback hosts for development. No CDNs, credentials or wildcards. At the extension boundary the origin is re-derived from the registered resource identifier, never trusted from provider metadata. The document CSP blocks direct fetch/WebSocket connections by default. connectDomains explicitly allows up to 16 validated HTTPS origins (or loopback HTTP during development) for fetch, XHR and WebSockets and publishes the same allowlist to the host. Treat each entry as a data-disclosure boundary; credentials and wildcards are rejected. Provider inline scripts, event handlers and static navigation/form markup are removed. JavaScript can implement local UI interactions without receiving provider credentials. Tool access requires the explicit per-view opt-in below.

HTML templates and React/Vue

createMcpAppResource() creates the resource definition; add it to createNativeMcp to register it. Its read handler includes the bootstrap automatically. For custom HTML assembly append the string from renderMcpAppBootstrapHtml({ displayModes: ["inline", "fullscreen", "pip"] }) from @weldall/sdk/mcp/display (also exported by /mcp/server) at the end of the body; return the result through the native UI resource, not as an unauthenticated HTML endpoint.

For a React/Vue UI, set scriptUrls: ["https://expenses.example.com/assets/app.js"] on the resource and render a mount point such as <div id="root"></div>. Bundle your framework normally and serve its ES modules, CSS and fonts from resourceOrigin, with correct MIME types and CORS headers for cross-origin iframe/module loading. These assets must be public and non-sensitive; authenticated HTML supplies the initial data. Inline hydration scripts are not supported. API fetching additionally requires the target origin in connectDomains and appropriate CORS headers. Native resource reads require Weldall CLI 0.22.1 or newer.

The framework-independent browser export has no React or Node dependencies:

import { mountMcpApp } from "@weldall/sdk/mcp/browser";

useEffect(() => mountMcpApp({ displayModes: ["inline", "fullscreen", "pip"] }), []);

The returned cleanup removes observers/listeners after the last mount. An existing HTML bootstrap and framework effects share one handshake; the first mount owns its configuration. When using createMcpAppResource, its automatic bootstrap already handles sizing, so this effect is optional.

Calling this resource's tools from a view

Add allowedTools: ["preview_expense_decision"] to createMcpAppResource to opt in. Names must belong to the same createNativeMcp catalog; unknown or duplicate names are rejected. The default is no tool calls. The policy is catalog metadata (included in its revision), not HTML.

In a public browser module, for example a React button handler:

import { callMcpAppTool } from "@weldall/sdk/mcp/browser";

const result = await callMcpAppTool("preview_expense_decision", {
  expenseId: "expense-1",
  decision: "approve",
});
if (result.isError) showError("The tool rejected the request.");
else showDecision(result.structuredContent);

The HTML bootstrap initializes the connection; otherwise mount it with mountMcpApp. The promise waits for initialization and returns the standard MCP result. Protocol/transport errors, unsupported hosts, teardown and timeouts reject it. Tool-level errors retain isError. Calls are bounded to 16 in flight and a 30-second response deadline. Never retry automatically: a timeout or closed view cannot undo an operation that already completed.

Host boundary: modules load only after the host advertises hostCapabilities.experimental["weldall/app-bridge"], ["weldall/display-only"] or ["weldall/resource-tool-calls"], with { version: 1 }. The last is offered only after a fresh resource read supplies an extension-owned view binding. The host keeps that opaque handle out of the iframe, supplies it itself on each call, and routes through an app-only dispatcher. The extension checks the live source binding and per-view allowlist before using the existing CLI authorization path. Same origin, name prefixes, HTML metadata and tool hints are not authorization.

All caller scopes and business-level checks still apply. Direct calls to discovery, connector or foreign-resource tools remain unavailable. Removed/replaced bindings invalidate existing handles; reload the view rather than replaying calls. Goose currently permits App tool calls (including the resource-access dispatcher) only in Auto mode; this feature does not change that policy. Other Goose apps retain their existing bridge. Older clients/extensions without the binding protocol deny resource operations; older display-only hosts can still run display scripts, and hosts without a recognized bridge capability get static content. Connector/connection Apps remain unsupported.

Chat, context, resources and host actions

@weldall/sdk/mcp/browser is framework-independent and has no browser runtime imports. Its protocol types use the existing optional @modelcontextprotocol/sdk peer. The bootstrap and imported helpers share one connection; do not mount a second protocol client on the same iframe.

| Function | Purpose | | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | | sendMcpAppMessage({ role: "user", content }) | Send a chat message and let the model respond. | | updateMcpAppContext({ content?, structuredContent? }) | Replace this app's context for future turns, without starting a turn. | | listMcpAppResources() / readMcpAppResource({ uri }) | List/read this view's own resource catalog, using original upstream URIs. No browser fetch(). | | openMcpAppLink({ url }) | Open a link through the host's existing external-link policy. | | downloadMcpAppFile({ contents }) | Save embedded text/base64 data or an authorized MCP resource link, with a host save dialog. | | requestMcpAppDisplayMode({ mode }) | Request inline/fullscreen/PiP; returns the actual accepted mode. | | sendMcpAppLog({ level, data }) | Send diagnostics to the host, not the chat. Never include secrets. | | subscribeMcpAppEvents(listener) | Observe/replay tool input, partial input, result, cancellation, host-context and teardown events. Returns unsubscribe. | | getMcpAppHost() | Read initialized capabilities and presentation context. | | requestMcpAppTeardown() | Ask the host to close the view. | | createMcpAppSamplingMessage(params) | Request a completion on hosts advertising sampling. Goose currently does not offer it. |

requestMcpApp and notifyMcpApp expose the corresponding typed protocol requests/notifications, including manual size reporting. Unsupported capabilities reject explicitly. Requests retain the 16-call, 1 MiB input and 30-second limits; no automatic retries. Action results can carry isError: true, including user cancellation. Notifications only acknowledge sending, not host acceptance.

Goose advertises text chat messages and text/structured context. Context is session/view-local, included as assistant-audience app data on the next prompt or steer, and cleared when the view closes; it is not persisted as a separate visible chat message. The total live context budget is 256 KiB per session. Downloads have a 1 MiB per-file limit and require a user-selected destination; linked files go through the same resource-bound reader, never arbitrary HTTP fetching. UI logs go to the renderer console.

A tool result does not automatically start a model turn. Do that explicitly when desired:

import { callMcpAppTool, sendMcpAppMessage } from "@weldall/sdk/mcp/browser";

const result = await callMcpAppTool("preview_expense_decision", {
  expenseId: "expense-1",
  decision: "approve",
});
if (!result.isError) {
  await sendMcpAppMessage({
    role: "user",
    content: [
      { type: "text", text: `Expense preview result: ${JSON.stringify(result.structuredContent)}` },
    ],
  });
}

Chat is a separate authority path: a message can make the model use any tools available to the chat, not just the view's own tools. The per-view tool allowlist restricts direct UI calls, not subsequent model actions. Authentication, scopes and business authorization still apply to those actions. Public assets remain same-origin and provider inline code is removed. Direct network access stays blocked unless the resource explicitly declares connectDomains.

The runnable example is Expenses. Protocol tests do not prove that an installed desktop host has loaded the branch extension; verify the iframe in that host before claiming desktop acceptance.

Scope rules

A route can require one or more scopes:

weldall.protect({ scopes: ["contracts:read", "contracts:export"] });

Every entry in scopes is required. When anyScopes is also present, the request needs at least one scope from that list as well. An empty policy still requires a valid Weldall request.

The framework-neutral Fetch API follows the same rules:

const auth = await weldall.verify(request, { scopes: ["contracts:read"] });
const result = await weldall.verifyNoThrow(request);

verify throws a WeldallAuthError when authentication fails. verifyNoThrow returns { ok: true, auth } on success or { ok: false, error, response } on failure.

Machine-to-machine calls

A backend can authenticate with its own registered machine identity. The calling service loads its ES256 key pair and requests a five-minute token for one resource:

import { loadEs256KeyPairFromEnv, requestMachineToken } from "@weldall/sdk";

const key = await loadEs256KeyPairFromEnv({
  privateName: "MACHINE_SIGNING_PRIVATE_JWK",
  privateValue: process.env.MACHINE_SIGNING_PRIVATE_JWK!,
  publicName: "MACHINE_SIGNING_PUBLIC_JWK",
  publicValue: process.env.MACHINE_SIGNING_PUBLIC_JWK!,
});

const token = await requestMachineToken({
  issuer: "https://weldall.example.com",
  clientId: "expenses-a",
  resource: "https://expenses-b.example.com/api",
  scopes: ["expenses-b:read"],
  kid: process.env.MACHINE_SIGNING_KID!,
  key,
});

The receiving service uses its normal initWeldall, verify, or framework adapter setup. Machine tokens are accepted automatically. Route access still comes from scopes and anyScopes; inspect auth.identity.type only when the handler needs to treat users and machines differently.

The SDK verifies the token profile, issuer, audience, subject, client, lifetime, scopes, key binding, and DPoP proof. Machine identities do not contain user email addresses and cannot impersonate users.

Store private keys in deployment secrets, KMS, Vault, or another credential system. Do not put them in Weldall, manifests, repositories, errors, or logs. To rotate a key, register the new public key, deploy the matching private key, and revoke the old key after the rollout.

When a ReplayStore is configured, every request needs a fresh DPoP proof. replayStore: "disabled" skips that check and accepts the resulting replay risk.

Call shared MCP connections as a machine

Provision a shared service-account key in the admin UI and assign weldall:mcp plus every connector/use scope. Request a token for Weldall itself, not a downstream resource:

import { requestMachineToken, createWeldallMcpClient } from "@weldall/sdk";
const token = await requestMachineToken({
  issuer,
  clientId,
  kid,
  key,
  resource: `${issuer}/api`,
  scopes: ["weldall:mcp", "atlassian-mcp:use-shared"],
});
const mcp = createWeldallMcpClient({ issuer, accessToken: token.accessToken, key });
const target = { connectorKey: "atlassian-mcp", connection: "production-bot" };
await mcp.listTools(target);
await mcp.callTool({ ...target, name: "TOOL_NAME", arguments: {} });

Use a stable shared connection key and discovered tool names/arguments. Also available: listConnectors, listConnections, listResources and readResource({ connectorKey, connection, uri }). The MCP API is also exported from @weldall/sdk/mcp.

All methods accept signal, use fresh DPoP proofs and validate bounded contracts: discovery 20 seconds, operations 30 seconds, requests/definitions 1 MiB, results 10 MiB. No automatic retries, including timeouts/401s: writes may already have executed. WeldallMcpError exposes sanitized code/status, not upstream bodies. Tokens/keys stay in memory; authorization is checked live. Machines cannot start OAuth or provision secrets.

Publishing skills

Skills can live next to the API they describe. Configure a static catalog or load one from a local source:

skills: {
  load: async () => loadPublishedSkills(),
}

A skill has a resource-local ID, a title, required scopes, visibility, and Markdown instructions. Optional meta.tags, meta.owner, meta.appearance, and lastUpdatedAt values supply directory metadata; tags are limited to 20 non-empty strings of at most 40 characters each. Appearance accepts extensible string metadata; Weldall recognizes canonical kebab-case Lucide icon names plus gradientFrom, gradientTo, darkGradientFrom, and darkGradientTo six-digit hex colors. Missing or invalid visual values use deterministic fallbacks.

{
  id: "list",
  title: "List contracts",
  requiredScopes: ["contracts:read"],
  visibility: "HIDDEN_IF_UNALLOWED",
  meta: {
    tags: ["contracts", "review"],
    owner: "Legal Operations",
    appearance: {
      icon: "file-text",
      gradientFrom: "#555BD6",
      gradientTo: "#7773E5",
      darkGradientFrom: "#2A2660",
      darkGradientTo: "#403A86",
    },
  },
  lastUpdatedAt: "2026-08-20",
  content: "# List contracts\n\n...",
}

Weldall adds the registered resource key to the local ID. For a resource named contracts, the public skill ID becomes contracts.list.

Visibility affects discovery, not authorization:

  • DEFAULT shows the skill and reports any missing scopes.
  • HIDDEN_IF_UNALLOWED hides the skill until all required scopes are granted.

The API route still needs its own scope policy. Skill instructions never grant access.

The catalog provider receives no employee identity and must return the same catalog for every caller. Catalogs may contain up to 100 skills or 1 MiB of JSON. IDs must be unique, and required scopes use the namespace:permission format. Weldall checks the catalog again and withholds skills that refer to unknown scopes.

Framework adapters

Next.js App Router

Create one shared SDK instance:

// src/lib/weldall.ts
import { initWeldall } from "@weldall/sdk/next";

export const weldall = initWeldall("https://weldall.example.com", options);

Protect a route with withWeldall:

// src/app/api/contracts/route.ts
import { weldall } from "@/lib/weldall";

export const runtime = "nodejs";
export const GET = weldall.withWeldall({ scopes: ["contracts:read"] }, async (_request, auth) =>
  Response.json({
    requestedBy: auth.identity.type === "machine" ? auth.identity.clientId : auth.identity.email,
  }),
);

Each protocol endpoint is a small handler export. For example:

// src/app/oauth/token/route.ts
import { weldall } from "@/lib/weldall";

export const runtime = "nodejs";
export const POST = weldall.handlers.token;

Add equivalent GET route files for the metadata, JWKS, and optional skill handlers listed in Protocol routes.

Astro SSR

Create one shared adapter instance:

// src/weldall.ts
import { initWeldall } from "@weldall/sdk/astro";

export const weldall = initWeldall("https://weldall.example.com", options);

Call weldall.protect() from middleware and read the result with weldall.getAuth(context). Add weldallAuth to App.Locals.

Astro endpoint files can export the handlers directly:

// src/pages/oauth/token.ts
import { weldall } from "../../weldall";

export const prerender = false;
export const POST = weldall.handlers.token;

Create GET endpoints for metadata, JWKS, and the optional skill catalog. Set prerender = false in every endpoint file.

Starlight search integration

@weldall/sdk/starlight turns a Starlight site into a Weldall resource with an agent-facing full-text search endpoint. The site's MDX content is indexed at build time with language-specific Orama stemming and stop-word removal. Agents query GET /api/search and read full pages from GET /api/content?path=… through weldall request — no browser. It also publishes the skill catalog so Weldall auto-discovers the search skill.

// astro.config.mjs — requires SSR (`output: "server"` + a server adapter)
import { weldallSearch } from "@weldall/sdk/starlight";

export default defineConfig({
  output: "server",
  adapter: node({ mode: "standalone" }),
  integrations: [
    starlight({ title: "Documentation" }),
    weldallSearch("https://weldall.example.com", {
      publicOrigin: "https://docs.example.com",
      resource: "https://docs.example.com/api",
      clientId: "weldall-cli-at-docs",
      requiredScopes: ["search:read"],
      language: "english",
    }),
  ],
});

The search endpoint returns ranked hits with a short excerpt centered on a literal query match when available, so agents can see why each page matched. Agents that need the full body request it by path from the content endpoint (also protected by the same scopes):

weldall request --scope search:read \
  "https://docs.example.com/api/content?path=/team/overview/"

Both endpoints require all of the configured requiredScopes. The auto-discovered search skill documents both commands so agents can search and then read a result end to end.

The generated search skill is customizable by the site owner through the skills.search option — a custom title, an opening description containing concrete discovery vocabulary and example tasks, a full content replacement, or extraRules appended to the generated operating rules:

weldallSearch("https://weldall.example.com", {
  // ...
  skills: {
    search: {
      description: "Use this for office support, vehicle booking, onboarding, and company facts.",
      extraRules: "Pages under /office describe internal support teams.",
    },
  },
});

The interface mirrors initWeldall(host, options). The search endpoint requires all of the configured requiredScopes, which are also the skill's requiredScopes and the resource's supported scopes. Configuration is prop-driven: the consuming site passes values in astro.config.mjs and may read them from its own environment variables however it likes — the library accepts values, never environment-variable names. Runtime-only values are not written into .weldall-search/config.json: set WELDALL_SIGNING_KEY in the server environment, and call configureWeldallSearchRuntime({ replayStore }) from server startup code before serving the injected routes.

language defaults to "english". It enables the matching stemmer and stop-word list; import SUPPORTED_SEARCH_LANGUAGES from @weldall/sdk/starlight to inspect the accepted values.

Orama and gray-matter are optional peer dependencies and must be installed in the consuming site:

pnpm add @orama/orama @orama/stemmers @orama/stopwords gray-matter

Protocol routes

Hono's registerRoutes mounts all required handlers. Fetch, Next.js, and Astro integrations mount the same handlers explicitly:

| Route | Handler | | ----------------------------------------- | ----------------------------- | | /.well-known/oauth-authorization-server | authorizationServerMetadata | | /.well-known/oauth-protected-resource | protectedResourceMetadata | | /.well-known/oauth-protected-resource/* | protectedResourceMetadata | | /.well-known/jwks.json | jwks | | /.well-known/weldall-skills | skills | | /oauth/token | token |

The handler is available as weldall.handlers.<name>. The resource-specific protected metadata path must match the path in resource. For https://contracts.example.com/api, expose /.well-known/oauth-protected-resource/api. The skill route is only needed when skills is configured.

Production checklist

  • Load a stable ES256 signing key. Generating one at startup invalidates tokens after every restart. KMS and Vault integrations can provide a signing-key provider with current() and jwks() methods.
  • Keep resource, publicOrigin, deployed protocol routes, and the resource registered in Weldall exactly aligned.
  • Use HTTPS. Set allowInsecureLoopback: true only for local loopback development.
  • Choose replay storage deliberately. inMemory() holds up to 10,000 live entries by default, belongs to one process, and clears on restart. A shared ReplayStore must return true only for the first consume(key, expiresAt) call and fail closed on storage errors.
  • Plan key rotation, restarts, draft upgrades, and independent conformance testing before rollout. The current protocol boundary has no DPoP nonce negotiation.

License

FSL-1.1-ALv2 (Functional Source License), converting to Apache-2.0 two years after each release.