@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 +3 -3
- package/dist/context.d.ts +1 -1
- package/dist/context.js +1 -1
- package/dist/tools/aesthetic.js +193 -11
- package/mcpb/manifest.json +1 -1
- package/package.json +4 -4
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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)
|
package/dist/tools/aesthetic.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
package/mcpb/manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"manifest_version": "0.3",
|
|
3
3
|
"name": "mosvera-mcp",
|
|
4
4
|
"display_name": "Mosvera",
|
|
5
|
-
"version": "0.1.
|
|
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.
|
|
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.
|
|
45
|
-
"mcpb:inspect": "node scripts/inspect-mcpb.mjs build/mosvera/mosvera-mcp-0.1.
|
|
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.
|
|
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",
|