@mosvera/mcp 0.1.5 → 0.1.7
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 +46 -11
- package/dist/context.d.ts +2 -1
- package/dist/context.js +78 -7
- package/dist/server.js +3 -1
- package/dist/tools/aesthetic.d.ts +1 -0
- package/dist/tools/aesthetic.js +197 -12
- package/dist/tools/compile-generation.d.ts +1 -0
- package/dist/tools/compile-generation.js +8 -1
- package/mcpb/manifest.json +1 -1
- package/package.json +11 -3
package/README.md
CHANGED
|
@@ -9,17 +9,23 @@ 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
|
|
13
|
-
and does not store API keys or secrets. It turns
|
|
14
|
-
canonical Mosvera models, portable design tokens,
|
|
15
|
-
deterministic provider payloads
|
|
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.
|
|
22
|
-
[GitHub release](https://github.com/mosvera/mcp/releases).
|
|
27
|
+
1. Download `mosvera-mcp-0.1.7.mcpb` from the latest
|
|
28
|
+
[GitHub release](https://github.com/mosvera/mcp/releases/latest).
|
|
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
|
|
25
31
|
Extension.
|
|
@@ -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
|
|
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.
|
|
198
|
+
build/mosvera/mosvera-mcp-0.1.7.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.
|
|
3
|
+
export declare const SERVER_VERSION = "0.1.7";
|
|
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.
|
|
23
|
+
export const SERVER_VERSION = "0.1.7";
|
|
16
24
|
function flagValue(argv, name) {
|
|
17
25
|
const direct = argv.find((arg) => arg.startsWith(`${name}=`));
|
|
18
26
|
if (direct !== undefined)
|
|
@@ -110,6 +118,73 @@ function emptyProject() {
|
|
|
110
118
|
strategies: {},
|
|
111
119
|
};
|
|
112
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
|
+
}
|
|
113
188
|
function loadProjectOrEmpty(directory, validator) {
|
|
114
189
|
if (!existsSync(directory))
|
|
115
190
|
return emptyProject();
|
|
@@ -128,11 +203,7 @@ function makeContext(registryDir, project, validator, options) {
|
|
|
128
203
|
project,
|
|
129
204
|
validator,
|
|
130
205
|
baseStrategies: composeStrategies(deriveStrategies(), project.strategies),
|
|
131
|
-
adapters:
|
|
132
|
-
[openaiAdapter.id]: openaiAdapter,
|
|
133
|
-
[fluxAdapter.id]: fluxAdapter,
|
|
134
|
-
[sdxlAdapter.id]: sdxlAdapter,
|
|
135
|
-
},
|
|
206
|
+
adapters: providerAdapters(),
|
|
136
207
|
};
|
|
137
208
|
if (options.fallbackReason !== undefined)
|
|
138
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;
|
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,
|
|
@@ -345,8 +496,17 @@ export function runCompileProviderPayload(ctx, args) {
|
|
|
345
496
|
});
|
|
346
497
|
}
|
|
347
498
|
try {
|
|
348
|
-
const
|
|
349
|
-
|
|
499
|
+
const emitOptions = { criticality: args.criticality ?? {} };
|
|
500
|
+
if (args.provider_options !== undefined)
|
|
501
|
+
emitOptions.providerOptions = args.provider_options;
|
|
502
|
+
const emission = adapter.emit(resolved.canonical, emitOptions);
|
|
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, {
|
|
350
510
|
status: "compiled",
|
|
351
511
|
provider: args.provider,
|
|
352
512
|
warnings: emission.warnings,
|
|
@@ -370,7 +530,12 @@ export function runDraftAesthetic(_ctx, args) {
|
|
|
370
530
|
if (overrides !== undefined)
|
|
371
531
|
options.overrides = overrides;
|
|
372
532
|
const document = createComposition(args.id, args.base, options);
|
|
373
|
-
|
|
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 });
|
|
374
539
|
}
|
|
375
540
|
catch (e) {
|
|
376
541
|
const failure = runtimeError(e);
|
|
@@ -393,7 +558,12 @@ export function runSaveAesthetic(ctx, args) {
|
|
|
393
558
|
try {
|
|
394
559
|
saveProjectDocument(ctx.registryDir, "composition", document);
|
|
395
560
|
reloadContext(ctx);
|
|
396
|
-
|
|
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 });
|
|
397
567
|
}
|
|
398
568
|
catch (e) {
|
|
399
569
|
return toolFail(runtimeError(e));
|
|
@@ -414,11 +584,21 @@ export function runSaveRegistryDocument(ctx, args) {
|
|
|
414
584
|
const manifest = document;
|
|
415
585
|
saveCapabilityManifest(ctx.registryDir, manifest);
|
|
416
586
|
reloadContext(ctx);
|
|
417
|
-
|
|
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 });
|
|
418
593
|
}
|
|
419
594
|
saveProjectDocument(ctx.registryDir, args.kind, document);
|
|
420
595
|
reloadContext(ctx);
|
|
421
|
-
|
|
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 });
|
|
422
602
|
}
|
|
423
603
|
catch (e) {
|
|
424
604
|
return toolFail(runtimeError(e));
|
|
@@ -491,7 +671,12 @@ export function runImportAestheticPack(ctx, args) {
|
|
|
491
671
|
writeMergeStrategies(ctx.registryDir, result.strategies);
|
|
492
672
|
}
|
|
493
673
|
reloadContext(ctx);
|
|
494
|
-
|
|
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, {
|
|
495
680
|
source: source.source,
|
|
496
681
|
path: source.path,
|
|
497
682
|
plan: result.plan,
|
|
@@ -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
|
-
|
|
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) {
|
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.7",
|
|
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.7",
|
|
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.7.mcpb",
|
|
45
|
+
"mcpb:inspect": "node scripts/inspect-mcpb.mjs build/mosvera/mosvera-mcp-0.1.7.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",
|