@mosvera/mcp 0.1.4 → 0.1.6

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 CHANGED
@@ -9,16 +9,22 @@ Context Protocol server that lets Claude Desktop, editors, and automation
9
9
  tools inspect, resolve, compile, draft, save, and delete Mosvera aesthetics in
10
10
  a local registry.
11
11
 
12
- The server never calls image providers, never sends provider HTTP requests,
13
- and does not store API keys or secrets. It turns aesthetic documents into
14
- canonical Mosvera models, portable design tokens, CSS variables, and
15
- deterministic provider payloads that other tools can consume.
12
+ The server runs locally. It never executes provider APIs, never sends provider
13
+ HTTP requests, and does not store API keys or secrets. It turns documents in
14
+ your local registry into canonical Mosvera models, portable design tokens,
15
+ CSS variables, deterministic provider payloads, and portable aesthetic packs.
16
+
17
+ ## 10-Minute Quickstart
18
+
19
+ New users should start with the
20
+ [`10-minute quickstart`](https://github.com/mosvera/spec/blob/main/docs/guides/10-minute-quickstart.md).
21
+ It begins with Claude Desktop, then shows the npm/MCP path and runtime paths.
16
22
 
17
23
  ## Install In Claude Desktop
18
24
 
19
25
  The easiest path for non-command-line users is the Mosvera MCP Bundle:
20
26
 
21
- 1. Download `mosvera-mcp-0.1.4.mcpb` from the latest
27
+ 1. Download `mosvera-mcp-0.1.5.mcpb` from the latest
22
28
  [GitHub release](https://github.com/mosvera/mcp/releases).
23
29
  2. Double-click the file, drag it into Claude Desktop, or install it from
24
30
  Claude Desktop Settings → Extensions → Advanced settings → Install
@@ -106,17 +112,26 @@ Export quiet-editorial as an aesthetic pack.
106
112
  Preview importing this aesthetic pack into my registry.
107
113
  ```
108
114
 
115
+ ```text
116
+ Import the claymation-playful-builder sample pack into my registry.
117
+ ```
118
+
109
119
  ```text
110
120
  Save a new aesthetic called executive-editorial based on quiet-editorial-base with a more compact, board-ready voice.
111
121
  ```
112
122
 
113
123
  The saved documents are deterministic JSON in your local registry directory.
114
124
 
125
+ Canonical sample pack:
126
+ <https://raw.githubusercontent.com/mosvera/spec/main/examples/packs/claymation-playful-builder.mosvera.json>
127
+
115
128
  ## Tools
116
129
 
117
130
  Every tool returns `structuredContent` plus a short text summary. Tools are
118
- annotated with MCP read/write hints so clients can present normal approval
119
- flows.
131
+ annotated with MCP read/write/destructive hints so clients can present normal
132
+ approval flows. Read tools do not mutate the registry. Write tools are only
133
+ registered when the active local registry is writable and the server is not in
134
+ read-only mode.
120
135
 
121
136
  | Tool | Mode | Purpose |
122
137
  |------|------|---------|
@@ -130,7 +145,7 @@ flows.
130
145
  | `export_aesthetic_pack` | Read | Export a named aesthetic as a portable `.mosvera.json` pack. |
131
146
  | `resolve_aesthetic` | Read | Resolve a named or inline aesthetic into canonical Mosvera JSON. |
132
147
  | `compile_design_tokens` | Read | Compile canonical output into portable design tokens and CSS variables. |
133
- | `compile_provider_payload` | Read | Advanced deterministic provider payload compilation; no provider HTTP call. |
148
+ | `compile_provider_payload` | Read | Advanced deterministic provider payload compilation; no provider HTTP call. Supports provider-specific `provider_options` such as HeyGen `avatar_id`/`script`, ElevenLabs `voice_id`, and short-video `duration` settings. |
134
149
  | `draft_aesthetic` | Read | Draft a composition document without saving it. |
135
150
  | `save_aesthetic` | Write | Create or update a named composition aesthetic. |
136
151
  | `save_registry_document` | Write | Advanced create/update for registry documents and manifests. |
@@ -158,6 +173,10 @@ templates, palettes, modifiers, composition documents, and merge strategies.
158
173
  They do not carry assets, provider manifests, credentials, remote URLs, or zip
159
174
  bundles in v1.
160
175
 
176
+ To test import/export without authoring a pack from scratch, use the canonical
177
+ sample pack in
178
+ [`mosvera/spec/examples/packs`](https://github.com/mosvera/spec/tree/main/examples/packs).
179
+
161
180
  IDs must be safe Mosvera references: lowercase letters, numbers, `_`, and `-`,
162
181
  starting with a letter. Absolute paths, dotfiles, path traversal, unknown
163
182
  kinds, and unsafe filenames are rejected.
@@ -176,7 +195,7 @@ npm run mcpb:inspect
176
195
  The MCPB pack step creates:
177
196
 
178
197
  ```text
179
- build/mosvera/mosvera-mcp-0.1.4.mcpb
198
+ build/mosvera/mosvera-mcp-0.1.6.mcpb
180
199
  ```
181
200
 
182
201
  ## Package Boundaries
@@ -187,7 +206,23 @@ runtime directly.
187
206
  Use the Python package `mosvera` when you want the peer Python runtime.
188
207
 
189
208
  Use `@mosvera/provider-*` packages when you want direct provider payload
190
- compilation without MCP.
209
+ compilation without MCP. The MCP package loads optional provider adapters when
210
+ they are installed and always keeps provider calls out of MCP. The current
211
+ compile-only provider ids are:
212
+
213
+ ```text
214
+ openai-gpt-image-1
215
+ bfl-flux-2-pro
216
+ sdxl-replicate
217
+ heygen-avatar-video
218
+ google-gemini-image
219
+ google-veo-video
220
+ runway-gen4-image
221
+ runway-gen45-video
222
+ elevenlabs-tts
223
+ adobe-firefly-image
224
+ meshy-text-to-3d
225
+ ```
191
226
 
192
227
  Use `@mosvera/mcp` when an agent, editor, or automation system should call
193
228
  Mosvera through MCP tools.
package/dist/context.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { type LoadedProject, type RegistryDiagnostic, type Validator } from "@mosvera/runtime";
2
2
  import type { ToolContext } from "./types.ts";
3
- export declare const SERVER_VERSION = "0.1.4";
3
+ export declare const SERVER_VERSION = "0.1.6";
4
4
  export interface CliOptions {
5
5
  registryDir?: string;
6
6
  readOnlyMode: boolean;
@@ -13,6 +13,7 @@ export declare function parseCliOptions(argv: string[]): CliOptions;
13
13
  export declare function defaultRegistryDir(platform?: NodeJS.Platform, env?: NodeJS.ProcessEnv): string;
14
14
  export declare function packagedRegistryDir(): string;
15
15
  export declare function registryDiagnostics(project: LoadedProject, validator: Validator): RegistryDiagnostic[];
16
+ export declare function loadOptionalProviderAdapters(ctx: ToolContext): Promise<void>;
16
17
  export declare function reloadContext(ctx: ToolContext): void;
17
18
  export declare function buildContext(input?: string | BuildContextOptions): ToolContext;
18
19
  export declare function bundleRoot(): string;
package/dist/context.js CHANGED
@@ -3,16 +3,24 @@
3
3
  // Server context loading: choose a user-owned registry directory, seed public
4
4
  // examples on first run, and fall back to packaged examples when local writes
5
5
  // are unavailable.
6
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
7
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
8
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
9
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
10
+ });
11
+ }
12
+ return path;
13
+ };
6
14
  import { cpSync, existsSync, mkdirSync, readdirSync, statSync, } from "node:fs";
7
15
  import { homedir } from "node:os";
8
16
  import { dirname, join, resolve } from "node:path";
9
- import { fileURLToPath } from "node:url";
17
+ import { fileURLToPath, pathToFileURL } from "node:url";
10
18
  import { composeStrategies, createValidator, deriveStrategies, validateRegistry, } from "@mosvera/runtime";
11
19
  import { loadProject, RegistryProjectError } from "@mosvera/runtime/node";
12
20
  import { fluxAdapter } from "@mosvera/provider-flux";
13
21
  import { openaiAdapter } from "@mosvera/provider-openai";
14
22
  import { sdxlAdapter } from "@mosvera/provider-sdxl";
15
- export const SERVER_VERSION = "0.1.4";
23
+ export const SERVER_VERSION = "0.1.6";
16
24
  function flagValue(argv, name) {
17
25
  const direct = argv.find((arg) => arg.startsWith(`${name}=`));
18
26
  if (direct !== undefined)
@@ -24,14 +32,23 @@ function flagValue(argv, name) {
24
32
  }
25
33
  function booleanFlag(argv, name) {
26
34
  const direct = argv.find((arg) => arg.startsWith(`${name}=`));
27
- if (direct !== undefined)
28
- return !["", "0", "false", "no"].includes(direct.slice(name.length + 1).toLowerCase());
35
+ if (direct !== undefined) {
36
+ const value = direct.slice(name.length + 1).toLowerCase();
37
+ if (value.includes("${"))
38
+ return false;
39
+ return !["", "0", "false", "no"].includes(value);
40
+ }
29
41
  return argv.includes(name);
30
42
  }
43
+ function configValue(value) {
44
+ if (value === undefined || value.length === 0 || value.includes("${"))
45
+ return undefined;
46
+ return value;
47
+ }
31
48
  export function parseCliOptions(argv) {
32
- const registryArg = flagValue(argv, "--registry");
33
- const envRegistry = process.env.MOSVERA_REGISTRY_DIR;
34
- const envReadOnly = process.env.MOSVERA_MCP_READ_ONLY ?? process.env.MOSVERA_READ_ONLY;
49
+ const registryArg = configValue(flagValue(argv, "--registry"));
50
+ const envRegistry = configValue(process.env.MOSVERA_REGISTRY_DIR);
51
+ const envReadOnly = configValue(process.env.MOSVERA_MCP_READ_ONLY ?? process.env.MOSVERA_READ_ONLY);
35
52
  const parsed = {
36
53
  readOnlyMode: booleanFlag(argv, "--read-only") ||
37
54
  booleanFlag(argv, "--readonly") ||
@@ -101,6 +118,73 @@ function emptyProject() {
101
118
  strategies: {},
102
119
  };
103
120
  }
121
+ function isProviderAdapter(value) {
122
+ if (typeof value !== "object" || value === null)
123
+ return false;
124
+ const adapter = value;
125
+ return (typeof adapter.id === "string" &&
126
+ typeof adapter.version === "string" &&
127
+ typeof adapter.manifest === "function" &&
128
+ typeof adapter.emit === "function" &&
129
+ typeof adapter.execute === "function");
130
+ }
131
+ function packageParts(packageName) {
132
+ return packageName.split("/");
133
+ }
134
+ function optionalPackageEntrypoint(packageName) {
135
+ let directory = dirname(fileURLToPath(import.meta.url));
136
+ const parts = packageParts(packageName);
137
+ while (true) {
138
+ const candidate = join(directory, "node_modules", ...parts, "dist", "index.js");
139
+ if (existsSync(candidate))
140
+ return candidate;
141
+ const parent = dirname(directory);
142
+ if (parent === directory)
143
+ return undefined;
144
+ directory = parent;
145
+ }
146
+ }
147
+ async function optionalAdapter(packageName, exportName) {
148
+ const entrypoint = optionalPackageEntrypoint(packageName);
149
+ if (entrypoint === undefined)
150
+ return undefined;
151
+ try {
152
+ const mod = await import(__rewriteRelativeImportExtension(pathToFileURL(entrypoint).href));
153
+ const adapter = mod[exportName];
154
+ return isProviderAdapter(adapter) ? adapter : undefined;
155
+ }
156
+ catch (e) {
157
+ const code = typeof e === "object" && e !== null && "code" in e ? e.code : undefined;
158
+ if (e instanceof Error &&
159
+ (code === "MODULE_NOT_FOUND" || code === "ERR_MODULE_NOT_FOUND" || code === "ERR_PACKAGE_PATH_NOT_EXPORTED")) {
160
+ return undefined;
161
+ }
162
+ throw e;
163
+ }
164
+ }
165
+ function providerAdapters() {
166
+ return {
167
+ [openaiAdapter.id]: openaiAdapter,
168
+ [fluxAdapter.id]: fluxAdapter,
169
+ [sdxlAdapter.id]: sdxlAdapter,
170
+ };
171
+ }
172
+ export async function loadOptionalProviderAdapters(ctx) {
173
+ const optional = await Promise.all([
174
+ optionalAdapter("@mosvera/provider-heygen", "heygenAdapter"),
175
+ optionalAdapter("@mosvera/provider-google", "googleGeminiImageAdapter"),
176
+ optionalAdapter("@mosvera/provider-google", "googleVeoVideoAdapter"),
177
+ optionalAdapter("@mosvera/provider-runway", "runwayGen4ImageAdapter"),
178
+ optionalAdapter("@mosvera/provider-runway", "runwayGen45VideoAdapter"),
179
+ optionalAdapter("@mosvera/provider-elevenlabs", "elevenLabsTtsAdapter"),
180
+ optionalAdapter("@mosvera/provider-firefly", "fireflyImageAdapter"),
181
+ optionalAdapter("@mosvera/provider-meshy", "meshyTextTo3DAdapter"),
182
+ ]);
183
+ ctx.adapters = {
184
+ ...(ctx.adapters ?? {}),
185
+ ...Object.fromEntries(optional.filter((adapter) => adapter !== undefined).map((adapter) => [adapter.id, adapter])),
186
+ };
187
+ }
104
188
  function loadProjectOrEmpty(directory, validator) {
105
189
  if (!existsSync(directory))
106
190
  return emptyProject();
@@ -119,11 +203,7 @@ function makeContext(registryDir, project, validator, options) {
119
203
  project,
120
204
  validator,
121
205
  baseStrategies: composeStrategies(deriveStrategies(), project.strategies),
122
- adapters: {
123
- [openaiAdapter.id]: openaiAdapter,
124
- [fluxAdapter.id]: fluxAdapter,
125
- [sdxlAdapter.id]: sdxlAdapter,
126
- },
206
+ adapters: providerAdapters(),
127
207
  };
128
208
  if (options.fallbackReason !== undefined)
129
209
  ctx.fallbackReason = options.fallbackReason;
package/dist/server.js CHANGED
@@ -9,7 +9,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
9
9
  import { realpathSync } from "node:fs";
10
10
  import { fileURLToPath } from "node:url";
11
11
  import { z } from "zod";
12
- import { buildContext, parseCliOptions, SERVER_VERSION } from "./context.js";
12
+ import { buildContext, loadOptionalProviderAdapters, parseCliOptions, SERVER_VERSION } from "./context.js";
13
13
  import { documentKinds, runCompileDesignTokens, runCompileProviderPayload, runDeleteRegistryDocument, runDraftAesthetic, runExportAestheticPack, runGetRegistryDocument, runImportAestheticPack, runListAesthetics, runPreviewAestheticImport, runResolveAesthetic, runSaveAesthetic, runSaveRegistryDocument, runServerStatus, runValidateAestheticPack, runValidateDocument, runValidateRegistry, runWriteMergeStrategies, } from "./tools/aesthetic.js";
14
14
  const docArg = z.union([z.string(), z.record(z.any())]);
15
15
  const packSourceArg = {
@@ -141,6 +141,7 @@ export function createServer(ctx) {
141
141
  inputSchema: {
142
142
  aesthetic: z.union([z.string(), z.record(z.any())]),
143
143
  provider: z.string(),
144
+ provider_options: z.record(z.any()).optional(),
144
145
  criticality: criticalityArg.optional(),
145
146
  merge_strategies: strategiesArg.optional(),
146
147
  },
@@ -210,6 +211,7 @@ export function createServer(ctx) {
210
211
  async function main() {
211
212
  const options = parseCliOptions(process.argv.slice(2));
212
213
  const ctx = buildContext(options);
214
+ await loadOptionalProviderAdapters(ctx);
213
215
  const server = createServer(ctx);
214
216
  await server.connect(new StdioServerTransport());
215
217
  }
@@ -44,6 +44,7 @@ export declare function runCompileDesignTokens(ctx: ToolContext, args: {
44
44
  export declare function runCompileProviderPayload(ctx: ToolContext, args: {
45
45
  aesthetic: string | object;
46
46
  provider: string;
47
+ provider_options?: Record<string, unknown>;
47
48
  criticality?: Record<string, Criticality>;
48
49
  merge_strategies?: MergeStrategies;
49
50
  }): CallToolResult;
@@ -157,6 +157,14 @@ function writeDisabled(ctx) {
157
157
  function toolFail(failure) {
158
158
  return fail(failure.error, failure.message, failure.detail);
159
159
  }
160
+ function topCssVariables(cssVariables, limit = 40) {
161
+ const entries = Object.entries(cssVariables).sort(([a], [b]) => a.localeCompare(b));
162
+ if (entries.length === 0)
163
+ return "No CSS variables were produced.";
164
+ const visible = entries.slice(0, limit).map(([key, value]) => `${key}: ${value};`);
165
+ const remaining = entries.length - visible.length;
166
+ return `${visible.join("\n")}${remaining > 0 ? `\n...and ${remaining} more CSS variable${remaining === 1 ? "" : "s"}.` : ""}`;
167
+ }
160
168
  export function runServerStatus(ctx) {
161
169
  const diagnostics = [...ctx.loadDiagnostics, ...registryDiagnostics(ctx.project, ctx.validator)];
162
170
  const registry = ctx.project.registry;
@@ -167,7 +175,15 @@ export function runServerStatus(ctx) {
167
175
  compositions: Object.keys(registry.compositions ?? {}).length,
168
176
  manifests: Object.keys(ctx.project.manifests).length,
169
177
  };
170
- return ok("Mosvera MCP server is ready.", {
178
+ const message = [
179
+ "Mosvera MCP server is ready.",
180
+ `Registry: ${ctx.registryDir}`,
181
+ `Writable: ${ctx.registryWritable ? "yes" : "no"}`,
182
+ `Read-only mode: ${ctx.readOnlyMode ? "yes" : "no"}`,
183
+ `Write tools enabled: ${ctx.registryWritable && !ctx.readOnlyMode ? "yes" : "no"}`,
184
+ `Loaded: ${counts.compositions} aesthetics, ${counts.templates} templates, ${counts.modifiers} modifiers, ${counts.palettes} palettes, ${counts.manifests} manifests.`,
185
+ ].join("\n");
186
+ return ok(message, {
171
187
  registry_path: ctx.registryDir,
172
188
  registry_writable: ctx.registryWritable,
173
189
  read_only_mode: ctx.readOnlyMode,
@@ -287,7 +303,14 @@ export function runCompileDesignTokens(ctx, args) {
287
303
  cssOptions.prefix = args.css_prefix;
288
304
  const tokens = compileDesignTokens(resolved.canonical, tokenOptions);
289
305
  const css_variables = toCssVariables(tokens, cssOptions);
290
- return ok("Compiled portable design tokens.", { source: resolved.source, canonical: resolved.canonical, tokens, css_variables });
306
+ const sections = Object.keys(tokens).sort();
307
+ const message = [
308
+ "Compiled portable design tokens.",
309
+ `Token sections: ${sections.length > 0 ? sections.join(", ") : "none"}.`,
310
+ "CSS variables:",
311
+ topCssVariables(css_variables),
312
+ ].join("\n");
313
+ return ok(message, { source: resolved.source, canonical: resolved.canonical, tokens, css_variables });
291
314
  }
292
315
  export function runCompileProviderPayload(ctx, args) {
293
316
  const input = { aesthetic: args.aesthetic };
@@ -322,7 +345,10 @@ export function runCompileProviderPayload(ctx, args) {
322
345
  });
323
346
  }
324
347
  try {
325
- const emission = adapter.emit(resolved.canonical, { criticality: args.criticality ?? {} });
348
+ const emitOptions = { criticality: args.criticality ?? {} };
349
+ if (args.provider_options !== undefined)
350
+ emitOptions.providerOptions = args.provider_options;
351
+ const emission = adapter.emit(resolved.canonical, emitOptions);
326
352
  return ok(`Compiled deterministic provider payload for "${args.provider}".`, {
327
353
  status: "compiled",
328
354
  provider: args.provider,
@@ -7,6 +7,7 @@ export interface CompileArgs {
7
7
  manifest?: CapabilityManifest;
8
8
  criticality?: Record<string, Criticality>;
9
9
  merge_strategies?: MergeStrategies;
10
+ provider_options?: Record<string, unknown>;
10
11
  emit?: boolean;
11
12
  }
12
13
  export declare function runCompileGeneration(ctx: ToolContext, args: CompileArgs): CompileResultMcp;
@@ -54,7 +54,14 @@ export function runCompileGeneration(ctx, args) {
54
54
  return toolError("unknown_provider", { provider: args.provider, adapter: "missing" });
55
55
  }
56
56
  try {
57
- return { status: "compiled", canonical, emission: adapter.emit(canonical, { criticality: args.criticality ?? {} }) };
57
+ const emitOptions = { criticality: args.criticality ?? {} };
58
+ if (args.provider_options !== undefined)
59
+ emitOptions.providerOptions = args.provider_options;
60
+ return {
61
+ status: "compiled",
62
+ canonical,
63
+ emission: adapter.emit(canonical, emitOptions),
64
+ };
58
65
  }
59
66
  catch (e) {
60
67
  if (e instanceof EmissionError) {
@@ -2,7 +2,7 @@
2
2
  "manifest_version": "0.3",
3
3
  "name": "mosvera-mcp",
4
4
  "display_name": "Mosvera",
5
- "version": "0.1.4",
5
+ "version": "0.1.6",
6
6
  "description": "Resolve, compile, and save local Mosvera aesthetics from Claude Desktop.",
7
7
  "long_description": "Mosvera runs locally against your own aesthetic registry. It lets Claude list named aesthetics, resolve them to canonical Mosvera models, compile portable design tokens and CSS variables, and save registry documents without sending provider requests or storing secrets.",
8
8
  "author": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mosvera/mcp",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Run Mosvera as local MCP tools for agents, editors, and Claude Desktop.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -41,8 +41,8 @@
41
41
  "ci": "npm run build && npm run typecheck && npm test",
42
42
  "clean": "rm -rf dist",
43
43
  "mcpb:stage": "npm run build && node scripts/stage-mcpb.mjs",
44
- "mcpb:pack": "npm run mcpb:stage && mcpb pack build/mcpb/mosvera build/mosvera/mosvera-mcp-0.1.4.mcpb",
45
- "mcpb:inspect": "node scripts/inspect-mcpb.mjs build/mosvera/mosvera-mcp-0.1.4.mcpb",
44
+ "mcpb:pack": "npm run mcpb:stage && mcpb pack build/mcpb/mosvera build/mosvera/mosvera-mcp-0.1.6.mcpb",
45
+ "mcpb:inspect": "node scripts/inspect-mcpb.mjs build/mosvera/mosvera-mcp-0.1.6.mcpb",
46
46
  "prepublishOnly": "npm run build && npm run typecheck && npm test",
47
47
  "start": "tsx src/server.ts",
48
48
  "test": "vitest run",
@@ -64,6 +64,14 @@
64
64
  "@modelcontextprotocol/sdk": "^1.29.0",
65
65
  "zod": "^3.24.1"
66
66
  },
67
+ "optionalDependencies": {
68
+ "@mosvera/provider-elevenlabs": "^0.1.2",
69
+ "@mosvera/provider-firefly": "^0.1.2",
70
+ "@mosvera/provider-google": "^0.1.2",
71
+ "@mosvera/provider-heygen": "^0.1.1",
72
+ "@mosvera/provider-meshy": "^0.1.2",
73
+ "@mosvera/provider-runway": "^0.1.2"
74
+ },
67
75
  "devDependencies": {
68
76
  "@anthropic-ai/mcpb": "^2.1.2",
69
77
  "typescript": "^5.7.2",