Skip to content
This repository was archived by the owner on Apr 18, 2026. It is now read-only.

Latest commit

 

History

History
283 lines (211 loc) · 8.65 KB

File metadata and controls

283 lines (211 loc) · 8.65 KB

mpak CLI

CLI for MCP bundles and Agent Skills.

Architecture

This is a standalone CLI that uses only the public v1 API. It has no dependencies on the server or client packages and generates its TypeScript types from the server's OpenAPI spec.

Key Files

File Purpose
src/program.ts Commander program setup with all commands
src/lib/api/registry-client.ts API client for bundle endpoints
src/lib/api/skills-client.ts API client for skill endpoints
src/lib/api/schema.d.ts Generated types from OpenAPI spec
src/commands/packages/*.ts Bundle commands (search, show, pull, run)
src/commands/skills/*.ts Skill commands (validate, pack, search, show, pull, install, list)
src/commands/config.ts Config commands (set, get, list, clear)
src/schemas/generated/skill.ts Skill validation schemas (Zod)
src/utils/config-manager.ts Config file handling (~/.mpak/config.json)

Command Structure

mpak search <query>     # Unified search (bundles + skills)
mpak bundle <command>   # MCP bundle operations
mpak skill <command>    # Agent skill operations
mpak config <command>   # Configuration management

Type Generation

Types are generated from the server's OpenAPI spec using openapi-typescript:

# Requires server running locally on port 3200
npm run generate:types

This generates src/lib/api/schema.d.ts from http://localhost:3200/documentation/json.

The registry-client.ts uses helper types to extract response types:

import type { paths } from './schema.js';

type ResponseOf<T> = T extends { responses: { 200: { content: { 'application/json': infer R } } } } ? R : never;

export type BundleSearchResponse = ResponseOf<paths['/v1/bundles/search']['get']>;
export type Bundle = BundleSearchResponse['bundles'][number];

v1 API Endpoints

Bundle Endpoints

Endpoint Description
GET /v1/bundles/search Search bundles by query, type, sort
GET /v1/bundles/@{scope}/{package} Get bundle details (metadata, readme)
GET /v1/bundles/@{scope}/{package}/versions List versions with platform availability
GET /v1/bundles/@{scope}/{package}/versions/{version}/download Get download URL for specific version/platform

Skill Endpoints

Endpoint Description
GET /v1/skills/search Search skills by query, tags, category
GET /v1/skills/@{scope}/{name} Get skill details
GET /v1/skills/@{scope}/{name}/download Get download URL for latest version
GET /v1/skills/@{scope}/{name}/versions/{version}/download Get download URL for specific version
POST /v1/skills/announce Announce skill version (OIDC auth)

Platform Selection (Bundles)

Download endpoints accept os and arch query parameters:

  • os: darwin, linux, win32, any
  • arch: x64, arm64, any

The server returns the best matching artifact using this priority:

  1. Exact match (requested os + arch)
  2. OS match with any arch
  3. any OS with exact arch
  4. Universal (any + any)

Local Development

Setup

npm install
npm run build

Testing with Local Server

The CLI defaults to https://api.mpak.dev. For local development:

# Start the server (from ../mpak/server)
cd ../mpak/server && npm run dev

# Run CLI commands with local registry
MPAK_REGISTRY_URL=http://localhost:3200 npm run dev -- bundle search echo
MPAK_REGISTRY_URL=http://localhost:3200 node dist/index.js skill search strategy

Verification

npm run build
npm run typecheck
npm run test:all

Publishing

Prereleases (beta):

npm version 0.0.1-beta.X
npm run build
npm publish --tag beta --otp=<code>
git push && git push --tags

Stable releases:

npm version X.X.X
npm run build
npm publish --otp=<code>
git push && git push --tags

Commands

Unified Search

Command Description
mpak search <query> Search bundles and skills
mpak search <query> --type bundle Search bundles only
mpak search <query> --type skill Search skills only

Run (Top-Level Alias)

Command Description
mpak run <package> Run an MCP server (alias for bundle run)

This is the recommended way to integrate with Claude Code:

claude mcp add --transport stdio echo -- mpak run @nimblebraininc/echo

Bundle Commands

Command Description
mpak bundle search <query> Search public bundles
mpak bundle show <package> Show bundle details with platforms
mpak bundle pull <package> Download a bundle
mpak bundle run <package> Run an MCP server (pulls, caches, executes)

Skill Commands

Command Description
mpak skill validate <path> Validate skill directory against Agent Skills spec
mpak skill pack <path> Create a .skill bundle
mpak skill search <query> Search skills in registry
mpak skill show <name> Show skill details
mpak skill pull <name> Download a .skill bundle
mpak skill install <name> Install to ~/.claude/skills/
mpak skill list List installed skills

Config Commands

Command Description
mpak config set <pkg> <k=v...> Store config values for a package
mpak config get <pkg> Show stored config (values masked)
mpak config list List packages with stored config
mpak config clear <pkg> [key] Clear stored config

User Config (MCPB v0.3)

Packages can declare user_config in their manifest for values like API keys:

{
  "user_config": {
    "api_key": {
      "type": "string",
      "title": "API Token",
      "sensitive": true,
      "required": false
    }
  },
  "server": {
    "mcp_config": {
      "env": {
        "API_TOKEN": "${user_config.api_key}"
      }
    }
  }
}

When mpak bundle run executes, it substitutes ${user_config.*} placeholders with actual values.

Two Ways to Provide Config

Option 1: mpak config (recommended for CLI use)

Use mpak config set with keys matching the manifest's user_config field names (not the env var names):

# Key is "api_key" (from manifest.user_config.api_key), NOT "IPINFO_API_TOKEN"
mpak config set @nimblebraininc/ipinfo api_key=your_token

# Run uses stored config automatically
mpak bundle run @nimblebraininc/ipinfo

# View stored config (values masked)
mpak config get @nimblebraininc/ipinfo

Option 2: Claude Desktop config (recommended for Claude Desktop)

Set the actual environment variable directly in your Claude Desktop config:

{
  "mcpServers": {
    "ipinfo": {
      "command": "mpak",
      "args": ["bundle", "run", "@nimblebraininc/ipinfo"],
      "env": {
        "IPINFO_API_TOKEN": "your_token"
      }
    }
  }
}

This bypasses user_config substitution entirely since the env var is already set.

Value Resolution Priority

  1. Process environment (highest): Env vars set by parent (e.g., Claude Desktop config)
  2. Stored config: ~/.mpak/config.json (set via mpak config set)
  3. Default value: From manifest's user_config.*.default
  4. Interactive prompt: If TTY and value is required

Claude Desktop env vars always take priority over mpak config values.

Important: Config Key Names

The mpak config set key must match the user_config field name in the manifest:

manifest.user_config.api_key  →  mpak config set ... api_key=xxx  →  env IPINFO_API_TOKEN
                     ^^^^^^^                        ^^^^^^^
                     Field name = config key (NOT the env var name)

Design Decisions

  1. Standalone: No shared dependencies with server/client. Types generated from OpenAPI.
  2. Public API only: Uses v1 API. Publishing requires OIDC via GitHub Actions.
  3. Platform detection: Auto-detects OS/arch, allows explicit override for cross-platform downloads.
  4. Config file: Stores registry URL in ~/.mpak/config.json, overridable via MPAK_REGISTRY_URL.
  5. Skill schemas: Uses Zod schemas in src/schemas/generated/skill.ts for validation.

Gotchas

  • No dotenv: Removed due to v17 banner output breaking Claude Desktop MCP integration
  • Type generation: Requires server running locally (npm run generate:types hits localhost:3200)
  • Schema changes: May introduce breaking type changes; always run full verification after regenerating
  • Env merge order: Use { ...substitutedEnv, ...process.env } so parent env vars (Claude Desktop) take priority over manifest substitutions
  • Skill endpoints: Currently implemented in server code but may not be deployed to production yet