@mosvera/mcp 0.1.6 → 0.1.8

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
@@ -24,8 +24,8 @@ It begins with Claude Desktop, then shows the npm/MCP path and runtime paths.
24
24
 
25
25
  The easiest path for non-command-line users is the Mosvera MCP Bundle:
26
26
 
27
- 1. Download `mosvera-mcp-0.1.5.mcpb` from the latest
28
- [GitHub release](https://github.com/mosvera/mcp/releases).
27
+ 1. Download `mosvera-mcp-0.1.8.mcpb` from the latest
28
+ [GitHub release](https://github.com/mosvera/mcp/releases/latest).
29
29
  2. Double-click the file, drag it into Claude Desktop, or install it from
30
30
  Claude Desktop Settings → Extensions → Advanced settings → Install
31
31
  Extension.
@@ -195,7 +195,7 @@ npm run mcpb:inspect
195
195
  The MCPB pack step creates:
196
196
 
197
197
  ```text
198
- build/mosvera/mosvera-mcp-0.1.6.mcpb
198
+ build/mosvera/mosvera-mcp-0.1.8.mcpb
199
199
  ```
200
200
 
201
201
  ## Package Boundaries
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.6";
3
+ export declare const SERVER_VERSION = "0.1.8";
4
4
  export interface CliOptions {
5
5
  registryDir?: string;
6
6
  readOnlyMode: boolean;
package/dist/context.js CHANGED
@@ -20,7 +20,7 @@ import { loadProject, RegistryProjectError } from "@mosvera/runtime/node";
20
20
  import { fluxAdapter } from "@mosvera/provider-flux";
21
21
  import { openaiAdapter } from "@mosvera/provider-openai";
22
22
  import { sdxlAdapter } from "@mosvera/provider-sdxl";
23
- export const SERVER_VERSION = "0.1.6";
23
+ export const SERVER_VERSION = "0.1.8";
24
24
  function flagValue(argv, name) {
25
25
  const direct = argv.find((arg) => arg.startsWith(`${name}=`));
26
26
  if (direct !== undefined)
@@ -41,6 +41,131 @@ function stable(value) {
41
41
  function stableStringify(value) {
42
42
  return JSON.stringify(stable(value));
43
43
  }
44
+ function stablePretty(value) {
45
+ return JSON.stringify(stable(value), null, 2);
46
+ }
47
+ function jsonPreview(label, value, limit = 3000) {
48
+ const pretty = stablePretty(value);
49
+ const suffix = pretty.length > limit ? `\n...truncated ${pretty.length - limit} character${pretty.length - limit === 1 ? "" : "s"}` : "";
50
+ return `${label}:\n\`\`\`json\n${pretty.slice(0, limit)}${suffix}\n\`\`\``;
51
+ }
52
+ function stringValue(value) {
53
+ return typeof value === "string" && value.length > 0 ? value : undefined;
54
+ }
55
+ function objectValue(value) {
56
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : undefined;
57
+ }
58
+ function canonicalHighlights(canonical) {
59
+ const lines = [];
60
+ const sections = Object.keys(canonical).sort();
61
+ lines.push(`Sections: ${sections.length > 0 ? sections.join(", ") : "none"}.`);
62
+ const voice = objectValue(canonical.voice);
63
+ const headline = stringValue(voice?.headline);
64
+ const body = stringValue(voice?.body);
65
+ if (headline !== undefined)
66
+ lines.push(`Voice headline: ${headline}`);
67
+ if (body !== undefined)
68
+ lines.push(`Voice body: ${body}`);
69
+ const palette = objectValue(canonical.palette);
70
+ if (palette !== undefined) {
71
+ const keys = ["accent", "accent_2", "background", "surface", "ink", "muted"];
72
+ const values = keys
73
+ .map((key) => [key, stringValue(palette[key])])
74
+ .filter((entry) => entry[1] !== undefined)
75
+ .map(([key, value]) => `${key} ${value}`);
76
+ if (values.length > 0)
77
+ lines.push(`Palette: ${values.join(", ")}.`);
78
+ }
79
+ const typography = objectValue(canonical.typography);
80
+ if (typography !== undefined) {
81
+ const values = ["display", "body", "mono", "scale"]
82
+ .map((key) => [key, stringValue(typography[key])])
83
+ .filter((entry) => entry[1] !== undefined)
84
+ .map(([key, value]) => `${key} ${value}`);
85
+ if (values.length > 0)
86
+ lines.push(`Typography: ${values.join(", ")}.`);
87
+ }
88
+ const layout = objectValue(canonical.layout);
89
+ if (layout !== undefined) {
90
+ const values = ["density", "radius", "max_width", "shadow"]
91
+ .map((key) => [key, stringValue(layout[key])])
92
+ .filter((entry) => entry[1] !== undefined)
93
+ .map(([key, value]) => `${key} ${value}`);
94
+ if (values.length > 0)
95
+ lines.push(`Layout: ${values.join(", ")}.`);
96
+ }
97
+ const imagery = objectValue(canonical.imagery);
98
+ if (imagery !== undefined) {
99
+ const treatment = stringValue(imagery.treatment);
100
+ const src = stringValue(imagery.src);
101
+ const alt = stringValue(imagery.alt);
102
+ const values = [
103
+ treatment !== undefined ? `treatment ${treatment}` : undefined,
104
+ src !== undefined ? `src ${src}` : undefined,
105
+ alt !== undefined ? `alt ${alt}` : undefined,
106
+ ].filter((value) => value !== undefined);
107
+ if (values.length > 0)
108
+ lines.push(`Imagery: ${values.join(", ")}.`);
109
+ }
110
+ const motion = objectValue(canonical.motion);
111
+ if (motion !== undefined) {
112
+ const values = ["pace", "duration"]
113
+ .map((key) => [key, stringValue(motion[key])])
114
+ .filter((entry) => entry[1] !== undefined)
115
+ .map(([key, value]) => `${key} ${value}`);
116
+ if (values.length > 0)
117
+ lines.push(`Motion: ${values.join(", ")}.`);
118
+ }
119
+ return lines;
120
+ }
121
+ function packCounts(pack) {
122
+ return {
123
+ templates: Object.keys(pack.documents.templates ?? {}).length,
124
+ palettes: Object.keys(pack.documents.palettes ?? {}).length,
125
+ modifiers: Object.keys(pack.documents.modifiers ?? {}).length,
126
+ compositions: Object.keys(pack.documents.compositions ?? {}).length,
127
+ };
128
+ }
129
+ function packSummary(pack) {
130
+ const counts = packCounts(pack);
131
+ return [
132
+ `Pack id: ${pack.id}`,
133
+ `Entrypoint: ${pack.entrypoint.kind}:${pack.entrypoint.id}`,
134
+ `Documents: ${counts.templates} templates, ${counts.palettes} palettes, ${counts.modifiers} modifiers, ${counts.compositions} compositions.`,
135
+ ];
136
+ }
137
+ function importPlanSummary(plan) {
138
+ const actionCounts = plan.operations.reduce((acc, op) => {
139
+ acc[op.action] = (acc[op.action] ?? 0) + 1;
140
+ return acc;
141
+ }, {});
142
+ const actions = Object.entries(actionCounts)
143
+ .sort(([a], [b]) => a.localeCompare(b))
144
+ .map(([action, count]) => `${count} ${action}`)
145
+ .join(", ");
146
+ const renamed = plan.operations
147
+ .filter((op) => op.action === "rename")
148
+ .map((op) => `${op.kind}:${op.original_id} -> ${op.id}`);
149
+ return [
150
+ `Pack id: ${plan.pack_id}`,
151
+ `Entrypoint: ${plan.entrypoint.kind}:${plan.entrypoint.id}`,
152
+ `Installed entrypoint: ${plan.installed_entrypoint.kind}:${plan.installed_entrypoint.id}`,
153
+ `Operations: ${plan.operations.length}${actions.length > 0 ? ` (${actions})` : ""}.`,
154
+ renamed.length > 0 ? `Renames:\n${renamed.map((line) => `- ${line}`).join("\n")}` : "Renames: none.",
155
+ `Diagnostics: ${plan.diagnostics.length}.`,
156
+ ];
157
+ }
158
+ function warningSummary(warnings) {
159
+ if (warnings === undefined || warnings.length === 0)
160
+ return "Warnings: none.";
161
+ const visible = warnings.slice(0, 12).map((warning) => {
162
+ const construct = typeof warning.construct === "string" ? warning.construct : "unknown";
163
+ const action = typeof warning.action === "string" ? warning.action : "warning";
164
+ return `- ${construct}: ${action}`;
165
+ });
166
+ const remaining = warnings.length - visible.length;
167
+ return `Warnings (${warnings.length}):\n${visible.join("\n")}${remaining > 0 ? `\n...and ${remaining} more warning${remaining === 1 ? "" : "s"}.` : ""}`;
168
+ }
44
169
  function validatePackPath(path) {
45
170
  if (/^https?:\/\//i.test(path)) {
46
171
  return { error: "unsafe_filename", message: "Aesthetic pack imports only accept local .mosvera.json files, not URLs." };
@@ -260,7 +385,12 @@ export function runPreviewAestheticImport(ctx, args) {
260
385
  const plan = previewAestheticPackImport(source.pack, ctx.project.registry, options);
261
386
  if (!plan.valid)
262
387
  return fail("invalid_document", "Aesthetic pack import preview found blocking diagnostics.", { plan });
263
- return ok("Previewed aesthetic pack import.", { source: source.source, path: source.path, plan });
388
+ const message = [
389
+ "Previewed aesthetic pack import.",
390
+ ...importPlanSummary(plan),
391
+ "No files were written.",
392
+ ].join("\n");
393
+ return ok(message, { source: source.source, path: source.path, plan });
264
394
  }
265
395
  export function runExportAestheticPack(ctx, args) {
266
396
  try {
@@ -275,7 +405,13 @@ export function runExportAestheticPack(ctx, args) {
275
405
  options.description = args.description;
276
406
  const pack = exportAestheticPack(args.aesthetic, ctx.project.registry, options);
277
407
  const suggested_filename = `${pack.id}.mosvera.json`;
278
- return ok(`Exported aesthetic pack "${pack.id}".`, { pack, suggested_filename });
408
+ const message = [
409
+ `Exported aesthetic pack "${pack.id}".`,
410
+ `Suggested filename: ${suggested_filename}`,
411
+ ...packSummary(pack),
412
+ jsonPreview("Pack JSON", pack, 8000),
413
+ ].join("\n");
414
+ return ok(message, { pack, suggested_filename });
279
415
  }
280
416
  catch (e) {
281
417
  return toolFail(runtimeError(e));
@@ -289,7 +425,12 @@ export function runResolveAesthetic(ctx, args) {
289
425
  if (maybeFail(resolved))
290
426
  return toolFail(resolved);
291
427
  const id = typeof args.aesthetic === "string" ? args.aesthetic : "inline";
292
- return ok(`Resolved aesthetic "${id}".`, { canonical: resolved.canonical });
428
+ const message = [
429
+ `Resolved aesthetic "${id}".`,
430
+ ...canonicalHighlights(resolved.canonical),
431
+ jsonPreview("Canonical model", resolved.canonical, 5000),
432
+ ].join("\n");
433
+ return ok(message, { canonical: resolved.canonical });
293
434
  }
294
435
  export function runCompileDesignTokens(ctx, args) {
295
436
  const resolved = resolveInput(ctx, args);
@@ -329,7 +470,12 @@ export function runCompileProviderPayload(ctx, args) {
329
470
  }
330
471
  const compiled = compile(resolved.canonical, manifest, args.criticality ?? {});
331
472
  if (compiled.status === "error") {
332
- return ok(`Provider "${args.provider}" cannot satisfy required construct "${compiled.construct}".`, {
473
+ const message = [
474
+ `Provider "${args.provider}" cannot satisfy required construct "${compiled.construct}".`,
475
+ `Error: ${compiled.error}`,
476
+ jsonPreview("Canonical model", resolved.canonical, 2500),
477
+ ].join("\n");
478
+ return ok(message, {
333
479
  status: "error",
334
480
  error: compiled.error,
335
481
  construct: compiled.construct,
@@ -337,7 +483,12 @@ export function runCompileProviderPayload(ctx, args) {
337
483
  });
338
484
  }
339
485
  if (adapter === undefined) {
340
- return ok(`Compiled provider contract for "${args.provider}".`, {
486
+ const message = [
487
+ `Compiled provider contract for "${args.provider}".`,
488
+ "No provider adapter is installed, so no payload was emitted.",
489
+ warningSummary(compiled.warnings),
490
+ ].join("\n");
491
+ return ok(message, {
341
492
  status: "compiled",
342
493
  provider: args.provider,
343
494
  warnings: compiled.warnings,
@@ -349,7 +500,13 @@ export function runCompileProviderPayload(ctx, args) {
349
500
  if (args.provider_options !== undefined)
350
501
  emitOptions.providerOptions = args.provider_options;
351
502
  const emission = adapter.emit(resolved.canonical, emitOptions);
352
- return ok(`Compiled deterministic provider payload for "${args.provider}".`, {
503
+ const message = [
504
+ `Compiled deterministic provider payload for "${args.provider}".`,
505
+ emission.prompt.length > 0 ? `Prompt: ${emission.prompt}` : "Prompt: none.",
506
+ warningSummary(emission.warnings),
507
+ jsonPreview("Payload JSON", emission.payload, 5000),
508
+ ].join("\n");
509
+ return ok(message, {
353
510
  status: "compiled",
354
511
  provider: args.provider,
355
512
  warnings: emission.warnings,
@@ -373,7 +530,12 @@ export function runDraftAesthetic(_ctx, args) {
373
530
  if (overrides !== undefined)
374
531
  options.overrides = overrides;
375
532
  const document = createComposition(args.id, args.base, options);
376
- return ok(`Drafted aesthetic "${args.id}".`, { kind: "composition", id: args.id, document });
533
+ const message = [
534
+ `Drafted aesthetic "${args.id}".`,
535
+ "This draft was not saved to the local registry. Use save_aesthetic to persist it.",
536
+ jsonPreview("Composition document", document, 4000),
537
+ ].join("\n");
538
+ return ok(message, { kind: "composition", id: args.id, document });
377
539
  }
378
540
  catch (e) {
379
541
  const failure = runtimeError(e);
@@ -396,7 +558,12 @@ export function runSaveAesthetic(ctx, args) {
396
558
  try {
397
559
  saveProjectDocument(ctx.registryDir, "composition", document);
398
560
  reloadContext(ctx);
399
- return ok(`Saved aesthetic "${args.id}".`, { kind: "composition", id: args.id, document });
561
+ const message = [
562
+ `Saved aesthetic "${args.id}" to the local registry.`,
563
+ `Registry: ${ctx.registryDir}`,
564
+ jsonPreview("Composition document", document, 4000),
565
+ ].join("\n");
566
+ return ok(message, { kind: "composition", id: args.id, document });
400
567
  }
401
568
  catch (e) {
402
569
  return toolFail(runtimeError(e));
@@ -417,11 +584,21 @@ export function runSaveRegistryDocument(ctx, args) {
417
584
  const manifest = document;
418
585
  saveCapabilityManifest(ctx.registryDir, manifest);
419
586
  reloadContext(ctx);
420
- return ok(`Saved capability manifest "${manifest.provider}".`, { kind: args.kind, id: manifest.provider, document });
587
+ const message = [
588
+ `Saved capability manifest "${manifest.provider}".`,
589
+ `Registry: ${ctx.registryDir}`,
590
+ jsonPreview("Capability manifest", document, 4000),
591
+ ].join("\n");
592
+ return ok(message, { kind: args.kind, id: manifest.provider, document });
421
593
  }
422
594
  saveProjectDocument(ctx.registryDir, args.kind, document);
423
595
  reloadContext(ctx);
424
- return ok(`Saved ${args.kind} "${String(document.id)}".`, { kind: args.kind, id: document.id, document });
596
+ const message = [
597
+ `Saved ${args.kind} "${String(document.id)}".`,
598
+ `Registry: ${ctx.registryDir}`,
599
+ jsonPreview("Registry document", document, 4000),
600
+ ].join("\n");
601
+ return ok(message, { kind: args.kind, id: document.id, document });
425
602
  }
426
603
  catch (e) {
427
604
  return toolFail(runtimeError(e));
@@ -494,7 +671,12 @@ export function runImportAestheticPack(ctx, args) {
494
671
  writeMergeStrategies(ctx.registryDir, result.strategies);
495
672
  }
496
673
  reloadContext(ctx);
497
- return ok(`Imported aesthetic pack "${result.pack.id}".`, {
674
+ const message = [
675
+ `Imported aesthetic pack "${result.pack.id}".`,
676
+ `Registry: ${ctx.registryDir}`,
677
+ ...importPlanSummary(result.plan),
678
+ ].join("\n");
679
+ return ok(message, {
498
680
  source: source.source,
499
681
  path: source.path,
500
682
  plan: result.plan,
@@ -2,7 +2,7 @@
2
2
  "manifest_version": "0.3",
3
3
  "name": "mosvera-mcp",
4
4
  "display_name": "Mosvera",
5
- "version": "0.1.6",
5
+ "version": "0.1.8",
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.6",
3
+ "version": "0.1.8",
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.6.mcpb",
45
- "mcpb:inspect": "node scripts/inspect-mcpb.mjs build/mosvera/mosvera-mcp-0.1.6.mcpb",
44
+ "mcpb:pack": "npm run mcpb:stage && mcpb pack build/mcpb/mosvera build/mosvera/mosvera-mcp-0.1.8.mcpb",
45
+ "mcpb:inspect": "node scripts/inspect-mcpb.mjs build/mosvera/mosvera-mcp-0.1.8.mcpb",
46
46
  "prepublishOnly": "npm run build && npm run typecheck && npm test",
47
47
  "start": "tsx src/server.ts",
48
48
  "test": "vitest run",
@@ -65,7 +65,7 @@
65
65
  "zod": "^3.24.1"
66
66
  },
67
67
  "optionalDependencies": {
68
- "@mosvera/provider-elevenlabs": "^0.1.2",
68
+ "@mosvera/provider-elevenlabs": "^0.1.3",
69
69
  "@mosvera/provider-firefly": "^0.1.2",
70
70
  "@mosvera/provider-google": "^0.1.2",
71
71
  "@mosvera/provider-heygen": "^0.1.1",