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

@nimblebrain/mpak-sdk

v0.6.0

Published

TypeScript SDK for mpak registry - MCPB bundles and Agent Skills

Readme

@nimblebrain/mpak-sdk

CI npm Node License mpak.dev

TypeScript SDK for the mpak registry — search, download, cache, configure, and run MCPB bundles and Agent Skills.

Installation

pnpm add @nimblebrain/mpak-sdk

Quick Start

The MpakSDK facade is the primary entry point. It wires together the registry client, local cache, and config manager:

import { MpakSDK } from '@nimblebrain/mpak-sdk';

const mpak = new MpakSDK();

// Prepare a bundle for execution (downloads if not cached)
const server = await mpak.prepareServer('@nimblebraininc/echo');

// Spawn the MCP server
import { spawn } from 'child_process';
const child = spawn(server.command, server.args, {
  env: { ...server.env, ...process.env },
  cwd: server.cwd,
  stdio: 'inherit',
});

Usage

Prepare and Run a Server

prepareServer handles the full lifecycle: download, cache, read manifest, validate config, and resolve the command:

const mpak = new MpakSDK();

// Latest version
const server = await mpak.prepareServer('@scope/bundle');

// Pinned version (inline)
const server = await mpak.prepareServer('@scope/[email protected]');

// Pinned version (option) + force re-download
const server = await mpak.prepareServer('@scope/bundle', {
  version: '1.2.0',
  force: true,
});

// Custom workspace directory for stateful bundles
const server = await mpak.prepareServer('@scope/bundle', {
  workspaceDir: '/path/to/project/.mpak',
});

// Extra env vars merged on top of manifest env
const server = await mpak.prepareServer('@scope/bundle', {
  env: { DEBUG: 'true' },
});

The returned ServerCommand contains everything needed to spawn:

server.command;  // e.g. 'node', 'python3', or '/path/to/binary'
server.args;     // e.g. ['/cache/dir/index.js']
server.env;      // manifest env + user config substitutions + overrides
server.cwd;      // extracted bundle cache directory
server.name;     // resolved package name
server.version;  // resolved version string

User Config (per-package settings)

Bundles can declare required configuration (API keys, ports, etc.) in their manifest. Store values before running:

const mpak = new MpakSDK();

// Set a config value
mpak.config.setPackageConfigValue('@scope/bundle', 'api_key', 'sk-...');

// Values are substituted into ${user_config.*} placeholders in the manifest env
// If required config is missing, prepareServer throws with a clear message

Parse Package Specs

Validate and parse @scope/name or @scope/name@version strings:

MpakSDK.parsePackageSpec('@scope/name');
// { name: '@scope/name' }

MpakSDK.parsePackageSpec('@scope/[email protected]');
// { name: '@scope/name', version: '1.0.0' }

MpakSDK.parsePackageSpec('invalid');
// throws: Invalid package spec

Search Bundles

const mpak = new MpakSDK();

const results = await mpak.client.searchBundles({ q: 'mcp', limit: 10 });
for (const bundle of results.bundles) {
  console.log(`${bundle.name}@${bundle.latest_version}`);
}

Get Bundle Details

const bundle = await mpak.client.getBundle('@nimblebraininc/echo');

console.log(bundle.description);
console.log(`Versions: ${bundle.versions.map(v => v.version).join(', ')}`);

Platform-Specific Downloads

const platform = MpakClient.detectPlatform();

const download = await mpak.client.getBundleDownload(
  '@nimblebraininc/echo',
  '0.1.3',
  platform
);

Cache Operations

const mpak = new MpakSDK();

// Download and cache a bundle
const result = await mpak.cache.loadBundle('@scope/name');
console.log(result.cacheDir);   // path to extracted bundle
console.log(result.version);    // resolved version
console.log(result.pulled);     // true if downloaded, false if from cache

// Read a cached bundle's manifest
const manifest = mpak.cache.readManifest('@scope/name');

// List all cached bundles
const bundles = mpak.cache.listCachedBundles();

// Check for updates (fire-and-forget, logs via logger callback)
await mpak.cache.checkForUpdateAsync('@scope/name');

// Read/write cache metadata
const meta = mpak.cache.getCacheMetadata('@scope/name');

Config Manager

const mpak = new MpakSDK();

// Registry URL
mpak.config.getRegistryUrl();
mpak.config.setRegistryUrl('https://custom.registry.dev');

// Per-package config
mpak.config.setPackageConfigValue('@scope/name', 'api_key', 'sk-...');
mpak.config.getPackageConfigValue('@scope/name', 'api_key');
mpak.config.getPackageConfig('@scope/name');       // all values for a package
mpak.config.clearPackageConfig('@scope/name');      // remove all config for a package
mpak.config.clearPackageConfigValue('@scope/name', 'api_key');  // remove one key
mpak.config.listPackagesWithConfig();               // list configured packages

Search & Download Skills

const mpak = new MpakSDK();

const skills = await mpak.client.searchSkills({ q: 'crm', limit: 10 });
for (const skill of skills.skills) {
  console.log(`${skill.name}: ${skill.description}`);
}

// Download skill with SHA256 integrity verification
const download = await mpak.client.getSkillDownload('@nimbletools/folk-crm');
const data = await mpak.client.downloadContent(download.url, download.skill.sha256);

Constructor Options

const mpak = new MpakSDK({
  mpakHome: '~/.mpak',                       // Root directory for config + cache
  registryUrl: 'https://registry.mpak.dev',   // Registry API URL
  timeout: 30000,                             // Request timeout in ms
  userAgent: 'my-app/1.0',                    // User-Agent header
  logger: (msg) => console.error(msg),        // Logger for cache operations
});

Error Handling

import {
  MpakSDK,
  MpakNotFoundError,
  MpakIntegrityError,
  MpakNetworkError,
} from '@nimblebrain/mpak-sdk';

try {
  const server = await mpak.prepareServer('@nonexistent/bundle');
} catch (error) {
  if (error instanceof MpakNotFoundError) {
    console.error('Bundle not found:', error.message);
  } else if (error instanceof MpakIntegrityError) {
    // Content was NOT returned (fail-closed)
    console.error('Expected SHA256:', error.expected);
    console.error('Actual SHA256:', error.actual);
  } else if (error instanceof MpakNetworkError) {
    console.error('Network error:', error.message);
  }
}

API Reference

MpakSDK (facade)

| Method | Description | |---|---| | prepareServer(packageName, options?) | Resolve a bundle into a ready-to-spawn ServerCommand | | MpakSDK.parsePackageSpec(spec) | Parse and validate a @scope/name[@version] string |

Properties: config (ConfigManager), client (MpakClient), cache (BundleCache).

MpakClient (mpak.client)

Bundle Methods

| Method | Description | |---|---| | searchBundles(params?) | Search for bundles | | getBundle(name) | Get bundle details | | getBundleVersions(name) | List all versions | | getBundleVersion(name, version) | Get specific version info | | getBundleDownload(name, version, platform?) | Get download URL and metadata | | downloadBundle(name, version?) | Download bundle with integrity verification | | downloadContent(url, sha256) | Download any content with SHA256 verification |

Skill Methods

| Method | Description | |---|---| | searchSkills(params?) | Search for skills | | getSkill(name) | Get skill details | | getSkillDownload(name) | Get latest version download info | | getSkillVersionDownload(name, version) | Get specific version download info | | downloadSkillBundle(name, version?) | Download skill bundle with integrity verification |

Static Methods

| Method | Description | |---|---| | MpakClient.detectPlatform() | Detect current OS and architecture |

BundleCache (mpak.cache)

| Method | Description | |---|---| | loadBundle(name, options?) | Download and cache a bundle (skips if cached) | | readManifest(packageName) | Read and validate a cached bundle's manifest.json | | getCacheMetadata(packageName) | Read cache metadata for a package | | writeCacheMetadata(packageName, metadata) | Write cache metadata | | listCachedBundles() | List all cached registry bundles | | getPackageCachePath(packageName) | Get the cache directory path for a package | | checkForUpdateAsync(packageName) | Fire-and-forget update check (logs result) |

Utility Exports

Standalone functions exported from the package (not methods on any class):

| Export | Description | |---|---| | extractZip(zipPath, destDir, options?) | Async. Stream-extract a ZIP with zip-bomb, path-traversal, and symlink protection. Pass { maxUncompressedSize } to override the default cap. | | MAX_UNCOMPRESSED_SIZE | Default uncompressed-size cap applied by extractZip (2 GB). | | isSemverEqual(a, b) | Compare semver strings (ignores v prefix) |

ConfigManager (mpak.config)

| Method | Description | |---|---| | getRegistryUrl() | Get the registry URL (respects MPAK_REGISTRY_URL env var) | | setRegistryUrl(url) | Override the registry URL | | getPackageConfig(packageName) | Get all stored config for a package | | getPackageConfigValue(packageName, key) | Get a single config value | | setPackageConfigValue(packageName, key, value) | Store a config value | | clearPackageConfig(packageName) | Remove all config for a package | | clearPackageConfigValue(packageName, key) | Remove a single config key | | listPackagesWithConfig() | List packages that have stored config |

Property: mpakHome (readonly) — the root directory for mpak state.

Error Types

| Class | Description | |---|---| | MpakError | Base error class | | MpakNotFoundError | Resource not found (404) | | MpakIntegrityError | SHA256 hash mismatch (content NOT returned) | | MpakNetworkError | Network failures, timeouts |

Types

| Type | Description | |---|---| | MpakSDKOptions | Constructor options for MpakSDK | | MpakClientConfig | Constructor options for MpakClient | | PrepareServerOptions | Options for prepareServer | | ServerCommand | Return type of prepareServer | | McpbManifest | Parsed MCPB manifest schema | | UserConfigField | User config field definition from manifest |

Development

pnpm install          # Install dependencies
pnpm test             # Run unit tests
pnpm test:integration # Run integration tests (hits live registry)
pnpm typecheck        # Type check
pnpm build            # Build

Verification

Run all checks before submitting changes:

pnpm --filter @nimblebrain/mpak-sdk lint
pnpm --filter @nimblebrain/mpak-sdk typecheck
pnpm --filter @nimblebrain/mpak-sdk test
pnpm --filter @nimblebrain/mpak-sdk test:integration

Releasing

Releases are automated via GitHub Actions. The publish workflow is triggered by git tags.

Version is defined in one place: package.json.

Steps

  1. Bump version in package.json:

    cd packages/sdk-typescript
    npm version patch   # 0.1.0 -> 0.1.1
    npm version minor   # 0.1.0 -> 0.2.0
    npm version major   # 0.1.0 -> 1.0.0
  2. Commit and push:

    git commit -am "sdk-typescript: bump to X.Y.Z"
    git push
  3. Tag and push (this triggers the publish):

    git tag sdk-typescript-vX.Y.Z
    git push origin sdk-typescript-vX.Y.Z

CI will run the full verification suite, verify the tag matches package.json, build, and publish to npm. See sdk-typescript-publish.yml.

License

Apache-2.0