@nimblebrain/mpak-sdk 0.1.3 → 0.2.1
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/README.md +241 -115
- package/dist/index.cjs +867 -176
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +484 -159
- package/dist/index.d.ts +484 -159
- package/dist/index.js +857 -164
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
[](https://github.com/NimbleBrainInc/mpak/blob/main/packages/sdk-typescript/LICENSE)
|
|
7
7
|
[](https://mpak.dev)
|
|
8
8
|
|
|
9
|
-
TypeScript SDK for the mpak registry
|
|
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 {
|
|
22
|
+
import { MpakSDK } from '@nimblebrain/mpak-sdk';
|
|
21
23
|
|
|
22
|
-
const
|
|
24
|
+
const mpak = new MpakSDK();
|
|
23
25
|
|
|
24
|
-
//
|
|
25
|
-
const
|
|
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
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
142
|
+
### Cache Operations
|
|
62
143
|
|
|
63
144
|
```typescript
|
|
64
|
-
const
|
|
65
|
-
q: 'crm',
|
|
66
|
-
surface: 'claude-code',
|
|
67
|
-
limit: 10,
|
|
68
|
-
});
|
|
145
|
+
const mpak = new MpakSDK();
|
|
69
146
|
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
153
|
+
// Read a cached bundle's manifest
|
|
154
|
+
const manifest = mpak.cache.readManifest('@scope/name');
|
|
76
155
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const download = await client.getSkillDownload('@nimbletools/folk-crm');
|
|
156
|
+
// List all cached bundles
|
|
157
|
+
const bundles = mpak.cache.listCachedBundles();
|
|
80
158
|
|
|
81
|
-
//
|
|
82
|
-
|
|
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
|
-
|
|
88
|
-
|
|
162
|
+
// Read/write cache metadata
|
|
163
|
+
const meta = mpak.cache.getCacheMetadata('@scope/name');
|
|
89
164
|
```
|
|
90
165
|
|
|
91
|
-
###
|
|
166
|
+
### Config Manager
|
|
92
167
|
|
|
93
168
|
```typescript
|
|
94
|
-
|
|
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
|
-
|
|
184
|
+
### Search & Download Skills
|
|
97
185
|
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
//
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
//
|
|
143
|
-
console.error('
|
|
144
|
-
console.error('
|
|
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
|
-
##
|
|
236
|
+
## API Reference
|
|
153
237
|
|
|
154
|
-
|
|
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
|
-
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
-
|
|
273
|
+
| Method | Description |
|
|
274
|
+
|---|---|
|
|
275
|
+
| `MpakClient.detectPlatform()` | Detect current OS and architecture |
|
|
185
276
|
|
|
186
|
-
###
|
|
277
|
+
### BundleCache (`mpak.cache`)
|
|
187
278
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
289
|
+
#### Static Methods
|
|
194
290
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
|
|
200
|
-
pnpm test
|
|
320
|
+
### Types
|
|
201
321
|
|
|
202
|
-
|
|
203
|
-
|
|
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
|
-
|
|
206
|
-
pnpm typecheck
|
|
331
|
+
## Development
|
|
207
332
|
|
|
208
|
-
|
|
209
|
-
pnpm
|
|
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
|
|
218
|
-
pnpm --filter @nimblebrain/mpak-sdk
|
|
219
|
-
pnpm --filter @nimblebrain/mpak-sdk
|
|
220
|
-
pnpm --filter @nimblebrain/mpak-sdk test
|
|
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.
|