@nimblebrain/mpak-sdk 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2026 NimbleBrain Inc.
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  [![License](https://img.shields.io/npm/l/@nimblebrain/mpak-sdk)](https://github.com/NimbleBrainInc/mpak/blob/main/packages/sdk-typescript/LICENSE)
7
7
  [![mpak.dev](https://mpak.dev/badge.svg)](https://mpak.dev)
8
8
 
9
- TypeScript SDK for the mpak registry - search, download, and resolve MCPB bundles and Agent Skills.
9
+ TypeScript SDK for the mpak registry search, download, cache, configure, and run MCPB bundles and Agent Skills.
10
10
 
11
11
  ## Installation
12
12
 
@@ -16,29 +16,112 @@ pnpm add @nimblebrain/mpak-sdk
16
16
 
17
17
  ## Quick Start
18
18
 
19
+ The `MpakSDK` facade is the primary entry point. It wires together the registry client, local cache, and config manager:
20
+
19
21
  ```typescript
20
- import { MpakClient } from '@nimblebrain/mpak-sdk';
22
+ import { MpakSDK } from '@nimblebrain/mpak-sdk';
21
23
 
22
- const client = new MpakClient();
24
+ const mpak = new MpakSDK();
23
25
 
24
- // Search for bundles
25
- const results = await client.searchBundles({ q: 'mcp', limit: 10 });
26
- for (const bundle of results.bundles) {
27
- console.log(`${bundle.name}@${bundle.latest_version}`);
28
- }
26
+ // Prepare a bundle for execution (downloads if not cached)
27
+ const server = await mpak.prepareServer('@nimblebraininc/echo');
29
28
 
30
- // Get download info
31
- const download = await client.getBundleDownload('@nimblebraininc/echo', 'latest');
32
- console.log(`Download URL: ${download.url}`);
33
- console.log(`SHA256: ${download.bundle.sha256}`);
29
+ // Spawn the MCP server
30
+ import { spawn } from 'child_process';
31
+ const child = spawn(server.command, server.args, {
32
+ env: { ...server.env, ...process.env },
33
+ cwd: server.cwd,
34
+ stdio: 'inherit',
35
+ });
34
36
  ```
35
37
 
36
38
  ## Usage
37
39
 
40
+ ### Prepare and Run a Server
41
+
42
+ `prepareServer` handles the full lifecycle: download, cache, read manifest, validate config, and resolve the command:
43
+
44
+ ```typescript
45
+ const mpak = new MpakSDK();
46
+
47
+ // Latest version
48
+ const server = await mpak.prepareServer('@scope/bundle');
49
+
50
+ // Pinned version (inline)
51
+ const server = await mpak.prepareServer('@scope/bundle@1.2.0');
52
+
53
+ // Pinned version (option) + force re-download
54
+ const server = await mpak.prepareServer('@scope/bundle', {
55
+ version: '1.2.0',
56
+ force: true,
57
+ });
58
+
59
+ // Custom workspace directory for stateful bundles
60
+ const server = await mpak.prepareServer('@scope/bundle', {
61
+ workspaceDir: '/path/to/project/.mpak',
62
+ });
63
+
64
+ // Extra env vars merged on top of manifest env
65
+ const server = await mpak.prepareServer('@scope/bundle', {
66
+ env: { DEBUG: 'true' },
67
+ });
68
+ ```
69
+
70
+ The returned `ServerCommand` contains everything needed to spawn:
71
+
72
+ ```typescript
73
+ server.command; // e.g. 'node', 'python3', or '/path/to/binary'
74
+ server.args; // e.g. ['/cache/dir/index.js']
75
+ server.env; // manifest env + user config substitutions + overrides
76
+ server.cwd; // extracted bundle cache directory
77
+ server.name; // resolved package name
78
+ server.version; // resolved version string
79
+ ```
80
+
81
+ ### User Config (per-package settings)
82
+
83
+ Bundles can declare required configuration (API keys, ports, etc.) in their manifest. Store values before running:
84
+
85
+ ```typescript
86
+ const mpak = new MpakSDK();
87
+
88
+ // Set a config value
89
+ mpak.config.setPackageConfigValue('@scope/bundle', 'api_key', 'sk-...');
90
+
91
+ // Values are substituted into ${user_config.*} placeholders in the manifest env
92
+ // If required config is missing, prepareServer throws with a clear message
93
+ ```
94
+
95
+ ### Parse Package Specs
96
+
97
+ Validate and parse `@scope/name` or `@scope/name@version` strings:
98
+
99
+ ```typescript
100
+ MpakSDK.parsePackageSpec('@scope/name');
101
+ // { name: '@scope/name' }
102
+
103
+ MpakSDK.parsePackageSpec('@scope/name@1.0.0');
104
+ // { name: '@scope/name', version: '1.0.0' }
105
+
106
+ MpakSDK.parsePackageSpec('invalid');
107
+ // throws: Invalid package spec
108
+ ```
109
+
110
+ ### Search Bundles
111
+
112
+ ```typescript
113
+ const mpak = new MpakSDK();
114
+
115
+ const results = await mpak.client.searchBundles({ q: 'mcp', limit: 10 });
116
+ for (const bundle of results.bundles) {
117
+ console.log(`${bundle.name}@${bundle.latest_version}`);
118
+ }
119
+ ```
120
+
38
121
  ### Get Bundle Details
39
122
 
40
123
  ```typescript
41
- const bundle = await client.getBundle('@nimblebraininc/echo');
124
+ const bundle = await mpak.client.getBundle('@nimblebraininc/echo');
42
125
 
43
126
  console.log(bundle.description);
44
127
  console.log(`Versions: ${bundle.versions.map(v => v.version).join(', ')}`);
@@ -47,77 +130,81 @@ console.log(`Versions: ${bundle.versions.map(v => v.version).join(', ')}`);
47
130
  ### Platform-Specific Downloads
48
131
 
49
132
  ```typescript
50
- // Detect current platform
51
133
  const platform = MpakClient.detectPlatform();
52
134
 
53
- // Get platform-specific download
54
- const download = await client.getBundleDownload(
135
+ const download = await mpak.client.getBundleDownload(
55
136
  '@nimblebraininc/echo',
56
137
  '0.1.3',
57
138
  platform
58
139
  );
59
140
  ```
60
141
 
61
- ### Search Skills
142
+ ### Cache Operations
62
143
 
63
144
  ```typescript
64
- const skills = await client.searchSkills({
65
- q: 'crm',
66
- surface: 'claude-code',
67
- limit: 10,
68
- });
145
+ const mpak = new MpakSDK();
69
146
 
70
- for (const skill of skills.skills) {
71
- console.log(`${skill.name}: ${skill.description}`);
72
- }
73
- ```
147
+ // Download and cache a bundle
148
+ const result = await mpak.cache.loadBundle('@scope/name');
149
+ console.log(result.cacheDir); // path to extracted bundle
150
+ console.log(result.version); // resolved version
151
+ console.log(result.pulled); // true if downloaded, false if from cache
74
152
 
75
- ### Download Skill with Integrity Verification
153
+ // Read a cached bundle's manifest
154
+ const manifest = mpak.cache.readManifest('@scope/name');
76
155
 
77
- ```typescript
78
- // Get skill download info
79
- const download = await client.getSkillDownload('@nimbletools/folk-crm');
156
+ // List all cached bundles
157
+ const bundles = mpak.cache.listCachedBundles();
80
158
 
81
- // Download content with SHA256 verification (fail-closed)
82
- const { content, verified } = await client.downloadSkillContent(
83
- download.url,
84
- download.skill.sha256 // If hash doesn't match, throws MpakIntegrityError
85
- );
159
+ // Check for updates (fire-and-forget, logs via logger callback)
160
+ await mpak.cache.checkForUpdateAsync('@scope/name');
86
161
 
87
- console.log(`Verified: ${verified}`);
88
- console.log(content);
162
+ // Read/write cache metadata
163
+ const meta = mpak.cache.getCacheMetadata('@scope/name');
89
164
  ```
90
165
 
91
- ### Resolve Skill References
166
+ ### Config Manager
92
167
 
93
168
  ```typescript
94
- import { MpakClient, SkillReference } from '@nimblebrain/mpak-sdk';
169
+ const mpak = new MpakSDK();
170
+
171
+ // Registry URL
172
+ mpak.config.getRegistryUrl();
173
+ mpak.config.setRegistryUrl('https://custom.registry.dev');
174
+
175
+ // Per-package config
176
+ mpak.config.setPackageConfigValue('@scope/name', 'api_key', 'sk-...');
177
+ mpak.config.getPackageConfigValue('@scope/name', 'api_key');
178
+ mpak.config.getPackageConfig('@scope/name'); // all values for a package
179
+ mpak.config.clearPackageConfig('@scope/name'); // remove all config for a package
180
+ mpak.config.clearPackageConfigValue('@scope/name', 'api_key'); // remove one key
181
+ mpak.config.listPackagesWithConfig(); // list configured packages
182
+ ```
95
183
 
96
- const client = new MpakClient();
184
+ ### Search & Download Skills
97
185
 
98
- // Resolve from mpak registry
99
- const skill = await client.resolveSkillRef({
100
- source: 'mpak',
101
- name: '@nimblebraininc/folk-crm',
102
- version: '1.3.0',
103
- });
104
- console.log(skill.content);
105
-
106
- // Resolve from GitHub
107
- const ghSkill = await client.resolveSkillRef({
108
- source: 'github',
109
- name: '@example/my-skill',
110
- version: 'v1.0.0',
111
- repo: 'owner/repo',
112
- path: 'skills/my-skill/SKILL.md',
113
- });
186
+ ```typescript
187
+ const mpak = new MpakSDK();
188
+
189
+ const skills = await mpak.client.searchSkills({ q: 'crm', limit: 10 });
190
+ for (const skill of skills.skills) {
191
+ console.log(`${skill.name}: ${skill.description}`);
192
+ }
193
+
194
+ // Download skill with SHA256 integrity verification
195
+ const download = await mpak.client.getSkillDownload('@nimbletools/folk-crm');
196
+ const data = await mpak.client.downloadContent(download.url, download.skill.sha256);
197
+ ```
114
198
 
115
- // Resolve from URL
116
- const urlSkill = await client.resolveSkillRef({
117
- source: 'url',
118
- name: '@example/custom',
119
- version: '1.0.0',
120
- url: 'https://example.com/skill.md',
199
+ ## Constructor Options
200
+
201
+ ```typescript
202
+ const mpak = new MpakSDK({
203
+ mpakHome: '~/.mpak', // Root directory for config + cache
204
+ registryUrl: 'https://registry.mpak.dev', // Registry API URL
205
+ timeout: 30000, // Request timeout in ms
206
+ userAgent: 'my-app/1.0', // User-Agent header
207
+ logger: (msg) => console.error(msg), // Logger for cache operations
121
208
  });
122
209
  ```
123
210
 
@@ -125,88 +212,130 @@ const urlSkill = await client.resolveSkillRef({
125
212
 
126
213
  ```typescript
127
214
  import {
128
- MpakClient,
215
+ MpakSDK,
129
216
  MpakNotFoundError,
130
217
  MpakIntegrityError,
131
218
  MpakNetworkError,
132
219
  } from '@nimblebrain/mpak-sdk';
133
220
 
134
- const client = new MpakClient();
135
-
136
221
  try {
137
- const bundle = await client.getBundle('@nonexistent/bundle');
222
+ const server = await mpak.prepareServer('@nonexistent/bundle');
138
223
  } catch (error) {
139
224
  if (error instanceof MpakNotFoundError) {
140
225
  console.error('Bundle not found:', error.message);
141
226
  } else if (error instanceof MpakIntegrityError) {
142
- // CRITICAL: Content was NOT returned (fail-closed)
143
- console.error('Integrity mismatch!');
144
- console.error('Expected:', error.expected);
145
- console.error('Actual:', error.actual);
227
+ // Content was NOT returned (fail-closed)
228
+ console.error('Expected SHA256:', error.expected);
229
+ console.error('Actual SHA256:', error.actual);
146
230
  } else if (error instanceof MpakNetworkError) {
147
231
  console.error('Network error:', error.message);
148
232
  }
149
233
  }
150
234
  ```
151
235
 
152
- ## Configuration
236
+ ## API Reference
153
237
 
154
- ```typescript
155
- const client = new MpakClient({
156
- registryUrl: 'https://registry.mpak.dev', // Custom registry URL
157
- timeout: 30000, // Request timeout in ms
158
- });
159
- ```
238
+ ### MpakSDK (facade)
160
239
 
161
- ## API Reference
240
+ | Method | Description |
241
+ |---|---|
242
+ | `prepareServer(packageName, options?)` | Resolve a bundle into a ready-to-spawn `ServerCommand` |
243
+ | `MpakSDK.parsePackageSpec(spec)` | Parse and validate a `@scope/name[@version]` string |
244
+
245
+ Properties: `config` (ConfigManager), `client` (MpakClient), `cache` (BundleCache).
162
246
 
163
- ### MpakClient
247
+ ### MpakClient (`mpak.client`)
164
248
 
165
249
  #### Bundle Methods
166
250
 
167
- - `searchBundles(params?)` - Search for bundles
168
- - `getBundle(name)` - Get bundle details
169
- - `getBundleVersions(name)` - List all versions
170
- - `getBundleVersion(name, version)` - Get specific version info
171
- - `getBundleDownload(name, version, platform?)` - Get download URL
251
+ | Method | Description |
252
+ |---|---|
253
+ | `searchBundles(params?)` | Search for bundles |
254
+ | `getBundle(name)` | Get bundle details |
255
+ | `getBundleVersions(name)` | List all versions |
256
+ | `getBundleVersion(name, version)` | Get specific version info |
257
+ | `getBundleDownload(name, version, platform?)` | Get download URL and metadata |
258
+ | `downloadBundle(name, version?)` | Download bundle with integrity verification |
259
+ | `downloadContent(url, sha256)` | Download any content with SHA256 verification |
172
260
 
173
261
  #### Skill Methods
174
262
 
175
- - `searchSkills(params?)` - Search for skills
176
- - `getSkill(name)` - Get skill details
177
- - `getSkillDownload(name)` - Get latest version download
178
- - `getSkillVersionDownload(name, version)` - Get specific version download
179
- - `downloadSkillContent(url, expectedSha256?)` - Download with optional integrity check
180
- - `resolveSkillRef(ref)` - Resolve a skill reference to content
263
+ | Method | Description |
264
+ |---|---|
265
+ | `searchSkills(params?)` | Search for skills |
266
+ | `getSkill(name)` | Get skill details |
267
+ | `getSkillDownload(name)` | Get latest version download info |
268
+ | `getSkillVersionDownload(name, version)` | Get specific version download info |
269
+ | `downloadSkillBundle(name, version?)` | Download skill bundle with integrity verification |
181
270
 
182
271
  #### Static Methods
183
272
 
184
- - `MpakClient.detectPlatform()` - Detect current OS/arch
273
+ | Method | Description |
274
+ |---|---|
275
+ | `MpakClient.detectPlatform()` | Detect current OS and architecture |
185
276
 
186
- ### Error Types
277
+ ### BundleCache (`mpak.cache`)
187
278
 
188
- - `MpakError` - Base error class
189
- - `MpakNotFoundError` - Resource not found (404)
190
- - `MpakIntegrityError` - Hash mismatch (content NOT returned)
191
- - `MpakNetworkError` - Network failures, timeouts
279
+ | Method | Description |
280
+ |---|---|
281
+ | `loadBundle(name, options?)` | Download and cache a bundle (skips if cached) |
282
+ | `readManifest(packageName)` | Read and validate a cached bundle's `manifest.json` |
283
+ | `getCacheMetadata(packageName)` | Read cache metadata for a package |
284
+ | `writeCacheMetadata(packageName, metadata)` | Write cache metadata |
285
+ | `listCachedBundles()` | List all cached registry bundles |
286
+ | `getPackageCachePath(packageName)` | Get the cache directory path for a package |
287
+ | `checkForUpdateAsync(packageName)` | Fire-and-forget update check (logs result) |
192
288
 
193
- ## Development
289
+ #### Static Methods
194
290
 
195
- ```bash
196
- # Install dependencies
197
- pnpm install
291
+ | Method | Description |
292
+ |---|---|
293
+ | `BundleCache.extractZip(zipPath, destDir)` | Extract a ZIP with zip-bomb protection |
294
+ | `BundleCache.isSemverEqual(a, b)` | Compare semver strings (ignores `v` prefix) |
295
+
296
+ ### ConfigManager (`mpak.config`)
297
+
298
+ | Method | Description |
299
+ |---|---|
300
+ | `getRegistryUrl()` | Get the registry URL (respects `MPAK_REGISTRY_URL` env var) |
301
+ | `setRegistryUrl(url)` | Override the registry URL |
302
+ | `getPackageConfig(packageName)` | Get all stored config for a package |
303
+ | `getPackageConfigValue(packageName, key)` | Get a single config value |
304
+ | `setPackageConfigValue(packageName, key, value)` | Store a config value |
305
+ | `clearPackageConfig(packageName)` | Remove all config for a package |
306
+ | `clearPackageConfigValue(packageName, key)` | Remove a single config key |
307
+ | `listPackagesWithConfig()` | List packages that have stored config |
308
+
309
+ Property: `mpakHome` (readonly) — the root directory for mpak state.
310
+
311
+ ### Error Types
312
+
313
+ | Class | Description |
314
+ |---|---|
315
+ | `MpakError` | Base error class |
316
+ | `MpakNotFoundError` | Resource not found (404) |
317
+ | `MpakIntegrityError` | SHA256 hash mismatch (content NOT returned) |
318
+ | `MpakNetworkError` | Network failures, timeouts |
198
319
 
199
- # Run unit tests
200
- pnpm test
320
+ ### Types
201
321
 
202
- # Run integration tests (hits real API)
203
- pnpm test:integration
322
+ | Type | Description |
323
+ |---|---|
324
+ | `MpakSDKOptions` | Constructor options for `MpakSDK` |
325
+ | `MpakClientConfig` | Constructor options for `MpakClient` |
326
+ | `PrepareServerOptions` | Options for `prepareServer` |
327
+ | `ServerCommand` | Return type of `prepareServer` |
328
+ | `McpbManifest` | Parsed MCPB manifest schema |
329
+ | `UserConfigField` | User config field definition from manifest |
204
330
 
205
- # Type check
206
- pnpm typecheck
331
+ ## Development
207
332
 
208
- # Build
209
- pnpm build
333
+ ```bash
334
+ pnpm install # Install dependencies
335
+ pnpm test # Run unit tests
336
+ pnpm test:integration # Run integration tests (hits live registry)
337
+ pnpm typecheck # Type check
338
+ pnpm build # Build
210
339
  ```
211
340
 
212
341
  ### Verification
@@ -214,15 +343,12 @@ pnpm build
214
343
  Run all checks before submitting changes:
215
344
 
216
345
  ```bash
217
- pnpm --filter @nimblebrain/mpak-sdk lint # lint
218
- pnpm --filter @nimblebrain/mpak-sdk exec prettier --check "src/**/*.ts" "tests/**/*.ts" # format
219
- pnpm --filter @nimblebrain/mpak-sdk typecheck # type check
220
- pnpm --filter @nimblebrain/mpak-sdk test # unit tests
221
- pnpm --filter @nimblebrain/mpak-sdk test:integration # integration tests (hits live registry)
346
+ pnpm --filter @nimblebrain/mpak-sdk lint
347
+ pnpm --filter @nimblebrain/mpak-sdk typecheck
348
+ pnpm --filter @nimblebrain/mpak-sdk test
349
+ pnpm --filter @nimblebrain/mpak-sdk test:integration
222
350
  ```
223
351
 
224
- CI runs lint, format check, typecheck, and unit tests on every PR via [`sdk-typescript-ci.yml`](../../.github/workflows/sdk-typescript-ci.yml).
225
-
226
352
  ## Releasing
227
353
 
228
354
  Releases are automated via GitHub Actions. The publish workflow is triggered by git tags.