@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/dist/index.d.ts CHANGED
@@ -1,53 +1,13 @@
1
- import { VersionDetail, DownloadInfo, VersionsResponse, PlatformInfo, FullProvenance, SkillSummary, SkillDetail, BundleSearchResponse, BundleDetail, SkillSearchResponse, SkillDownloadInfo } from '@nimblebrain/mpak-schemas';
2
- export { Bundle, BundleDetail, BundleDetail as BundleDetailResponse, DownloadInfo as BundleDownloadResponse, BundleSearchResponse, VersionDetail as BundleVersionResponse, VersionsResponse as BundleVersionsResponse, Pagination, SkillDetail, SkillDetail as SkillDetailResponse, SkillDownloadInfo, SkillDownloadInfo as SkillDownloadResponse, SkillSearchResponse } from '@nimblebrain/mpak-schemas';
1
+ import { BundleSearchParamsInput, BundleSearchResponse, BundleDetail, VersionsResponse, VersionDetail, PlatformInfo, DownloadInfo, SkillSearchParamsInput, SkillSearchResponse, SkillDetail, SkillDownloadInfo, CacheMetadata, McpbManifest, CachedBundleInfo } from '@nimblebrain/mpak-schemas';
2
+ import { z } from 'zod';
3
3
 
4
4
  /**
5
5
  * SDK-specific type definitions for mpak SDK
6
6
  *
7
- * API response types are re-exported from @nimblebrain/mpak-schemas.
8
- * This file contains SDK-specific types (client config, skill references, etc.)
7
+ * API response types should be imported directly from @nimblebrain/mpak-schemas.
8
+ * This file contains only SDK-specific types (client config).
9
9
  */
10
10
 
11
- /** Version in versions listing */
12
- type BundleVersion = VersionsResponse['versions'][number];
13
- /** Artifact in version detail */
14
- type BundleArtifact = VersionDetail['artifacts'][number];
15
- /** Download info alias */
16
- type BundleDownloadInfo = DownloadInfo;
17
- /** Skill summary in search results */
18
- type Skill = SkillSummary;
19
-
20
- /** Skill version in detail */
21
- type SkillVersion = SkillDetail['versions'][number];
22
- /** Platform identifier (os + arch) */
23
- type Platform = PlatformInfo;
24
-
25
- /** Provenance information for verified packages */
26
- type Provenance = FullProvenance;
27
- /** Author information */
28
- interface Author {
29
- name: string;
30
- url?: string;
31
- email?: string;
32
- }
33
- /** Query parameters for bundle search */
34
- interface BundleSearchParams {
35
- q?: string;
36
- type?: string;
37
- sort?: 'downloads' | 'recent' | 'name';
38
- limit?: number;
39
- offset?: number;
40
- }
41
- /** Query parameters for skill search */
42
- interface SkillSearchParams {
43
- q?: string;
44
- tags?: string;
45
- category?: string;
46
- surface?: string;
47
- sort?: 'downloads' | 'recent' | 'name';
48
- limit?: number;
49
- offset?: number;
50
- }
51
11
  /**
52
12
  * Configuration options for MpakClient
53
13
  */
@@ -68,64 +28,11 @@ interface MpakClientConfig {
68
28
  */
69
29
  userAgent?: string;
70
30
  }
71
- /**
72
- * Base fields shared by all skill reference types
73
- */
74
- interface SkillReferenceBase {
75
- /** Skill artifact identifier (e.g., '@nimbletools/folk-crm') */
76
- name: string;
77
- /** Semver version (e.g., '1.0.0') or 'latest' */
78
- version: string;
79
- /** SHA256 integrity hash (format: 'sha256-hexdigest') */
80
- integrity?: string;
81
- }
82
- /**
83
- * Skill reference from mpak registry
84
- */
85
- interface MpakSkillReference extends SkillReferenceBase {
86
- source: 'mpak';
87
- }
88
- /**
89
- * Skill reference from GitHub repository
90
- */
91
- interface GithubSkillReference extends SkillReferenceBase {
92
- source: 'github';
93
- /** GitHub repository (owner/repo) */
94
- repo: string;
95
- /** Path to skill file in repo */
96
- path: string;
97
- }
98
- /**
99
- * Skill reference from direct URL
100
- */
101
- interface UrlSkillReference extends SkillReferenceBase {
102
- source: 'url';
103
- /** Direct download URL */
104
- url: string;
105
- }
106
- /**
107
- * Discriminated union of skill reference types
108
- */
109
- type SkillReference = MpakSkillReference | GithubSkillReference | UrlSkillReference;
110
- /**
111
- * Result of resolving a skill reference
112
- */
113
- interface ResolvedSkill {
114
- /** The markdown content of the skill */
115
- content: string;
116
- /** Version that was resolved */
117
- version: string;
118
- /** Source the skill was fetched from */
119
- source: 'mpak' | 'github' | 'url';
120
- /** Whether integrity was verified */
121
- verified: boolean;
122
- }
123
31
 
124
32
  /**
125
33
  * Client for interacting with the mpak registry
126
34
  *
127
35
  * Requires Node.js 18+ for native fetch support.
128
- * Uses jszip for skill bundle extraction.
129
36
  */
130
37
  declare class MpakClient {
131
38
  private readonly registryUrl;
@@ -135,7 +42,7 @@ declare class MpakClient {
135
42
  /**
136
43
  * Search for bundles
137
44
  */
138
- searchBundles(params?: BundleSearchParams): Promise<BundleSearchResponse>;
45
+ searchBundles(params?: BundleSearchParamsInput): Promise<BundleSearchResponse>;
139
46
  /**
140
47
  * Get bundle details
141
48
  */
@@ -151,11 +58,11 @@ declare class MpakClient {
151
58
  /**
152
59
  * Get download info for a bundle
153
60
  */
154
- getBundleDownload(name: string, version: string, platform?: Platform): Promise<DownloadInfo>;
61
+ getBundleDownload(name: string, version: string, platform?: PlatformInfo): Promise<DownloadInfo>;
155
62
  /**
156
63
  * Search for skills
157
64
  */
158
- searchSkills(params?: SkillSearchParams): Promise<SkillSearchResponse>;
65
+ searchSkills(params?: SkillSearchParamsInput): Promise<SkillSearchResponse>;
159
66
  /**
160
67
  * Get skill details
161
68
  */
@@ -169,96 +76,456 @@ declare class MpakClient {
169
76
  */
170
77
  getSkillVersionDownload(name: string, version: string): Promise<SkillDownloadInfo>;
171
78
  /**
172
- * Download skill content and verify integrity
79
+ * Download content from a URL and verify its SHA-256 integrity.
173
80
  *
174
- * @throws {MpakIntegrityError} If expectedSha256 is provided and doesn't match (fail-closed)
81
+ * @throws {MpakIntegrityError} If SHA-256 doesn't match
82
+ * @throws {MpakNetworkError} For network failures
175
83
  */
176
- downloadSkillContent(downloadUrl: string, expectedSha256?: string): Promise<{
177
- content: string;
178
- verified: boolean;
179
- }>;
84
+ downloadContent(url: string, sha256: string): Promise<Uint8Array>;
180
85
  /**
181
- * Resolve a skill reference to actual content
86
+ * Download a bundle by name, with optional version and platform.
87
+ * Defaults to latest version and auto-detected platform.
182
88
  *
183
- * Supports mpak, github, and url sources. This is the main method for
184
- * fetching skill content from any supported source.
89
+ * @throws {MpakNotFoundError} If bundle not found
90
+ * @throws {MpakIntegrityError} If SHA-256 doesn't match
91
+ * @throws {MpakNetworkError} For network failures
92
+ */
93
+ downloadBundle(name: string, version?: string, platform?: PlatformInfo): Promise<{
94
+ data: Uint8Array;
95
+ metadata: DownloadInfo['bundle'];
96
+ }>;
97
+ /**
98
+ * Download a skill bundle by name, with optional version.
99
+ * Defaults to latest version.
185
100
  *
186
101
  * @throws {MpakNotFoundError} If skill not found
187
- * @throws {MpakIntegrityError} If integrity check fails (fail-closed)
102
+ * @throws {MpakIntegrityError} If SHA-256 doesn't match
188
103
  * @throws {MpakNetworkError} For network failures
104
+ */
105
+ downloadSkillBundle(name: string, version?: string): Promise<{
106
+ data: Uint8Array;
107
+ metadata: SkillDownloadInfo['skill'];
108
+ }>;
109
+ /**
110
+ * Detect the current platform
111
+ */
112
+ static detectPlatform(): PlatformInfo;
113
+ /**
114
+ * Compute SHA256 hash of content
115
+ */
116
+ private computeSha256;
117
+ /**
118
+ * Validate that a name is scoped (@scope/name)
119
+ */
120
+ private validateScopedName;
121
+ /**
122
+ * Fetch with timeout support
123
+ */
124
+ private fetchWithTimeout;
125
+ }
126
+
127
+ interface MpakBundleCacheOptions {
128
+ mpakHome?: string;
129
+ }
130
+ /**
131
+ * Manages the local bundle cache (`~/.mpak/cache/`).
132
+ *
133
+ * Handles downloading, extracting, and tracking cached bundles.
134
+ * The cache directory is derived from `mpakHome` — the root directory
135
+ * for all mpak state. Consumers can wire this to `MpakConfigManager.mpakHome`
136
+ * for a shared base, or pass any directory.
137
+ *
138
+ * Requires an `MpakClient` for registry operations (download, update checks).
139
+ *
140
+ * @example
141
+ * ```ts
142
+ * // Via MpakSDK facade (recommended)
143
+ * const mpak = new MpakSDK();
144
+ * await mpak.cache.loadBundle('@scope/name');
145
+ *
146
+ * // Standalone
147
+ * const client = new MpakClient();
148
+ * const cache = new MpakBundleCache(client, { mpakHome: '/path/to/.mpak' });
149
+ * await cache.loadBundle('@scope/name');
150
+ * ```
151
+ */
152
+ declare class MpakBundleCache {
153
+ readonly cacheHome: string;
154
+ private readonly mpakClient;
155
+ constructor(client: MpakClient, options?: MpakBundleCacheOptions);
156
+ /**
157
+ * Compute the cache path for a package. Does not create the directory.
158
+ * @example getPackageCachePath('@scope/name') => '<cacheBase>/scope-name'
159
+ */
160
+ getBundleCacheDirName(packageName: string): string;
161
+ /**
162
+ * Read and validate cache metadata for a package.
163
+ * Returns `null` if the package does not exist in the cache.
164
+ * throws Error if metadata is corrupt
165
+ */
166
+ getBundleMetadata(packageName: string): CacheMetadata | null;
167
+ /**
168
+ * Read and validate the MCPB manifest from a cached package.
169
+ * Returns `null` if the package is not cached (directory doesn't exist).
170
+ *
171
+ * @throws {MpakCacheCorruptedError} If the cache directory exists but
172
+ * `manifest.json` is missing, contains invalid JSON, or fails schema validation.
173
+ */
174
+ getBundleManifest(packageName: string): McpbManifest | null;
175
+ /**
176
+ * Scan the cache directory and return metadata for every cached registry bundle.
177
+ * Skips the `_local/` directory (local dev bundles) and entries with
178
+ * missing/corrupt metadata or manifests.
179
+ */
180
+ listCachedBundles(): CachedBundleInfo[];
181
+ /**
182
+ * Remove a cached bundle from disk.
183
+ * @returns `true` if the bundle was cached and removed, `false` if it wasn't cached.
184
+ */
185
+ removeCachedBundle(packageName: string): boolean;
186
+ /**
187
+ * Load a bundle into the local cache, downloading from the registry only
188
+ * if the cache is missing or stale. Returns the cache directory and version.
189
189
  *
190
- * @example
191
- * ```typescript
192
- * // Resolve from mpak registry
193
- * const skill = await client.resolveSkillRef({
194
- * source: 'mpak',
195
- * name: '@nimblebraininc/folk-crm',
196
- * version: '1.3.0',
197
- * });
190
+ * Requires an `MpakClient` to be provided at construction time.
198
191
  *
199
- * // Resolve from GitHub
200
- * const skill = await client.resolveSkillRef({
201
- * source: 'github',
202
- * name: '@example/my-skill',
203
- * version: 'v1.0.0',
204
- * repo: 'owner/repo',
205
- * path: 'skills/my-skill/SKILL.md',
206
- * });
192
+ * @param name - Scoped package name (e.g. `@scope/bundle`)
193
+ * @param options.version - Specific version to load. Omit for "latest".
194
+ * @param options.force - Skip cache checks and always re-download.
207
195
  *
208
- * // Resolve from URL
209
- * const skill = await client.resolveSkillRef({
210
- * source: 'url',
211
- * name: '@example/custom',
212
- * version: '1.0.0',
213
- * url: 'https://example.com/skill.md',
214
- * });
215
- * ```
216
- */
217
- resolveSkillRef(ref: SkillReference): Promise<ResolvedSkill>;
218
- /**
219
- * Resolve a skill from mpak registry
196
+ * @returns `cacheDir` path to the extracted bundle on disk,
197
+ * `version` the resolved version string,
198
+ * `pulled` — whether a download actually occurred.
220
199
  *
221
- * The API returns a ZIP bundle containing SKILL.md and metadata.
200
+ * @throws If no `MpakClient` was provided at construction time.
222
201
  */
223
- private resolveMpakSkill;
202
+ loadBundle(name: string, options?: {
203
+ version?: string;
204
+ force?: boolean;
205
+ }): Promise<{
206
+ cacheDir: string;
207
+ version: string;
208
+ pulled: boolean;
209
+ }>;
224
210
  /**
225
- * Resolve a skill from GitHub releases
211
+ * Fire-and-forget background check for bundle updates.
212
+ * Return the latest version string if an update is available, null otherwise (not cached, skipped, up-to-date, or error).
213
+ * The caller can just check `if (result) { console.log("update available: " + result) }`
214
+ * @param packageName - Scoped package name (e.g. `@scope/bundle`)
226
215
  */
227
- private resolveGithubSkill;
216
+ checkForUpdate(packageName: string, options?: {
217
+ force?: boolean;
218
+ }): Promise<string | null>;
228
219
  /**
229
- * Resolve a skill from a direct URL
220
+ * Write cache metadata for a package.
221
+ * @throws If the metadata fails schema validation.
230
222
  */
231
- private resolveUrlSkill;
223
+ private writeCacheMetadata;
232
224
  /**
233
- * Extract SKILL.md content from a skill bundle ZIP
225
+ * Download a bundle using pre-resolved download info, extract it into
226
+ * the cache, and write metadata.
234
227
  */
235
- private extractSkillFromZip;
228
+ private downloadAndExtract;
229
+ }
230
+
231
+ /**
232
+ * Zod schema for per-package user configuration.
233
+ * Each key-value pair represents a user-supplied config value
234
+ * (e.g. API keys, workspace IDs) referenced via `${user_config.*}` in manifests.
235
+ */
236
+ declare const PackageConfigSchema: z.ZodRecord<z.ZodString, z.ZodString>;
237
+ /**
238
+ * Per-package user configuration — a string-to-string map of values
239
+ * that bundles reference via `${user_config.*}` placeholders.
240
+ */
241
+ type PackageConfig = z.infer<typeof PackageConfigSchema>;
242
+ /**
243
+ * Manages the mpak user configuration file (`config.json`).
244
+ *
245
+ * Handles:
246
+ * - **Registry URL** — custom registry endpoint with hardcoded default fallback
247
+ * - **Per-package config** — key-value pairs for `${user_config.*}` substitution
248
+ *
249
+ * The config is lazy-loaded on first access and cached in memory.
250
+ * The config directory is created lazily on first write — read-only usage
251
+ * never touches the filesystem.
252
+ * All writes are validated against the schema before flushing to disk
253
+ * with `0o600` permissions (owner read/write only).
254
+ *
255
+ * @example
256
+ * ```ts
257
+ * // Default: ~/.mpak/config.json
258
+ * const config = new MpakConfigManager();
259
+ *
260
+ * // Custom home and registry URL
261
+ * const config = new MpakConfigManager({ mpakHome: '/tmp/test-mpak', registryUrl: 'https://custom.registry.dev' });
262
+ *
263
+ * // Registry URL (config > default)
264
+ * config.getRegistryUrl();
265
+ *
266
+ * // Per-package user config for ${user_config.*} substitution
267
+ * config.setPackageConfigValue('@scope/bundle', 'api_key', 'sk-...');
268
+ * config.getPackageConfig('@scope/bundle'); // { api_key: 'sk-...' }
269
+ * ```
270
+ */
271
+ interface MpakConfigManagerOptions {
272
+ mpakHome?: string;
273
+ registryUrl?: string;
274
+ }
275
+ declare class MpakConfigManager {
276
+ readonly mpakHome: string;
277
+ private configFile;
278
+ private config;
279
+ constructor(options?: MpakConfigManagerOptions);
236
280
  /**
237
- * Verify content integrity and throw if mismatch (fail-closed)
281
+ * Resolve the registry URL with a 2-tier fallback:
282
+ * 1. Saved value in config file
283
+ * 2. Default: `https://registry.mpak.dev`
284
+ *
285
+ * @returns The resolved registry URL
238
286
  */
239
- private verifyIntegrityOrThrow;
287
+ getRegistryUrl(): string;
240
288
  /**
241
- * Extract hash from integrity string (removes prefix)
289
+ * Get all stored user config values for a package.
290
+ *
291
+ * @param packageName - Scoped package name (e.g. `@scope/bundle`)
292
+ * @returns The key-value map, or `undefined` if the package has no stored config
242
293
  */
243
- private extractHash;
294
+ getPackageConfig(packageName: string): PackageConfig | undefined;
244
295
  /**
245
- * Detect the current platform
296
+ * Store a user config value for a package. Creates the package entry if needed.
297
+ *
298
+ * @param packageName - Scoped package name (e.g. `@scope/bundle`)
299
+ * @param key - The config key (e.g. `api_key`)
300
+ * @param value - The value to store
246
301
  */
247
- static detectPlatform(): Platform;
302
+ setPackageConfigValue(packageName: string, key: string, value: string): void;
248
303
  /**
249
- * Compute SHA256 hash of content
304
+ * Remove all stored config for a package.
305
+ *
306
+ * @param packageName - Scoped package name (e.g. `@scope/bundle`)
307
+ * @returns `true` if the package had config that was removed, `false` if it didn't exist
250
308
  */
251
- private computeSha256;
309
+ clearPackageConfig(packageName: string): boolean;
252
310
  /**
253
- * Validate that a name is scoped (@scope/name)
311
+ * Remove a single config value for a package. If this was the last key,
312
+ * the package entry is cleaned up entirely.
313
+ *
314
+ * @param packageName - Scoped package name (e.g. `@scope/bundle`)
315
+ * @param key - The config key to remove
316
+ * @returns `true` if the key existed and was removed, `false` otherwise
254
317
  */
255
- private validateScopedName;
318
+ clearPackageConfigValue(packageName: string, key: string): boolean;
256
319
  /**
257
- * Fetch with timeout support
320
+ * List all package names that have stored user config.
321
+ *
322
+ * @returns Array of scoped package names (e.g. `['@scope/pkg1', '@scope/pkg2']`)
258
323
  */
259
- private fetchWithTimeout;
324
+ getPackageNames(): string[];
325
+ /**
326
+ * Load the config from disk, or create a fresh one if the file doesn't exist yet.
327
+ * The result is cached — subsequent calls return the in-memory copy without
328
+ * re-reading the file.
329
+ *
330
+ * @returns The validated config object
331
+ * @throws {MpakConfigCorruptedError} If the file exists but contains invalid JSON or fails schema validation
332
+ */
333
+ private loadConfig;
334
+ /**
335
+ * Read the config file from disk, parse JSON, and validate against the schema.
336
+ *
337
+ * @returns The validated config object
338
+ * @throws {MpakConfigCorruptedError} If the file can't be read, contains invalid JSON,
339
+ * or doesn't match the expected schema
340
+ */
341
+ private readAndValidateConfig;
342
+ /**
343
+ * Flush the in-memory config to disk. Creates the config directory if needed,
344
+ * validates against the schema, updates `lastUpdated`, and writes
345
+ * with mode `0o600` (owner read/write only — config may contain secrets).
346
+ *
347
+ * @throws {MpakConfigCorruptedError} If the in-memory config fails schema validation
348
+ */
349
+ private saveConfig;
350
+ /**
351
+ * Persist a custom registry URL to the config file.
352
+ *
353
+ * @param url - The registry URL to save (e.g. `https://registry.example.com`)
354
+ */
355
+ private setRegistryUrl;
356
+ }
357
+
358
+ /**
359
+ * Options for the {@link Mpak} facade.
360
+ *
361
+ * All fields are optional — sensible defaults are derived from
362
+ * `MpakConfigManager` (registry URL, mpakHome) when omitted.
363
+ */
364
+ interface MpakOptions {
365
+ /** Root directory for mpak state. Defaults to `~/.mpak`. */
366
+ mpakHome?: string;
367
+ /** Registry URL override. Defaults to `MpakConfigManager.getRegistryUrl()`. */
368
+ registryUrl?: string;
369
+ /** Request timeout in milliseconds for the client. */
370
+ timeout?: number;
371
+ /** User-Agent string sent with every request. */
372
+ userAgent?: string;
373
+ }
374
+ /**
375
+ * Specifies which bundle to prepare.
376
+ *
377
+ * - `{ name, version? }` — registry bundle. Omit `version` for "latest".
378
+ * - `{ local }` — a local `.mcpb` file on disk. The caller is responsible for
379
+ * validating that the path exists and has a `.mcpb` extension before calling.
380
+ */
381
+ type PrepareServerSpec = {
382
+ name: string;
383
+ version?: string;
384
+ } | {
385
+ local: string;
386
+ };
387
+ /**
388
+ * Options for {@link Mpak.prepareServer}.
389
+ */
390
+ interface PrepareServerOptions {
391
+ /** Skip cache and re-download/re-extract. */
392
+ force?: boolean;
393
+ /** Extra environment variables merged on top of the manifest env. */
394
+ env?: Record<string, string>;
395
+ /**
396
+ * Directory for `MPAK_WORKSPACE` — where stateful bundles write
397
+ * project-local data (databases, logs, etc.). Defaults to `process.cwd()/.mpak`.
398
+ */
399
+ workspaceDir?: string;
400
+ }
401
+ /**
402
+ * Fully resolved server configuration, ready to spawn.
403
+ */
404
+ interface ServerCommand {
405
+ /** The executable command (e.g. `"node"`, `"python3"`, or an absolute binary path). */
406
+ command: string;
407
+ /** Arguments to pass to the command. */
408
+ args: string[];
409
+ /** Environment variables (manifest env + user config substitutions + caller overrides). */
410
+ env: Record<string, string>;
411
+ /** Working directory for the spawned process — the extracted bundle's cache directory. */
412
+ cwd: string;
413
+ /** The resolved package name. */
414
+ name: string;
415
+ /** The resolved version string. */
416
+ version: string;
417
+ }
418
+ /**
419
+ * Top-level facade that wires together the SDK's core components:
420
+ * `MpakConfigManager`, `MpakClient`, and `BundleCache`.
421
+ *
422
+ * Provides a single entry point for the common setup pattern,
423
+ * while still exposing each component for direct use.
424
+ *
425
+ * @example
426
+ * ```ts
427
+ * import { MpakSDK } from '@nimblebrain/mpak-sdk';
428
+ *
429
+ * const mpak = new MpakSDK();
430
+ *
431
+ * // Prepare a server for spawning
432
+ * const server = await mpak.prepareServer('@scope/pkg');
433
+ * const child = spawn(server.command, server.args, {
434
+ * env: { ...server.env, ...process.env },
435
+ * cwd: server.cwd,
436
+ * stdio: 'inherit',
437
+ * });
438
+ *
439
+ * // Access components directly
440
+ * mpak.config.setPackageConfigValue('@scope/pkg', 'api_key', 'sk-...');
441
+ * const bundles = await mpak.client.searchBundles({ q: 'mcp' });
442
+ * ```
443
+ */
444
+ declare class Mpak {
445
+ /** User configuration manager (`config.json`). */
446
+ readonly configManager: MpakConfigManager;
447
+ /** Registry API client. */
448
+ readonly client: MpakClient;
449
+ /** Local bundle cache. */
450
+ readonly bundleCache: MpakBundleCache;
451
+ constructor(options?: MpakOptions);
452
+ /**
453
+ * Prepare a bundle for execution.
454
+ *
455
+ * Accepts either a registry spec (`{ name, version? }`) or a local bundle
456
+ * spec (`{ local }`). Downloads/extracts as needed, reads the manifest,
457
+ * validates user config, and resolves the command, args, and env needed
458
+ * to spawn the MCP server process.
459
+ *
460
+ * @param spec - Which bundle to prepare. See {@link PrepareServerSpec}.
461
+ * @param options - Force re-download/re-extract, extra env, and workspace dir.
462
+ *
463
+ * @throws {MpakConfigError} If required user config values are missing.
464
+ * @throws {MpakCacheCorruptedError} If the manifest is missing or corrupt after download.
465
+ */
466
+ prepareServer(spec: PrepareServerSpec, options?: PrepareServerOptions): Promise<ServerCommand>;
467
+ /**
468
+ * Load a registry bundle into cache and read its manifest.
469
+ */
470
+ private prepareRegistryBundle;
471
+ /**
472
+ * Extract a local `.mcpb` bundle (if stale) and read its manifest.
473
+ * Local bundles are cached under `<cacheHome>/_local/<hash>`.
474
+ *
475
+ * The caller is responsible for validating that `bundlePath` exists
476
+ * and has a `.mcpb` extension before calling this method.
477
+ */
478
+ private prepareLocalBundle;
479
+ /**
480
+ * Gather stored user config values and validate that all required fields are present.
481
+ * @throws If required config values are missing.
482
+ */
483
+ private gatherUserConfig;
484
+ /**
485
+ * Resolve the manifest's `server` block into a spawnable command, args, and env.
486
+ *
487
+ * Handles three server types:
488
+ * - **binary** — runs the compiled executable at `entry_point`, chmod'd +x.
489
+ * - **node** — runs `mcp_config.command` (default `"node"`) with `mcp_config.args`,
490
+ * or falls back to `node <entry_point>` when args are empty.
491
+ * - **python** — like node, but resolves `python3`/`python` at runtime and
492
+ * prepends `<cacheDir>/deps` to `PYTHONPATH` for bundled dependencies.
493
+ *
494
+ * All `${__dirname}` placeholders in args are replaced with `cacheDir`.
495
+ * All `${user_config.*}` placeholders in env are replaced with gathered user values.
496
+ *
497
+ * @throws For unsupported server types.
498
+ */
499
+ private resolveCommand;
500
+ /**
501
+ * Substitute `${__dirname}` placeholders in args.
502
+ */
503
+ private static resolveArgs;
504
+ /**
505
+ * Substitute `${user_config.*}` placeholders in env vars.
506
+ */
507
+ private static substituteEnvVars;
508
+ /**
509
+ * Find a working Python executable. Tries `python3` first, falls back to `python`.
510
+ */
511
+ private static findPythonCommand;
260
512
  }
261
513
 
514
+ /**
515
+ * Parse a scoped package spec (`@scope/name` or `@scope/name@version`)
516
+ * into its name and optional version components.
517
+ *
518
+ * @throws {MpakError} If the spec is not a valid scoped package name.
519
+ *
520
+ * @example
521
+ * parsePackageSpec('@scope/name') // { name: '@scope/name' }
522
+ * parsePackageSpec('@scope/name@1.0.0') // { name: '@scope/name', version: '1.0.0' }
523
+ */
524
+ declare function parsePackageSpec(spec: string): {
525
+ name: string;
526
+ version?: string;
527
+ };
528
+
262
529
  /**
263
530
  * Base error class for mpak SDK errors
264
531
  */
@@ -288,5 +555,63 @@ declare class MpakIntegrityError extends MpakError {
288
555
  declare class MpakNetworkError extends MpakError {
289
556
  constructor(message: string);
290
557
  }
558
+ /**
559
+ * Thrown when the config file cannot be read, parsed, or validated.
560
+ *
561
+ * @param message - Human-readable description of what went wrong
562
+ * @param configPath - Absolute path to the config file that failed
563
+ * @param cause - The underlying error (parse failure, read error, etc.)
564
+ */
565
+ declare class MpakConfigCorruptedError extends MpakError {
566
+ readonly configPath: string;
567
+ readonly cause?: Error | undefined;
568
+ constructor(message: string, configPath: string, cause?: Error | undefined);
569
+ }
570
+ /**
571
+ * Thrown when required user config fields are missing for a package.
572
+ *
573
+ * @param packageName - The package that requires config
574
+ * @param missingFields - Structured list of missing fields
575
+ */
576
+ /**
577
+ * Thrown when cache metadata or manifest is missing, corrupt, or fails validation.
578
+ *
579
+ * @param message - Human-readable description of what went wrong
580
+ * @param filePath - Absolute path to the file that failed
581
+ * @param cause - The underlying error (parse failure, validation error, etc.)
582
+ */
583
+ declare class MpakCacheCorruptedError extends MpakError {
584
+ readonly filePath: string;
585
+ readonly cause?: Error | undefined;
586
+ constructor(message: string, filePath: string, cause?: Error | undefined);
587
+ }
588
+ /**
589
+ * Thrown when a local `.mcpb` bundle is invalid — e.g. manifest is missing,
590
+ * contains invalid JSON, or fails schema validation.
591
+ *
592
+ * @param message - Human-readable description of what went wrong
593
+ * @param bundlePath - Absolute path to the `.mcpb` file
594
+ * @param cause - The underlying error
595
+ */
596
+ declare class MpakInvalidBundleError extends MpakError {
597
+ readonly bundlePath: string;
598
+ readonly cause?: Error | undefined;
599
+ constructor(message: string, bundlePath: string, cause?: Error | undefined);
600
+ }
601
+ declare class MpakConfigError extends MpakError {
602
+ readonly packageName: string;
603
+ readonly missingFields: Array<{
604
+ key: string;
605
+ title: string;
606
+ description?: string;
607
+ sensitive: boolean;
608
+ }>;
609
+ constructor(packageName: string, missingFields: Array<{
610
+ key: string;
611
+ title: string;
612
+ description?: string;
613
+ sensitive: boolean;
614
+ }>);
615
+ }
291
616
 
292
- export { type Author, type BundleArtifact, type BundleDownloadInfo, type BundleSearchParams, type BundleVersion, type GithubSkillReference, MpakClient, type MpakClientConfig, MpakError, MpakIntegrityError, MpakNetworkError, MpakNotFoundError, type MpakSkillReference, type Platform, type Provenance, type ResolvedSkill, type Skill, type SkillReference, type SkillSearchParams, type SkillVersion, type UrlSkillReference };
617
+ export { Mpak, MpakBundleCache, type MpakBundleCacheOptions, MpakCacheCorruptedError, MpakClient, type MpakClientConfig, MpakConfigCorruptedError, MpakConfigError, MpakConfigManager, type MpakConfigManagerOptions, MpakError, MpakIntegrityError, MpakInvalidBundleError, MpakNetworkError, MpakNotFoundError, type MpakOptions, type PackageConfig, type PrepareServerOptions, type PrepareServerSpec, type ServerCommand, parsePackageSpec };