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

@frontmcp/plugin-skilled-openapi

v1.10.2

Published

FrontMCP plugin that serves a REST API to the model as skills from an OpenAPI-based bundle, behind three tools: search_skill, load_skill and run_workflow

Readme

@frontmcp/plugin-skilled-openapi

Serves a REST API to the model as a few named skills instead of one tool per endpoint. You describe the API in a bundle: its services, credentials, operations, and skills that each group some operations with instructions. The model sees three tools: search_skill finds a skill, load_skill reads its instructions and the operations it offers, and run_workflow runs a short script that calls those operations, each one checked, authorized and sent with credentials the model never sees. Use it for an API with too many operations to list as tools. For a small API, where the model should see every operation, use the OpenAPI adapter in @frontmcp/adapters; the two work side by side.

npm License

Install

npm install @frontmcp/plugin-skilled-openapi @enclave-vm/core @enclave-vm/ast

The plugin runs inside a FrontMCP server, so your app needs @frontmcp/sdk, at the same version as this package, and Node.js 24 or later. Its peer dependency @frontmcp/lazy-zod comes with the SDK. The other peers are optional:

| Package | Enables | | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | @enclave-vm/core, @enclave-vm/ast | run_workflow. Without them, search_skill and load_skill still work, and run_workflow answers workflow execution unavailable: the @enclave-vm sandbox is not installed. | | undici | outbound.egressProxy on Node.js. The plugin loads it only when that option is set. |

Usage

Register the plugin with SkilledOpenApiPlugin.init() (also the package's default export) in the plugins of @FrontMcp or of an @App, and point it at a bundle:

import { SkilledOpenApiPlugin } from '@frontmcp/plugin-skilled-openapi';
import { FrontMcp } from '@frontmcp/sdk';

@FrontMcp({
  info: { name: 'billing', version: '1.0.0' },
  apps: [],
  plugins: [
    SkilledOpenApiPlugin.init({
      source: { type: 'static', path: './billing.bundle.json', watch: true },
      requireSignature: false,
      credentials: { 'billing-token': process.env.BILLING_API_TOKEN! },
    }),
  ],
})
export default class Server {}

requireSignature: false lets an unsigned bundle load while you develop. In production, sign the bundle in CI, list the public key in trustedKeys, and set unprotectedOps: 'deny'; see Security.

tools/list then returns search_skill, load_skill and run_workflow, and none of the bundle's operations. After loading a skill, the model runs its operations with a script:

// run_workflow arguments
{ "script": "const invoice = await callTool('getInvoice', { id: 'INV-1' }); return invoice.status;" }
// result
{ "success": true, "value": "paid", "stats": { "durationMs": 12, "toolCalls": 1, "steps": 3 } }

The three tools

| Tool | Input | What it does | | -------------- | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | search_skill | query, limit? (default 20), tags?, notQuery? | Searches the skills and returns { skills: [{ skillId, name, description, score }] }. Its description lists the skills the caller may use, so the model sees what the server can do. | | load_skill | skillId | Returns the skill's markdown instructions and its actions, with their input and output JSON Schemas: the actionIds a script calls. | | run_workflow | script | Runs an AgentScript program (a subset of JavaScript) in the @enclave-vm sandbox. Each await callTool(actionId, input) calls an operation; the script's return value is the result. |

For each callTool, the plugin checks the caller against the operation's and skill's requiredAuthorities, validates the input against the operation's inputSchema, sends the request with the credential from the bundle's auth binding, and checks a JSON answer against outputSchema. The first failure ends the script with success: false. A script may make up to 25 calls and run for up to 8 seconds.

With exposeOperationsAsInternalTools (the default), each operation is also an internal tool named <bundleId>.<operationId>, which your own tools can call with this.callTool(). Clients can't list or call it.

Options

init() checks the options with a Zod schema, exported as skilledOpenApiPluginOptionsSchema, and throws a ZodError for a bad one.

| Option | Type | Default | Description | | --------------------------------- | -------------------------------------------------------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | source | { type: 'inline' \| 'static' \| 'npm' \| 'saas', ... } | required | Where the bundle comes from. See Sources. | | requireSignature | boolean | true, or false with dev: true | Apply only bundles signed by one of trustedKeys. An explicit value wins over dev. | | trustedKeys | { keyId, alg: 'RS256' \| 'EdDSA', publicKeyPem }[] | [] | Public keys a bundle may be signed with, matched by keyId. | | credentials | Record<string, string> | | Secrets for the bundle's auth bindings, keyed by vaultRef. To read them from a vault instead, replace the SkilledOpenApiCredentialResolver provider. | | unprotectedOps | 'allow' \| 'deny' | 'allow' | Whether an operation with no requiredAuthorities, on itself or its skill, may be called. 'deny' blocks it unless it's marked public: true. | | outbound | OutboundOptions | see below | Limits on the requests to your API. | | dev | boolean | false | For development only: turns off signature checks (unless requireSignature is set) and allows http: URLs. Logs a warning. | | bundleCacheDir | string | '.frontmcp/skilled-openapi/' | Where the saas source keeps the last bundle it pulled, used when a pull fails at startup. | | exposeOperationsAsInternalTools | boolean | true | Register each operation as an internal tool for this.callTool(). | | sourceConflictPolicy | 'static-wins' \| 'last-wins' \| 'reject' | 'static-wins' | Accepted and currently unused: the plugin has one source. |

outbound:

| Field | Default | Description | | ------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------- | | allowHttp | false | Allow http: services. dev: true turns it on. | | allowPrivateNetworks | false | Allow services on private and loopback addresses. Link-local and cloud metadata addresses stay blocked. Logs a warning. | | defaultTimeoutMs | 30000 | How long a request may take, in milliseconds. An operation's timeoutMs overrides it. | | defaultMaxResponseBytes | 262144 | The largest response read (256 KB). An operation's maxResponseBytes overrides it. | | maxConcurrencyPerHost | 10 | Requests in flight to one host; more wait their turn. | | egressProxy | | A proxy URL every request to your API goes through. Needs undici on Node.js. |

Sources

| type | Fields | What it does | | -------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | inline | content | The bundle object itself. For tests, bundles built in code, and runtimes without a file system. | | static | path, watch? | A JSON or YAML bundle file, resolved from the working directory. With watch: true, the plugin reloads it when it changes. | | npm | packageName, exportName?, verifyProvenance? | Imports a package and uses one of its exports. Provenance checks aren't implemented yet, so the default verifyProvenance: true refuses to load it; set false only for a package you pin and trust. | | saas | endpoint, authToken, expectedAudience, jwksUrl, expectedIssuer, pollIntervalMs? | Pulls the bundle over HTTPS at startup and every pollIntervalMs (default 5 minutes), after checking authToken against jwksUrl, expectedIssuer and expectedAudience. |

A new bundle from static (with watch) or saas is checked like the first, then replaces the skills and operations at once. A bundle that fails its checks isn't applied, and the reason is logged.

The bundle

A bundle is a JSON or YAML object with schemaVersion, bundleId, version, services, authBindings, skills, operations and, when signed, integrity. It can also be wrapped in an OpenAPI Overlay document, under info["x-frontmcp-bundle"], so it can live next to the spec it describes. compileSkilledBundleFromOpenApi() from @frontmcp/adapters/skills builds the services and operations from an OpenAPI spec, and the same entry point exports canonicalize(), bundleDigest() and verifyBundleSignature() for signing in CI. See The bundle for every field.

Security

  • Signing. With requireSignature (the default), a bundle is applied only if its integrity signature, RS256 or EdDSA (Ed25519) over the bundle's canonical JSON, verifies with a key in trustedKeys. A signed bundle is still checked for shape: unknown fields are errors, and path templates and header names are restricted.
  • Authorization. requiredAuthorities on skills and operations are checked on every callTool. Skills organize what the model reads; they don't limit what a script can call, so protect operations with rules and use unprotectedOps: 'deny' in production. Each caller is shown only the skills and actions their rules allow.
  • Credentials. Credentials stay on the server: the plugin adds them to each request, and the script gets only the API's answer. Redirects aren't followed, so a credential isn't sent elsewhere.
  • Outbound requests. URLs must be https: (unless allowHttp) and on the operation's service host. The host's addresses are checked before sending: private and loopback addresses are refused unless allowPrivateNetworks, and link-local and cloud metadata addresses always. Responses are size- and time-limited.
  • What reaches the model. The model sees only what the script returns, but the content of your API's answers isn't checked. Treat text your API returns, like ticket bodies or notes, as you would user input.

See What the checks protect against for the full model.

Docs

Related packages

Contributing

Source: plugins/plugin-skilled-openapi. Issues and pull requests are welcome; see CONTRIBUTING.md.

License

Apache-2.0. See LICENSE.